Changeset View
Changeset View
Standalone View
Standalone View
test/functional/feature_block.py
Show First 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | def serialize(self): | ||||
for tx in self.vtx: | for tx in self.vtx: | ||||
r += tx.serialize() | r += tx.serialize() | ||||
return r | return r | ||||
def normal_serialize(self): | def normal_serialize(self): | ||||
return super().serialize() | return super().serialize() | ||||
# Valid for block at height 120 | |||||
DUPLICATE_COINBASE_SCRIPT_SIG = b'\x01\x78' | |||||
class FullBlockTest(BitcoinTestFramework): | class FullBlockTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 1 | self.num_nodes = 1 | ||||
self.setup_clean_chain = True | self.setup_clean_chain = True | ||||
# This is a consensus block test, we don't care about tx policy | # This is a consensus block test, we don't care about tx policy | ||||
self.extra_args = [['-noparkdeepreorg', | self.extra_args = [['-noparkdeepreorg', | ||||
'-maxreorgdepth=-1', '-acceptnonstdtxn=1']] | '-maxreorgdepth=-1', '-acceptnonstdtxn=1']] | ||||
def run_test(self): | def run_test(self): | ||||
node = self.nodes[0] # convenience reference to the node | node = self.nodes[0] # convenience reference to the node | ||||
self.bootstrap_p2p() # Add one p2p connection to the node | self.bootstrap_p2p() # Add one p2p connection to the node | ||||
self.block_heights = {} | self.block_heights = {} | ||||
self.coinbase_key = ECKey() | self.coinbase_key = ECKey() | ||||
self.coinbase_key.generate() | self.coinbase_key.generate() | ||||
self.coinbase_pubkey = self.coinbase_key.get_pubkey().get_bytes() | self.coinbase_pubkey = self.coinbase_key.get_pubkey().get_bytes() | ||||
self.tip = None | self.tip = None | ||||
self.blocks = {} | self.blocks = {} | ||||
self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) | self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) | ||||
self.block_heights[self.genesis_hash] = 0 | self.block_heights[self.genesis_hash] = 0 | ||||
self.spendable_outputs = [] | self.spendable_outputs = [] | ||||
# Create a new block | # Create a new block | ||||
b_dup_cb = self.next_block('dup_cb') | |||||
b_dup_cb.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG | |||||
b_dup_cb.vtx[0].rehash() | |||||
duplicate_tx = b_dup_cb.vtx[0] | |||||
b_dup_cb = self.update_block('dup_cb', []) | |||||
self.send_blocks([b_dup_cb]) | |||||
b0 = self.next_block(0) | b0 = self.next_block(0) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
self.send_blocks([b0]) | self.send_blocks([b0]) | ||||
# These constants chosen specifically to trigger an immature coinbase spend | # These constants chosen specifically to trigger an immature coinbase spend | ||||
# at a certain time below. | # at a certain time below. | ||||
NUM_BUFFER_BLOCKS_TO_GENERATE = 99 | NUM_BUFFER_BLOCKS_TO_GENERATE = 99 | ||||
NUM_OUTPUTS_TO_COLLECT = 33 | NUM_OUTPUTS_TO_COLLECT = 33 | ||||
▲ Show 20 Lines • Show All 538 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
self.move_tip(57) | self.move_tip(57) | ||||
# The tip is not updated because 57p2 seen first | # The tip is not updated because 57p2 seen first | ||||
self.send_blocks([b57], False) | self.send_blocks([b57], False) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# Test a few invalid tx types | # Test a few invalid tx types | ||||
# | # | ||||
# -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) | # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 () | ||||
# \-> ??? (17) | # \-> ??? (17) | ||||
# | # | ||||
# tx with prevout.n out of range | # tx with prevout.n out of range | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a transaction with prevout.n out of range") | "Reject a block with a transaction with prevout.n out of range") | ||||
self.move_tip(57) | self.move_tip(57) | ||||
b58 = self.next_block(58, spend=out[17]) | b58 = self.next_block(58, spend=out[17]) | ||||
Show All 15 Lines | def run_test(self): | ||||
b59 = self.next_block(59) | b59 = self.next_block(59) | ||||
tx = self.create_and_sign_transaction(out[17], 51 * COIN) | tx = self.create_and_sign_transaction(out[17], 51 * COIN) | ||||
b59 = self.update_block(59, [tx]) | b59 = self.update_block(59, [tx]) | ||||
self.send_blocks([b59], success=False, | self.send_blocks([b59], success=False, | ||||
reject_reason='bad-txns-in-belowout', reconnect=True) | reject_reason='bad-txns-in-belowout', reconnect=True) | ||||
# reset to good chain | # reset to good chain | ||||
self.move_tip(57) | self.move_tip(57) | ||||
b60 = self.next_block(60, spend=out[17]) | b60 = self.next_block(60) | ||||
self.send_blocks([b60], True) | self.send_blocks([b60], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# Test BIP30 | # Test BIP30 (reject duplicate) | ||||
# | # | ||||
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) | # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 () | ||||
# \-> b61 (18) | # \-> b61 () | ||||
# | # | ||||
# Blocks are not allowed to contain a transaction whose id matches that of an earlier, | # Blocks are not allowed to contain a transaction whose id matches that of an earlier, | ||||
# not-fully-spent transaction in the same chain. To test, make identical coinbases; | # not-fully-spent transaction in the same chain. To test, make identical coinbases; | ||||
# the second one should be rejected. | # the second one should be rejected. | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)") | "Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)") | ||||
self.move_tip(60) | self.move_tip(60) | ||||
b61 = self.next_block(61, spend=out[18]) | b61 = self.next_block(61) | ||||
# Equalize the coinbases | b61.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG | ||||
b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig | |||||
b61.vtx[0].rehash() | b61.vtx[0].rehash() | ||||
b61 = self.update_block(61, []) | b61 = self.update_block(61, []) | ||||
assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize()) | assert_equal(duplicate_tx.serialize(), b61.vtx[0].serialize()) | ||||
self.send_blocks([b61], success=False, | self.send_blocks([b61], success=False, | ||||
reject_reason='bad-txns-BIP30', reconnect=True) | reject_reason='bad-txns-BIP30', reconnect=True) | ||||
# Test BIP30 (allow duplicate if spent) | |||||
# | |||||
# -> b57 (16) -> b60 () | |||||
# \-> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () | |||||
# | |||||
self.move_tip(57) | |||||
b_spend_dup_cb = self.next_block('spend_dup_cb') | |||||
tx = CTransaction() | |||||
tx.vin.append(CTxIn(COutPoint(duplicate_tx.sha256, 0))) | |||||
tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) | |||||
self.sign_tx(tx, duplicate_tx) | |||||
tx.rehash() | |||||
b_spend_dup_cb = self.update_block('spend_dup_cb', [tx]) | |||||
b_dup_2 = self.next_block('dup_2') | |||||
b_dup_2.vtx[0].vin[0].scriptSig = DUPLICATE_COINBASE_SCRIPT_SIG | |||||
b_dup_2.vtx[0].rehash() | |||||
b_dup_2 = self.update_block('dup_2', []) | |||||
assert_equal(duplicate_tx.serialize(), b_dup_2.vtx[0].serialize()) | |||||
assert_equal( | |||||
self.nodes[0].gettxout( | |||||
txid=duplicate_tx.hash, | |||||
n=0)['confirmations'], | |||||
119) | |||||
self.send_blocks([b_spend_dup_cb, b_dup_2], success=True) | |||||
# The duplicate has less confirmations | |||||
assert_equal( | |||||
self.nodes[0].gettxout( | |||||
txid=duplicate_tx.hash, | |||||
n=0)['confirmations'], | |||||
1) | |||||
# Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests) | # Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests) | ||||
# | # | ||||
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) | # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () | ||||
# \-> b62 (18) | # \-> b62 (18) | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a transaction with a nonfinal locktime") | "Reject a block with a transaction with a nonfinal locktime") | ||||
self.move_tip(60) | self.move_tip('dup_2') | ||||
b62 = self.next_block(62) | b62 = self.next_block(62) | ||||
tx = CTransaction() | tx = CTransaction() | ||||
tx.nLockTime = 0xffffffff # this locktime is non-final | tx.nLockTime = 0xffffffff # this locktime is non-final | ||||
# don't set nSequence | # don't set nSequence | ||||
tx.vin.append(CTxIn(COutPoint(out[18].sha256, 0))) | tx.vin.append(CTxIn(COutPoint(out[18].sha256, 0))) | ||||
tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) | tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) | ||||
assert tx.vin[0].nSequence < 0xffffffff | assert tx.vin[0].nSequence < 0xffffffff | ||||
tx.calc_sha256() | tx.calc_sha256() | ||||
b62 = self.update_block(62, [tx]) | b62 = self.update_block(62, [tx]) | ||||
self.send_blocks( | self.send_blocks( | ||||
[b62], | [b62], | ||||
success=False, | success=False, | ||||
reject_reason='bad-txns-nonfinal', | reject_reason='bad-txns-nonfinal', | ||||
reconnect=True) | reconnect=True) | ||||
# Test a non-final coinbase is also rejected | # Test a non-final coinbase is also rejected | ||||
# | # | ||||
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) | # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () | ||||
# \-> b63 (-) | # \-> b63 (-) | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a coinbase transaction with a nonfinal locktime") | "Reject a block with a coinbase transaction with a nonfinal locktime") | ||||
self.move_tip(60) | self.move_tip('dup_2') | ||||
b63 = self.next_block(63) | b63 = self.next_block(63) | ||||
b63.vtx[0].nLockTime = 0xffffffff | b63.vtx[0].nLockTime = 0xffffffff | ||||
b63.vtx[0].vin[0].nSequence = 0xDEADBEEF | b63.vtx[0].vin[0].nSequence = 0xDEADBEEF | ||||
b63.vtx[0].rehash() | b63.vtx[0].rehash() | ||||
b63 = self.update_block(63, []) | b63 = self.update_block(63, []) | ||||
self.send_blocks( | self.send_blocks( | ||||
[b63], | [b63], | ||||
success=False, | success=False, | ||||
reject_reason='bad-txns-nonfinal', | reject_reason='bad-txns-nonfinal', | ||||
reconnect=True) | reconnect=True) | ||||
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that | # This checks that a block with a bloated VARINT between the block_header and the array of tx such that | ||||
# the block is > LEGACY_MAX_BLOCK_SIZE with the bloated varint, but <= LEGACY_MAX_BLOCK_SIZE without the bloated varint, | # the block is > LEGACY_MAX_BLOCK_SIZE with the bloated varint, but <= LEGACY_MAX_BLOCK_SIZE without the bloated varint, | ||||
# does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not | # does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not | ||||
# care whether the bloated block is accepted or rejected; it only cares that the second block is accepted. | # care whether the bloated block is accepted or rejected; it only cares that the second block is accepted. | ||||
# | # | ||||
# What matters is that the receiving node should not reject the bloated block, and then reject the canonical | # What matters is that the receiving node should not reject the bloated block, and then reject the canonical | ||||
# block on the basis that it's the same as an already-rejected block (which would be a consensus failure.) | # block on the basis that it's the same as an already-rejected block (which would be a consensus failure.) | ||||
# | # | ||||
# -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) | # -> b_spend_dup_cb (b_dup_cb) -> b_dup_2 () -> b64 (18) | ||||
# \ | # \ | ||||
# b64a (18) | # b64a (18) | ||||
# b64a is a bloated block (non-canonical varint) | # b64a is a bloated block (non-canonical varint) | ||||
# b64 is a good block (same as b64 but w/ canonical varint) | # b64 is a good block (same as b64 but w/ canonical varint) | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Accept a valid block even if a bloated version of the block has previously been sent") | "Accept a valid block even if a bloated version of the block has previously been sent") | ||||
self.move_tip(60) | self.move_tip('dup_2') | ||||
regular_block = self.next_block("64a", spend=out[18]) | regular_block = self.next_block("64a", spend=out[18]) | ||||
# make it a "broken_block," with non-canonical serialization | # make it a "broken_block," with non-canonical serialization | ||||
b64a = CBrokenBlock(regular_block) | b64a = CBrokenBlock(regular_block) | ||||
b64a.initialize(regular_block) | b64a.initialize(regular_block) | ||||
self.blocks["64a"] = b64a | self.blocks["64a"] = b64a | ||||
self.tip = b64a | self.tip = b64a | ||||
tx = CTransaction() | tx = CTransaction() | ||||
Show All 11 Lines | def run_test(self): | ||||
# bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently | # bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently | ||||
# resend the header message, it won't send us the getdata message again. Just | # resend the header message, it won't send us the getdata message again. Just | ||||
# disconnect and reconnect and then call send_blocks. | # disconnect and reconnect and then call send_blocks. | ||||
# TODO: improve this test to be less dependent on P2P DOS behaviour. | # TODO: improve this test to be less dependent on P2P DOS behaviour. | ||||
node.disconnect_p2ps() | node.disconnect_p2ps() | ||||
self.reconnect_p2p() | self.reconnect_p2p() | ||||
self.move_tip(60) | self.move_tip('dup_2') | ||||
b64 = CBlock(b64a) | b64 = CBlock(b64a) | ||||
b64.vtx = copy.deepcopy(b64a.vtx) | b64.vtx = copy.deepcopy(b64a.vtx) | ||||
assert_equal(b64.hash, b64a.hash) | assert_equal(b64.hash, b64a.hash) | ||||
assert_equal(len(b64.serialize()), LEGACY_MAX_BLOCK_SIZE) | assert_equal(len(b64.serialize()), LEGACY_MAX_BLOCK_SIZE) | ||||
self.blocks[64] = b64 | self.blocks[64] = b64 | ||||
b64 = self.update_block(64, []) | b64 = self.update_block(64, []) | ||||
self.send_blocks([b64], True) | self.send_blocks([b64], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# Spend an output created in the block itself | # Spend an output created in the block itself | ||||
# | # | ||||
# -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) | # -> b_dup_2 () -> b64 (18) -> b65 (19) | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Accept a block with a transaction spending an output created in the same block") | "Accept a block with a transaction spending an output created in the same block") | ||||
self.move_tip(64) | self.move_tip(64) | ||||
b65 = self.next_block(65) | b65 = self.next_block(65) | ||||
tx1 = self.create_and_sign_transaction(out[19], out[19].vout[0].nValue) | tx1 = self.create_and_sign_transaction(out[19], out[19].vout[0].nValue) | ||||
tx2 = self.create_and_sign_transaction(tx1, 0) | tx2 = self.create_and_sign_transaction(tx1, 0) | ||||
b65 = self.update_block(65, [tx1, tx2]) | b65 = self.update_block(65, [tx1, tx2]) | ||||
self.send_blocks([b65], True) | self.send_blocks([b65], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# Attempt to double-spend a transaction created in a block | # Attempt to double-spend a transaction created in a block | ||||
# | # | ||||
# -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) | # -> b64 (18) -> b65 (19) | ||||
# \-> b67 (20) | # \-> b67 (20) | ||||
# | # | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Reject a block with a transaction double spending a transaction created in the same block") | "Reject a block with a transaction double spending a transaction created in the same block") | ||||
self.move_tip(65) | self.move_tip(65) | ||||
b67 = self.next_block(67) | b67 = self.next_block(67) | ||||
tx1 = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue) | tx1 = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue) | ||||
tx2 = self.create_and_sign_transaction(tx1, 1) | tx2 = self.create_and_sign_transaction(tx1, 1) | ||||
tx3 = self.create_and_sign_transaction(tx1, 2) | tx3 = self.create_and_sign_transaction(tx1, 2) | ||||
b67 = self.update_block(67, [tx1, tx2, tx3]) | b67 = self.update_block(67, [tx1, tx2, tx3]) | ||||
self.send_blocks([b67], success=False, | self.send_blocks([b67], success=False, | ||||
reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# More tests of block subsidy | # More tests of block subsidy | ||||
# | # | ||||
# -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) | # -> b64 (18) -> b65 (19) -> b69 (20) | ||||
# \-> b68 (20) | # \-> b68 (20) | ||||
# | # | ||||
# b68 - coinbase with an extra 10 satoshis, | # b68 - coinbase with an extra 10 satoshis, | ||||
# creates a tx that has 9 satoshis from out[20] go to fees | # creates a tx that has 9 satoshis from out[20] go to fees | ||||
# this fails because the coinbase is trying to claim 1 satoshi too much in fees | # this fails because the coinbase is trying to claim 1 satoshi too much in fees | ||||
# | # | ||||
# b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee | # b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee | ||||
# this succeeds | # this succeeds | ||||
# | # | ||||
Show All 14 Lines | def run_test(self): | ||||
tx = self.create_and_sign_transaction( | tx = self.create_and_sign_transaction( | ||||
out[20], out[20].vout[0].nValue - 10) | out[20], out[20].vout[0].nValue - 10) | ||||
self.update_block(69, [tx]) | self.update_block(69, [tx]) | ||||
self.send_blocks([b69], True) | self.send_blocks([b69], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
# Test spending the outpoint of a non-existent transaction | # Test spending the outpoint of a non-existent transaction | ||||
# | # | ||||
# -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) | # -> b65 (19) -> b69 (20) | ||||
# \-> b70 (21) | # \-> b70 (21) | ||||
# | # | ||||
self.log.info( | self.log.info( | ||||
"Reject a block containing a transaction spending from a non-existent input") | "Reject a block containing a transaction spending from a non-existent input") | ||||
self.move_tip(69) | self.move_tip(69) | ||||
b70 = self.next_block(70, spend=out[21]) | b70 = self.next_block(70, spend=out[21]) | ||||
bogus_tx = CTransaction() | bogus_tx = CTransaction() | ||||
bogus_tx.sha256 = uint256_from_str( | bogus_tx.sha256 = uint256_from_str( | ||||
b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c") | b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c") | ||||
tx = CTransaction() | tx = CTransaction() | ||||
tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff)) | tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff)) | ||||
tx.vout.append(CTxOut(1, b"")) | tx.vout.append(CTxOut(1, b"")) | ||||
pad_tx(tx) | pad_tx(tx) | ||||
b70 = self.update_block(70, [tx]) | b70 = self.update_block(70, [tx]) | ||||
self.send_blocks([b70], success=False, | self.send_blocks([b70], success=False, | ||||
reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | reject_reason='bad-txns-inputs-missingorspent', reconnect=True) | ||||
# Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) | # Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) | ||||
# | # | ||||
# -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) | # -> b65 (19) -> b69 (20) -> b72 (21) | ||||
# \-> b71 (21) | # \-> b71 (21) | ||||
# | # | ||||
# b72 is a good block. | # b72 is a good block. | ||||
# b71 is a copy of 72, but re-adds one of its transactions. However, | # b71 is a copy of 72, but re-adds one of its transactions. However, | ||||
# it has the same hash as b72. | # it has the same hash as b72. | ||||
self.log.info( | self.log.info( | ||||
"Reject a block containing a duplicate transaction but with the same Merkle root (Merkle tree malleability") | "Reject a block containing a duplicate transaction but with the same Merkle root (Merkle tree malleability") | ||||
self.move_tip(69) | self.move_tip(69) | ||||
b72 = self.next_block(72) | b72 = self.next_block(72) | ||||
Show All 15 Lines | def run_test(self): | ||||
self.send_blocks([b71], success=False, | self.send_blocks([b71], success=False, | ||||
reject_reason='bad-txns-duplicate', reconnect=True) | reject_reason='bad-txns-duplicate', reconnect=True) | ||||
self.move_tip(72) | self.move_tip(72) | ||||
self.send_blocks([b72], True) | self.send_blocks([b72], True) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
self.log.info("Skipped sigops tests") | self.log.info("Skipped sigops tests") | ||||
# tests were moved to feature_block_sigops.py | # sigops tests were moved to feature_block_sigops.py, | ||||
# then deleted from Bitcoin ABC after the May 2020 upgrade | |||||
b75 = self.next_block(75) | b75 = self.next_block(75) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
b76 = self.next_block(76) | b76 = self.next_block(76) | ||||
self.save_spendable_output() | self.save_spendable_output() | ||||
self.send_blocks([b75, b76], True) | self.send_blocks([b75, b76], True) | ||||
# Test transaction resurrection | # Test transaction resurrection | ||||
# | # | ||||
▲ Show 20 Lines • Show All 331 Lines • Show Last 20 Lines |