Skip to content

Commit 8866983

Browse files
author
Ross Nicoll
committed
Add support for encoding to compact
Add support for encoding uint256 values to compact form. Add unit tests for compact for decode/encode Add code to handle very small compact values (these are unlikely in real data, however should be handled anyway)
1 parent d721c36 commit 8866983

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

bitcoin/core/serialize.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,9 +311,30 @@ def uint256_from_compact(c):
311311
Used for the nBits compact encoding of the target in the block header.
312312
"""
313313
nbytes = (c >> 24) & 0xFF
314-
v = (c & 0xFFFFFF) << (8 * (nbytes - 3))
314+
if nbytes <= 3:
315+
v = (c & 0xFFFFFF) >> 8 * (3 - nbytes)
316+
else:
317+
v = (c & 0xFFFFFF) << (8 * (nbytes - 3))
315318
return v
316319

320+
def compact_from_uint256(v):
321+
"""Convert uint256 to compact encoding
322+
"""
323+
nbytes = (v.bit_length() + 7) >> 3
324+
compact = 0
325+
if nbytes <= 3:
326+
compact = (v & 0xFFFFFF) << 8 * (3 - nbytes)
327+
else:
328+
compact = v >> 8 * (nbytes - 3)
329+
compact = compact & 0xFFFFFF
330+
331+
# If the sign bit (0x00800000) is set, divide the mantissa by 256 and
332+
# increase the exponent to get an encoding without it set.
333+
if compact & 0x00800000:
334+
compact >>= 8
335+
nbytes += 1
336+
337+
return compact | nbytes << 24
317338

318339
def uint256_to_shortstr(u):
319340
s = "%064x" % (u,)
@@ -339,5 +360,6 @@ def uint256_to_shortstr(u):
339360
'VarStringSerializer',
340361
'uint256_from_str',
341362
'uint256_from_compact',
363+
'compact_from_uint256',
342364
'uint256_to_shortstr',
343365
)

bitcoin/tests/test_serialize.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,30 @@ def T(serialized, ex_cls=SerializationTruncationError):
107107
T(b'01')
108108
T(b'0200')
109109
T(b'ff00000000000000ff11223344', SerializationError) # > max_size
110+
111+
class Test_Compact(unittest.TestCase):
112+
def test_from_compact_zero(self):
113+
self.assertEqual(uint256_from_compact(0x00123456), 0)
114+
self.assertEqual(uint256_from_compact(0x01003456), 0)
115+
self.assertEqual(uint256_from_compact(0x02000056), 0)
116+
self.assertEqual(uint256_from_compact(0x03000000), 0)
117+
self.assertEqual(uint256_from_compact(0x04000000), 0)
118+
self.assertEqual(uint256_from_compact(0x00923456), 0)
119+
def test_from_compact_negative_zero(self):
120+
# Negative bit isn't supported yet
121+
# self.assertEqual(uint256_from_compact(0x01803456), 0)
122+
# self.assertEqual(uint256_from_compact(0x02800056), 0)
123+
# self.assertEqual(uint256_from_compact(0x03800000), 0)
124+
# self.assertEqual(uint256_from_compact(0x04800000), 0)
125+
return
126+
127+
def test_twelve(self):
128+
self.assertEqual(uint256_from_compact(0x01123456), 0x0012)
129+
self.assertEqual(compact_from_uint256(0x0012), 0x01120000)
130+
131+
def test_from_uint256(self):
132+
self.assertEqual(compact_from_uint256(0x1234), 0x02123400)
133+
self.assertEqual(compact_from_uint256(0x123456), 0x03123456)
134+
self.assertEqual(compact_from_uint256(0x12345600), 0x04123456)
135+
self.assertEqual(compact_from_uint256(0x92340000), 0x05009234)
136+
self.assertEqual(compact_from_uint256(0x1234560000000000000000000000000000000000000000000000000000000000), 0x20123456)

0 commit comments

Comments
 (0)