Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-mempool-accept-txn.py
Show All 10 Lines | |||||
import time | import time | ||||
from test_framework.blocktools import ( | from test_framework.blocktools import ( | ||||
create_block, | create_block, | ||||
create_coinbase, | create_coinbase, | ||||
create_transaction, | create_transaction, | ||||
) | ) | ||||
from test_framework.cdefs import MAX_STANDARD_TX_SIGOPS | from test_framework.cdefs import MAX_STANDARD_TX_SIGOPS | ||||
from test_framework.comptool import TestInstance, TestManager | |||||
from test_framework.key import CECKey | from test_framework.key import CECKey | ||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
COutPoint, | COutPoint, | ||||
CTransaction, | CTransaction, | ||||
CTxIn, | CTxIn, | ||||
CTxOut, | CTxOut, | ||||
ToHex, | ToHex, | ||||
) | ) | ||||
from test_framework.mininode import network_thread_start | from test_framework.mininode import ( | ||||
network_thread_start, | |||||
P2PDataStore, | |||||
) | |||||
from test_framework.script import ( | from test_framework.script import ( | ||||
CScript, | CScript, | ||||
hash160, | hash160, | ||||
OP_2DUP, | OP_2DUP, | ||||
OP_CHECKSIG, | OP_CHECKSIG, | ||||
OP_CHECKSIGVERIFY, | OP_CHECKSIGVERIFY, | ||||
OP_EQUAL, | OP_EQUAL, | ||||
OP_HASH160, | OP_HASH160, | ||||
OP_TRUE, | OP_TRUE, | ||||
SIGHASH_ALL, | SIGHASH_ALL, | ||||
SIGHASH_FORKID, | SIGHASH_FORKID, | ||||
SignatureHashForkId, | SignatureHashForkId, | ||||
) | ) | ||||
from test_framework.test_framework import ComparisonTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import assert_equal, assert_raises_rpc_error | from test_framework.util import assert_equal, assert_raises_rpc_error | ||||
# Error for too many sigops in one TX | # Error for too many sigops in one TX | ||||
RPC_TXNS_TOO_MANY_SIGOPS_ERROR = "bad-txns-too-many-sigops" | RPC_TXNS_TOO_MANY_SIGOPS_ERROR = "bad-txns-too-many-sigops" | ||||
class PreviousSpendableOutput(): | class PreviousSpendableOutput(): | ||||
def __init__(self, tx=CTransaction(), n=-1): | def __init__(self, tx=CTransaction(), n=-1): | ||||
self.tx = tx | self.tx = tx | ||||
self.n = n # the output we're spending | # The output we're spending | ||||
self.n = n | |||||
class FullBlockTest(ComparisonTestFramework): | |||||
# Can either run this test as 1 node with expected answers, or two and compare them. | class FullBlockTest(BitcoinTestFramework): | ||||
# Change the "outcome" variable from each TestInstance object to only do | |||||
# the comparison. | |||||
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 | ||||
self.block_heights = {} | self.block_heights = {} | ||||
self.coinbase_key = CECKey() | self.coinbase_key = CECKey() | ||||
self.coinbase_key.set_secretbytes(b"horsebattery") | self.coinbase_key.set_secretbytes(b"horsebattery") | ||||
self.coinbase_pubkey = self.coinbase_key.get_pubkey() | self.coinbase_pubkey = self.coinbase_key.get_pubkey() | ||||
self.tip = None | self.tip = None | ||||
self.blocks = {} | self.blocks = {} | ||||
def setup_network(self): | def setup_network(self): | ||||
self.extra_args = [['-norelaypriority']] | self.extra_args = [['-norelaypriority']] | ||||
self.add_nodes(self.num_nodes, self.extra_args) | self.add_nodes(self.num_nodes, self.extra_args) | ||||
self.start_nodes() | self.start_nodes() | ||||
def add_options(self, parser): | def add_options(self, parser): | ||||
super().add_options(parser) | super().add_options(parser) | ||||
parser.add_argument( | parser.add_argument( | ||||
"--runbarelyexpensive", dest="runbarelyexpensive", default=True) | "--runbarelyexpensive", dest="runbarelyexpensive", default=True) | ||||
def run_test(self): | |||||
self.test = TestManager(self, self.options.tmpdir) | |||||
self.test.add_all_connections(self.nodes) | |||||
network_thread_start() | |||||
self.test.run() | |||||
def add_transactions_to_block(self, block, tx_list): | def add_transactions_to_block(self, block, tx_list): | ||||
[tx.rehash() for tx in tx_list] | [tx.rehash() for tx in tx_list] | ||||
block.vtx.extend(tx_list) | block.vtx.extend(tx_list) | ||||
block.vtx = [block.vtx[0]] + \ | block.vtx = [block.vtx[0]] + \ | ||||
sorted(block.vtx[1:], key=lambda tx: tx.get_id()) | sorted(block.vtx[1:], key=lambda tx: tx.get_id()) | ||||
# this is a little handier to use than the version in blocktools.py | # this is a little handier to use than the version in blocktools.py | ||||
def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])): | def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])): | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE])): | ||||
# Do PoW, which is very inexpensive on regnet | # Do PoW, which is very inexpensive 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 get_tests(self): | def run_test(self): | ||||
node = self.nodes[0] | |||||
node.add_p2p_connection(P2PDataStore()) | |||||
network_thread_start() | |||||
node.p2p.wait_for_verack() | |||||
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 = [] | ||||
# 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) | ||||
# returns a test case that asserts that the current tip was accepted | |||||
def accepted(): | |||||
return TestInstance([[self.tip, True]]) | |||||
# returns a test case that asserts that the current tip was rejected | |||||
def rejected(reject=None): | |||||
if reject is None: | |||||
return TestInstance([[self.tip, False]]) | |||||
else: | |||||
return TestInstance([[self.tip, reject]]) | |||||
# move the tip back to a previous block | # move the tip back to a previous block | ||||
def tip(number): | def tip(number): | ||||
self.tip = self.blocks[number] | self.tip = self.blocks[number] | ||||
# 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] | ||||
self.add_transactions_to_block(block, new_transactions) | self.add_transactions_to_block(block, new_transactions) | ||||
old_sha256 = block.sha256 | old_sha256 = block.sha256 | ||||
block.hashMerkleRoot = block.calc_merkle_root() | block.hashMerkleRoot = block.calc_merkle_root() | ||||
block.solve() | block.solve() | ||||
# Update the internal state just like in next_block | # Update the internal state just like in next_block | ||||
self.tip = block | self.tip = block | ||||
if block.sha256 != old_sha256: | if block.sha256 != old_sha256: | ||||
self.block_heights[ | self.block_heights[ | ||||
block.sha256] = self.block_heights[old_sha256] | block.sha256] = self.block_heights[old_sha256] | ||||
del self.block_heights[old_sha256] | del self.block_heights[old_sha256] | ||||
self.blocks[block_number] = block | self.blocks[block_number] = block | ||||
return block | return block | ||||
# shorthand for functions | # shorthand for functions | ||||
block = self.next_block | block = self.next_block | ||||
# shorthand for variables | |||||
node = self.nodes[0] | |||||
# Create a new block | # Create a new block | ||||
block(0) | block(0) | ||||
save_spendable_output() | save_spendable_output() | ||||
yield accepted() | node.p2p.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. | ||||
test = TestInstance(sync_every_block=False) | maturity_blocks = [] | ||||
for i in range(99): | for i in range(99): | ||||
block(5000 + i) | block(5000 + i) | ||||
test.blocks_and_transactions.append([self.tip, True]) | maturity_blocks.append(self.tip) | ||||
save_spendable_output() | save_spendable_output() | ||||
yield test | node.p2p.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(33): | for i in range(33): | ||||
out.append(get_spendable_output()) | out.append(get_spendable_output()) | ||||
# P2SH | # P2SH | ||||
# Build the redeem script, hash it, use hash to create the p2sh script | # Build the redeem script, hash it, use hash to create the p2sh script | ||||
Show All 21 Lines | def run_test(self): | ||||
# P2SH tests | # P2SH tests | ||||
# Create a p2sh transaction | # Create a p2sh transaction | ||||
p2sh_tx = self.create_and_sign_transaction( | p2sh_tx = self.create_and_sign_transaction( | ||||
out[0].tx, out[0].n, 1, p2sh_script) | out[0].tx, out[0].n, 1, p2sh_script) | ||||
# Add the transaction to the block | # Add the transaction to the block | ||||
block(1) | block(1) | ||||
update_block(1, [p2sh_tx]) | update_block(1, [p2sh_tx]) | ||||
yield accepted() | node.p2p.send_blocks_and_test([self.tip], node) | ||||
# Sigops p2sh limit for the mempool test | # Sigops p2sh limit for the mempool test | ||||
p2sh_sigops_limit_mempool = MAX_STANDARD_TX_SIGOPS - \ | p2sh_sigops_limit_mempool = MAX_STANDARD_TX_SIGOPS - \ | ||||
redeem_script.GetSigOpCount(True) | redeem_script.GetSigOpCount(True) | ||||
# Too many sigops in one p2sh script | # Too many sigops in one p2sh script | ||||
too_many_p2sh_sigops_mempool = CScript( | too_many_p2sh_sigops_mempool = CScript( | ||||
[OP_CHECKSIG] * (p2sh_sigops_limit_mempool + 1)) | [OP_CHECKSIG] * (p2sh_sigops_limit_mempool + 1)) | ||||
Show All 12 Lines | def run_test(self): | ||||
max_p2sh_sigops_txn = spend_p2sh_tx(p2sh_tx, max_p2sh_sigops_mempool) | max_p2sh_sigops_txn = spend_p2sh_tx(p2sh_tx, max_p2sh_sigops_mempool) | ||||
max_p2sh_sigops_txn_id = node.sendrawtransaction( | max_p2sh_sigops_txn_id = node.sendrawtransaction( | ||||
ToHex(max_p2sh_sigops_txn)) | ToHex(max_p2sh_sigops_txn)) | ||||
assert_equal(set(node.getrawmempool()), {max_p2sh_sigops_txn_id}) | assert_equal(set(node.getrawmempool()), {max_p2sh_sigops_txn_id}) | ||||
# Mine the transaction | # Mine the transaction | ||||
block(2, spend=out[1]) | block(2, spend=out[1]) | ||||
update_block(2, [max_p2sh_sigops_txn]) | update_block(2, [max_p2sh_sigops_txn]) | ||||
yield accepted() | node.p2p.send_blocks_and_test([self.tip], node) | ||||
# The transaction has been mined, it's not in the mempool anymore | # The transaction has been mined, it's not in the mempool anymore | ||||
assert_equal(set(node.getrawmempool()), set()) | assert_equal(set(node.getrawmempool()), set()) | ||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
FullBlockTest().main() | FullBlockTest().main() |