diff --git a/src/addresses.py b/src/addresses.py index 560849df..abeeeb49 100644 --- a/src/addresses.py +++ b/src/addresses.py @@ -20,6 +20,8 @@ def encodeBase58(num, alphabet=ALPHABET): num: The number to encode alphabet: The alphabet to use for encoding """ + if num < 0: + return None if num == 0: return alphabet[0] arr = [] diff --git a/src/protocol.py b/src/protocol.py index 2868d828..1934d9cc 100644 --- a/src/protocol.py +++ b/src/protocol.py @@ -284,7 +284,7 @@ def isProofOfWorkSufficient( # Packet creation -def CreatePacket(command, payload=''): +def CreatePacket(command, payload=b''): """Construct and return a packet""" payload_length = len(payload) checksum = hashlib.sha512(payload).digest()[0:4] @@ -302,7 +302,7 @@ def assembleVersionMessage( Construct the payload of a version message, return the resulting bytes of running `CreatePacket` on it """ - payload = '' + payload = b'' payload += pack('>L', 3) # protocol version. # bitflags of the services I offer. payload += pack( @@ -337,7 +337,7 @@ def assembleVersionMessage( ) # = 127.0.0.1. This will be ignored by the remote host. # The actual remote connected IP will be used. - payload += '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack( + payload += b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + pack( '>L', 2130706433) # we have a separate extPort and incoming over clearnet # or outgoing through clearnet @@ -359,7 +359,7 @@ def assembleVersionMessage( payload += nodeid[0:8] else: payload += eightBytesOfRandomDataUsedToDetectConnectionsToSelf - userAgent = '/PyBitmessage:' + softwareVersion + '/' + userAgent = ('/PyBitmessage:%s/' % softwareVersion).encode('utf-8') payload += encodeVarint(len(userAgent)) payload += userAgent @@ -373,7 +373,7 @@ def assembleVersionMessage( if count >= 160000: break - return CreatePacket('version', payload) + return CreatePacket(b'version', payload) def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''): @@ -387,7 +387,7 @@ def assembleErrorMessage(fatal=0, banTime=0, inventoryVector='', errorText=''): payload += inventoryVector payload += encodeVarint(len(errorText)) payload += errorText - return CreatePacket('error', payload) + return CreatePacket(b'error', payload) # Packet decoding diff --git a/src/tests/samples.py b/src/tests/samples.py index c6cef927..17ec03bd 100644 --- a/src/tests/samples.py +++ b/src/tests/samples.py @@ -3,6 +3,8 @@ from binascii import unhexlify +magic = 0xE9BEB4D9 + # These keys are from addresses test script sample_pubsigningkey = unhexlify( '044a367f049ec16cb6b6118eb734a9962d10b8db59c890cd08f210c43ff08bdf09d' @@ -14,6 +16,7 @@ sample_privsigningkey = \ b'93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665' sample_privencryptionkey = \ b'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a' + sample_ripe = b'003cd097eb7f35c87b5dc8b4538c22cb55312a9f' # stream: 1, version: 2 sample_address = 'BM-onkVu1KKL2UaUss5Upg9vXmqd3esTmV79' @@ -27,5 +30,8 @@ sample_point = ( sample_seed = 'TIGER, tiger, burning bright. In the forests of the night' # Deterministic addresses with stream 1 and versions 3, 4 +sample_deterministic_ripe = b'00cfb69416ae76f68a81c459de4e13460c7d17eb' sample_deterministic_addr3 = 'BM-2DBPTgeSawWYZceFD69AbDT5q4iUWtj1ZN' sample_deterministic_addr4 = 'BM-2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK' +sample_daddr3_512 = 18875720106589866286514488037355423395410802084648916523381 +sample_daddr4_512 = 25152821841976547050350277460563089811513157529113201589004 diff --git a/src/tests/test_addresses.py b/src/tests/test_addresses.py index e37e2854..8f9c283d 100644 --- a/src/tests/test_addresses.py +++ b/src/tests/test_addresses.py @@ -4,7 +4,13 @@ from binascii import unhexlify from pybitmessage import addresses -from .samples import sample_address, sample_ripe +from .samples import ( + sample_address, sample_daddr3_512, sample_daddr4_512, + sample_deterministic_addr4, sample_deterministic_addr3, + sample_deterministic_ripe, sample_ripe) + +sample_addr3 = sample_deterministic_addr3.split('-')[1] +sample_addr4 = sample_deterministic_addr4.split('-')[1] class TestAddresses(unittest.TestCase): @@ -17,19 +23,39 @@ class TestAddresses(unittest.TestCase): ('success', 2, 1, unhexlify(sample_ripe))) status, version, stream, ripe1 = addresses.decodeAddress( - '2cWzSnwjJ7yRP3nLEWUV5LisTZyREWSzUK') + sample_deterministic_addr4) self.assertEqual(status, 'success') self.assertEqual(stream, 1) self.assertEqual(version, 4) - status, version, stream, ripe2 = addresses.decodeAddress( - '2DBPTgeSawWYZceFD69AbDT5q4iUWtj1ZN') + status, version, stream, ripe2 = addresses.decodeAddress(sample_addr3) self.assertEqual(status, 'success') self.assertEqual(stream, 1) self.assertEqual(version, 3) self.assertEqual(ripe1, ripe2) + self.assertEqual(ripe1, unhexlify(sample_deterministic_ripe)) def test_encode(self): """Encode sample ripe and compare the result to sample address""" self.assertEqual( sample_address, addresses.encodeAddress(2, 1, unhexlify(sample_ripe))) + ripe = unhexlify(sample_deterministic_ripe) + self.assertEqual( + addresses.encodeAddress(3, 1, ripe), + 'BM-%s' % addresses.encodeBase58(sample_daddr3_512)) + + def test_base58(self): + """Check Base58 encoding and decoding""" + self.assertEqual(addresses.decodeBase58('1'), 0) + self.assertEqual(addresses.decodeBase58('!'), 0) + self.assertEqual( + addresses.decodeBase58(sample_addr4), sample_daddr4_512) + self.assertEqual( + addresses.decodeBase58(sample_addr3), sample_daddr3_512) + + self.assertEqual(addresses.encodeBase58(0), '1') + self.assertEqual(addresses.encodeBase58(-1), None) + self.assertEqual( + sample_addr4, addresses.encodeBase58(sample_daddr4_512)) + self.assertEqual( + sample_addr3, addresses.encodeBase58(sample_daddr3_512)) diff --git a/src/tests/test_packets.py b/src/tests/test_packets.py new file mode 100644 index 00000000..65ee0d44 --- /dev/null +++ b/src/tests/test_packets.py @@ -0,0 +1,68 @@ + +import unittest +from binascii import unhexlify +from struct import pack + +from pybitmessage import addresses, protocol + +from .samples import magic + + +class TestSerialize(unittest.TestCase): + """Test serializing and deserializing packet data""" + + def test_varint(self): + """Test varint encoding and decoding""" + data = addresses.encodeVarint(0) + self.assertEqual(data, b'\x00') + data = addresses.encodeVarint(42) + self.assertEqual(data, b'*') + data = addresses.encodeVarint(252) + self.assertEqual(data, unhexlify('fc')) + data = addresses.encodeVarint(253) + self.assertEqual(data, unhexlify('fd00fd')) + data = addresses.encodeVarint(100500) + self.assertEqual(data, unhexlify('fe00018894')) + data = addresses.encodeVarint(65535) + self.assertEqual(data, unhexlify('fdffff')) + data = addresses.encodeVarint(4294967295) + self.assertEqual(data, unhexlify('feffffffff')) + data = addresses.encodeVarint(4294967296) + self.assertEqual(data, unhexlify('ff0000000100000000')) + data = addresses.encodeVarint(18446744073709551615) + self.assertEqual(data, unhexlify('ffffffffffffffffff')) + + with self.assertRaises(addresses.varintEncodeError): + addresses.encodeVarint(18446744073709551616) + + value, length = addresses.decodeVarint(b'\xfeaddr') + self.assertEqual(value, protocol.OBJECT_ADDR) + self.assertEqual(length, 5) + value, length = addresses.decodeVarint(b'\xfe\x00tor') + self.assertEqual(value, protocol.OBJECT_ONIONPEER) + self.assertEqual(length, 5) + + def test_packet(self): + """Check the packet created by protocol.CreatePacket()""" + head = unhexlify(b'%x' % magic) + self.assertEqual( + protocol.CreatePacket(b'ping')[:len(head)], head) + + def test_encodehost(self): + """Check the result of protocol.encodeHost()""" + self.assertEqual( + protocol.encodeHost('127.0.0.1'), + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF' + + pack('>L', 2130706433)) + self.assertEqual( + protocol.encodeHost('191.168.1.1'), + unhexlify('00000000000000000000ffffbfa80101')) + self.assertEqual( + protocol.encodeHost('1.1.1.1'), + unhexlify('00000000000000000000ffff01010101')) + self.assertEqual( + protocol.encodeHost('0102:0304:0506:0708:090A:0B0C:0D0E:0F10'), + unhexlify('0102030405060708090a0b0c0d0e0f10')) + self.assertEqual( + protocol.encodeHost('quzwelsuziwqgpt2.onion'), + unhexlify('fd87d87eeb438533622e54ca2d033e7a'))