Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-segwit-recovery.py
Show First 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | def next_block(self, number): | ||||
# Do PoW, which is cheap on regnet | # Do PoW, which is cheap on regnet | ||||
block.solve() | block.solve() | ||||
self.tip = block | self.tip = block | ||||
self.block_heights[block.sha256] = height | self.block_heights[block.sha256] = height | ||||
assert number not in self.blocks | assert number not in self.blocks | ||||
self.blocks[number] = block | self.blocks[number] = block | ||||
return block | return block | ||||
def bootstrap_p2p(self, *, num_connections=1): | |||||
"""Add a P2P connection to the node. | |||||
Helper to connect and wait for version handshake.""" | |||||
for node in self.nodes: | |||||
for _ in range(num_connections): | |||||
node.add_p2p_connection(P2PDataStore()) | |||||
def reconnect_p2p(self, **kwargs): | |||||
"""Tear down and bootstrap the P2P connection to the node. | |||||
The node gets disconnected several times in this test. This helper | |||||
method reconnects the p2p and restarts the network thread.""" | |||||
for node in self.nodes: | |||||
node.disconnect_p2ps() | |||||
self.bootstrap_p2p(**kwargs) | |||||
def run_test(self): | def run_test(self): | ||||
self.bootstrap_p2p() | |||||
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 | ||||
spendable_outputs = [] | spendable_outputs = [] | ||||
# shorthand | # shorthand | ||||
block = self.next_block | block = self.next_block | ||||
node_nonstd = self.nodes[0] | node_nonstd = self.nodes[0] | ||||
node_std = self.nodes[1] | node_std = self.nodes[1] | ||||
peer_nonstd = node_nonstd.add_p2p_connection(P2PDataStore()) | |||||
peer_std = node_std.add_p2p_connection(P2PDataStore()) | |||||
# 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(): | ||||
spendable_outputs.append(self.tip) | spendable_outputs.append(self.tip) | ||||
# get an output that we previously marked as spendable | # get an output that we previously marked as spendable | ||||
def get_spendable_output(): | def get_spendable_output(): | ||||
return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) | return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) | ||||
# submit current tip and check it was accepted | |||||
def accepted(node): | |||||
node.p2p.send_blocks_and_test([self.tip], node) | |||||
# adds transactions to the block and updates state | # adds transactions to the block and updates state | ||||
def update_block(block_number, new_transactions): | def update_block(block_number, new_transactions): | ||||
block = self.blocks[block_number] | block = self.blocks[block_number] | ||||
block.vtx.extend(new_transactions) | block.vtx.extend(new_transactions) | ||||
old_sha256 = block.sha256 | old_sha256 = block.sha256 | ||||
make_conform_to_ctor(block) | make_conform_to_ctor(block) | ||||
block.hashMerkleRoot = block.calc_merkle_root() | block.hashMerkleRoot = block.calc_merkle_root() | ||||
block.solve() | block.solve() | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
txspend.vin.append( | txspend.vin.append( | ||||
CTxIn(COutPoint(txfund.sha256, i), CScript([redeem_scripts[i]]))) | CTxIn(COutPoint(txfund.sha256, i), CScript([redeem_scripts[i]]))) | ||||
txspend.vout = [CTxOut(50 * COIN - 2000, | txspend.vout = [CTxOut(50 * COIN - 2000, | ||||
CScript([OP_HASH160, hash160(CScript([OP_TRUE])), OP_EQUAL]))] | CScript([OP_HASH160, hash160(CScript([OP_TRUE])), OP_EQUAL]))] | ||||
txspend.rehash() | txspend.rehash() | ||||
return txfund, txspend | return txfund, txspend | ||||
# Check we are not banned when sending a txn that is rejected. | |||||
def check_for_no_ban_on_rejected_tx(node, tx, reject_reason): | |||||
node.p2p.send_txs_and_test( | |||||
[tx], node, success=False, reject_reason=reject_reason) | |||||
# Create a new block | # Create a new block | ||||
block(0) | block(0) | ||||
save_spendable_output() | save_spendable_output() | ||||
accepted(node_nonstd) | peer_nonstd.send_blocks_and_test([self.tip], node_nonstd) | ||||
# 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. | ||||
matureblocks = [] | matureblocks = [] | ||||
for i in range(199): | for i in range(199): | ||||
block(5000 + i) | block(5000 + i) | ||||
matureblocks.append(self.tip) | matureblocks.append(self.tip) | ||||
save_spendable_output() | save_spendable_output() | ||||
node_nonstd.p2p.send_blocks_and_test(matureblocks, node_nonstd) | peer_nonstd.send_blocks_and_test(matureblocks, node_nonstd) | ||||
# 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()) | ||||
# Create segwit funding and spending transactions | # Create segwit funding and spending transactions | ||||
txfund, txspend = create_segwit_fund_and_spend_tx(out[0]) | txfund, txspend = create_segwit_fund_and_spend_tx(out[0]) | ||||
txfund_case0, txspend_case0 = create_segwit_fund_and_spend_tx( | txfund_case0, txspend_case0 = create_segwit_fund_and_spend_tx( | ||||
out[1], True) | out[1], True) | ||||
# Mine txfund, as it can't go into node_std mempool because it's | # Mine txfund, as it can't go into node_std mempool because it's | ||||
# nonstandard. | # nonstandard. | ||||
block(5555) | block(5555) | ||||
update_block(5555, [txfund, txfund_case0]) | update_block(5555, [txfund, txfund_case0]) | ||||
accepted(node_nonstd) | peer_nonstd.send_blocks_and_test([self.tip], node_nonstd) | ||||
# Check both nodes are synchronized before continuing. | # Check both nodes are synchronized before continuing. | ||||
self.sync_blocks() | self.sync_blocks() | ||||
# Check that upgraded nodes checking for standardness are not banning | # Check that upgraded nodes checking for standardness are not banning | ||||
# nodes sending segwit spending txns. | # nodes sending segwit spending txns. | ||||
check_for_no_ban_on_rejected_tx( | peer_nonstd.send_txs_and_test([txspend], node_nonstd, success=False, | ||||
node_nonstd, txspend, CLEANSTACK_ERROR) | reject_reason=CLEANSTACK_ERROR) | ||||
check_for_no_ban_on_rejected_tx( | peer_nonstd.send_txs_and_test([txspend_case0], node_nonstd, success=False, | ||||
node_nonstd, txspend_case0, EVAL_FALSE_ERROR) | reject_reason=EVAL_FALSE_ERROR) | ||||
check_for_no_ban_on_rejected_tx( | peer_std.send_txs_and_test([txspend], node_std, success=False, | ||||
node_std, txspend, CLEANSTACK_ERROR) | reject_reason=CLEANSTACK_ERROR) | ||||
check_for_no_ban_on_rejected_tx( | peer_std.send_txs_and_test([txspend_case0], node_std, success=False, | ||||
node_std, txspend_case0, EVAL_FALSE_ERROR) | reject_reason=EVAL_FALSE_ERROR) | ||||
# Segwit recovery txns are never accepted into the mempool, | # Segwit recovery txns are never accepted into the mempool, | ||||
# as they are included in standard flags. | # as they are included in standard flags. | ||||
assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR, | assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR, | ||||
node_nonstd.sendrawtransaction, ToHex(txspend)) | node_nonstd.sendrawtransaction, ToHex(txspend)) | ||||
assert_raises_rpc_error(-26, RPC_EVAL_FALSE_ERROR, | assert_raises_rpc_error(-26, RPC_EVAL_FALSE_ERROR, | ||||
node_nonstd.sendrawtransaction, ToHex(txspend_case0)) | node_nonstd.sendrawtransaction, ToHex(txspend_case0)) | ||||
assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR, | assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR, | ||||
node_std.sendrawtransaction, ToHex(txspend)) | node_std.sendrawtransaction, ToHex(txspend)) | ||||
assert_raises_rpc_error(-26, RPC_EVAL_FALSE_ERROR, | assert_raises_rpc_error(-26, RPC_EVAL_FALSE_ERROR, | ||||
node_std.sendrawtransaction, ToHex(txspend_case0)) | node_std.sendrawtransaction, ToHex(txspend_case0)) | ||||
# Blocks containing segwit spending txns are accepted in both nodes. | # Blocks containing segwit spending txns are accepted in both nodes. | ||||
block(5) | block(5) | ||||
update_block(5, [txspend, txspend_case0]) | update_block(5, [txspend, txspend_case0]) | ||||
accepted(node_nonstd) | peer_nonstd.send_blocks_and_test([self.tip], node_nonstd) | ||||
self.sync_blocks() | self.sync_blocks() | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
SegwitRecoveryTest().main() | SegwitRecoveryTest().main() |