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