class TestVerifyHeader(ElectrumTestCase): # Data for Garlicoin block header #100. valid_header = "01000000fb040b4b30f4f0d90b5d4819f566a156669565f73998fb37cf072c4ec5daac08fd5a56f1650756fec9f1a587f9f69e5298723bf4e296aff71670b7d471f1a31e9e55964ef0ff0f1e27010000" target = Blockchain.bits_to_target(0x1e0ffff0) prev_hash = "08acdac54e2c07cf37fb9839f765956656a166f519485d0bd9f0f4304b0b04fb" def setUp(self): super().setUp() self.header = deserialize_header(bfh(self.valid_header), 100) def test_valid_header(self): Blockchain.verify_header(self.header, self.prev_hash, self.target) def test_expected_hash_mismatch(self): with self.assertRaises(Exception): Blockchain.verify_header(self.header, self.prev_hash, self.target, expected_header_hash="foo") def test_prev_hash_mismatch(self): with self.assertRaises(Exception): Blockchain.verify_header(self.header, "foo", self.target) def test_target_mismatch(self): with self.assertRaises(Exception): other_target = Blockchain.bits_to_target(0x1d00eeee) Blockchain.verify_header(self.header, self.prev_hash, other_target) def test_insufficient_pow(self): with self.assertRaises(Exception): self.header["nonce"] = 42 Blockchain.verify_header(self.header, self.prev_hash, self.target)
def test_parents_after_forking(self): blockchain.blockchains[constants.net.GENESIS] = chain_u = Blockchain( config=self.config, forkpoint=0, parent=None, forkpoint_hash=constants.net.GENESIS, prev_hash=None) open(chain_u.path(), 'w+').close() self._append_header(chain_u, self.HEADERS['A']) self._append_header(chain_u, self.HEADERS['B']) self._append_header(chain_u, self.HEADERS['C']) self._append_header(chain_u, self.HEADERS['D']) self._append_header(chain_u, self.HEADERS['E']) self._append_header(chain_u, self.HEADERS['F']) self._append_header(chain_u, self.HEADERS['O']) self._append_header(chain_u, self.HEADERS['P']) self._append_header(chain_u, self.HEADERS['Q']) self.assertEqual(None, chain_u.parent) chain_l = chain_u.fork(self.HEADERS['G']) self._append_header(chain_l, self.HEADERS['H']) self._append_header(chain_l, self.HEADERS['I']) self._append_header(chain_l, self.HEADERS['J']) self._append_header(chain_l, self.HEADERS['K']) self._append_header(chain_l, self.HEADERS['L']) self.assertEqual(None, chain_l.parent) self.assertEqual(chain_l, chain_u.parent) chain_z = chain_l.fork(self.HEADERS['M']) self._append_header(chain_z, self.HEADERS['N']) self._append_header(chain_z, self.HEADERS['X']) self._append_header(chain_z, self.HEADERS['Y']) self._append_header(chain_z, self.HEADERS['Z']) self.assertEqual(chain_z, chain_u.parent) self.assertEqual(chain_z, chain_l.parent) self.assertEqual(None, chain_z.parent) self._append_header(chain_u, self.HEADERS['R']) self._append_header(chain_u, self.HEADERS['S']) self._append_header(chain_u, self.HEADERS['T']) self._append_header(chain_u, self.HEADERS['U']) self.assertEqual(chain_z, chain_u.parent) self.assertEqual(chain_z, chain_l.parent) self.assertEqual(None, chain_z.parent)
def test_insufficient_pow(self): with self.assertRaises(Exception): self.header["nonce"] = 42 Blockchain.verify_header(self.header, self.prev_hash, self.target)
def test_target_mismatch(self): with self.assertRaises(Exception): other_target = Blockchain.bits_to_target(0x1d00eeee) Blockchain.verify_header(self.header, self.prev_hash, other_target)
def test_prev_hash_mismatch(self): with self.assertRaises(Exception): Blockchain.verify_header(self.header, "foo", self.target)
def test_expected_hash_mismatch(self): with self.assertRaises(Exception): Blockchain.verify_header(self.header, self.prev_hash, self.target, expected_header_hash="foo")
def test_valid_header(self): Blockchain.verify_header(self.header, self.prev_hash, self.target)
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)
def test_get_chains_that_contain_header(self): blockchain.blockchains[constants.net.GENESIS] = chain_u = Blockchain( config=self.config, forkpoint=0, parent=None, forkpoint_hash=constants.net.GENESIS, prev_hash=None) open(chain_u.path(), 'w+').close() self._append_header(chain_u, self.HEADERS['A']) self._append_header(chain_u, self.HEADERS['B']) self._append_header(chain_u, self.HEADERS['C']) self._append_header(chain_u, self.HEADERS['D']) self._append_header(chain_u, self.HEADERS['E']) self._append_header(chain_u, self.HEADERS['F']) self._append_header(chain_u, self.HEADERS['O']) self._append_header(chain_u, self.HEADERS['P']) self._append_header(chain_u, self.HEADERS['Q']) chain_l = chain_u.fork(self.HEADERS['G']) self._append_header(chain_l, self.HEADERS['H']) self._append_header(chain_l, self.HEADERS['I']) self._append_header(chain_l, self.HEADERS['J']) self._append_header(chain_l, self.HEADERS['K']) self._append_header(chain_l, self.HEADERS['L']) chain_z = chain_l.fork(self.HEADERS['M']) self.assertEqual([chain_l, chain_z, chain_u], self.get_chains_that_contain_header_helper( self.HEADERS['A'])) self.assertEqual([chain_l, chain_z, chain_u], self.get_chains_that_contain_header_helper( self.HEADERS['C'])) self.assertEqual([chain_l, chain_z, chain_u], self.get_chains_that_contain_header_helper( self.HEADERS['F'])) self.assertEqual([chain_l, chain_z], self.get_chains_that_contain_header_helper( self.HEADERS['G'])) self.assertEqual([chain_l, chain_z], self.get_chains_that_contain_header_helper( self.HEADERS['I'])) self.assertEqual([chain_z], self.get_chains_that_contain_header_helper( self.HEADERS['M'])) self.assertEqual([chain_l], self.get_chains_that_contain_header_helper( self.HEADERS['K'])) self._append_header(chain_z, self.HEADERS['N']) self._append_header(chain_z, self.HEADERS['X']) self._append_header(chain_z, self.HEADERS['Y']) self._append_header(chain_z, self.HEADERS['Z']) self.assertEqual([chain_z, chain_l, chain_u], self.get_chains_that_contain_header_helper( self.HEADERS['A'])) self.assertEqual([chain_z, chain_l, chain_u], self.get_chains_that_contain_header_helper( self.HEADERS['C'])) self.assertEqual([chain_z, chain_l, chain_u], self.get_chains_that_contain_header_helper( self.HEADERS['F'])) 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']))
def test_doing_multiple_swaps_after_single_new_header(self): blockchain.blockchains[constants.net.GENESIS] = chain_u = Blockchain( config=self.config, forkpoint=0, parent=None, forkpoint_hash=constants.net.GENESIS, prev_hash=None) open(chain_u.path(), 'w+').close() self._append_header(chain_u, self.HEADERS['A']) self._append_header(chain_u, self.HEADERS['B']) self._append_header(chain_u, self.HEADERS['C']) self._append_header(chain_u, self.HEADERS['D']) self._append_header(chain_u, self.HEADERS['E']) self._append_header(chain_u, self.HEADERS['F']) self._append_header(chain_u, self.HEADERS['O']) self._append_header(chain_u, self.HEADERS['P']) self._append_header(chain_u, self.HEADERS['Q']) self._append_header(chain_u, self.HEADERS['R']) self._append_header(chain_u, self.HEADERS['S']) self.assertEqual(1, len(blockchain.blockchains)) self.assertEqual(0, len(os.listdir(os.path.join(self.data_dir, "forks")))) chain_l = chain_u.fork(self.HEADERS['G']) self._append_header(chain_l, self.HEADERS['H']) self._append_header(chain_l, self.HEADERS['I']) self._append_header(chain_l, self.HEADERS['J']) self._append_header(chain_l, self.HEADERS['K']) # now chain_u is best chain, but it's tied with chain_l self.assertEqual(2, len(blockchain.blockchains)) self.assertEqual(1, len(os.listdir(os.path.join(self.data_dir, "forks")))) chain_z = chain_l.fork(self.HEADERS['M']) self._append_header(chain_z, self.HEADERS['N']) self._append_header(chain_z, self.HEADERS['X']) self.assertEqual(3, len(blockchain.blockchains)) self.assertEqual(2, len(os.listdir(os.path.join(self.data_dir, "forks")))) # chain_z became best chain, do checks self.assertEqual(0, chain_z.forkpoint) self.assertEqual(None, chain_z.parent) self.assertEqual(constants.net.GENESIS, chain_z._forkpoint_hash) self.assertEqual(None, chain_z._prev_hash) self.assertEqual(os.path.join(self.data_dir, "blockchain_headers"), chain_z.path()) self.assertEqual(12 * 80, os.stat(chain_z.path()).st_size) self.assertEqual(9, chain_l.forkpoint) self.assertEqual(chain_z, chain_l.parent) self.assertEqual(hash_header(self.HEADERS['J']), chain_l._forkpoint_hash) self.assertEqual(hash_header(self.HEADERS['I']), chain_l._prev_hash) self.assertEqual( os.path.join( self.data_dir, "forks", "fork2_9_9673e915a8b3372bce7d577136b2208e6da927caf2324cd238b46eb9f64fa6bf_59c49e1a21bd292585b92c93927ea65369fa6a0bc9b60c8f7c17c1ed9d53e0b9" ), chain_l.path()) self.assertEqual(2 * 80, os.stat(chain_l.path()).st_size) self.assertEqual(6, chain_u.forkpoint) self.assertEqual(chain_z, chain_u.parent) self.assertEqual(hash_header(self.HEADERS['O']), chain_u._forkpoint_hash) self.assertEqual(hash_header(self.HEADERS['F']), chain_u._prev_hash) self.assertEqual( os.path.join( self.data_dir, "forks", "fork2_6_90791a08906278ce5c9581e0f8e54b729c54049f77d7397ae6a4d41229e2cb9a_a05a28a6464817d8b020a3016897b9a52239c1e7757fec9c32121854f1a0cc18" ), chain_u.path()) self.assertEqual(5 * 80, os.stat(chain_u.path()).st_size) self.assertEqual(constants.net.GENESIS, chain_z.get_hash(0)) self.assertEqual(hash_header(self.HEADERS['F']), chain_z.get_hash(5)) self.assertEqual(hash_header(self.HEADERS['G']), chain_z.get_hash(6)) self.assertEqual(hash_header(self.HEADERS['I']), chain_z.get_hash(8)) self.assertEqual(hash_header(self.HEADERS['M']), chain_z.get_hash(9)) self.assertEqual(hash_header(self.HEADERS['X']), chain_z.get_hash(11)) for b in (chain_u, chain_l, chain_z): self.assertTrue( all([ b.can_connect(b.read_header(i), False) for i in range(b.height()) ]))
def test_get_height_of_last_common_block_with_chain(self): blockchain.blockchains[constants.net.GENESIS] = chain_u = Blockchain( config=self.config, forkpoint=0, parent=None, forkpoint_hash=constants.net.GENESIS, prev_hash=None) open(chain_u.path(), 'w+').close() self._append_header(chain_u, self.HEADERS['A']) self._append_header(chain_u, self.HEADERS['B']) self._append_header(chain_u, self.HEADERS['C']) self._append_header(chain_u, self.HEADERS['D']) self._append_header(chain_u, self.HEADERS['E']) self._append_header(chain_u, self.HEADERS['F']) self._append_header(chain_u, self.HEADERS['O']) self._append_header(chain_u, self.HEADERS['P']) self._append_header(chain_u, self.HEADERS['Q']) chain_l = chain_u.fork(self.HEADERS['G']) self._append_header(chain_l, self.HEADERS['H']) self._append_header(chain_l, self.HEADERS['I']) self._append_header(chain_l, self.HEADERS['J']) self._append_header(chain_l, self.HEADERS['K']) self._append_header(chain_l, self.HEADERS['L']) self.assertEqual({ chain_u: 8, chain_l: 5 }, chain_u.get_parent_heights()) self.assertEqual({chain_l: 11}, chain_l.get_parent_heights()) chain_z = chain_l.fork(self.HEADERS['M']) self._append_header(chain_z, self.HEADERS['N']) self._append_header(chain_z, self.HEADERS['X']) self._append_header(chain_z, self.HEADERS['Y']) self._append_header(chain_z, self.HEADERS['Z']) self.assertEqual({ chain_u: 8, chain_z: 5 }, chain_u.get_parent_heights()) self.assertEqual({ chain_l: 11, chain_z: 8 }, chain_l.get_parent_heights()) self.assertEqual({chain_z: 13}, chain_z.get_parent_heights()) self.assertEqual( 5, chain_u.get_height_of_last_common_block_with_chain(chain_l)) self.assertEqual( 5, chain_l.get_height_of_last_common_block_with_chain(chain_u)) self.assertEqual( 5, chain_u.get_height_of_last_common_block_with_chain(chain_z)) self.assertEqual( 5, chain_z.get_height_of_last_common_block_with_chain(chain_u)) self.assertEqual( 8, chain_l.get_height_of_last_common_block_with_chain(chain_z)) self.assertEqual( 8, chain_z.get_height_of_last_common_block_with_chain(chain_l)) self._append_header(chain_u, self.HEADERS['R']) self._append_header(chain_u, self.HEADERS['S']) self._append_header(chain_u, self.HEADERS['T']) self._append_header(chain_u, self.HEADERS['U']) self.assertEqual({ chain_u: 12, chain_z: 5 }, chain_u.get_parent_heights()) self.assertEqual({ chain_l: 11, chain_z: 8 }, chain_l.get_parent_heights()) self.assertEqual({chain_z: 13}, chain_z.get_parent_heights()) self.assertEqual( 5, chain_u.get_height_of_last_common_block_with_chain(chain_l)) self.assertEqual( 5, chain_l.get_height_of_last_common_block_with_chain(chain_u)) self.assertEqual( 5, chain_u.get_height_of_last_common_block_with_chain(chain_z)) self.assertEqual( 5, chain_z.get_height_of_last_common_block_with_chain(chain_u)) self.assertEqual( 8, chain_l.get_height_of_last_common_block_with_chain(chain_z)) self.assertEqual( 8, chain_z.get_height_of_last_common_block_with_chain(chain_l))
def _append_header(self, chain: Blockchain, header: dict): self.assertTrue(chain.can_connect(header)) chain.save_header(header)