diff --git a/test/functional/abc-mempool-accept-txn.py b/test/functional/abc-mempool-accept-txn.py index a73f4695de..bacfac0f5f 100755 --- a/test/functional/abc-mempool-accept-txn.py +++ b/test/functional/abc-mempool-accept-txn.py @@ -1,267 +1,262 @@ #!/usr/bin/env python3 # Copyright (c) 2017 The Bitcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """ This test checks acceptance of transactions by the mempool It is derived from the much more complex p2p-fullblocktest. """ import time from test_framework.blocktools import ( create_block, create_coinbase, create_transaction, ) from test_framework.cdefs import MAX_STANDARD_TX_SIGOPS from test_framework.key import CECKey from test_framework.messages import ( COutPoint, CTransaction, CTxIn, CTxOut, ToHex, ) from test_framework.mininode import ( P2PDataStore, ) from test_framework.script import ( CScript, hash160, OP_2DUP, OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_EQUAL, OP_HASH160, OP_TRUE, SIGHASH_ALL, SIGHASH_FORKID, SignatureHashForkId, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error # Error for too many sigops in one TX RPC_TXNS_TOO_MANY_SIGOPS_ERROR = "bad-txns-too-many-sigops" class PreviousSpendableOutput(): def __init__(self, tx=CTransaction(), n=-1): self.tx = tx # The output we're spending self.n = n class FullBlockTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True self.block_heights = {} self.coinbase_key = CECKey() self.coinbase_key.set_secretbytes(b"horsebattery") self.coinbase_pubkey = self.coinbase_key.get_pubkey() self.tip = None self.blocks = {} - def setup_network(self): - self.extra_args = [['-norelaypriority']] - self.add_nodes(self.num_nodes, self.extra_args) - self.start_nodes() - def add_options(self, parser): super().add_options(parser) parser.add_argument( "--runbarelyexpensive", dest="runbarelyexpensive", default=True) def add_transactions_to_block(self, block, tx_list): [tx.rehash() for tx in tx_list] block.vtx.extend(tx_list) block.vtx = [block.vtx[0]] + \ sorted(block.vtx[1:], key=lambda tx: tx.get_id()) # 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])): tx = create_transaction(spend_tx, n, b"", value, script) return tx # sign a transaction, using the key we know about # this signs input 0 in tx, which is assumed to be spending output n in # spend_tx def sign_tx(self, tx, spend_tx, n): scriptPubKey = bytearray(spend_tx.vout[n].scriptPubKey) if (scriptPubKey[0] == OP_TRUE): # an anyone-can-spend tx.vin[0].scriptSig = CScript() return sighash = SignatureHashForkId( spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL | SIGHASH_FORKID, spend_tx.vout[n].nValue) tx.vin[0].scriptSig = CScript( [self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID]))]) def create_and_sign_transaction(self, spend_tx, n, value, script=CScript([OP_TRUE])): tx = self.create_tx(spend_tx, n, value, script) self.sign_tx(tx, spend_tx, n) tx.rehash() return tx def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE])): if self.tip == None: base_block_hash = self.genesis_hash block_time = int(time.time()) + 1 else: base_block_hash = self.tip.sha256 block_time = self.tip.nTime + 1 # First create the coinbase height = self.block_heights[base_block_hash] + 1 coinbase = create_coinbase(height, self.coinbase_pubkey) coinbase.vout[0].nValue += additional_coinbase_value coinbase.rehash() if spend == None: block = create_block(base_block_hash, coinbase, block_time) else: # all but one satoshi to fees coinbase.vout[0].nValue += spend.tx.vout[ spend.n].nValue - 1 coinbase.rehash() block = create_block(base_block_hash, coinbase, block_time) # spend 1 satoshi tx = create_transaction(spend.tx, spend.n, b"", 1, script) self.sign_tx(tx, spend.tx, spend.n) self.add_transactions_to_block(block, [tx]) block.hashMerkleRoot = block.calc_merkle_root() # Do PoW, which is very inexpensive on regnet block.solve() self.tip = block self.block_heights[block.sha256] = height assert number not in self.blocks self.blocks[number] = block return block def run_test(self): node = self.nodes[0] node.add_p2p_connection(P2PDataStore()) self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # move the tip back to a previous block def tip(number): self.tip = self.blocks[number] # adds transactions to the block and updates state def update_block(block_number, new_transactions): block = self.blocks[block_number] self.add_transactions_to_block(block, new_transactions) old_sha256 = block.sha256 block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Update the internal state just like in next_block self.tip = block if block.sha256 != old_sha256: self.block_heights[ block.sha256] = self.block_heights[old_sha256] del self.block_heights[old_sha256] self.blocks[block_number] = block return block # shorthand for functions block = self.next_block # Create a new block block(0) save_spendable_output() node.p2p.send_blocks_and_test([self.tip], node) # Now we need that block to mature so we can spend the coinbase. maturity_blocks = [] for i in range(99): block(5000 + i) maturity_blocks.append(self.tip) save_spendable_output() node.p2p.send_blocks_and_test(maturity_blocks, node) # Collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(33): out.append(get_spendable_output()) # P2SH # Build the redeem script, hash it, use hash to create the p2sh script redeem_script = CScript([self.coinbase_pubkey] + [ OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG]) redeem_script_hash = hash160(redeem_script) p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) # Creates a new transaction using a p2sh transaction as input def spend_p2sh_tx(p2sh_tx_to_spend, output_script=CScript([OP_TRUE])): # Create the transaction spent_p2sh_tx = CTransaction() spent_p2sh_tx.vin.append( CTxIn(COutPoint(p2sh_tx_to_spend.sha256, 0), b'')) - spent_p2sh_tx.vout.append(CTxOut(1, output_script)) + spent_p2sh_tx.vout.append(CTxOut(1000, output_script)) # Sign the transaction using the redeem script sighash = SignatureHashForkId( redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx_to_spend.vout[0].nValue) sig = self.coinbase_key.sign( sighash) + bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) spent_p2sh_tx.rehash() return spent_p2sh_tx # P2SH tests # Create a p2sh transaction p2sh_tx = self.create_and_sign_transaction( - out[0].tx, out[0].n, 1, p2sh_script) + out[0].tx, out[0].n, 10000, p2sh_script) # Add the transaction to the block block(1) update_block(1, [p2sh_tx]) node.p2p.send_blocks_and_test([self.tip], node) # Sigops p2sh limit for the mempool test p2sh_sigops_limit_mempool = MAX_STANDARD_TX_SIGOPS - \ redeem_script.GetSigOpCount(True) # Too many sigops in one p2sh script too_many_p2sh_sigops_mempool = CScript( [OP_CHECKSIG] * (p2sh_sigops_limit_mempool + 1)) # A transaction with this output script can't get into the mempool assert_raises_rpc_error(-26, RPC_TXNS_TOO_MANY_SIGOPS_ERROR, node.sendrawtransaction, ToHex(spend_p2sh_tx(p2sh_tx, too_many_p2sh_sigops_mempool))) # The transaction is rejected, so the mempool should still be empty assert_equal(set(node.getrawmempool()), set()) # Max sigops in one p2sh txn max_p2sh_sigops_mempool = CScript( [OP_CHECKSIG] * (p2sh_sigops_limit_mempool)) # A transaction with this output script can get into the mempool max_p2sh_sigops_txn = spend_p2sh_tx(p2sh_tx, max_p2sh_sigops_mempool) max_p2sh_sigops_txn_id = node.sendrawtransaction( ToHex(max_p2sh_sigops_txn)) assert_equal(set(node.getrawmempool()), {max_p2sh_sigops_txn_id}) # Mine the transaction block(2, spend=out[1]) update_block(2, [max_p2sh_sigops_txn]) node.p2p.send_blocks_and_test([self.tip], node) # The transaction has been mined, it's not in the mempool anymore assert_equal(set(node.getrawmempool()), set()) if __name__ == '__main__': FullBlockTest().main() diff --git a/test/functional/abc-p2p-compactblocks.py b/test/functional/abc-p2p-compactblocks.py index 696542865b..560402d0b6 100755 --- a/test/functional/abc-p2p-compactblocks.py +++ b/test/functional/abc-p2p-compactblocks.py @@ -1,355 +1,358 @@ #!/usr/bin/env python3 # Copyright (c) 2015-2016 The Bitcoin Core developers # Copyright (c) 2017 The Bitcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """ This test checks simple acceptance of bigger blocks via p2p. It is derived from the much more complex p2p-fullblocktest. The intention is that small tests can be derived from this one, or this one can be extended, to cover the checks done for bigger blocks (e.g. sigops limits). """ from collections import deque import random import time from test_framework.blocktools import ( create_block, create_coinbase, create_transaction, make_conform_to_ctor, ) from test_framework.cdefs import ONE_MEGABYTE from test_framework.messages import ( COutPoint, CTransaction, CTxIn, CTxOut, HeaderAndShortIDs, msg_cmpctblock, msg_sendcmpct, ser_compact_size, ) from test_framework.mininode import ( mininode_lock, P2PDataStore, P2PInterface, ) from test_framework.script import CScript, OP_RETURN, OP_TRUE from test_framework.test_framework import BitcoinTestFramework from test_framework.txtools import pad_tx from test_framework.util import assert_equal, wait_until class PreviousSpendableOutput(): def __init__(self, tx=CTransaction(), n=-1): self.tx = tx # the output we're spending self.n = n # TestP2PConn: A peer we use to send messages to bitcoind, and store responses. class TestP2PConn(P2PInterface): def __init__(self): self.last_sendcmpct = None self.last_cmpctblock = None self.last_getheaders = None self.last_headers = None super().__init__() def on_sendcmpct(self, message): self.last_sendcmpct = message def on_cmpctblock(self, message): self.last_cmpctblock = message self.last_cmpctblock.header_and_shortids.header.calc_sha256() def on_getheaders(self, message): self.last_getheaders = message def on_headers(self, message): self.last_headers = message for x in self.last_headers.headers: x.calc_sha256() def clear_block_data(self): with mininode_lock: self.last_sendcmpct = None self.last_cmpctblock = None class FullBlockTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True self.block_heights = {} self.tip = None self.blocks = {} self.excessive_block_size = 16 * ONE_MEGABYTE + # -norelaypriority is needed so that the invalidateblock at the end + # will insert free transactions back into the mempool. self.extra_args = [['-norelaypriority', '-whitelist=127.0.0.1', '-limitancestorcount=999999', '-limitancestorsize=999999', '-limitdescendantcount=999999', '-limitdescendantsize=999999', '-maxmempool=99999', "-excessiveblocksize={}".format(self.excessive_block_size)]] def add_options(self, parser): super().add_options(parser) parser.add_argument( "--runbarelyexpensive", dest="runbarelyexpensive", default=True) def add_transactions_to_block(self, block, tx_list): [tx.rehash() for tx in tx_list] block.vtx.extend(tx_list) # 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])): tx = create_transaction(spend_tx, n, b"", value, script) return tx def next_block(self, number, spend=None, script=CScript([OP_TRUE]), block_size=0, extra_txns=0): if self.tip == None: base_block_hash = self.genesis_hash block_time = int(time.time()) + 1 else: base_block_hash = self.tip.sha256 block_time = self.tip.nTime + 1 # First create the coinbase height = self.block_heights[base_block_hash] + 1 coinbase = create_coinbase(height) coinbase.rehash() if spend == None: # We need to have something to spend to fill the block. assert_equal(block_size, 0) block = create_block(base_block_hash, coinbase, block_time) else: # all but one satoshi to fees coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 coinbase.rehash() block = create_block(base_block_hash, coinbase, block_time) # Make sure we have plenty enough to spend going forward. spendable_outputs = deque([spend]) def get_base_transaction(): # Create the new transaction tx = CTransaction() # Spend from one of the spendable outputs spend = spendable_outputs.popleft() tx.vin.append(CTxIn(COutPoint(spend.tx.sha256, spend.n))) # Add spendable outputs for i in range(4): tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) spendable_outputs.append(PreviousSpendableOutput(tx, i)) pad_tx(tx) return tx tx = get_base_transaction() # Make it the same format as transaction added for padding and save the size. # It's missing the padding output, so we add a constant to account for it. tx.rehash() # If a specific script is required, add it. if script != None: tx.vout.append(CTxOut(1, script)) # Put some random data into the first transaction of the chain to randomize ids. tx.vout.append( CTxOut(0, CScript([random.randint(0, 256), OP_RETURN]))) # Add the transaction to the block self.add_transactions_to_block(block, [tx]) # Add transaction until we reach the expected transaction count for _ in range(extra_txns): self.add_transactions_to_block(block, [get_base_transaction()]) # If we have a block size requirement, just fill # the block until we get there current_block_size = len(block.serialize()) overage_bytes = 0 while current_block_size < block_size: # We will add a new transaction. That means the size of # the field enumerating how many transaction go in the block # may change. current_block_size -= len(ser_compact_size(len(block.vtx))) current_block_size += len(ser_compact_size(len(block.vtx) + 1)) # Add padding to fill the block. left_to_fill = block_size - current_block_size # Don't go over the 1 mb limit for a txn if left_to_fill > 500000: # Make sure we eat up non-divisible by 100 amounts quickly # Also keep transaction less than 1 MB left_to_fill = 500000 + left_to_fill % 100 # Create the new transaction tx = get_base_transaction() pad_tx(tx, left_to_fill - overage_bytes) if len(tx.serialize()) + current_block_size > block_size: # Our padding was too big try again overage_bytes += 1 continue # Add the tx to the list of transactions to be included # in the block. self.add_transactions_to_block(block, [tx]) current_block_size += len(tx.serialize()) # Now that we added a bunch of transaction, we need to recompute # the merkle root. make_conform_to_ctor(block) block.hashMerkleRoot = block.calc_merkle_root() # Check that the block size is what's expected if block_size > 0: assert_equal(len(block.serialize()), block_size) # Do PoW, which is cheap on regnet block.solve() self.tip = block self.block_heights[block.sha256] = height assert number not in self.blocks self.blocks[number] = block return block def run_test(self): node = self.nodes[0] default_p2p = node.add_p2p_connection(P2PDataStore()) test_p2p = node.add_p2p_connection(TestP2PConn()) # Set the blocksize to 2MB as initial condition node.setexcessiveblock(self.excessive_block_size) self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # move the tip back to a previous block def tip(number): self.tip = self.blocks[number] # shorthand for functions block = self.next_block # Create a new block block(0) save_spendable_output() default_p2p.send_blocks_and_test([self.tip], node) # Now we need that block to mature so we can spend the coinbase. maturity_blocks = [] for i in range(99): block(5000 + i) maturity_blocks.append(self.tip) save_spendable_output() # Get to one block of the May 15, 2018 HF activation for i in range(6): block(5100 + i) maturity_blocks.append(self.tip) # Send it all to the node at once. default_p2p.send_blocks_and_test(maturity_blocks, node) # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Check that compact block also work for big blocks # Wait for SENDCMPCT def received_sendcmpct(): return (test_p2p.last_sendcmpct != None) wait_until(received_sendcmpct, timeout=30) sendcmpct = msg_sendcmpct() sendcmpct.version = 1 sendcmpct.announce = True test_p2p.send_and_ping(sendcmpct) # Exchange headers def received_getheaders(): return (test_p2p.last_getheaders != None) wait_until(received_getheaders, timeout=30) # Return the favor test_p2p.send_message(test_p2p.last_getheaders) # Wait for the header list def received_headers(): return (test_p2p.last_headers != None) wait_until(received_headers, timeout=30) # It's like we know about the same headers ! test_p2p.send_message(test_p2p.last_headers) # Send a block b1 = block(1, spend=out[0], block_size=ONE_MEGABYTE + 1) default_p2p.send_blocks_and_test([self.tip], node) # Checks the node to forward it via compact block def received_block(): return (test_p2p.last_cmpctblock != None) wait_until(received_block, timeout=30) # Was it our block ? cmpctblk_header = test_p2p.last_cmpctblock.header_and_shortids.header cmpctblk_header.calc_sha256() assert cmpctblk_header.sha256 == b1.sha256 # Send a large block with numerous transactions. test_p2p.clear_block_data() b2 = block(2, spend=out[1], extra_txns=70000, block_size=self.excessive_block_size - 1000) default_p2p.send_blocks_and_test([self.tip], node) # Checks the node forwards it via compact block wait_until(received_block, timeout=30) # Was it our block ? cmpctblk_header = test_p2p.last_cmpctblock.header_and_shortids.header cmpctblk_header.calc_sha256() assert cmpctblk_header.sha256 == b2.sha256 # In order to avoid having to resend a ton of transactions, we invalidate - # b2, which will send all its transactions in the mempool. + # b2, which will send all its transactions in the mempool. Note that this + # assumes reorgs will insert low-fee transactions back into the mempool. node.invalidateblock(node.getbestblockhash()) # Let's send a compact block and see if the node accepts it. # Let's modify b2 and use it so that we can reuse the mempool. tx = b2.vtx[0] tx.vout.append(CTxOut(0, CScript([random.randint(0, 256), OP_RETURN]))) tx.rehash() b2.vtx[0] = tx b2.hashMerkleRoot = b2.calc_merkle_root() b2.solve() # Now we create the compact block and send it comp_block = HeaderAndShortIDs() comp_block.initialize_from_block(b2) test_p2p.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) # Check that compact block is received properly assert int(node.getbestblockhash(), 16) == b2.sha256 if __name__ == '__main__': FullBlockTest().main() diff --git a/test/functional/abc-replay-protection.py b/test/functional/abc-replay-protection.py index f660615d93..70c3e45f45 100755 --- a/test/functional/abc-replay-protection.py +++ b/test/functional/abc-replay-protection.py @@ -1,316 +1,316 @@ #!/usr/bin/env python3 # Copyright (c) 2015-2016 The Bitcoin Core developers # Copyright (c) 2017 The Bitcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """ This test checks activation of UAHF and the different consensus related to this activation. It is derived from the much more complex p2p-fullblocktest. """ import time from test_framework.blocktools import ( create_block, create_coinbase, create_transaction, make_conform_to_ctor, ) from test_framework.key import CECKey from test_framework.messages import ( COIN, COutPoint, CTransaction, CTxIn, CTxOut, ToHex, ) from test_framework.mininode import P2PDataStore from test_framework.script import ( CScript, OP_CHECKSIG, OP_TRUE, SIGHASH_ALL, SIGHASH_FORKID, SignatureHashForkId, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error # far into the future REPLAY_PROTECTION_START_TIME = 2000000000 # Error due to invalid signature RPC_INVALID_SIGNATURE_ERROR = "mandatory-script-verify-flag-failed (Signature must be zero for failed CHECK(MULTI)SIG operation) (code 16)" class PreviousSpendableOutput(object): def __init__(self, tx=CTransaction(), n=-1): self.tx = tx self.n = n class ReplayProtectionTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True self.block_heights = {} self.tip = None self.blocks = {} self.extra_args = [['-whitelist=127.0.0.1', "-replayprotectionactivationtime={}".format(REPLAY_PROTECTION_START_TIME)]] def next_block(self, number): if self.tip == None: base_block_hash = self.genesis_hash block_time = int(time.time()) + 1 else: base_block_hash = self.tip.sha256 block_time = self.tip.nTime + 1 # First create the coinbase height = self.block_heights[base_block_hash] + 1 coinbase = create_coinbase(height) coinbase.rehash() block = create_block(base_block_hash, coinbase, block_time) # Do PoW, which is cheap on regnet block.solve() self.tip = block self.block_heights[block.sha256] = height assert number not in self.blocks self.blocks[number] = block return block def run_test(self): node = self.nodes[0] node.add_p2p_connection(P2PDataStore()) node.setmocktime(REPLAY_PROTECTION_START_TIME) self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # move the tip back to a previous block def tip(number): self.tip = self.blocks[number] # adds transactions to the block and updates state def update_block(block_number, new_transactions): block = self.blocks[block_number] block.vtx.extend(new_transactions) old_sha256 = block.sha256 make_conform_to_ctor(block) block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Update the internal state just like in next_block self.tip = block if block.sha256 != old_sha256: self.block_heights[ block.sha256] = self.block_heights[old_sha256] del self.block_heights[old_sha256] self.blocks[block_number] = block return block # shorthand block = self.next_block # Create a new block block(0) save_spendable_output() node.p2p.send_blocks_and_test([self.tip], node) # Now we need that block to mature so we can spend the coinbase. maturity_blocks = [] for i in range(99): block(5000 + i) maturity_blocks.append(self.tip) save_spendable_output() node.p2p.send_blocks_and_test(maturity_blocks, node) # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Generate a key pair to test P2SH sigops count private_key = CECKey() private_key.set_secretbytes(b"replayprotection") public_key = private_key.get_pubkey() # This is a little handier to use than the version in blocktools.py def create_fund_and_spend_tx(spend, forkvalue=0): # Fund transaction script = CScript([public_key, OP_CHECKSIG]) txfund = create_transaction( - spend.tx, spend.n, b'', 50 * COIN, script) + spend.tx, spend.n, b'', 50 * COIN - 1000, script) txfund.rehash() # Spend transaction txspend = CTransaction() - txspend.vout.append(CTxOut(50 * COIN - 1000, CScript([OP_TRUE]))) + txspend.vout.append(CTxOut(50 * COIN - 2000, CScript([OP_TRUE]))) txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b'')) # Sign the transaction sighashtype = (forkvalue << 8) | SIGHASH_ALL | SIGHASH_FORKID sighash = SignatureHashForkId( - script, txspend, 0, sighashtype, 50 * COIN) + script, txspend, 0, sighashtype, 50 * COIN - 1000) sig = private_key.sign(sighash) + \ bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) txspend.vin[0].scriptSig = CScript([sig]) txspend.rehash() return [txfund, txspend] def send_transaction_to_mempool(tx): tx_id = node.sendrawtransaction(ToHex(tx)) assert tx_id in set(node.getrawmempool()) return tx_id # Before the fork, no replay protection required to get in the mempool. txns = create_fund_and_spend_tx(out[0]) send_transaction_to_mempool(txns[0]) send_transaction_to_mempool(txns[1]) # And txns get mined in a block properly. block(1) update_block(1, txns) node.p2p.send_blocks_and_test([self.tip], node) # Replay protected transactions are rejected. replay_txns = create_fund_and_spend_tx(out[1], 0xffdead) send_transaction_to_mempool(replay_txns[0]) assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, node.sendrawtransaction, ToHex(replay_txns[1])) # And block containing them are rejected as well. block(2) update_block(2, replay_txns) node.p2p.send_blocks_and_test( [self.tip], node, success=False, reject_reason='blk-bad-inputs') # Rewind bad block tip(1) # Create a block that would activate the replay protection. bfork = block(5555) bfork.nTime = REPLAY_PROTECTION_START_TIME - 1 update_block(5555, []) node.p2p.send_blocks_and_test([self.tip], node) activation_blocks = [] for i in range(5): block(5100 + i) activation_blocks.append(self.tip) node.p2p.send_blocks_and_test(activation_blocks, node) # Check we are just before the activation time assert_equal(node.getblockheader(node.getbestblockhash())['mediantime'], REPLAY_PROTECTION_START_TIME - 1) # We are just before the fork, replay protected txns still are rejected assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, node.sendrawtransaction, ToHex(replay_txns[1])) block(3) update_block(3, replay_txns) node.p2p.send_blocks_and_test( [self.tip], node, success=False, reject_reason='blk-bad-inputs') # Rewind bad block tip(5104) # Send some non replay protected txns in the mempool to check # they get cleaned at activation. txns = create_fund_and_spend_tx(out[2]) send_transaction_to_mempool(txns[0]) tx_id = send_transaction_to_mempool(txns[1]) # Activate the replay protection block(5556) node.p2p.send_blocks_and_test([self.tip], node) # Check we just activated the replay protection assert_equal(node.getblockheader(node.getbestblockhash())['mediantime'], REPLAY_PROTECTION_START_TIME) # Non replay protected transactions are not valid anymore, # so they should be removed from the mempool. assert tx_id not in set(node.getrawmempool()) # Good old transactions are now invalid. send_transaction_to_mempool(txns[0]) assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, node.sendrawtransaction, ToHex(txns[1])) # They also cannot be mined block(4) update_block(4, txns) node.p2p.send_blocks_and_test( [self.tip], node, success=False, reject_reason='blk-bad-inputs') # Rewind bad block tip(5556) # The replay protected transaction is now valid replay_tx0_id = send_transaction_to_mempool(replay_txns[0]) replay_tx1_id = send_transaction_to_mempool(replay_txns[1]) # Make sure the transaction are ready to be mined. tmpl = node.getblocktemplate() found_id0 = False found_id1 = False for txn in tmpl['transactions']: txid = txn['txid'] if txid == replay_tx0_id: found_id0 = True elif txid == replay_tx1_id: found_id1 = True assert found_id0 and found_id1 # And the mempool is still in good shape. assert replay_tx0_id in set(node.getrawmempool()) assert replay_tx1_id in set(node.getrawmempool()) # They also can also be mined block(5) update_block(5, replay_txns) node.p2p.send_blocks_and_test([self.tip], node) # Ok, now we check if a reorg work properly across the activation. postforkblockid = node.getbestblockhash() node.invalidateblock(postforkblockid) assert replay_tx0_id in set(node.getrawmempool()) assert replay_tx1_id in set(node.getrawmempool()) # Deactivating replay protection. forkblockid = node.getbestblockhash() node.invalidateblock(forkblockid) # The funding tx is not evicted from the mempool, since it's valid in # both sides of the fork assert replay_tx0_id in set(node.getrawmempool()) assert replay_tx1_id not in set(node.getrawmempool()) # Check that we also do it properly on deeper reorg. node.reconsiderblock(forkblockid) node.reconsiderblock(postforkblockid) node.invalidateblock(forkblockid) assert replay_tx0_id in set(node.getrawmempool()) assert replay_tx1_id not in set(node.getrawmempool()) if __name__ == '__main__': ReplayProtectionTest().main() diff --git a/test/functional/abc-rpc.py b/test/functional/abc-rpc.py index 13c3312623..c52d00a625 100755 --- a/test/functional/abc-rpc.py +++ b/test/functional/abc-rpc.py @@ -1,86 +1,85 @@ #!/usr/bin/env python3 # Copyright (c) 2017 The Bitcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. # Exercise the Bitcoin ABC RPC calls. import re from test_framework.cdefs import ( DEFAULT_MAX_BLOCK_SIZE, LEGACY_MAX_BLOCK_SIZE, ONE_MEGABYTE, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error class ABC_RPC_Test (BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.tip = None self.setup_clean_chain = True - self.extra_args = [['-norelaypriority', - '-whitelist=127.0.0.1']] + self.extra_args = [['-whitelist=127.0.0.1']] def check_subversion(self, pattern_str): # Check that the subversion is set as expected netinfo = self.nodes[0].getnetworkinfo() subversion = netinfo['subversion'] pattern = re.compile(pattern_str) assert pattern.match(subversion) def test_excessiveblock(self): # Check that we start with DEFAULT_MAX_BLOCK_SIZE getsize = self.nodes[0].getexcessiveblock() ebs = getsize['excessiveBlockSize'] assert_equal(ebs, DEFAULT_MAX_BLOCK_SIZE) # Check that setting to legacy size is ok self.nodes[0].setexcessiveblock(LEGACY_MAX_BLOCK_SIZE + 1) getsize = self.nodes[0].getexcessiveblock() ebs = getsize['excessiveBlockSize'] assert_equal(ebs, LEGACY_MAX_BLOCK_SIZE + 1) # Check that going below legacy size is not accepted assert_raises_rpc_error(-8, "Invalid parameter, excessiveblock must be larger than {}".format( LEGACY_MAX_BLOCK_SIZE), self.nodes[0].setexcessiveblock, LEGACY_MAX_BLOCK_SIZE) getsize = self.nodes[0].getexcessiveblock() ebs = getsize['excessiveBlockSize'] assert_equal(ebs, LEGACY_MAX_BLOCK_SIZE + 1) # Check setting to 2MB self.nodes[0].setexcessiveblock(2 * ONE_MEGABYTE) getsize = self.nodes[0].getexcessiveblock() ebs = getsize['excessiveBlockSize'] assert_equal(ebs, 2 * ONE_MEGABYTE) # Check for EB correctness in the subver string self.check_subversion(r"/Bitcoin ABC:.*\(EB2\.0; .*\)/") # Check setting to 13MB self.nodes[0].setexcessiveblock(13 * ONE_MEGABYTE) getsize = self.nodes[0].getexcessiveblock() ebs = getsize['excessiveBlockSize'] assert_equal(ebs, 13 * ONE_MEGABYTE) # Check for EB correctness in the subver string self.check_subversion(r"/Bitcoin ABC:.*\(EB13\.0; .*\)/") # Check setting to 13.14MB self.nodes[0].setexcessiveblock(13140000) getsize = self.nodes[0].getexcessiveblock() ebs = getsize['excessiveBlockSize'] assert_equal(ebs, 13.14 * ONE_MEGABYTE) # check for EB correctness in the subver string self.check_subversion(r"/Bitcoin ABC:.*\(EB13\.1; .*\)/") def run_test(self): self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) self.test_excessiveblock() if __name__ == '__main__': ABC_RPC_Test().main() diff --git a/test/functional/abc-transaction-ordering.py b/test/functional/abc-transaction-ordering.py index 2217a2d9ed..b99b8abd60 100755 --- a/test/functional/abc-transaction-ordering.py +++ b/test/functional/abc-transaction-ordering.py @@ -1,234 +1,233 @@ #!/usr/bin/env python3 # Copyright (c) 2018 The Bitcoin developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """ This test checks that the node software accepts transactions in non topological order once the feature is activated. """ from collections import deque import random import time from test_framework.blocktools import ( create_block, create_coinbase, make_conform_to_ctor, ) from test_framework.messages import ( COutPoint, CTransaction, CTxIn, CTxOut, ) from test_framework.mininode import P2PDataStore from test_framework.script import ( CScript, OP_RETURN, OP_TRUE, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class PreviousSpendableOutput(): def __init__(self, tx=CTransaction(), n=-1): self.tx = tx self.n = n # the output we're spending class TransactionOrderingTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True self.block_heights = {} self.tip = None self.blocks = {} - self.extra_args = [['-whitelist=127.0.0.1', - '-relaypriority=0']] + self.extra_args = [['-whitelist=127.0.0.1']] def add_transactions_to_block(self, block, tx_list): [tx.rehash() for tx in tx_list] block.vtx.extend(tx_list) def next_block(self, number, spend=None, tx_count=0): if self.tip == None: base_block_hash = self.genesis_hash block_time = int(time.time()) + 1 else: base_block_hash = self.tip.sha256 block_time = self.tip.nTime + 1 # First create the coinbase height = self.block_heights[base_block_hash] + 1 coinbase = create_coinbase(height) coinbase.rehash() if spend == None: # We need to have something to spend to fill the block. block = create_block(base_block_hash, coinbase, block_time) else: # all but one satoshi to fees coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 coinbase.rehash() block = create_block(base_block_hash, coinbase, block_time) # Make sure we have plenty enough to spend going forward. spendable_outputs = deque([spend]) def get_base_transaction(): # Create the new transaction tx = CTransaction() # Spend from one of the spendable outputs spend = spendable_outputs.popleft() tx.vin.append(CTxIn(COutPoint(spend.tx.sha256, spend.n))) # Add spendable outputs for i in range(4): tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) spendable_outputs.append(PreviousSpendableOutput(tx, i)) # Put some random data into the transaction in order to randomize ids. # This also ensures that transaction are larger than 100 bytes. rand = random.getrandbits(256) tx.vout.append(CTxOut(0, CScript([rand, OP_RETURN]))) return tx tx = get_base_transaction() # Make it the same format as transaction added for padding and save the size. # It's missing the padding output, so we add a constant to account for it. tx.rehash() # Add the transaction to the block self.add_transactions_to_block(block, [tx]) # If we have a transaction count requirement, just fill the block until we get there while len(block.vtx) < tx_count: # Create the new transaction and add it. tx = get_base_transaction() self.add_transactions_to_block(block, [tx]) # Now that we added a bunch of transaction, we need to recompute # the merkle root. block.hashMerkleRoot = block.calc_merkle_root() if tx_count > 0: assert_equal(len(block.vtx), tx_count) # Do PoW, which is cheap on regnet block.solve() self.tip = block self.block_heights[block.sha256] = height assert number not in self.blocks self.blocks[number] = block return block def run_test(self): node = self.nodes[0] node.add_p2p_connection(P2PDataStore()) self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # move the tip back to a previous block def tip(number): self.tip = self.blocks[number] # adds transactions to the block and updates state def update_block(block_number, new_transactions=[]): block = self.blocks[block_number] self.add_transactions_to_block(block, new_transactions) old_sha256 = block.sha256 block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Update the internal state just like in next_block self.tip = block if block.sha256 != old_sha256: self.block_heights[block.sha256] = self.block_heights[old_sha256] del self.block_heights[old_sha256] self.blocks[block_number] = block return block # shorthand for functions block = self.next_block # Create a new block block(0) save_spendable_output() node.p2p.send_blocks_and_test([self.tip], node) # Now we need that block to mature so we can spend the coinbase. maturity_blocks = [] for i in range(99): block(5000 + i) maturity_blocks.append(self.tip) save_spendable_output() node.p2p.send_blocks_and_test(maturity_blocks, node) # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Let's build some blocks and test them. for i in range(17): n = i + 1 node.p2p.send_blocks_and_test([block(n)], node) node.p2p.send_blocks_and_test([block(5556)], node) # Block with regular ordering are now rejected. node.p2p.send_blocks_and_test([block( 5557, out[17], tx_count=16)], node, success=False, reject_reason='tx-ordering') # Rewind bad block. tip(5556) # After we activate the Nov 15, 2018 HF, transaction order is enforced. def ordered_block(block_number, spend): b = block(block_number, spend=spend, tx_count=16) make_conform_to_ctor(b) update_block(block_number) return b # Now that the fork activated, we need to order transaction per txid. node.p2p.send_blocks_and_test([ordered_block(4445, out[17])], node) node.p2p.send_blocks_and_test([ordered_block(4446, out[18])], node) # Generate a block with a duplicated transaction. double_tx_block = ordered_block(4447, out[19]) assert_equal(len(double_tx_block.vtx), 16) double_tx_block.vtx = double_tx_block.vtx[:8] + \ [double_tx_block.vtx[8]] + double_tx_block.vtx[8:] update_block(4447) node.p2p.send_blocks_and_test( [self.tip], node, success=False, reject_reason='bad-txns-duplicate') # Rewind bad block. tip(4446) # Check over two blocks. proper_block = ordered_block(4448, out[20]) node.p2p.send_blocks_and_test([self.tip], node) replay_tx_block = ordered_block(4449, out[21]) assert_equal(len(replay_tx_block.vtx), 16) replay_tx_block.vtx.append(proper_block.vtx[5]) replay_tx_block.vtx = [replay_tx_block.vtx[0]] + \ sorted(replay_tx_block.vtx[1:], key=lambda tx: tx.get_id()) update_block(4449) node.p2p.send_blocks_and_test( [self.tip], node, success=False, reject_reason='bad-txns-BIP30') if __name__ == '__main__': TransactionOrderingTest().main()