Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-replay-protection.py
Show First 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | class ReplayProtectionTest(BitcoinTestFramework): | ||||
def set_tip(self, number: int): | def set_tip(self, number: int): | ||||
""" | """ | ||||
Move the tip back to a previous block. | Move the tip back to a previous block. | ||||
""" | """ | ||||
self.tip = self.blocks[number] | self.tip = self.blocks[number] | ||||
def run_test(self): | def run_test(self): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
node.add_p2p_connection(P2PDataStore()) | peer = node.add_p2p_connection(P2PDataStore()) | ||||
node.setmocktime(REPLAY_PROTECTION_START_TIME) | node.setmocktime(REPLAY_PROTECTION_START_TIME) | ||||
self.genesis_hash = int(node.getbestblockhash(), 16) | self.genesis_hash = int(node.getbestblockhash(), 16) | ||||
self.block_heights[self.genesis_hash] = 0 | self.block_heights[self.genesis_hash] = 0 | ||||
spendable_outputs = [] | spendable_outputs = [] | ||||
# save the current tip so it can be spent by a later block | # save the current tip so it can be spent by a later block | ||||
def save_spendable_output(): | def save_spendable_output(): | ||||
Show All 21 Lines | def run_test(self): | ||||
return block | return block | ||||
# shorthand | # shorthand | ||||
block = self.next_block | block = self.next_block | ||||
# Create a new block | # Create a new block | ||||
block(0) | block(0) | ||||
save_spendable_output() | save_spendable_output() | ||||
node.p2p.send_blocks_and_test([self.tip], node) | peer.send_blocks_and_test([self.tip], node) | ||||
# Now we need that block to mature so we can spend the coinbase. | # Now we need that block to mature so we can spend the coinbase. | ||||
maturity_blocks = [] | maturity_blocks = [] | ||||
for i in range(99): | for i in range(99): | ||||
block(5000 + i) | block(5000 + i) | ||||
maturity_blocks.append(self.tip) | maturity_blocks.append(self.tip) | ||||
save_spendable_output() | save_spendable_output() | ||||
node.p2p.send_blocks_and_test(maturity_blocks, node) | peer.send_blocks_and_test(maturity_blocks, node) | ||||
# collect spendable outputs now to avoid cluttering the code later on | # collect spendable outputs now to avoid cluttering the code later on | ||||
out = [] | out = [] | ||||
for i in range(100): | for i in range(100): | ||||
out.append(get_spendable_output()) | out.append(get_spendable_output()) | ||||
# Generate a key pair to test P2SH sigops count | # Generate a key pair to test P2SH sigops count | ||||
private_key = ECKey() | private_key = ECKey() | ||||
Show All 32 Lines | def run_test(self): | ||||
# Before the fork, no replay protection required to get in the mempool. | # Before the fork, no replay protection required to get in the mempool. | ||||
txns = create_fund_and_spend_tx(out[0]) | txns = create_fund_and_spend_tx(out[0]) | ||||
send_transaction_to_mempool(txns[0]) | send_transaction_to_mempool(txns[0]) | ||||
send_transaction_to_mempool(txns[1]) | send_transaction_to_mempool(txns[1]) | ||||
# And txns get mined in a block properly. | # And txns get mined in a block properly. | ||||
block(1) | block(1) | ||||
update_block(1, txns) | update_block(1, txns) | ||||
node.p2p.send_blocks_and_test([self.tip], node) | peer.send_blocks_and_test([self.tip], node) | ||||
# Replay protected transactions are rejected. | # Replay protected transactions are rejected. | ||||
replay_txns = create_fund_and_spend_tx(out[1], 0xffdead) | replay_txns = create_fund_and_spend_tx(out[1], 0xffdead) | ||||
send_transaction_to_mempool(replay_txns[0]) | send_transaction_to_mempool(replay_txns[0]) | ||||
assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, | assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, | ||||
node.sendrawtransaction, ToHex(replay_txns[1])) | node.sendrawtransaction, ToHex(replay_txns[1])) | ||||
# And block containing them are rejected as well. | # And block containing them are rejected as well. | ||||
block(2) | block(2) | ||||
update_block(2, replay_txns) | update_block(2, replay_txns) | ||||
node.p2p.send_blocks_and_test( | peer.send_blocks_and_test( | ||||
[self.tip], node, success=False, reject_reason='blk-bad-inputs') | [self.tip], node, success=False, reject_reason='blk-bad-inputs') | ||||
# Rewind bad block | # Rewind bad block | ||||
self.set_tip(1) | self.set_tip(1) | ||||
# Create a block that would activate the replay protection. | # Create a block that would activate the replay protection. | ||||
bfork = block(5555) | bfork = block(5555) | ||||
bfork.nTime = REPLAY_PROTECTION_START_TIME - 1 | bfork.nTime = REPLAY_PROTECTION_START_TIME - 1 | ||||
update_block(5555, []) | update_block(5555, []) | ||||
node.p2p.send_blocks_and_test([self.tip], node) | peer.send_blocks_and_test([self.tip], node) | ||||
activation_blocks = [] | activation_blocks = [] | ||||
for i in range(5): | for i in range(5): | ||||
block(5100 + i) | block(5100 + i) | ||||
activation_blocks.append(self.tip) | activation_blocks.append(self.tip) | ||||
node.p2p.send_blocks_and_test(activation_blocks, node) | peer.send_blocks_and_test(activation_blocks, node) | ||||
# Check we are just before the activation time | # Check we are just before the activation time | ||||
assert_equal( | assert_equal( | ||||
node.getblockchaininfo()['mediantime'], | node.getblockchaininfo()['mediantime'], | ||||
REPLAY_PROTECTION_START_TIME - 1) | REPLAY_PROTECTION_START_TIME - 1) | ||||
# We are just before the fork, replay protected txns still are rejected | # We are just before the fork, replay protected txns still are rejected | ||||
assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, | assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, | ||||
node.sendrawtransaction, ToHex(replay_txns[1])) | node.sendrawtransaction, ToHex(replay_txns[1])) | ||||
block(3) | block(3) | ||||
update_block(3, replay_txns) | update_block(3, replay_txns) | ||||
node.p2p.send_blocks_and_test( | peer.send_blocks_and_test( | ||||
[self.tip], node, success=False, reject_reason='blk-bad-inputs') | [self.tip], node, success=False, reject_reason='blk-bad-inputs') | ||||
# Rewind bad block | # Rewind bad block | ||||
self.set_tip(5104) | self.set_tip(5104) | ||||
# Send some non replay protected txns in the mempool to check | # Send some non replay protected txns in the mempool to check | ||||
# they get cleaned at activation. | # they get cleaned at activation. | ||||
txns = create_fund_and_spend_tx(out[2]) | txns = create_fund_and_spend_tx(out[2]) | ||||
send_transaction_to_mempool(txns[0]) | send_transaction_to_mempool(txns[0]) | ||||
tx_id = send_transaction_to_mempool(txns[1]) | tx_id = send_transaction_to_mempool(txns[1]) | ||||
# Activate the replay protection | # Activate the replay protection | ||||
block(5556) | block(5556) | ||||
node.p2p.send_blocks_and_test([self.tip], node) | peer.send_blocks_and_test([self.tip], node) | ||||
# Check we just activated the replay protection | # Check we just activated the replay protection | ||||
assert_equal( | assert_equal( | ||||
node.getblockchaininfo()['mediantime'], | node.getblockchaininfo()['mediantime'], | ||||
REPLAY_PROTECTION_START_TIME) | REPLAY_PROTECTION_START_TIME) | ||||
# Non replay protected transactions are not valid anymore, | # Non replay protected transactions are not valid anymore, | ||||
# so they should be removed from the mempool. | # so they should be removed from the mempool. | ||||
assert tx_id not in set(node.getrawmempool()) | assert tx_id not in set(node.getrawmempool()) | ||||
# Good old transactions are now invalid. | # Good old transactions are now invalid. | ||||
send_transaction_to_mempool(txns[0]) | send_transaction_to_mempool(txns[0]) | ||||
assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, | assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, | ||||
node.sendrawtransaction, ToHex(txns[1])) | node.sendrawtransaction, ToHex(txns[1])) | ||||
# They also cannot be mined | # They also cannot be mined | ||||
block(4) | block(4) | ||||
update_block(4, txns) | update_block(4, txns) | ||||
node.p2p.send_blocks_and_test( | peer.send_blocks_and_test( | ||||
[self.tip], node, success=False, reject_reason='blk-bad-inputs') | [self.tip], node, success=False, reject_reason='blk-bad-inputs') | ||||
# Rewind bad block | # Rewind bad block | ||||
self.set_tip(5556) | self.set_tip(5556) | ||||
# The replay protected transaction is now valid | # The replay protected transaction is now valid | ||||
replay_tx0_id = send_transaction_to_mempool(replay_txns[0]) | replay_tx0_id = send_transaction_to_mempool(replay_txns[0]) | ||||
replay_tx1_id = send_transaction_to_mempool(replay_txns[1]) | replay_tx1_id = send_transaction_to_mempool(replay_txns[1]) | ||||
Show All 15 Lines | def run_test(self): | ||||
# And the mempool is still in good shape. | # And the mempool is still in good shape. | ||||
assert replay_tx0_id in set(node.getrawmempool()) | assert replay_tx0_id in set(node.getrawmempool()) | ||||
assert replay_tx1_id in set(node.getrawmempool()) | assert replay_tx1_id in set(node.getrawmempool()) | ||||
# They also can also be mined | # They also can also be mined | ||||
block(5) | block(5) | ||||
update_block(5, replay_txns) | update_block(5, replay_txns) | ||||
node.p2p.send_blocks_and_test([self.tip], node) | peer.send_blocks_and_test([self.tip], node) | ||||
# Ok, now we check if a reorg work properly across the activation. | # Ok, now we check if a reorg work properly across the activation. | ||||
postforkblockid = node.getbestblockhash() | postforkblockid = node.getbestblockhash() | ||||
node.invalidateblock(postforkblockid) | node.invalidateblock(postforkblockid) | ||||
assert replay_tx0_id in set(node.getrawmempool()) | assert replay_tx0_id in set(node.getrawmempool()) | ||||
assert replay_tx1_id in set(node.getrawmempool()) | assert replay_tx1_id in set(node.getrawmempool()) | ||||
# Deactivating replay protection. | # Deactivating replay protection. | ||||
Show All 17 Lines |