Skip to content

Commit 0e4e1b2

Browse files
committed
Rework message serialization
Add new class 'MsgSerializable' subclassing the already existing 'Serializable' class. Remove messagemap, the message strings are now a static attribute of every message class. Remove message_read, replaced by the classmethod MsgSerializeable.stream_deserialize() Remove message_to_str, replaced by MsgSerializeable.to_bytes() Make classes in bitcoin.net subclasses of Serializable Add tests for messages and net Add examples/msg-serializable.py Add test_suite to setup.py
1 parent ec7d1b6 commit 0e4e1b2

File tree

10 files changed

+657
-341
lines changed

10 files changed

+657
-341
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33

44
local*.cfg
55

6+
build/
7+
python_bitcoinlib.egg-info/

bitcoin/core/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
MAX_BLOCK_SIZE = 1000000
2424
MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50
2525

26+
BIP0031_VERSION = 60000
2627
PROTO_VERSION = 60002
2728
MIN_PROTO_VERSION = 209
2829

30+
CADDR_TIME_VERSION = 31402
31+
2932
def MoneyRange(nValue):
3033
return 0<= nValue <= MAX_MONEY
3134

@@ -69,7 +72,7 @@ def b2lx(b):
6972

7073
def str_money_value(value):
7174
"""Convert an integer money value to a fixed point string"""
72-
r = '%i.%08i' % (value // 100000000, value % 100000000)
75+
r = '%i.%08i' % (value // COIN, value % COIN)
7376
r = r.rstrip('0')
7477
if r[-1] == '.':
7578
r += '0'
@@ -224,9 +227,9 @@ def __repr__(self):
224227

225228
class CBlockHeader(Serializable):
226229
"""A block header"""
227-
__slots__ = ['nVersion', 'hashPrevBlock', 'hashMerkleRoot', 'nTime', 'nBits', 'nBits']
230+
__slots__ = ['nVersion', 'hashPrevBlock', 'hashMerkleRoot', 'nTime', 'nBits', 'nNonce']
228231

229-
def __init__(self, nVersion=2, hashPrevBlock=None, hashMerkleRoot=None, nTime=None, nBits=None, nNonce=None):
232+
def __init__(self, nVersion=2, hashPrevBlock=num_null_bytes(32), hashMerkleRoot=num_null_bytes(32), nTime=0, nBits=0, nNonce=0):
230233
self.nVersion = nVersion
231234
assert len(hashPrevBlock) == 32
232235
self.hashPrevBlock = hashPrevBlock
@@ -279,7 +282,7 @@ class CBlock(CBlockHeader):
279282
"""A block including all transactions in it"""
280283
__slots__ = ['vtx']
281284

282-
def __init__(self, nVersion=2, hashPrevBlock=None, hashMerkleRoot=None, nTime=None, nBits=None, nNonce=None, vtx=None):
285+
def __init__(self, nVersion=2, hashPrevBlock=num_null_bytes(32), hashMerkleRoot=num_null_bytes(32), nTime=0, nBits=0, nNonce=0, vtx=None):
283286
super(CBlock, self).__init__(nVersion, hashPrevBlock, hashMerkleRoot, nTime, nBits, nNonce)
284287
if not vtx:
285288
vtx = []

bitcoin/core/serialize.py

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,30 @@
1818

1919
# Py3 compatibility
2020
import sys
21-
bchr = chr
22-
bord = ord
21+
2322
if sys.version > '3':
2423
bchr = lambda x: bytes([x])
2524
bord = lambda x: x[0]
2625
from io import BytesIO
2726
else:
27+
bchr = chr
28+
bord = ord
2829
from cStringIO import StringIO as BytesIO
2930

3031
MAX_SIZE = 0x02000000
3132

33+
3234
class SerializationError(Exception):
3335
"""Base class for serialization errors"""
3436

37+
3538
class SerializationTruncationError(Exception):
3639
"""Serialized data was truncated
3740
3841
Thrown by deserialize() and stream_deserialize()
3942
"""
4043

44+
4145
def ser_read(f, n):
4246
"""Read from a stream safely
4347
@@ -49,9 +53,17 @@ def ser_read(f, n):
4953
raise SerializationError('Asked to read 0x%x bytes; MAX_SIZE exceeded')
5054
r = f.read(n)
5155
if len(r) < n:
52-
raise SerializationTruncationError()
56+
raise SerializationTruncationError('Asked to read %i bytes, but only got %i' % (n, len(r)))
5357
return r
5458

59+
60+
def num_null_bytes(num):
61+
res = b''
62+
for i in range(num):
63+
res += b'\x00'
64+
return res
65+
66+
5567
class Serializable(object):
5668
"""Base class for serializable objects"""
5769
def stream_serialize(self, f):
@@ -86,6 +98,7 @@ def __ne__(self, other):
8698
def __hash__(self):
8799
return hash(self.serialize())
88100

101+
89102
class Serializer(object):
90103
"""Base class for object serializers"""
91104
def __new__(cls):
@@ -108,6 +121,7 @@ def serialize(cls, obj):
108121
def deserialize(cls, buf):
109122
return cls.stream_deserialize(BytesIO(buf))
110123

124+
111125
class VarIntSerializer(Serializer):
112126
"""Serialization of variable length ints"""
113127
@classmethod
@@ -138,6 +152,7 @@ def stream_deserialize(cls, f):
138152
else:
139153
return struct.unpack(b'<Q', ser_read(f, 8))[0]
140154

155+
141156
class BytesSerializer(Serializer):
142157
"""Serialization of bytes instances"""
143158
@classmethod
@@ -150,6 +165,7 @@ def stream_deserialize(cls, f):
150165
l = VarIntSerializer.stream_deserialize(f)
151166
return ser_read(f, l)
152167

168+
153169
class VectorSerializer(Serializer):
154170
"""Base class for serializers of object vectors"""
155171
@classmethod
@@ -166,23 +182,55 @@ def stream_deserialize(cls, inner_cls, f):
166182
r.append(inner_cls.stream_deserialize(f))
167183
return r
168184

185+
169186
class uint256VectorSerializer(Serializer):
170187
"""Serialize vectors of uint256"""
171188
@classmethod
172-
def stream_serialize(cls, inner_cls, objs, f):
173-
VarIntSerializer.stream_serialize(len(objs), f)
174-
for obj in objs:
175-
assert len(obj) == 32
176-
f.write(obj)
189+
def stream_serialize(cls, uints, f):
190+
VarIntSerializer.stream_serialize(len(uints), f)
191+
for uint in uints:
192+
assert len(uint) == 32
193+
f.write(uint)
177194

178195
@classmethod
179-
def stream_deserialize(cls, inner_cls, f):
196+
def stream_deserialize(cls, f):
180197
n = VarIntSerializer.stream_deserialize(f)
181198
r = []
182199
for i in range(n):
183200
r.append(ser_read(f, 32))
184201
return r
185202

203+
204+
class intVectorSerialzer(Serializer):
205+
@classmethod
206+
def stream_serialize(cls, ints, f):
207+
l = len(ints)
208+
VarIntSerializer.stream_serialize(l, f)
209+
for i in ints:
210+
f.write(struct.pack(b"<i", i))
211+
212+
@classmethod
213+
def stream_deserialize(cls, f):
214+
l = VarIntSerializer.stream_deserialize(f)
215+
ints = []
216+
for i in range(l):
217+
ints.append(struct.unpack(b"<i", ser_read(f, 4)))
218+
219+
220+
class VarStringSerializer(Serializer):
221+
"""Serialize variable length strings"""
222+
@classmethod
223+
def stream_serialize(cls, s, f):
224+
l = len(s)
225+
VarIntSerializer.stream_serialize(l, f)
226+
f.write(s)
227+
228+
@classmethod
229+
def stream_deserialize(cls, f):
230+
l = VarIntSerializer.stream_deserialize(f)
231+
return ser_read(f, l)
232+
233+
186234
def uint256_from_str(s):
187235
"""Convert bytes to uint256"""
188236
r = 0
@@ -191,6 +239,7 @@ def uint256_from_str(s):
191239
r += t[i] << (i * 32)
192240
return r
193241

242+
194243
def uint256_from_compact(c):
195244
"""Convert compact encoding to uint256
196245
@@ -200,44 +249,17 @@ def uint256_from_compact(c):
200249
v = (c & 0xFFFFFF) << (8 * (nbytes - 3))
201250
return v
202251

252+
203253
def uint256_to_shortstr(u):
204254
s = "%064x" % (u,)
205255
return s[:16]
206256

207-
def deser_int_vector(f):
208-
"""Deserialize a vector of ints"""
209-
nit = struct.unpack(b"<B", f.read(1))[0]
210-
if nit == 253:
211-
nit = struct.unpack(b"<H", f.read(2))[0]
212-
elif nit == 254:
213-
nit = struct.unpack(b"<I", f.read(4))[0]
214-
elif nit == 255:
215-
nit = struct.unpack(b"<Q", f.read(8))[0]
216-
r = []
217-
for i in range(nit):
218-
t = struct.unpack(b"<i", f.read(4))[0]
219-
r.append(t)
220-
return r
221-
222-
def ser_int_vector(l):
223-
"""Serialize a vector of ints"""
224-
r = b""
225-
if len(l) < 253:
226-
r = bchr(len(l))
227-
elif len(s) < 0x10000:
228-
r = bchr(253) + struct.pack(b"<H", len(l))
229-
elif len(s) < 0x100000000:
230-
r = bchr(254) + struct.pack(b"<I", len(l))
231-
else:
232-
r = bchr(255) + struct.pack(b"<Q", len(l))
233-
for i in l:
234-
r += struct.pack(b"<i", i)
235-
return r
236257

237258
def Hash(msg):
238259
"""SHA256^2)(msg) -> bytes"""
239260
return hashlib.sha256(hashlib.sha256(msg).digest()).digest()
240261

262+
241263
def Hash160(msg):
242264
"""RIPEME160(SHA256(msg)) -> bytes"""
243265
h = hashlib.new('ripemd160')

0 commit comments

Comments
 (0)