Merge pull request #7562 from SomberNight/202111_blockchain_target
blockchain.py: bugfixes re `bits_to_target` and `target_to_bits`
This commit is contained in:
@@ -37,7 +37,9 @@ from .logging import get_logger, Logger
|
|||||||
_logger = get_logger(__name__)
|
_logger = get_logger(__name__)
|
||||||
|
|
||||||
HEADER_SIZE = 80 # bytes
|
HEADER_SIZE = 80 # bytes
|
||||||
MAX_TARGET = 0x00000000FFFF0000000000000000000000000000000000000000000000000000
|
|
||||||
|
# see https://github.com/bitcoin/bitcoin/blob/feedb9c84e72e4fff489810a2bbeec09bcda5763/src/chainparams.cpp#L76
|
||||||
|
MAX_TARGET = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff # compact: 0x1d00ffff
|
||||||
|
|
||||||
|
|
||||||
class MissingHeader(Exception):
|
class MissingHeader(Exception):
|
||||||
@@ -540,20 +542,37 @@ class Blockchain(Logger):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def bits_to_target(cls, bits: int) -> int:
|
def bits_to_target(cls, bits: int) -> int:
|
||||||
|
# arith_uint256::SetCompact in Bitcoin Core
|
||||||
|
if not (0 <= bits < (1 << 32)):
|
||||||
|
raise Exception(f"bits should be uint32. got {bits!r}")
|
||||||
bitsN = (bits >> 24) & 0xff
|
bitsN = (bits >> 24) & 0xff
|
||||||
if not (0x03 <= bitsN <= 0x1d):
|
bitsBase = bits & 0x7fffff
|
||||||
raise Exception("First part of bits should be in [0x03, 0x1d]")
|
if bitsN <= 3:
|
||||||
bitsBase = bits & 0xffffff
|
target = bitsBase >> (8 * (3-bitsN))
|
||||||
if not (0x8000 <= bitsBase <= 0x7fffff):
|
else:
|
||||||
raise Exception("Second part of bits should be in [0x8000, 0x7fffff]")
|
target = bitsBase << (8 * (bitsN-3))
|
||||||
return bitsBase << (8 * (bitsN-3))
|
if target != 0 and bits & 0x800000 != 0:
|
||||||
|
# Bit number 24 (0x800000) represents the sign of N
|
||||||
|
raise Exception("target cannot be negative")
|
||||||
|
if (target != 0 and
|
||||||
|
(bitsN > 34 or
|
||||||
|
(bitsN > 33 and bitsBase > 0xff) or
|
||||||
|
(bitsN > 32 and bitsBase > 0xffff))):
|
||||||
|
raise Exception("target has overflown")
|
||||||
|
return target
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def target_to_bits(cls, target: int) -> int:
|
def target_to_bits(cls, target: int) -> int:
|
||||||
c = ("%064x" % target)[2:]
|
# arith_uint256::GetCompact in Bitcoin Core
|
||||||
while c[:2] == '00' and len(c) > 6:
|
# see https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/arith_uint256.cpp#L223
|
||||||
c = c[2:]
|
c = target.to_bytes(length=32, byteorder='big')
|
||||||
bitsN, bitsBase = len(c) // 2, int.from_bytes(bfh(c[:6]), byteorder='big')
|
bitsN = len(c)
|
||||||
|
while bitsN > 0 and c[0] == 0:
|
||||||
|
c = c[1:]
|
||||||
|
bitsN -= 1
|
||||||
|
if len(c) < 3:
|
||||||
|
c += b'\x00'
|
||||||
|
bitsBase = int.from_bytes(c[:3], byteorder='big')
|
||||||
if bitsBase >= 0x800000:
|
if bitsBase >= 0x800000:
|
||||||
bitsN += 1
|
bitsN += 1
|
||||||
bitsBase >>= 8
|
bitsBase >>= 8
|
||||||
|
|||||||
@@ -384,6 +384,47 @@ class TestBlockchain(ElectrumTestCase):
|
|||||||
self.assertEqual([chain_u], self.get_chains_that_contain_header_helper(self.HEADERS['O']))
|
self.assertEqual([chain_u], self.get_chains_that_contain_header_helper(self.HEADERS['O']))
|
||||||
self.assertEqual([chain_z, chain_l], self.get_chains_that_contain_header_helper(self.HEADERS['I']))
|
self.assertEqual([chain_z, chain_l], self.get_chains_that_contain_header_helper(self.HEADERS['I']))
|
||||||
|
|
||||||
|
def test_target_to_bits(self):
|
||||||
|
# https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/arith_uint256.h#L269
|
||||||
|
self.assertEqual(0x05123456, Blockchain.target_to_bits(0x1234560000))
|
||||||
|
self.assertEqual(0x0600c0de, Blockchain.target_to_bits(0xc0de000000))
|
||||||
|
|
||||||
|
# tests from https://github.com/bitcoin/bitcoin/blob/a7d17daa5cd8bf6398d5f8d7e77290009407d6ea/src/test/arith_uint256_tests.cpp#L411
|
||||||
|
tuples = (
|
||||||
|
(0, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x00123456, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x01003456, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x02000056, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x03000000, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x04000000, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x00923456, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x01803456, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x02800056, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x03800000, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x04800000, 0x0000000000000000000000000000000000000000000000000000000000000000, 0),
|
||||||
|
(0x01123456, 0x0000000000000000000000000000000000000000000000000000000000000012, 0x01120000),
|
||||||
|
(0x02123456, 0x0000000000000000000000000000000000000000000000000000000000001234, 0x02123400),
|
||||||
|
(0x03123456, 0x0000000000000000000000000000000000000000000000000000000000123456, 0x03123456),
|
||||||
|
(0x04123456, 0x0000000000000000000000000000000000000000000000000000000012345600, 0x04123456),
|
||||||
|
(0x05009234, 0x0000000000000000000000000000000000000000000000000000000092340000, 0x05009234),
|
||||||
|
(0x20123456, 0x1234560000000000000000000000000000000000000000000000000000000000, 0x20123456),
|
||||||
|
)
|
||||||
|
for nbits1, target, nbits2 in tuples:
|
||||||
|
with self.subTest(original_compact_nbits=nbits1.to_bytes(length=4, byteorder="big").hex()):
|
||||||
|
num = Blockchain.bits_to_target(nbits1)
|
||||||
|
self.assertEqual(target, num)
|
||||||
|
self.assertEqual(nbits2, Blockchain.target_to_bits(num))
|
||||||
|
|
||||||
|
# Make sure that we don't generate compacts with the 0x00800000 bit set
|
||||||
|
self.assertEqual(0x02008000, Blockchain.target_to_bits(0x80))
|
||||||
|
|
||||||
|
with self.assertRaises(Exception): # target cannot be negative
|
||||||
|
Blockchain.bits_to_target(0x01fedcba)
|
||||||
|
with self.assertRaises(Exception): # target cannot be negative
|
||||||
|
Blockchain.bits_to_target(0x04923456)
|
||||||
|
with self.assertRaises(Exception): # overflow
|
||||||
|
Blockchain.bits_to_target(0xff123456)
|
||||||
|
|
||||||
|
|
||||||
class TestVerifyHeader(ElectrumTestCase):
|
class TestVerifyHeader(ElectrumTestCase):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user