From 662d6d007ee90846bfc0bc69974da6ae3e7bb02e Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 22 Nov 2017 12:57:58 -0500 Subject: [PATCH 01/14] [tests] Fix flake8 warnings in feature_block.py --- test/functional/p2p-fullblocktest.py | 207 ++++++++++++++------------- 1 file changed, 108 insertions(+), 99 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index 19ed083bf393..f45896f69fdb 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -2,26 +2,58 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test block processing. - -This reimplements tests from the bitcoinj/FullBlockTestGenerator used -by the pull-tester. - -We use the testing framework in which we expect a particular answer from -each test. -""" +"""Test block processing.""" +import copy +import struct +import time +from test_framework.blocktools import create_block, create_coinbase, create_transaction, get_legacy_sigopcount_block +from test_framework.comptool import RejectResult, TestInstance, TestManager +from test_framework.key import CECKey +from test_framework.messages import ( + CBlock, + CBlockHeader, + COIN, + COutPoint, + CTransaction, + CTxIn, + CTxOut, + MAX_BLOCK_BASE_SIZE, + uint256_from_compact, + uint256_from_str, +) +from test_framework.mininode import network_thread_start +from test_framework.script import ( + CScript, + MAX_SCRIPT_ELEMENT_SIZE, + OP_2DUP, + OP_CHECKMULTISIG, + OP_CHECKMULTISIGVERIFY, + OP_CHECKSIG, + OP_CHECKSIGVERIFY, + OP_ELSE, + OP_ENDIF, + OP_EQUAL, + OP_FALSE, + OP_HASH160, + OP_IF, + OP_INVALIDOPCODE, + OP_RETURN, + OP_TRUE, + SIGHASH_ALL, + SignatureHash, + hash160, +) from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * from test_framework.comptool import TestManager, TestInstance, RejectResult from test_framework.blocktools import * from test_framework.key import CECKey from test_framework.script import * -from test_framework.mininode import network_thread_start import struct class PreviousSpendableOutput(object): - def __init__(self, tx = CTransaction(), n = -1): + def __init__(self, tx=CTransaction(), n=-1): self.tx = tx self.n = n # the output we're spending @@ -74,7 +106,7 @@ def run_test(self): self.test.run() 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) # this is a little handier to use than the version in blocktools.py @@ -99,7 +131,7 @@ def create_and_sign_transaction(self, spend_tx, n, value, script=CScript([OP_TRU return tx def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), solve=True): - if self.tip == None: + if self.tip is None: base_block_hash = self.genesis_hash block_time = self.mocktime + 1 else: @@ -110,10 +142,10 @@ def next_block(self, number, spend=None, additional_coinbase_value=0, script=CSc coinbase = create_coinbase(height, self.coinbase_pubkey) coinbase.vout[0].nValue += additional_coinbase_value coinbase.rehash() - if spend == None: + if spend is None: block = create_block(base_block_hash, coinbase, block_time) else: - coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees + coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees coinbase.rehash() block = create_block(base_block_hash, coinbase, block_time) tx = create_transaction(spend.tx, spend.n, b"", 1, script) # spend 1 satoshi @@ -146,7 +178,7 @@ def accepted(): return TestInstance([[self.tip, True]]) # returns a test case that asserts that the current tip was rejected - def rejected(reject = None): + def rejected(reject=None): if reject is None: return TestInstance([[self.tip, False]]) else: @@ -179,14 +211,12 @@ def update_block(block_number, new_transactions): # these must be updated if consensus changes MAX_BLOCK_SIGOPS = 20000 - # Create a new block block(0) save_spendable_output() yield accepted() - - # 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) for i in range(99): block(5000 + i) @@ -221,7 +251,6 @@ def update_block(block_number, new_transactions): txout_b3 = PreviousSpendableOutput(b3.vtx[1], 0) yield rejected() - # Now we add another block to make the alternative chain longer. # # genesis -> b1 (0) -> b2 (1) @@ -229,7 +258,6 @@ def update_block(block_number, new_transactions): block(4, spend=out[2]) yield accepted() - # ... and back to the first chain. # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b3 (1) -> b4 (2) @@ -271,7 +299,6 @@ def update_block(block_number, new_transactions): block(11, spend=out[4], additional_coinbase_value=1) yield rejected(RejectResult(16, b'bad-cb-amount')) - # Try again, but with a valid fork first # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b14 (5) @@ -291,7 +318,7 @@ def update_block(block_number, new_transactions): block(14, spend=out[5], additional_coinbase_value=1) yield rejected() - yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13. + yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13. # Add a block with MAX_BLOCK_SIGOPS and one with one more sigop # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -305,13 +332,11 @@ def update_block(block_number, new_transactions): yield accepted() save_spendable_output() - # Test that a block with too many checksigs is rejected too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS)) block(16, spend=out[6], script=too_many_checksigs) yield rejected(RejectResult(16, b'bad-blk-sigops')) - # Attempt to spend a transaction created on a different fork # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1]) @@ -374,10 +399,10 @@ def update_block(block_number, new_transactions): tip(15) b24 = block(24, spend=out[6]) script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69 - script_output = CScript([b'\x00' * (script_length+1)]) + script_output = CScript([b'\x00' * (script_length + 1)]) tx.vout = [CTxOut(0, script_output)] b24 = update_block(24, [tx]) - assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE+1) + assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE + 1) yield rejected(RejectResult(16, b'bad-blk-length')) block(25, spend=out[7]) @@ -431,7 +456,7 @@ def update_block(block_number, new_transactions): # # MULTISIG: each op code counts as 20 sigops. To create the edge case, pack another 19 sigops at the end. - lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS-1) // 20) + [OP_CHECKSIG] * 19) + lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19) b31 = block(31, spend=out[8], script=lots_of_multisigs) assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS) yield accepted() @@ -443,10 +468,9 @@ def update_block(block_number, new_transactions): assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS + 1) yield rejected(RejectResult(16, b'bad-blk-sigops')) - # CHECKMULTISIGVERIFY tip(31) - lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS-1) // 20) + [OP_CHECKSIG] * 19) + lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19) block(33, spend=out[9], script=lots_of_multisigs) yield accepted() save_spendable_output() @@ -455,7 +479,6 @@ def update_block(block_number, new_transactions): block(34, spend=out[10], script=too_many_multisigs) yield rejected(RejectResult(16, b'bad-blk-sigops')) - # CHECKSIGVERIFY tip(33) lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS - 1)) @@ -467,7 +490,6 @@ def update_block(block_number, new_transactions): block(36, spend=out[11], script=too_many_checksigs) yield rejected(RejectResult(16, b'bad-blk-sigops')) - # Check spending of a transaction in a block which failed to connect # # b6 (3) @@ -506,7 +528,7 @@ def update_block(block_number, new_transactions): b39_sigops_per_output = 6 # 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 = 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]) @@ -523,7 +545,7 @@ def update_block(block_number, new_transactions): # Until block is full, add tx's with 1 satoshi to p2sh_script, the rest to OP_TRUE tx_new = None tx_last = tx - total_size=len(b39.serialize()) + total_size = len(b39.serialize()) while(total_size < MAX_BLOCK_SIZE): tx_new = create_tx(tx_last, 1, 1, p2sh_script) tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE]))) @@ -531,7 +553,7 @@ def update_block(block_number, new_transactions): total_size += len(tx_new.serialize()) if total_size >= MAX_BLOCK_SIZE: break - b39.vtx.append(tx_new) # add tx to block + b39.vtx.append(tx_new) # add tx to block tx_last = tx_new b39_outputs += 1 @@ -539,7 +561,6 @@ def update_block(block_number, new_transactions): yield accepted() save_spendable_output() - # Test sigops in P2SH redeem scripts # # b40 creates 3333 tx's spending the 6-sigop P2SH outputs from b39 for a total of 19998 sigops. @@ -555,7 +576,7 @@ def update_block(block_number, new_transactions): lastOutpoint = COutPoint(b40.vtx[1].sha256, 0) new_txs = [] - for i in range(1, numTxes+1): + for i in range(1, numTxes + 1): tx = CTransaction() tx.vout.append(CTxOut(1, CScript([OP_TRUE]))) tx.vin.append(CTxIn(lastOutpoint, b'')) @@ -606,7 +627,6 @@ def update_block(block_number, new_transactions): yield accepted() save_spendable_output() - # Test a number of really invalid scenarios # # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b44 (14) @@ -638,7 +658,7 @@ def update_block(block_number, new_transactions): b45.hashMerkleRoot = b45.calc_merkle_root() b45.calc_sha256() b45.solve() - self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256]+1 + self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256] + 1 self.tip = b45 self.blocks[45] = b45 yield rejected(RejectResult(16, b'bad-cb-missing')) @@ -646,24 +666,23 @@ def update_block(block_number, new_transactions): # A block with no txns tip(44) b46 = CBlock() - b46.nTime = b44.nTime+1 + b46.nTime = b44.nTime + 1 b46.hashPrevBlock = b44.sha256 b46.nBits = 0x207fffff b46.vtx = [] b46.hashMerkleRoot = 0 b46.solve() - self.block_heights[b46.sha256] = self.block_heights[b44.sha256]+1 + self.block_heights[b46.sha256] = self.block_heights[b44.sha256] + 1 self.tip = b46 assert 46 not in self.blocks self.blocks[46] = b46 - s = ser_uint256(b46.hashMerkleRoot) yield rejected(RejectResult(16, b'bad-blk-length')) # A block with invalid work tip(44) b47 = block(47, solve=False) target = uint256_from_compact(b47.nBits) - while b47.sha256 < target: #changed > to < + while b47.sha256 < target: # changed > to < b47.nNonce += 1 b47.rehash() yield rejected(RejectResult(16, b'high-hash')) @@ -691,9 +710,9 @@ def update_block(block_number, new_transactions): # A block with two coinbase txns tip(44) - b51 = block(51) + block(51) cb2 = create_coinbase(51, self.coinbase_pubkey) - b51 = update_block(51, [cb2]) + update_block(51, [cb2]) yield rejected(RejectResult(16, b'bad-cb-multiple')) # A block w/ duplicate txns @@ -710,7 +729,7 @@ def update_block(block_number, new_transactions): # tip(43) block(53, spend=out[14]) - yield rejected() # rejected since b44 is at same height + yield rejected() # rejected since b44 is at same height save_spendable_output() # invalid timestamp (b35 is 5 blocks back, so its time is MedianTimePast) @@ -727,7 +746,6 @@ def update_block(block_number, new_transactions): yield accepted() save_spendable_output() - # Test CVE-2012-2459 # # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16) @@ -735,7 +753,7 @@ def update_block(block_number, new_transactions): # \-> b56p2 (16) # \-> b56 (16) # - # Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without + # Merkle tree malleability (CVE-2012-2459): repeating sequences of transactions in a block without # affecting the merkle root of a block, while still invalidating it. # See: src/consensus/merkle.h # @@ -766,7 +784,7 @@ def update_block(block_number, new_transactions): tip(55) b56 = copy.deepcopy(b57) self.blocks[56] = b56 - assert_equal(len(b56.vtx),3) + assert_equal(len(b56.vtx), 3) b56 = update_block(56, [tx1]) assert_equal(b56.hash, b57.hash) yield rejected(RejectResult(16, b'bad-txns-duplicate')) @@ -786,7 +804,7 @@ def update_block(block_number, new_transactions): b56p2 = copy.deepcopy(b57p2) self.blocks["b56p2"] = b56p2 assert_equal(b56p2.hash, b57p2.hash) - assert_equal(len(b56p2.vtx),6) + assert_equal(len(b56p2.vtx), 6) b56p2 = update_block("b56p2", [tx3, tx4]) yield rejected(RejectResult(16, b'bad-txns-duplicate')) @@ -794,7 +812,7 @@ def update_block(block_number, new_transactions): yield accepted() tip(57) - yield rejected() #rejected because 57p2 seen first + yield rejected() # rejected because 57p2 seen first save_spendable_output() # Test a few invalid tx types @@ -805,20 +823,20 @@ def update_block(block_number, new_transactions): # tx with prevout.n out of range tip(57) - b58 = block(58, spend=out[17]) + block(58, spend=out[17]) tx = CTransaction() assert(len(out[17].tx.vout) < 42) tx.vin.append(CTxIn(COutPoint(out[17].tx.sha256, 42), CScript([OP_TRUE]), 0xffffffff)) tx.vout.append(CTxOut(0, b"")) tx.calc_sha256() - b58 = update_block(58, [tx]) + update_block(58, [tx]) yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # tx with output value > input value out of range tip(57) - b59 = block(59) - tx = create_and_sign_tx(out[17].tx, out[17].n, 510*COIN) - b59 = update_block(59, [tx]) + block(59) + tx = create_and_sign_tx(out[17].tx, out[17].n, 510 * COIN) + update_block(59, [tx]) yield rejected(RejectResult(16, b'bad-txns-in-belowout')) # reset to good chain @@ -838,31 +856,29 @@ def update_block(block_number, new_transactions): # tip(60) b61 = block(61, spend=out[18]) - b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig #equalize the coinbases + b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # equalize the coinbases b61.vtx[0].rehash() b61 = update_block(61, []) assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize()) yield rejected(RejectResult(16, b'bad-txns-BIP30')) - # 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) # \-> b62 (18) # tip(60) - b62 = block(62) + block(62) tx = CTransaction() - tx.nLockTime = 0xffffffff #this locktime is non-final + tx.nLockTime = 0xffffffff # this locktime is non-final assert(out[18].n < len(out[18].tx.vout)) - tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256, out[18].n))) # don't set nSequence + tx.vin.append(CTxIn(COutPoint(out[18].tx.sha256, out[18].n))) # don't set nSequence tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) assert(tx.vin[0].nSequence < 0xffffffff) tx.calc_sha256() - b62 = update_block(62, [tx]) + update_block(62, [tx]) yield rejected(RejectResult(16, b'bad-txns-nonfinal')) - # Test a non-final coinbase is also rejected # # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) @@ -876,7 +892,6 @@ def update_block(block_number, new_transactions): b63 = update_block(63, []) yield rejected(RejectResult(16, b'bad-txns-nonfinal')) - # This checks that a block with a bloated VARINT between the block_header and the array of tx such that # the block is > MAX_BLOCK_SIZE with the bloated varint, but <= MAX_BLOCK_SIZE without the bloated varint, # does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not @@ -974,13 +989,13 @@ def update_block(block_number, new_transactions): # tip(65) block(68, additional_coinbase_value=10) - tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue-9) + tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9) update_block(68, [tx]) yield rejected(RejectResult(16, b'bad-cb-amount')) tip(65) b69 = block(69, additional_coinbase_value=10) - tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue-10) + tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10) update_block(69, [tx]) yield accepted() save_spendable_output() @@ -1000,7 +1015,6 @@ def update_block(block_number, new_transactions): update_block(70, [tx]) yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) - # 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) @@ -1029,7 +1043,6 @@ def update_block(block_number, new_transactions): yield accepted() save_spendable_output() - # Test some invalid scripts and MAX_BLOCK_SIGOPS # # -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) @@ -1051,17 +1064,17 @@ def update_block(block_number, new_transactions): b73 = block(73) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1 a = bytearray([OP_CHECKSIG] * size) - a[MAX_BLOCK_SIGOPS - 1] = int("4e",16) # OP_PUSHDATA4 + a[MAX_BLOCK_SIGOPS - 1] = int("4e", 16) # OP_PUSHDATA4 element_size = MAX_SCRIPT_ELEMENT_SIZE + 1 a[MAX_BLOCK_SIGOPS] = element_size % 256 - a[MAX_BLOCK_SIGOPS+1] = element_size // 256 - a[MAX_BLOCK_SIGOPS+2] = 0 - a[MAX_BLOCK_SIGOPS+3] = 0 + a[MAX_BLOCK_SIGOPS + 1] = element_size // 256 + a[MAX_BLOCK_SIGOPS + 2] = 0 + a[MAX_BLOCK_SIGOPS + 3] = 0 tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a)) b73 = update_block(73, [tx]) - assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS+1) + assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS + 1) yield rejected(RejectResult(16, b'bad-blk-sigops')) # b74/75 - if we push an invalid script element, all prevous sigops are counted, @@ -1077,40 +1090,40 @@ def update_block(block_number, new_transactions): # # tip(72) - b74 = block(74) - size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561 + block(74) + size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS] = 0x4e - a[MAX_BLOCK_SIGOPS+1] = 0xfe - a[MAX_BLOCK_SIGOPS+2] = 0xff - a[MAX_BLOCK_SIGOPS+3] = 0xff - a[MAX_BLOCK_SIGOPS+4] = 0xff + a[MAX_BLOCK_SIGOPS + 1] = 0xfe + a[MAX_BLOCK_SIGOPS + 2] = 0xff + a[MAX_BLOCK_SIGOPS + 3] = 0xff + a[MAX_BLOCK_SIGOPS + 4] = 0xff tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a)) - b74 = update_block(74, [tx]) + update_block(74, [tx]) yield rejected(RejectResult(16, b'bad-blk-sigops')) tip(72) - b75 = block(75) + block(75) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 a = bytearray([OP_CHECKSIG] * size) - a[MAX_BLOCK_SIGOPS-1] = 0x4e + a[MAX_BLOCK_SIGOPS - 1] = 0x4e a[MAX_BLOCK_SIGOPS] = 0xff - a[MAX_BLOCK_SIGOPS+1] = 0xff - a[MAX_BLOCK_SIGOPS+2] = 0xff - a[MAX_BLOCK_SIGOPS+3] = 0xff + a[MAX_BLOCK_SIGOPS + 1] = 0xff + a[MAX_BLOCK_SIGOPS + 2] = 0xff + a[MAX_BLOCK_SIGOPS + 3] = 0xff tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a)) - b75 = update_block(75, [tx]) + update_block(75, [tx]) yield accepted() save_spendable_output() # Check that if we push an element filled with CHECKSIGs, they are not counted tip(75) - b76 = block(76) + block(76) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 a = bytearray([OP_CHECKSIG] * size) - a[MAX_BLOCK_SIGOPS-1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs + a[MAX_BLOCK_SIGOPS - 1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs tx = create_and_sign_tx(out[23].tx, 0, 1, CScript(a)) - b76 = update_block(76, [tx]) + update_block(76, [tx]) yield accepted() save_spendable_output() @@ -1134,18 +1147,18 @@ def update_block(block_number, new_transactions): # tip(76) block(77) - tx77 = create_and_sign_tx(out[24].tx, out[24].n, 10*COIN) + tx77 = create_and_sign_tx(out[24].tx, out[24].n, 10 * COIN) update_block(77, [tx77]) yield accepted() save_spendable_output() block(78) - tx78 = create_tx(tx77, 0, 9*COIN) + tx78 = create_tx(tx77, 0, 9 * COIN) update_block(78, [tx78]) yield accepted() block(79) - tx79 = create_tx(tx78, 0, 8*COIN) + tx79 = create_tx(tx78, 0, 8 * COIN) update_block(79, [tx79]) yield accepted() @@ -1158,7 +1171,7 @@ def update_block(block_number, new_transactions): save_spendable_output() block(81, spend=out[26]) - yield rejected() # other chain is same length + yield rejected() # other chain is same length save_spendable_output() block(82, spend=out[27]) @@ -1171,7 +1184,6 @@ def update_block(block_number, new_transactions): assert(tx78.hash in mempool) assert(tx79.hash in mempool) - # Test invalid opcodes in dead execution paths. # # -> b81 (26) -> b82 (27) -> b83 (28) @@ -1189,7 +1201,6 @@ def update_block(block_number, new_transactions): yield accepted() save_spendable_output() - # Reorg on/off blocks that have OP_RETURN in them (and try to spend them) # # -> b81 (26) -> b82 (27) -> b83 (28) -> b84 (29) -> b87 (30) -> b88 (31) @@ -1213,7 +1224,7 @@ def update_block(block_number, new_transactions): tx4.vout.append(CTxOut(0, CScript([OP_RETURN]))) tx5 = create_tx(tx1, 4, 0, CScript([OP_RETURN])) - update_block(84, [tx1,tx2,tx3,tx4,tx5]) + update_block(84, [tx1, tx2, tx3, tx4, tx5]) yield accepted() save_spendable_output() @@ -1239,7 +1250,6 @@ def update_block(block_number, new_transactions): update_block("89a", [tx]) yield rejected() - # Test re-org of a ~2 days' worth of blocks (1088 blocks) # This test takes a minute or two and can be accomplished in memory # @@ -1247,7 +1257,7 @@ def update_block(block_number, new_transactions): tip(88) LARGE_REORG_SIZE = 1088 test1 = TestInstance(sync_every_block=False) - spend=out[32] + spend = out[32] for i in range(89, LARGE_REORG_SIZE + 89): b = block(i, spend) tx = CTransaction() @@ -1268,7 +1278,7 @@ def update_block(block_number, new_transactions): tip(88) test2 = TestInstance(sync_every_block=False) for i in range(89, LARGE_REORG_SIZE + 89): - block("alt"+str(i)) + block("alt" + str(i)) test2.blocks_and_transactions.append([self.tip, False]) yield test2 @@ -1286,6 +1296,5 @@ def update_block(block_number, new_transactions): chain1_tip += 2 - if __name__ == '__main__': FullBlockTest().main() From f1beeb1863e82cd4acfb31bb8306ff75e852243a Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 4 Jan 2020 15:43:06 +0100 Subject: [PATCH 02/14] Temporarely rename MAX_BLOCK_SIZE -> MAX_BLOCK_BASE_SIZE We'll undo this after the next commit. This avoids merge many conflicts and makes reviewing easier. --- test/functional/p2p-fullblocktest.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index f45896f69fdb..84dc545c623c 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -377,7 +377,7 @@ def update_block(block_number, new_transactions): block(22, spend=out[5]) yield rejected() - # Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected + # Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) # \-> b24 (6) -> b25 (7) @@ -385,24 +385,24 @@ def update_block(block_number, new_transactions): tip(15) b23 = block(23, spend=out[6]) tx = CTransaction() - script_length = MAX_BLOCK_SIZE - len(b23.serialize()) - 69 + script_length = MAX_BLOCK_BASE_SIZE - len(b23.serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0))) b23 = update_block(23, [tx]) # Make sure the math above worked out to produce a max-sized block - assert_equal(len(b23.serialize()), MAX_BLOCK_SIZE) + assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE) yield accepted() save_spendable_output() # Make the next block one byte bigger and check that it fails tip(15) b24 = block(24, spend=out[6]) - script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69 + script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69 script_output = CScript([b'\x00' * (script_length + 1)]) tx.vout = [CTxOut(0, script_output)] b24 = update_block(24, [tx]) - assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE + 1) + assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1) yield rejected(RejectResult(16, b'bad-blk-length')) block(25, spend=out[7]) @@ -546,12 +546,12 @@ def update_block(block_number, new_transactions): tx_new = None tx_last = tx total_size = len(b39.serialize()) - while(total_size < MAX_BLOCK_SIZE): + while(total_size < MAX_BLOCK_BASE_SIZE): tx_new = create_tx(tx_last, 1, 1, p2sh_script) tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE]))) tx_new.rehash() total_size += len(tx_new.serialize()) - if total_size >= MAX_BLOCK_SIZE: + if total_size >= MAX_BLOCK_BASE_SIZE: break b39.vtx.append(tx_new) # add tx to block tx_last = tx_new @@ -893,7 +893,7 @@ def update_block(block_number, new_transactions): yield rejected(RejectResult(16, b'bad-txns-nonfinal')) # This checks that a block with a bloated VARINT between the block_header and the array of tx such that - # the block is > MAX_BLOCK_SIZE with the bloated varint, but <= MAX_BLOCK_SIZE without the bloated varint, + # the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint, # 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. # @@ -917,12 +917,12 @@ def update_block(block_number, new_transactions): tx = CTransaction() # use canonical serialization to calculate size - script_length = MAX_BLOCK_SIZE - len(b64a.normal_serialize()) - 69 + script_length = MAX_BLOCK_BASE_SIZE - len(b64a.normal_serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0))) b64a = update_block("64a", [tx]) - assert_equal(len(b64a.serialize()), MAX_BLOCK_SIZE + 8) + assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8) yield TestInstance([[self.tip, None]]) # comptool workaround: to make sure b64 is delivered, manually erase b64a from blockstore @@ -932,7 +932,7 @@ def update_block(block_number, new_transactions): b64 = CBlock(b64a) b64.vtx = copy.deepcopy(b64a.vtx) assert_equal(b64.hash, b64a.hash) - assert_equal(len(b64.serialize()), MAX_BLOCK_SIZE) + assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE) self.blocks[64] = b64 update_block(64, []) yield accepted() @@ -1261,12 +1261,12 @@ def update_block(block_number, new_transactions): for i in range(89, LARGE_REORG_SIZE + 89): b = block(i, spend) tx = CTransaction() - script_length = MAX_BLOCK_SIZE - len(b.serialize()) - 69 + script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0))) b = update_block(i, [tx]) - assert_equal(len(b.serialize()), MAX_BLOCK_SIZE) + assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE) test1.blocks_and_transactions.append([self.tip, True]) save_spendable_output() spend = get_spendable_output() From 1fee55023ea3164bcd562877d8dc4c2177d3079c Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 22 Nov 2017 13:47:03 -0500 Subject: [PATCH 03/14] [tests] Tidy up feature_block.py - move all helper methods to the end - remove block, create_tx and create_and_sign_tx shortcuts - remove --runbarelyexpensive option, since it defaults to True and it's unlikely that anyone ever runs the test with this option set to false. --- test/functional/p2p-fullblocktest.py | 1007 +++++++++++++------------- 1 file changed, 498 insertions(+), 509 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index 84dc545c623c..ea3dad6f13cb 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -52,6 +52,8 @@ from test_framework.script import * import struct +MAX_BLOCK_SIGOPS = 20000 + class PreviousSpendableOutput(object): def __init__(self, tx=CTransaction(), n=-1): self.tx = tx @@ -95,150 +97,45 @@ def set_test_params(self): # Must set '-dip3params=2000:2000' to create pre-dip3 blocks only self.extra_args = [['-whitelist=127.0.0.1', '-dip3params=2000:2000']] - def add_options(self, parser): - super().add_options(parser) - parser.add_option("--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): - [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 - - # 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, err) = SignatureHash(spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL) - tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))]) - - 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]), solve=True): - if self.tip is None: - base_block_hash = self.genesis_hash - block_time = self.mocktime + 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 is None: - block = create_block(base_block_hash, coinbase, block_time) - else: - coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees - coinbase.rehash() - block = create_block(base_block_hash, coinbase, block_time) - tx = create_transaction(spend.tx, spend.n, b"", 1, script) # spend 1 satoshi - self.sign_tx(tx, spend.tx, spend.n) - self.add_transactions_to_block(block, [tx]) - block.hashMerkleRoot = block.calc_merkle_root() - if solve: - block.solve() - self.tip = block - self.block_heights[block.sha256] = height - assert number not in self.blocks - self.blocks[number] = block - return block - def get_tests(self): 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) - - # 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 - 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_tx = self.create_tx - create_and_sign_tx = self.create_and_sign_transaction - - # these must be updated if consensus changes - MAX_BLOCK_SIGOPS = 20000 + self.spendable_outputs = [] # Create a new block - block(0) - save_spendable_output() - yield accepted() + self.next_block(0) + self.save_spendable_output() + yield self.accepted() # Now we need that block to mature so we can spend the coinbase. test = TestInstance(sync_every_block=False) for i in range(99): - block(5000 + i) + self.next_block(5000 + i) test.blocks_and_transactions.append([self.tip, True]) - save_spendable_output() + self.save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(33): - out.append(get_spendable_output()) + out.append(self.get_spendable_output()) # Start by building a couple of blocks on top (which output is spent is # in parentheses): # genesis -> b1 (0) -> b2 (1) - block(1, spend=out[0]) - save_spendable_output() - yield accepted() + self.next_block(1, spend=out[0]) + self.save_spendable_output() + yield self.accepted() - block(2, spend=out[1]) - yield accepted() - save_spendable_output() + self.next_block(2, spend=out[1]) + yield self.accepted() + self.save_spendable_output() # so fork like this: # @@ -246,77 +143,77 @@ def update_block(block_number, new_transactions): # \-> b3 (1) # # Nothing should happen at this point. We saw b2 first so it takes priority. - tip(1) - b3 = block(3, spend=out[1]) + self.move_tip(1) + b3 = self.next_block(3, spend=out[1]) txout_b3 = PreviousSpendableOutput(b3.vtx[1], 0) - yield rejected() + yield self.rejected() # Now we add another block to make the alternative chain longer. # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) -> b4 (2) - block(4, spend=out[2]) - yield accepted() + self.next_block(4, spend=out[2]) + yield self.accepted() # ... and back to the first chain. # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b3 (1) -> b4 (2) - tip(2) - block(5, spend=out[2]) - save_spendable_output() - yield rejected() + self.move_tip(2) + self.next_block(5, spend=out[2]) + self.save_spendable_output() + yield self.rejected() - block(6, spend=out[3]) - yield accepted() + self.next_block(6, spend=out[3]) + yield self.accepted() # Try to create a fork that double-spends # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b7 (2) -> b8 (4) # \-> b3 (1) -> b4 (2) - tip(5) - block(7, spend=out[2]) - yield rejected() + self.move_tip(5) + self.next_block(7, spend=out[2]) + yield self.rejected() - block(8, spend=out[4]) - yield rejected() + self.next_block(8, spend=out[4]) + yield self.rejected() # Try to create a block that has too much fee # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b9 (4) # \-> b3 (1) -> b4 (2) - tip(6) - block(9, spend=out[4], additional_coinbase_value=1) - yield rejected(RejectResult(16, b'bad-cb-amount')) + self.move_tip(6) + self.next_block(9, spend=out[4], additional_coinbase_value=1) + yield self.rejected(RejectResult(16, b'bad-cb-amount')) # Create a fork that ends in a block with too much fee (the one that causes the reorg) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b10 (3) -> b11 (4) # \-> b3 (1) -> b4 (2) - tip(5) - block(10, spend=out[3]) - yield rejected() + self.move_tip(5) + self.next_block(10, spend=out[3]) + yield self.rejected() - block(11, spend=out[4], additional_coinbase_value=1) - yield rejected(RejectResult(16, b'bad-cb-amount')) + self.next_block(11, spend=out[4], additional_coinbase_value=1) + yield self.rejected(RejectResult(16, b'bad-cb-amount')) # Try again, but with a valid fork first # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b14 (5) # (b12 added last) # \-> b3 (1) -> b4 (2) - tip(5) - b12 = block(12, spend=out[3]) - save_spendable_output() - b13 = block(13, spend=out[4]) + self.move_tip(5) + b12 = self.next_block(12, spend=out[3]) + self.save_spendable_output() + b13 = self.next_block(13, spend=out[4]) # Deliver the block header for b12, and the block b13. # b13 should be accepted but the tip won't advance until b12 is delivered. yield TestInstance([[CBlockHeader(b12), None], [b13, False]]) - save_spendable_output() + self.save_spendable_output() # b14 is invalid, but the node won't know that until it tries to connect # Tip still can't advance because b12 is missing - block(14, spend=out[5], additional_coinbase_value=1) - yield rejected() + self.next_block(14, spend=out[5], additional_coinbase_value=1) + yield self.rejected() yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13. @@ -327,125 +224,125 @@ def update_block(block_number, new_transactions): # Test that a block with a lot of checksigs is okay lots_of_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS - 1)) - tip(13) - block(15, spend=out[5], script=lots_of_checksigs) - yield accepted() - save_spendable_output() + self.move_tip(13) + self.next_block(15, spend=out[5], script=lots_of_checksigs) + yield self.accepted() + self.save_spendable_output() # Test that a block with too many checksigs is rejected too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS)) - block(16, spend=out[6], script=too_many_checksigs) - yield rejected(RejectResult(16, b'bad-blk-sigops')) + self.next_block(16, spend=out[6], script=too_many_checksigs) + yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # Attempt to spend a transaction created on a different fork # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1]) # \-> b3 (1) -> b4 (2) - tip(15) - block(17, spend=txout_b3) - yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + self.move_tip(15) + self.next_block(17, spend=txout_b3) + yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # Attempt to spend a transaction created on a different fork (on a fork this time) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) # \-> b18 (b3.vtx[1]) -> b19 (6) # \-> b3 (1) -> b4 (2) - tip(13) - block(18, spend=txout_b3) - yield rejected() + self.move_tip(13) + self.next_block(18, spend=txout_b3) + yield self.rejected() - block(19, spend=out[6]) - yield rejected() + self.next_block(19, spend=out[6]) + yield self.rejected() # Attempt to spend a coinbase at depth too low # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7) # \-> b3 (1) -> b4 (2) - tip(15) - block(20, spend=out[7]) - yield rejected(RejectResult(16, b'bad-txns-premature-spend-of-coinbase')) + self.move_tip(15) + self.next_block(20, spend=out[7]) + yield self.rejected(RejectResult(16, b'bad-txns-premature-spend-of-coinbase')) # Attempt to spend a coinbase at depth too low (on a fork this time) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) # \-> b21 (6) -> b22 (5) # \-> b3 (1) -> b4 (2) - tip(13) - block(21, spend=out[6]) - yield rejected() + self.move_tip(13) + self.next_block(21, spend=out[6]) + yield self.rejected() - block(22, spend=out[5]) - yield rejected() + self.next_block(22, spend=out[5]) + yield self.rejected() # Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) # \-> b24 (6) -> b25 (7) # \-> b3 (1) -> b4 (2) - tip(15) - b23 = block(23, spend=out[6]) + self.move_tip(15) + b23 = self.next_block(23, spend=out[6]) tx = CTransaction() script_length = MAX_BLOCK_BASE_SIZE - len(b23.serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0))) - b23 = update_block(23, [tx]) + b23 = self.update_block(23, [tx]) # Make sure the math above worked out to produce a max-sized block assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE) - yield accepted() - save_spendable_output() + yield self.accepted() + self.save_spendable_output() # Make the next block one byte bigger and check that it fails - tip(15) - b24 = block(24, spend=out[6]) + self.move_tip(15) + b24 = self.next_block(24, spend=out[6]) script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69 script_output = CScript([b'\x00' * (script_length + 1)]) tx.vout = [CTxOut(0, script_output)] - b24 = update_block(24, [tx]) + b24 = self.update_block(24, [tx]) assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1) - yield rejected(RejectResult(16, b'bad-blk-length')) + yield self.rejected(RejectResult(16, b'bad-blk-length')) - block(25, spend=out[7]) - yield rejected() + self.next_block(25, spend=out[7]) + yield self.rejected() # Create blocks with a coinbase input script size out of range # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) # \-> ... (6) -> ... (7) # \-> b3 (1) -> b4 (2) - tip(15) - b26 = block(26, spend=out[6]) + self.move_tip(15) + b26 = self.next_block(26, spend=out[6]) b26.vtx[0].vin[0].scriptSig = b'\x00' b26.vtx[0].rehash() # update_block causes the merkle root to get updated, even with no new # transactions, and updates the required state. - b26 = update_block(26, []) - yield rejected(RejectResult(16, b'bad-cb-length')) + b26 = self.update_block(26, []) + yield self.rejected(RejectResult(16, b'bad-cb-length')) # Extend the b26 chain to make sure bitcoind isn't accepting b26 - block(27, spend=out[7]) - yield rejected(False) + self.next_block(27, spend=out[7]) + yield self.rejected(False) # Now try a too-large-coinbase script - tip(15) - b28 = block(28, spend=out[6]) + self.move_tip(15) + b28 = self.next_block(28, spend=out[6]) b28.vtx[0].vin[0].scriptSig = b'\x00' * 101 b28.vtx[0].rehash() - b28 = update_block(28, []) - yield rejected(RejectResult(16, b'bad-cb-length')) + b28 = self.update_block(28, []) + yield self.rejected(RejectResult(16, b'bad-cb-length')) # Extend the b28 chain to make sure bitcoind isn't accepting b28 - block(29, spend=out[7]) - yield rejected(False) + self.next_block(29, spend=out[7]) + yield self.rejected(False) # b30 has a max-sized coinbase scriptSig. - tip(23) - b30 = block(30) + self.move_tip(23) + b30 = self.next_block(30) b30.vtx[0].vin[0].scriptSig = b'\x00' * 100 b30.vtx[0].rehash() - b30 = update_block(30, []) - yield accepted() - save_spendable_output() + b30 = self.update_block(30, []) + yield self.accepted() + self.save_spendable_output() # b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY # @@ -457,38 +354,38 @@ def update_block(block_number, new_transactions): # MULTISIG: each op code counts as 20 sigops. To create the edge case, pack another 19 sigops at the end. lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19) - b31 = block(31, spend=out[8], script=lots_of_multisigs) + b31 = self.next_block(31, spend=out[8], script=lots_of_multisigs) assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS) - yield accepted() - save_spendable_output() + yield self.accepted() + self.save_spendable_output() # this goes over the limit because the coinbase has one sigop too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS // 20)) - b32 = block(32, spend=out[9], script=too_many_multisigs) + b32 = self.next_block(32, spend=out[9], script=too_many_multisigs) assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS + 1) - yield rejected(RejectResult(16, b'bad-blk-sigops')) + yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # CHECKMULTISIGVERIFY - tip(31) + self.move_tip(31) lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19) - block(33, spend=out[9], script=lots_of_multisigs) - yield accepted() - save_spendable_output() + self.next_block(33, spend=out[9], script=lots_of_multisigs) + yield self.accepted() + self.save_spendable_output() too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS // 20)) - block(34, spend=out[10], script=too_many_multisigs) - yield rejected(RejectResult(16, b'bad-blk-sigops')) + self.next_block(34, spend=out[10], script=too_many_multisigs) + yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # CHECKSIGVERIFY - tip(33) + self.move_tip(33) lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS - 1)) - b35 = block(35, spend=out[10], script=lots_of_checksigs) - yield accepted() - save_spendable_output() + b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs) + yield self.accepted() + self.save_spendable_output() too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS)) - block(36, spend=out[11], script=too_many_checksigs) - yield rejected(RejectResult(16, b'bad-blk-sigops')) + self.next_block(36, spend=out[11], script=too_many_checksigs) + yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # Check spending of a transaction in a block which failed to connect # @@ -499,17 +396,17 @@ def update_block(block_number, new_transactions): # # save 37's spendable output, but then double-spend out11 to invalidate the block - tip(35) - b37 = block(37, spend=out[11]) + self.move_tip(35) + b37 = self.next_block(37, spend=out[11]) txout_b37 = PreviousSpendableOutput(b37.vtx[1], 0) - tx = create_and_sign_tx(out[11].tx, out[11].n, 0) - b37 = update_block(37, [tx]) - yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + tx = self.create_and_sign_transaction(out[11].tx, out[11].n, 0) + b37 = self.update_block(37, [tx]) + yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid - tip(35) - block(38, spend=txout_b37) - yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + self.move_tip(35) + self.next_block(38, spend=txout_b37) + yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # Check P2SH SigOp counting # @@ -522,8 +419,8 @@ def update_block(block_number, new_transactions): # redeem_script = COINBASE_PUBKEY, (OP_2DUP+OP_CHECKSIGVERIFY) * 5, OP_CHECKSIG # p2sh_script = OP_HASH160, ripemd160(sha256(script)), OP_EQUAL # - tip(35) - b39 = block(39) + self.move_tip(35) + b39 = self.next_block(39) b39_outputs = 0 b39_sigops_per_output = 6 @@ -535,11 +432,11 @@ def update_block(block_number, new_transactions): # Create a transaction that spends one satoshi to the p2sh_script, the rest to OP_TRUE # This must be signed because it is spending a coinbase spend = out[11] - tx = create_tx(spend.tx, spend.n, 1, p2sh_script) + tx = self.create_tx(spend.tx, spend.n, 1, p2sh_script) tx.vout.append(CTxOut(spend.tx.vout[spend.n].nValue - 1, CScript([OP_TRUE]))) self.sign_tx(tx, spend.tx, spend.n) tx.rehash() - b39 = update_block(39, [tx]) + b39 = self.update_block(39, [tx]) b39_outputs += 1 # Until block is full, add tx's with 1 satoshi to p2sh_script, the rest to OP_TRUE @@ -547,7 +444,7 @@ def update_block(block_number, new_transactions): tx_last = tx total_size = len(b39.serialize()) while(total_size < MAX_BLOCK_BASE_SIZE): - tx_new = create_tx(tx_last, 1, 1, p2sh_script) + tx_new = self.create_tx(tx_last, 1, 1, p2sh_script) tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE]))) tx_new.rehash() total_size += len(tx_new.serialize()) @@ -557,9 +454,9 @@ def update_block(block_number, new_transactions): tx_last = tx_new b39_outputs += 1 - b39 = update_block(39, []) - yield accepted() - save_spendable_output() + b39 = self.update_block(39, []) + yield self.accepted() + self.save_spendable_output() # Test sigops in P2SH redeem scripts # @@ -568,8 +465,8 @@ def update_block(block_number, new_transactions): # # b41 does the same, less one, so it has the maximum sigops permitted. # - tip(39) - b40 = block(40, spend=out[12]) + self.move_tip(39) + b40 = self.next_block(40, spend=out[12]) sigops = get_legacy_sigopcount_block(b40) numTxes = (MAX_BLOCK_SIGOPS - sigops) // b39_sigops_per_output assert_equal(numTxes <= b39_outputs, True) @@ -598,34 +495,34 @@ def update_block(block_number, new_transactions): tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b40_sigops_to_fill))) tx.rehash() new_txs.append(tx) - update_block(40, new_txs) - yield rejected(RejectResult(16, b'bad-blk-sigops')) + self.update_block(40, new_txs) + yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # same as b40, but one less sigop - tip(39) - block(41, spend=None) - update_block(41, b40.vtx[1:-1]) + self.move_tip(39) + self.next_block(41, spend=None) + self.update_block(41, b40.vtx[1:-1]) b41_sigops_to_fill = b40_sigops_to_fill - 1 tx = CTransaction() tx.vin.append(CTxIn(lastOutpoint, b'')) tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill))) tx.rehash() - update_block(41, [tx]) - yield accepted() + self.update_block(41, [tx]) + yield self.accepted() # Fork off of b39 to create a constant base again # # b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) # \-> b41 (12) # - tip(39) - block(42, spend=out[12]) - yield rejected() - save_spendable_output() + self.move_tip(39) + self.next_block(42, spend=out[12]) + yield self.rejected() + self.save_spendable_output() - block(43, spend=out[13]) - yield accepted() - save_spendable_output() + self.next_block(43, spend=out[13]) + yield self.accepted() + self.save_spendable_output() # Test a number of really invalid scenarios # @@ -646,10 +543,10 @@ def update_block(block_number, new_transactions): self.tip = b44 self.block_heights[b44.sha256] = height self.blocks[44] = b44 - yield accepted() + yield self.accepted() # A block with a non-coinbase as the first tx - non_coinbase = create_tx(out[15].tx, out[15].n, 1) + non_coinbase = self.create_tx(out[15].tx, out[15].n, 1) b45 = CBlock() b45.nTime = self.tip.nTime + 1 b45.hashPrevBlock = self.tip.sha256 @@ -661,10 +558,10 @@ def update_block(block_number, new_transactions): self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256] + 1 self.tip = b45 self.blocks[45] = b45 - yield rejected(RejectResult(16, b'bad-cb-missing')) + yield self.rejected(RejectResult(16, b'bad-cb-missing')) # A block with no txns - tip(44) + self.move_tip(44) b46 = CBlock() b46.nTime = b44.nTime + 1 b46.hashPrevBlock = b44.sha256 @@ -676,75 +573,75 @@ def update_block(block_number, new_transactions): self.tip = b46 assert 46 not in self.blocks self.blocks[46] = b46 - yield rejected(RejectResult(16, b'bad-blk-length')) + yield self.rejected(RejectResult(16, b'bad-blk-length')) # A block with invalid work - tip(44) - b47 = block(47, solve=False) + self.move_tip(44) + b47 = self.next_block(47, solve=False) target = uint256_from_compact(b47.nBits) while b47.sha256 < target: # changed > to < b47.nNonce += 1 b47.rehash() - yield rejected(RejectResult(16, b'high-hash')) + yield self.rejected(RejectResult(16, b'high-hash')) # A block with timestamp > 2 hrs in the future - tip(44) - b48 = block(48, solve=False) - b48.nTime = self.mocktime + 60 * 60 * 3 + self.move_tip(44) + b48 = self.next_block(48, solve=False) + b48.nTime = int(self.mocktime) + 60 * 60 * 3 b48.solve() - yield rejected(RejectResult(16, b'time-too-new')) + yield self.rejected(RejectResult(16, b'time-too-new')) # A block with an invalid merkle hash - tip(44) - b49 = block(49) + self.move_tip(44) + b49 = self.next_block(49) b49.hashMerkleRoot += 1 b49.solve() - yield rejected(RejectResult(16, b'bad-txnmrklroot')) + yield self.rejected(RejectResult(16, b'bad-txnmrklroot')) # A block with an incorrect POW limit - tip(44) - b50 = block(50) + self.move_tip(44) + b50 = self.next_block(50) b50.nBits = b50.nBits - 1 b50.solve() - yield rejected(RejectResult(16, b'bad-diffbits')) + yield self.rejected(RejectResult(16, b'bad-diffbits')) # A block with two coinbase txns - tip(44) - block(51) + self.move_tip(44) + self.next_block(51) cb2 = create_coinbase(51, self.coinbase_pubkey) - update_block(51, [cb2]) - yield rejected(RejectResult(16, b'bad-cb-multiple')) + self.update_block(51, [cb2]) + yield self.rejected(RejectResult(16, b'bad-cb-multiple')) # A block w/ duplicate txns # Note: txns have to be in the right position in the merkle tree to trigger this error - tip(44) - b52 = block(52, spend=out[15]) - tx = create_tx(b52.vtx[1], 0, 1) - b52 = update_block(52, [tx, tx]) - yield rejected(RejectResult(16, b'bad-txns-duplicate')) + self.move_tip(44) + b52 = self.next_block(52, spend=out[15]) + tx = self.create_tx(b52.vtx[1], 0, 1) + b52 = self.update_block(52, [tx, tx]) + yield self.rejected(RejectResult(16, b'bad-txns-duplicate')) # Test block timestamps # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) # \-> b54 (15) # - tip(43) - block(53, spend=out[14]) - yield rejected() # rejected since b44 is at same height - save_spendable_output() + self.move_tip(43) + self.next_block(53, spend=out[14]) + yield self.rejected() # rejected since b44 is at same height + self.save_spendable_output() # invalid timestamp (b35 is 5 blocks back, so its time is MedianTimePast) - b54 = block(54, spend=out[15]) + b54 = self.next_block(54, spend=out[15]) b54.nTime = b35.nTime - 1 b54.solve() - yield rejected(RejectResult(16, b'time-too-old')) + yield self.rejected(RejectResult(16, b'time-too-old')) # valid timestamp - tip(53) - b55 = block(55, spend=out[15]) + self.move_tip(53) + b55 = self.next_block(55, spend=out[15]) b55.nTime = b35.nTime - update_block(55, []) - yield accepted() - save_spendable_output() + self.update_block(55, []) + yield self.accepted() + self.save_spendable_output() # Test CVE-2012-2459 # @@ -774,46 +671,46 @@ def update_block(block_number, new_transactions): # that the error was caught early, avoiding a DOS vulnerability.) # b57 - a good block with 2 txs, don't submit until end - tip(55) - b57 = block(57) - tx = create_and_sign_tx(out[16].tx, out[16].n, 1) - tx1 = create_tx(tx, 0, 1) - b57 = update_block(57, [tx, tx1]) + self.move_tip(55) + b57 = self.next_block(57) + tx = self.create_and_sign_transaction(out[16].tx, out[16].n, 1) + tx1 = self.create_tx(tx, 0, 1) + b57 = self.update_block(57, [tx, tx1]) # b56 - copy b57, add a duplicate tx - tip(55) + self.move_tip(55) b56 = copy.deepcopy(b57) self.blocks[56] = b56 assert_equal(len(b56.vtx), 3) - b56 = update_block(56, [tx1]) + b56 = self.update_block(56, [tx1]) assert_equal(b56.hash, b57.hash) - yield rejected(RejectResult(16, b'bad-txns-duplicate')) + yield self.rejected(RejectResult(16, b'bad-txns-duplicate')) # b57p2 - a good block with 6 tx'es, don't submit until end - tip(55) - b57p2 = block("57p2") - tx = create_and_sign_tx(out[16].tx, out[16].n, 1) - tx1 = create_tx(tx, 0, 1) - tx2 = create_tx(tx1, 0, 1) - tx3 = create_tx(tx2, 0, 1) - tx4 = create_tx(tx3, 0, 1) - b57p2 = update_block("57p2", [tx, tx1, tx2, tx3, tx4]) + self.move_tip(55) + b57p2 = self.next_block("57p2") + tx = self.create_and_sign_transaction(out[16].tx, out[16].n, 1) + tx1 = self.create_tx(tx, 0, 1) + tx2 = self.create_tx(tx1, 0, 1) + tx3 = self.create_tx(tx2, 0, 1) + tx4 = self.create_tx(tx3, 0, 1) + b57p2 = self.update_block("57p2", [tx, tx1, tx2, tx3, tx4]) # b56p2 - copy b57p2, duplicate two non-consecutive tx's - tip(55) + self.move_tip(55) b56p2 = copy.deepcopy(b57p2) self.blocks["b56p2"] = b56p2 assert_equal(b56p2.hash, b57p2.hash) assert_equal(len(b56p2.vtx), 6) - b56p2 = update_block("b56p2", [tx3, tx4]) - yield rejected(RejectResult(16, b'bad-txns-duplicate')) + b56p2 = self.update_block("b56p2", [tx3, tx4]) + yield self.rejected(RejectResult(16, b'bad-txns-duplicate')) - tip("57p2") - yield accepted() + self.move_tip("57p2") + yield self.accepted() - tip(57) - yield rejected() # rejected because 57p2 seen first - save_spendable_output() + self.move_tip(57) + yield self.rejected() # rejected because 57p2 seen first + self.save_spendable_output() # Test a few invalid tx types # @@ -822,28 +719,28 @@ def update_block(block_number, new_transactions): # # tx with prevout.n out of range - tip(57) - block(58, spend=out[17]) + self.move_tip(57) + self.next_block(58, spend=out[17]) tx = CTransaction() assert(len(out[17].tx.vout) < 42) tx.vin.append(CTxIn(COutPoint(out[17].tx.sha256, 42), CScript([OP_TRUE]), 0xffffffff)) tx.vout.append(CTxOut(0, b"")) tx.calc_sha256() - update_block(58, [tx]) - yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + self.update_block(58, [tx]) + yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # tx with output value > input value out of range - tip(57) - block(59) - tx = create_and_sign_tx(out[17].tx, out[17].n, 510 * COIN) - update_block(59, [tx]) - yield rejected(RejectResult(16, b'bad-txns-in-belowout')) + self.move_tip(57) + self.next_block(59) + tx = self.create_and_sign_transaction(out[17].tx, out[17].n, 510 * COIN) + self.update_block(59, [tx]) + yield self.rejected(RejectResult(16, b'bad-txns-in-belowout')) # reset to good chain - tip(57) - b60 = block(60, spend=out[17]) - yield accepted() - save_spendable_output() + self.move_tip(57) + b60 = self.next_block(60, spend=out[17]) + yield self.accepted() + self.save_spendable_output() # Test BIP30 # @@ -854,21 +751,21 @@ def update_block(block_number, new_transactions): # not-fully-spent transaction in the same chain. To test, make identical coinbases; # the second one should be rejected. # - tip(60) - b61 = block(61, spend=out[18]) + self.move_tip(60) + b61 = self.next_block(61, spend=out[18]) b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # equalize the coinbases b61.vtx[0].rehash() - b61 = update_block(61, []) + b61 = self.update_block(61, []) assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize()) - yield rejected(RejectResult(16, b'bad-txns-BIP30')) + yield self.rejected(RejectResult(16, b'bad-txns-BIP30')) # 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) # \-> b62 (18) # - tip(60) - block(62) + self.move_tip(60) + self.next_block(62) tx = CTransaction() tx.nLockTime = 0xffffffff # this locktime is non-final assert(out[18].n < len(out[18].tx.vout)) @@ -876,21 +773,21 @@ def update_block(block_number, new_transactions): tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) assert(tx.vin[0].nSequence < 0xffffffff) tx.calc_sha256() - update_block(62, [tx]) - yield rejected(RejectResult(16, b'bad-txns-nonfinal')) + self.update_block(62, [tx]) + yield self.rejected(RejectResult(16, b'bad-txns-nonfinal')) # Test a non-final coinbase is also rejected # # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> b63 (-) # - tip(60) - b63 = block(63) + self.move_tip(60) + b63 = self.next_block(63) b63.vtx[0].nLockTime = 0xffffffff b63.vtx[0].vin[0].nSequence = 0xDEADBEEF b63.vtx[0].rehash() - b63 = update_block(63, []) - yield rejected(RejectResult(16, b'bad-txns-nonfinal')) + b63 = self.update_block(63, []) + yield self.rejected(RejectResult(16, b'bad-txns-nonfinal')) # This checks that a block with a bloated VARINT between the block_header and the array of tx such that # the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint, @@ -906,8 +803,8 @@ def update_block(block_number, new_transactions): # b64a is a bloated block (non-canonical varint) # b64 is a good block (same as b64 but w/ canonical varint) # - tip(60) - regular_block = block("64a", spend=out[18]) + self.move_tip(60) + regular_block = self.next_block("64a", spend=out[18]) # make it a "broken_block," with non-canonical serialization b64a = CBrokenBlock(regular_block) @@ -921,45 +818,45 @@ def update_block(block_number, new_transactions): script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0))) - b64a = update_block("64a", [tx]) + b64a = self.update_block("64a", [tx]) assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8) yield TestInstance([[self.tip, None]]) # comptool workaround: to make sure b64 is delivered, manually erase b64a from blockstore self.test.block_store.erase(b64a.sha256) - tip(60) + self.move_tip(60) b64 = CBlock(b64a) b64.vtx = copy.deepcopy(b64a.vtx) assert_equal(b64.hash, b64a.hash) assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE) self.blocks[64] = b64 - update_block(64, []) - yield accepted() - save_spendable_output() + self.update_block(64, []) + yield self.accepted() + self.save_spendable_output() # Spend an output created in the block itself # # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # - tip(64) - block(65) - tx1 = create_and_sign_tx(out[19].tx, out[19].n, out[19].tx.vout[0].nValue) - tx2 = create_and_sign_tx(tx1, 0, 0) - update_block(65, [tx1, tx2]) - yield accepted() - save_spendable_output() + self.move_tip(64) + self.next_block(65) + tx1 = self.create_and_sign_transaction(out[19].tx, out[19].n, out[19].tx.vout[0].nValue) + tx2 = self.create_and_sign_transaction(tx1, 0, 0) + self.update_block(65, [tx1, tx2]) + yield self.accepted() + self.save_spendable_output() # Attempt to spend an output created later in the same block # # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # \-> b66 (20) - tip(65) - block(66) - tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) - tx2 = create_and_sign_tx(tx1, 0, 1) - update_block(66, [tx2, tx1]) - yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + self.move_tip(65) + self.next_block(66) + tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) + tx2 = self.create_and_sign_transaction(tx1, 0, 1) + self.update_block(66, [tx2, tx1]) + yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # Attempt to double-spend a transaction created in a block # @@ -967,13 +864,13 @@ def update_block(block_number, new_transactions): # \-> b67 (20) # # - tip(65) - block(67) - tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) - tx2 = create_and_sign_tx(tx1, 0, 1) - tx3 = create_and_sign_tx(tx1, 0, 2) - update_block(67, [tx1, tx2, tx3]) - yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + self.move_tip(65) + self.next_block(67) + tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) + tx2 = self.create_and_sign_transaction(tx1, 0, 1) + tx3 = self.create_and_sign_transaction(tx1, 0, 2) + self.update_block(67, [tx1, tx2, tx3]) + yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # More tests of block subsidy # @@ -987,33 +884,33 @@ def update_block(block_number, new_transactions): # b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee # this succeeds # - tip(65) - block(68, additional_coinbase_value=10) - tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9) - update_block(68, [tx]) - yield rejected(RejectResult(16, b'bad-cb-amount')) + self.move_tip(65) + self.next_block(68, additional_coinbase_value=10) + tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9) + self.update_block(68, [tx]) + yield self.rejected(RejectResult(16, b'bad-cb-amount')) - tip(65) - b69 = block(69, additional_coinbase_value=10) - tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10) - update_block(69, [tx]) - yield accepted() - save_spendable_output() + self.move_tip(65) + b69 = self.next_block(69, additional_coinbase_value=10) + tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10) + self.update_block(69, [tx]) + yield self.accepted() + self.save_spendable_output() # Test spending the outpoint of a non-existent transaction # # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) # \-> b70 (21) # - tip(69) - block(70, spend=out[21]) + self.move_tip(69) + self.next_block(70, spend=out[21]) bogus_tx = CTransaction() bogus_tx.sha256 = uint256_from_str(b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c") tx = CTransaction() tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff)) tx.vout.append(CTxOut(1, b"")) - update_block(70, [tx]) - yield rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + self.update_block(70, [tx]) + yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) # @@ -1023,11 +920,11 @@ def update_block(block_number, new_transactions): # b72 is a good block. # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b71. # - tip(69) - b72 = block(72) - tx1 = create_and_sign_tx(out[21].tx, out[21].n, 2) - tx2 = create_and_sign_tx(tx1, 0, 1) - b72 = update_block(72, [tx1, tx2]) # now tip is 72 + self.move_tip(69) + b72 = self.next_block(72) + tx1 = self.create_and_sign_transaction(out[21].tx, out[21].n, 2) + tx2 = self.create_and_sign_transaction(tx1, 0, 1) + b72 = self.update_block(72, [tx1, tx2]) # now tip is 72 b71 = copy.deepcopy(b72) b71.vtx.append(tx2) # add duplicate tx2 self.block_heights[b71.sha256] = self.block_heights[b69.sha256] + 1 # b71 builds off b69 @@ -1037,11 +934,11 @@ def update_block(block_number, new_transactions): assert_equal(len(b72.vtx), 3) assert_equal(b72.sha256, b71.sha256) - tip(71) - yield rejected(RejectResult(16, b'bad-txns-duplicate')) - tip(72) - yield accepted() - save_spendable_output() + self.move_tip(71) + yield self.rejected(RejectResult(16, b'bad-txns-duplicate')) + self.move_tip(72) + yield self.accepted() + self.save_spendable_output() # Test some invalid scripts and MAX_BLOCK_SIGOPS # @@ -1060,8 +957,8 @@ def update_block(block_number, new_transactions): # bytearray[20,004-20,525]: unread data (script_element) # bytearray[20,526] : OP_CHECKSIG (this puts us over the limit) # - tip(72) - b73 = block(73) + self.move_tip(72) + b73 = self.next_block(73) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS - 1] = int("4e", 16) # OP_PUSHDATA4 @@ -1072,10 +969,10 @@ def update_block(block_number, new_transactions): a[MAX_BLOCK_SIGOPS + 2] = 0 a[MAX_BLOCK_SIGOPS + 3] = 0 - tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a)) - b73 = update_block(73, [tx]) + tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) + b73 = self.update_block(73, [tx]) assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS + 1) - yield rejected(RejectResult(16, b'bad-blk-sigops')) + yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # b74/75 - if we push an invalid script element, all prevous sigops are counted, # but sigops after the element are not counted. @@ -1089,8 +986,8 @@ def update_block(block_number, new_transactions): # b75 succeeds because we put MAX_BLOCK_SIGOPS before the element # # - tip(72) - block(74) + self.move_tip(72) + self.next_block(74) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS] = 0x4e @@ -1098,12 +995,12 @@ def update_block(block_number, new_transactions): a[MAX_BLOCK_SIGOPS + 2] = 0xff a[MAX_BLOCK_SIGOPS + 3] = 0xff a[MAX_BLOCK_SIGOPS + 4] = 0xff - tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a)) - update_block(74, [tx]) - yield rejected(RejectResult(16, b'bad-blk-sigops')) + tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) + self.update_block(74, [tx]) + yield self.rejected(RejectResult(16, b'bad-blk-sigops')) - tip(72) - block(75) + self.move_tip(72) + self.next_block(75) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS - 1] = 0x4e @@ -1111,21 +1008,21 @@ def update_block(block_number, new_transactions): a[MAX_BLOCK_SIGOPS + 1] = 0xff a[MAX_BLOCK_SIGOPS + 2] = 0xff a[MAX_BLOCK_SIGOPS + 3] = 0xff - tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a)) - update_block(75, [tx]) - yield accepted() - save_spendable_output() + tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) + self.update_block(75, [tx]) + yield self.accepted() + self.save_spendable_output() # Check that if we push an element filled with CHECKSIGs, they are not counted - tip(75) - block(76) + self.move_tip(75) + self.next_block(76) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS - 1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs - tx = create_and_sign_tx(out[23].tx, 0, 1, CScript(a)) - update_block(76, [tx]) - yield accepted() - save_spendable_output() + tx = self.create_and_sign_transaction(out[23].tx, 0, 1, CScript(a)) + self.update_block(76, [tx]) + yield self.accepted() + self.save_spendable_output() # Test transaction resurrection # @@ -1145,38 +1042,38 @@ def update_block(block_number, new_transactions): # spend to OP_TRUE. If the standard-ness rules change, this test would need to be # updated. (Perhaps to spend to a P2SH OP_TRUE script) # - tip(76) - block(77) - tx77 = create_and_sign_tx(out[24].tx, out[24].n, 10 * COIN) - update_block(77, [tx77]) - yield accepted() - save_spendable_output() + self.move_tip(76) + self.next_block(77) + tx77 = self.create_and_sign_transaction(out[24].tx, out[24].n, 10 * COIN) + self.update_block(77, [tx77]) + yield self.accepted() + self.save_spendable_output() - block(78) - tx78 = create_tx(tx77, 0, 9 * COIN) - update_block(78, [tx78]) - yield accepted() + self.next_block(78) + tx78 = self.create_tx(tx77, 0, 9 * COIN) + self.update_block(78, [tx78]) + yield self.accepted() - block(79) - tx79 = create_tx(tx78, 0, 8 * COIN) - update_block(79, [tx79]) - yield accepted() + self.next_block(79) + tx79 = self.create_tx(tx78, 0, 8 * COIN) + self.update_block(79, [tx79]) + yield self.accepted() # mempool should be empty assert_equal(len(self.nodes[0].getrawmempool()), 0) - tip(77) - block(80, spend=out[25]) - yield rejected() - save_spendable_output() + self.move_tip(77) + self.next_block(80, spend=out[25]) + yield self.rejected() + self.save_spendable_output() - block(81, spend=out[26]) - yield rejected() # other chain is same length - save_spendable_output() + self.next_block(81, spend=out[26]) + yield self.rejected() # other chain is same length + self.save_spendable_output() - block(82, spend=out[27]) - yield accepted() # now this chain is longer, triggers re-org - save_spendable_output() + self.next_block(82, spend=out[27]) + yield self.accepted() # now this chain is longer, triggers re-org + self.save_spendable_output() # now check that tx78 and tx79 have been put back into the peer's mempool mempool = self.nodes[0].getrawmempool() @@ -1188,18 +1085,18 @@ def update_block(block_number, new_transactions): # # -> b81 (26) -> b82 (27) -> b83 (28) # - block(83) + self.next_block(83) op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF] script = CScript(op_codes) - tx1 = create_and_sign_tx(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script) + tx1 = self.create_and_sign_transaction(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script) - tx2 = create_and_sign_tx(tx1, 0, 0, CScript([OP_TRUE])) + tx2 = self.create_and_sign_transaction(tx1, 0, 0, CScript([OP_TRUE])) tx2.vin[0].scriptSig = CScript([OP_FALSE]) tx2.rehash() - update_block(83, [tx1, tx2]) - yield accepted() - save_spendable_output() + self.update_block(83, [tx1, tx2]) + yield self.accepted() + self.save_spendable_output() # Reorg on/off blocks that have OP_RETURN in them (and try to spend them) # @@ -1207,8 +1104,8 @@ def update_block(block_number, new_transactions): # \-> b85 (29) -> b86 (30) \-> b89a (32) # # - block(84) - tx1 = create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN])) + self.next_block(84) + tx1 = self.create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN])) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) @@ -1216,84 +1113,176 @@ def update_block(block_number, new_transactions): tx1.calc_sha256() self.sign_tx(tx1, out[29].tx, out[29].n) tx1.rehash() - tx2 = create_tx(tx1, 1, 0, CScript([OP_RETURN])) + tx2 = self.create_tx(tx1, 1, 0, CScript([OP_RETURN])) tx2.vout.append(CTxOut(0, CScript([OP_RETURN]))) - tx3 = create_tx(tx1, 2, 0, CScript([OP_RETURN])) + tx3 = self.create_tx(tx1, 2, 0, CScript([OP_RETURN])) tx3.vout.append(CTxOut(0, CScript([OP_TRUE]))) - tx4 = create_tx(tx1, 3, 0, CScript([OP_TRUE])) + tx4 = self.create_tx(tx1, 3, 0, CScript([OP_TRUE])) tx4.vout.append(CTxOut(0, CScript([OP_RETURN]))) - tx5 = create_tx(tx1, 4, 0, CScript([OP_RETURN])) + tx5 = self.create_tx(tx1, 4, 0, CScript([OP_RETURN])) - update_block(84, [tx1, tx2, tx3, tx4, tx5]) - yield accepted() - save_spendable_output() + self.update_block(84, [tx1, tx2, tx3, tx4, tx5]) + yield self.accepted() + self.save_spendable_output() - tip(83) - block(85, spend=out[29]) - yield rejected() + self.move_tip(83) + self.next_block(85, spend=out[29]) + yield self.rejected() - block(86, spend=out[30]) - yield accepted() + self.next_block(86, spend=out[30]) + yield self.accepted() - tip(84) - block(87, spend=out[30]) - yield rejected() - save_spendable_output() + self.move_tip(84) + self.next_block(87, spend=out[30]) + yield self.rejected() + self.save_spendable_output() - block(88, spend=out[31]) - yield accepted() - save_spendable_output() + self.next_block(88, spend=out[31]) + yield self.accepted() + self.save_spendable_output() # trying to spend the OP_RETURN output is rejected - block("89a", spend=out[32]) - tx = create_tx(tx1, 0, 0, CScript([OP_TRUE])) - update_block("89a", [tx]) - yield rejected() - - # Test re-org of a ~2 days' worth of blocks (1088 blocks) - # This test takes a minute or two and can be accomplished in memory - # - if self.options.runbarelyexpensive: - tip(88) - LARGE_REORG_SIZE = 1088 - test1 = TestInstance(sync_every_block=False) - spend = out[32] - for i in range(89, LARGE_REORG_SIZE + 89): - b = block(i, spend) - tx = CTransaction() - script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69 - script_output = CScript([b'\x00' * script_length]) - tx.vout.append(CTxOut(0, script_output)) - tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0))) - b = update_block(i, [tx]) - assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE) - test1.blocks_and_transactions.append([self.tip, True]) - save_spendable_output() - spend = get_spendable_output() - - yield test1 - chain1_tip = i - - # now create alt chain of same length - tip(88) - test2 = TestInstance(sync_every_block=False) - for i in range(89, LARGE_REORG_SIZE + 89): - block("alt" + str(i)) - test2.blocks_and_transactions.append([self.tip, False]) - yield test2 - - # extend alt chain to trigger re-org - block("alt" + str(chain1_tip + 1)) - yield accepted() - - # ... and re-org back to the first chain - tip(chain1_tip) - block(chain1_tip + 1) - yield rejected() - block(chain1_tip + 2) - yield accepted() - - chain1_tip += 2 + self.next_block("89a", spend=out[32]) + tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE])) + self.update_block("89a", [tx]) + yield self.rejected() + + self.move_tip(88) + LARGE_REORG_SIZE = 1088 + test1 = TestInstance(sync_every_block=False) + spend = out[32] + for i in range(89, LARGE_REORG_SIZE + 89): + b = self.next_block(i, spend) + tx = CTransaction() + script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69 + script_output = CScript([b'\x00' * script_length]) + tx.vout.append(CTxOut(0, script_output)) + tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0))) + b = self.update_block(i, [tx]) + assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE) + test1.blocks_and_transactions.append([self.tip, True]) + self.save_spendable_output() + spend = self.get_spendable_output() + + yield test1 + chain1_tip = i + + # now create alt chain of same length + self.move_tip(88) + test2 = TestInstance(sync_every_block=False) + for i in range(89, LARGE_REORG_SIZE + 89): + self.next_block("alt" + str(i)) + test2.blocks_and_transactions.append([self.tip, False]) + yield test2 + + # extend alt chain to trigger re-org + self.next_block("alt" + str(chain1_tip + 1)) + yield self.accepted() + + # ... and re-org back to the first chain + self.move_tip(chain1_tip) + self.next_block(chain1_tip + 1) + yield self.rejected() + self.next_block(chain1_tip + 2) + yield self.accepted() + + chain1_tip += 2 + + # Helper methods + ################ + + 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 + + # 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, err) = SignatureHash(spend_tx.vout[n].scriptPubKey, tx, 0, SIGHASH_ALL) + tx.vin[0].scriptSig = CScript([self.coinbase_key.sign(sighash) + bytes(bytearray([SIGHASH_ALL]))]) + + 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]), solve=True): + if self.tip is None: + base_block_hash = self.genesis_hash + block_time = self.mocktime + 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 is None: + block = create_block(base_block_hash, coinbase, block_time) + else: + coinbase.vout[0].nValue += spend.tx.vout[spend.n].nValue - 1 # all but one satoshi to fees + coinbase.rehash() + block = create_block(base_block_hash, coinbase, block_time) + tx = create_transaction(spend.tx, spend.n, b"", 1, script) # spend 1 satoshi + self.sign_tx(tx, spend.tx, spend.n) + self.add_transactions_to_block(block, [tx]) + block.hashMerkleRoot = block.calc_merkle_root() + if solve: + block.solve() + self.tip = block + self.block_heights[block.sha256] = height + assert number not in self.blocks + self.blocks[number] = block + return block + + # save the current tip so it can be spent by a later block + def save_spendable_output(self): + self.spendable_outputs.append(self.tip) + + # get an output that we previously marked as spendable + def get_spendable_output(self): + return PreviousSpendableOutput(self.spendable_outputs.pop(0).vtx[0], 0) + + # returns a test case that asserts that the current tip was accepted + def accepted(self): + return TestInstance([[self.tip, True]]) + + # returns a test case that asserts that the current tip was rejected + def rejected(self, 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 + def move_tip(self, number): + self.tip = self.blocks[number] + + # adds transactions to the block and updates state + def update_block(self, 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 if __name__ == '__main__': From 9d759230ecc4025cf2baa566a0b7e5304705eadf Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 22 Nov 2017 14:07:26 -0500 Subject: [PATCH 04/14] [tests] Add logging to feature_block.py --- test/functional/p2p-fullblocktest.py | 77 +++++++++++++++++++++------- 1 file changed, 58 insertions(+), 19 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index ea3dad6f13cb..9d45676b430a 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -143,6 +143,7 @@ def get_tests(self): # \-> b3 (1) # # Nothing should happen at this point. We saw b2 first so it takes priority. + self.log.info("Don't reorg to a chain of the same length") self.move_tip(1) b3 = self.next_block(3, spend=out[1]) txout_b3 = PreviousSpendableOutput(b3.vtx[1], 0) @@ -152,6 +153,7 @@ def get_tests(self): # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) -> b4 (2) + self.log.info("Reorg to a longer chain") self.next_block(4, spend=out[2]) yield self.accepted() @@ -163,6 +165,7 @@ def get_tests(self): self.save_spendable_output() yield self.rejected() + self.log.info("Reorg back to the original chain") self.next_block(6, spend=out[3]) yield self.accepted() @@ -170,6 +173,7 @@ def get_tests(self): # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b7 (2) -> b8 (4) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a chain with a double spend, even if it is longer") self.move_tip(5) self.next_block(7, spend=out[2]) yield self.rejected() @@ -181,6 +185,7 @@ def get_tests(self): # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b9 (4) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a block where the miner creates too much coinbase reward") self.move_tip(6) self.next_block(9, spend=out[4], additional_coinbase_value=1) yield self.rejected(RejectResult(16, b'bad-cb-amount')) @@ -189,6 +194,7 @@ def get_tests(self): # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b10 (3) -> b11 (4) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer") self.move_tip(5) self.next_block(10, spend=out[3]) yield self.rejected() @@ -201,6 +207,7 @@ def get_tests(self): # \-> b12 (3) -> b13 (4) -> b14 (5) # (b12 added last) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer (on a forked chain)") self.move_tip(5) b12 = self.next_block(12, spend=out[3]) self.save_spendable_output() @@ -221,15 +228,14 @@ def get_tests(self): # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6) # \-> b3 (1) -> b4 (2) - - # Test that a block with a lot of checksigs is okay + self.log.info("Accept a block with lots of checksigs") lots_of_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS - 1)) self.move_tip(13) self.next_block(15, spend=out[5], script=lots_of_checksigs) yield self.accepted() self.save_spendable_output() - # Test that a block with too many checksigs is rejected + self.log.info("Reject a block with too many checksigs") too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS)) self.next_block(16, spend=out[6], script=too_many_checksigs) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) @@ -238,6 +244,7 @@ def get_tests(self): # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b17 (b3.vtx[1]) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a block with a spend from a re-org'ed out tx") self.move_tip(15) self.next_block(17, spend=txout_b3) yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) @@ -247,6 +254,7 @@ def get_tests(self): # \-> b12 (3) -> b13 (4) -> b15 (5) # \-> b18 (b3.vtx[1]) -> b19 (6) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a block with a spend from a re-org'ed out tx (on a forked chain)") self.move_tip(13) self.next_block(18, spend=txout_b3) yield self.rejected() @@ -258,6 +266,7 @@ def get_tests(self): # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b20 (7) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a block spending an immature coinbase.") self.move_tip(15) self.next_block(20, spend=out[7]) yield self.rejected(RejectResult(16, b'bad-txns-premature-spend-of-coinbase')) @@ -267,6 +276,7 @@ def get_tests(self): # \-> b12 (3) -> b13 (4) -> b15 (5) # \-> b21 (6) -> b22 (5) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a block spending an immature coinbase (on a forked chain)") self.move_tip(13) self.next_block(21, spend=out[6]) yield self.rejected() @@ -279,6 +289,7 @@ def get_tests(self): # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) # \-> b24 (6) -> b25 (7) # \-> b3 (1) -> b4 (2) + self.log.info("Accept a block of size MAX_BLOCK_BASE_SIZE") self.move_tip(15) b23 = self.next_block(23, spend=out[6]) tx = CTransaction() @@ -292,7 +303,7 @@ def get_tests(self): yield self.accepted() self.save_spendable_output() - # Make the next block one byte bigger and check that it fails + self.log.info("Reject a block of size MAX_BLOCK_BASE_SIZE + 1") self.move_tip(15) b24 = self.next_block(24, spend=out[6]) script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69 @@ -310,6 +321,7 @@ def get_tests(self): # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) # \-> ... (6) -> ... (7) # \-> b3 (1) -> b4 (2) + self.log.info("Reject a block with coinbase input script size out of range") self.move_tip(15) b26 = self.next_block(26, spend=out[6]) b26.vtx[0].vin[0].scriptSig = b'\x00' @@ -353,6 +365,7 @@ def get_tests(self): # # MULTISIG: each op code counts as 20 sigops. To create the edge case, pack another 19 sigops at the end. + self.log.info("Accept a block with the max number of OP_CHECKMULTISIG sigops") lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19) b31 = self.next_block(31, spend=out[8], script=lots_of_multisigs) assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS) @@ -360,29 +373,34 @@ def get_tests(self): self.save_spendable_output() # this goes over the limit because the coinbase has one sigop + self.log.info("Reject a block with too many OP_CHECKMULTISIG sigops") too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS // 20)) b32 = self.next_block(32, spend=out[9], script=too_many_multisigs) assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS + 1) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # CHECKMULTISIGVERIFY + self.log.info("Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops") self.move_tip(31) lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19) self.next_block(33, spend=out[9], script=lots_of_multisigs) yield self.accepted() self.save_spendable_output() + self.log.info("Reject a block with too many OP_CHECKMULTISIGVERIFY sigops") too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS // 20)) self.next_block(34, spend=out[10], script=too_many_multisigs) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # CHECKSIGVERIFY + self.log.info("Accept a block with the max number of OP_CHECKSIGVERIFY sigops") self.move_tip(33) lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS - 1)) b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs) yield self.accepted() self.save_spendable_output() + self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops") too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS)) self.next_block(36, spend=out[11], script=too_many_checksigs) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) @@ -396,6 +414,7 @@ def get_tests(self): # # save 37's spendable output, but then double-spend out11 to invalidate the block + self.log.info("Reject a block spending transaction from a block which failed to connect") self.move_tip(35) b37 = self.next_block(37, spend=out[11]) txout_b37 = PreviousSpendableOutput(b37.vtx[1], 0) @@ -419,6 +438,7 @@ def get_tests(self): # redeem_script = COINBASE_PUBKEY, (OP_2DUP+OP_CHECKSIGVERIFY) * 5, OP_CHECKSIG # p2sh_script = OP_HASH160, ripemd160(sha256(script)), OP_EQUAL # + self.log.info("Check P2SH SIGOPS are correctly counted") self.move_tip(35) b39 = self.next_block(39) b39_outputs = 0 @@ -465,6 +485,7 @@ def get_tests(self): # # b41 does the same, less one, so it has the maximum sigops permitted. # + self.log.info("Reject a block with too many P2SH sigops") self.move_tip(39) b40 = self.next_block(40, spend=out[12]) sigops = get_legacy_sigopcount_block(b40) @@ -499,6 +520,7 @@ def get_tests(self): yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # same as b40, but one less sigop + self.log.info("Accept a block with the max number of P2SH sigops") self.move_tip(39) self.next_block(41, spend=None) self.update_block(41, b40.vtx[1:-1]) @@ -531,6 +553,7 @@ def get_tests(self): # The next few blocks are going to be created "by hand" since they'll do funky things, such as having # the first transaction be non-coinbase, etc. The purpose of b44 is to make sure this works. + self.log.info("Build block 44 manually") height = self.block_heights[self.tip.sha256] + 1 coinbase = create_coinbase(height, self.coinbase_pubkey) b44 = CBlock() @@ -545,7 +568,7 @@ def get_tests(self): self.blocks[44] = b44 yield self.accepted() - # A block with a non-coinbase as the first tx + self.log.info("Reject a block with a non-coinbase as the first tx") non_coinbase = self.create_tx(out[15].tx, out[15].n, 1) b45 = CBlock() b45.nTime = self.tip.nTime + 1 @@ -560,7 +583,7 @@ def get_tests(self): self.blocks[45] = b45 yield self.rejected(RejectResult(16, b'bad-cb-missing')) - # A block with no txns + self.log.info("Reject a block with no transactions") self.move_tip(44) b46 = CBlock() b46.nTime = b44.nTime + 1 @@ -575,7 +598,7 @@ def get_tests(self): self.blocks[46] = b46 yield self.rejected(RejectResult(16, b'bad-blk-length')) - # A block with invalid work + self.log.info("Reject a block with invalid work") self.move_tip(44) b47 = self.next_block(47, solve=False) target = uint256_from_compact(b47.nBits) @@ -584,35 +607,35 @@ def get_tests(self): b47.rehash() yield self.rejected(RejectResult(16, b'high-hash')) - # A block with timestamp > 2 hrs in the future + self.log.info("Reject a block with a timestamp >2 hours in the future") self.move_tip(44) b48 = self.next_block(48, solve=False) b48.nTime = int(self.mocktime) + 60 * 60 * 3 b48.solve() yield self.rejected(RejectResult(16, b'time-too-new')) - # A block with an invalid merkle hash + self.log.info("Reject a block with invalid merkle hash") self.move_tip(44) b49 = self.next_block(49) b49.hashMerkleRoot += 1 b49.solve() yield self.rejected(RejectResult(16, b'bad-txnmrklroot')) - # A block with an incorrect POW limit + self.log.info("Reject a block with incorrect POW limit") self.move_tip(44) b50 = self.next_block(50) b50.nBits = b50.nBits - 1 b50.solve() yield self.rejected(RejectResult(16, b'bad-diffbits')) - # A block with two coinbase txns + self.log.info("Reject a block with two coinbase transactions") self.move_tip(44) self.next_block(51) cb2 = create_coinbase(51, self.coinbase_pubkey) self.update_block(51, [cb2]) yield self.rejected(RejectResult(16, b'bad-cb-multiple')) - # A block w/ duplicate txns + self.log.info("Reject a block with duplicate transactions") # Note: txns have to be in the right position in the merkle tree to trigger this error self.move_tip(44) b52 = self.next_block(52, spend=out[15]) @@ -629,7 +652,7 @@ def get_tests(self): yield self.rejected() # rejected since b44 is at same height self.save_spendable_output() - # invalid timestamp (b35 is 5 blocks back, so its time is MedianTimePast) + self.log.info("Reject a block with timestamp before MedianTimePast") b54 = self.next_block(54, spend=out[15]) b54.nTime = b35.nTime - 1 b54.solve() @@ -678,6 +701,7 @@ def get_tests(self): b57 = self.update_block(57, [tx, tx1]) # b56 - copy b57, add a duplicate tx + self.log.info("Reject a block with a duplicate transaction in the Merkle Tree (but with a valid Merkle Root)") self.move_tip(55) b56 = copy.deepcopy(b57) self.blocks[56] = b56 @@ -697,6 +721,7 @@ def get_tests(self): b57p2 = self.update_block("57p2", [tx, tx1, tx2, tx3, tx4]) # b56p2 - copy b57p2, duplicate two non-consecutive tx's + self.log.info("Reject a block with two duplicate transactions in the Merkle Tree (but with a valid Merkle Root)") self.move_tip(55) b56p2 = copy.deepcopy(b57p2) self.blocks["b56p2"] = b56p2 @@ -719,6 +744,7 @@ def get_tests(self): # # tx with prevout.n out of range + self.log.info("Reject a block with a transaction with prevout.n out of range") self.move_tip(57) self.next_block(58, spend=out[17]) tx = CTransaction() @@ -730,6 +756,7 @@ def get_tests(self): yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # tx with output value > input value out of range + self.log.info("Reject a block with a transaction with outputs > inputs") self.move_tip(57) self.next_block(59) tx = self.create_and_sign_transaction(out[17].tx, out[17].n, 510 * COIN) @@ -751,6 +778,7 @@ def get_tests(self): # not-fully-spent transaction in the same chain. To test, make identical coinbases; # the second one should be rejected. # + self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)") self.move_tip(60) b61 = self.next_block(61, spend=out[18]) b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # equalize the coinbases @@ -764,6 +792,7 @@ def get_tests(self): # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> b62 (18) # + self.log.info("Reject a block with a transaction with a nonfinal locktime") self.move_tip(60) self.next_block(62) tx = CTransaction() @@ -781,6 +810,7 @@ def get_tests(self): # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> b63 (-) # + self.log.info("Reject a block with a coinbase transaction with a nonfinal locktime") self.move_tip(60) b63 = self.next_block(63) b63.vtx[0].nLockTime = 0xffffffff @@ -803,6 +833,7 @@ def get_tests(self): # b64a is a bloated block (non-canonical varint) # b64 is a good block (same as b64 but w/ canonical varint) # + self.log.info("Accept a valid block even if a bloated version of the block has previously been sent") self.move_tip(60) regular_block = self.next_block("64a", spend=out[18]) @@ -839,6 +870,7 @@ def get_tests(self): # # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # + self.log.info("Accept a block with a transaction spending an output created in the same block") self.move_tip(64) self.next_block(65) tx1 = self.create_and_sign_transaction(out[19].tx, out[19].n, out[19].tx.vout[0].nValue) @@ -851,6 +883,7 @@ def get_tests(self): # # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # \-> b66 (20) + self.log.info("Reject a block with a transaction spending an output created later in the same block") self.move_tip(65) self.next_block(66) tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) @@ -864,6 +897,7 @@ def get_tests(self): # \-> b67 (20) # # + self.log.info("Reject a block with a transaction double spending a transaction creted in the same block") self.move_tip(65) self.next_block(67) tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) @@ -884,12 +918,14 @@ def get_tests(self): # b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee # this succeeds # + self.log.info("Reject a block trying to claim too much subsidy in the coinbase transaction") self.move_tip(65) self.next_block(68, additional_coinbase_value=10) tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9) self.update_block(68, [tx]) yield self.rejected(RejectResult(16, b'bad-cb-amount')) + self.log.info("Accept a block claiming the correct subsidy in the coinbase transaction") self.move_tip(65) b69 = self.next_block(69, additional_coinbase_value=10) tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10) @@ -902,6 +938,7 @@ def get_tests(self): # -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) # \-> b70 (21) # + self.log.info("Reject a block containing a transaction spending from a non-existent input") self.move_tip(69) self.next_block(70, spend=out[21]) bogus_tx = CTransaction() @@ -919,7 +956,7 @@ def get_tests(self): # # b72 is a good block. # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b71. - # + self.log.info("Reject a block containing a duplicate transaction but with the same Merkle root (Merkle tree malleability") self.move_tip(69) b72 = self.next_block(72) tx1 = self.create_and_sign_transaction(out[21].tx, out[21].n, 2) @@ -956,7 +993,7 @@ def get_tests(self): # bytearray[20,000-20,003]: 521 (max_script_element_size+1, in little-endian format) # bytearray[20,004-20,525]: unread data (script_element) # bytearray[20,526] : OP_CHECKSIG (this puts us over the limit) - # + self.log.info("Reject a block containing too many sigops after a large script element") self.move_tip(72) b73 = self.next_block(73) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 + 1 @@ -984,8 +1021,7 @@ def get_tests(self): # # b74 fails because we put MAX_BLOCK_SIGOPS+1 before the element # b75 succeeds because we put MAX_BLOCK_SIGOPS before the element - # - # + self.log.info("Check sigops are counted correctly after an invalid script element") self.move_tip(72) self.next_block(74) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561 @@ -1041,7 +1077,7 @@ def get_tests(self): # To get around this issue, we construct transactions which are not signed and which # spend to OP_TRUE. If the standard-ness rules change, this test would need to be # updated. (Perhaps to spend to a P2SH OP_TRUE script) - # + self.log.info("Test transaction resurrection during a re-org") self.move_tip(76) self.next_block(77) tx77 = self.create_and_sign_transaction(out[24].tx, out[24].n, 10 * COIN) @@ -1085,6 +1121,7 @@ def get_tests(self): # # -> b81 (26) -> b82 (27) -> b83 (28) # + self.log.info("Accept a block with invalid opcodes in dead execution paths") self.next_block(83) op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF] script = CScript(op_codes) @@ -1103,7 +1140,7 @@ def get_tests(self): # -> b81 (26) -> b82 (27) -> b83 (28) -> b84 (29) -> b87 (30) -> b88 (31) # \-> b85 (29) -> b86 (30) \-> b89a (32) # - # + self.log.info("Test re-orging blocks with OP_RETURN in them") self.next_block(84) tx1 = self.create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN])) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) @@ -1147,6 +1184,8 @@ def get_tests(self): self.update_block("89a", [tx]) yield self.rejected() + self.log.info("Test a re-org of ~2 days' worth of blocks (1088 blocks)") + self.move_tip(88) LARGE_REORG_SIZE = 1088 test1 = TestInstance(sync_every_block=False) From 7a81a42806c264ae19c72138bc93fe56523a0aca Mon Sep 17 00:00:00 2001 From: John Newbery Date: Sat, 8 Apr 2017 15:59:04 -0400 Subject: [PATCH 05/14] [tests] Change feature_block.py to use BitcoinTestFramework --- test/functional/p2p-fullblocktest.py | 479 +++++++++++++-------------- 1 file changed, 235 insertions(+), 244 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index 9d45676b430a..3d57f92c5832 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -8,11 +8,9 @@ import time from test_framework.blocktools import create_block, create_coinbase, create_transaction, get_legacy_sigopcount_block -from test_framework.comptool import RejectResult, TestInstance, TestManager from test_framework.key import CECKey from test_framework.messages import ( CBlock, - CBlockHeader, COIN, COutPoint, CTransaction, @@ -22,7 +20,7 @@ uint256_from_compact, uint256_from_str, ) -from test_framework.mininode import network_thread_start +from test_framework.mininode import P2PDataStore, network_thread_start, network_thread_join from test_framework.script import ( CScript, MAX_SCRIPT_ELEMENT_SIZE, @@ -44,13 +42,8 @@ SignatureHash, hash160, ) -from test_framework.test_framework import ComparisonTestFramework -from test_framework.util import * -from test_framework.comptool import TestManager, TestInstance, RejectResult -from test_framework.blocktools import * -from test_framework.key import CECKey -from test_framework.script import * -import struct +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal MAX_BLOCK_SIGOPS = 20000 @@ -62,9 +55,6 @@ def __init__(self, tx=CTransaction(), n=-1): # Use this class for tests that require behavior other than normal "mininode" behavior. # For now, it is used to serialize a bloated varint (b64). class CBrokenBlock(CBlock): - def __init__(self, header=None): - super(CBrokenBlock, self).__init__(header) - def initialize(self, base_block): self.vtx = copy.deepcopy(base_block.vtx) self.hashMerkleRoot = self.calc_merkle_root() @@ -78,48 +68,44 @@ def serialize(self): return r def normal_serialize(self): - r = b"" - r += super(CBrokenBlock, self).serialize() - return r + return super().serialize() -class FullBlockTest(ComparisonTestFramework): - # Can either run this test as 1 node with expected answers, or two and compare them. - # Change the "outcome" variable from each TestInstance object to only do the comparison. +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 = {} # Must set '-dip3params=2000:2000' to create pre-dip3 blocks only self.extra_args = [['-whitelist=127.0.0.1', '-dip3params=2000:2000']] def run_test(self): - self.test = TestManager(self, self.options.tmpdir) - self.test.add_all_connections(self.nodes) + node = self.nodes[0] # convenience reference to the node + + # reconnect_p2p() expects the network thread to be running network_thread_start() - self.test.run() - def get_tests(self): + self.reconnect_p2p() + + 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 = {} self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 self.spendable_outputs = [] # Create a new block - self.next_block(0) + b0 = self.next_block(0) self.save_spendable_output() - yield self.accepted() + self.sync_blocks([b0]) - # Now we need that block to mature so we can spend the coinbase. - test = TestInstance(sync_every_block=False) + # Allow the block to mature + blocks = [] for i in range(99): - self.next_block(5000 + i) - test.blocks_and_transactions.append([self.tip, True]) + blocks.append(self.next_block(5000 + i)) self.save_spendable_output() - yield test + self.sync_blocks(blocks) # collect spendable outputs now to avoid cluttering the code later on out = [] @@ -129,15 +115,15 @@ def get_tests(self): # Start by building a couple of blocks on top (which output is spent is # in parentheses): # genesis -> b1 (0) -> b2 (1) - self.next_block(1, spend=out[0]) + b1 = self.next_block(1, spend=out[0]) self.save_spendable_output() - yield self.accepted() - self.next_block(2, spend=out[1]) - yield self.accepted() + b2 = self.next_block(2, spend=out[1]) self.save_spendable_output() - # so fork like this: + self.sync_blocks([b1, b2]) + + # Fork like this: # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) @@ -147,27 +133,27 @@ def get_tests(self): self.move_tip(1) b3 = self.next_block(3, spend=out[1]) txout_b3 = PreviousSpendableOutput(b3.vtx[1], 0) - yield self.rejected() + self.sync_blocks([b3], False) # Now we add another block to make the alternative chain longer. # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) -> b4 (2) self.log.info("Reorg to a longer chain") - self.next_block(4, spend=out[2]) - yield self.accepted() + b4 = self.next_block(4, spend=out[2]) + self.sync_blocks([b4]) # ... and back to the first chain. # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b3 (1) -> b4 (2) self.move_tip(2) - self.next_block(5, spend=out[2]) + b5 = self.next_block(5, spend=out[2]) self.save_spendable_output() - yield self.rejected() + self.sync_blocks([b5], False) self.log.info("Reorg back to the original chain") - self.next_block(6, spend=out[3]) - yield self.accepted() + b6 = self.next_block(6, spend=out[3]) + self.sync_blocks([b6], True) # Try to create a fork that double-spends # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -175,11 +161,11 @@ def get_tests(self): # \-> b3 (1) -> b4 (2) self.log.info("Reject a chain with a double spend, even if it is longer") self.move_tip(5) - self.next_block(7, spend=out[2]) - yield self.rejected() + b7 = self.next_block(7, spend=out[2]) + self.sync_blocks([b7], False) - self.next_block(8, spend=out[4]) - yield self.rejected() + b8 = self.next_block(8, spend=out[4]) + self.sync_blocks([b8], False, reconnect=True) # Try to create a block that has too much fee # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -187,8 +173,8 @@ def get_tests(self): # \-> b3 (1) -> b4 (2) self.log.info("Reject a block where the miner creates too much coinbase reward") self.move_tip(6) - self.next_block(9, spend=out[4], additional_coinbase_value=1) - yield self.rejected(RejectResult(16, b'bad-cb-amount')) + b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1) + self.sync_blocks([b9], False, 16, b'bad-cb-amount', reconnect=True) # Create a fork that ends in a block with too much fee (the one that causes the reorg) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -196,33 +182,27 @@ def get_tests(self): # \-> b3 (1) -> b4 (2) self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer") self.move_tip(5) - self.next_block(10, spend=out[3]) - yield self.rejected() + b10 = self.next_block(10, spend=out[3]) + self.sync_blocks([b10], False) - self.next_block(11, spend=out[4], additional_coinbase_value=1) - yield self.rejected(RejectResult(16, b'bad-cb-amount')) + b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1) + self.sync_blocks([b11], False, 16, b'bad-cb-amount', reconnect=True) # Try again, but with a valid fork first # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b14 (5) - # (b12 added last) # \-> b3 (1) -> b4 (2) self.log.info("Reject a chain where the miner creates too much coinbase reward, even if the chain is longer (on a forked chain)") self.move_tip(5) b12 = self.next_block(12, spend=out[3]) self.save_spendable_output() b13 = self.next_block(13, spend=out[4]) - # Deliver the block header for b12, and the block b13. - # b13 should be accepted but the tip won't advance until b12 is delivered. - yield TestInstance([[CBlockHeader(b12), None], [b13, False]]) - self.save_spendable_output() - # b14 is invalid, but the node won't know that until it tries to connect - # Tip still can't advance because b12 is missing - self.next_block(14, spend=out[5], additional_coinbase_value=1) - yield self.rejected() + b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1) + self.sync_blocks([b12, b13, b14], False, 16, b'bad-cb-amount', reconnect=True) - yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13. + # New tip should be b13. + assert_equal(node.getbestblockhash(), b13.hash) # Add a block with MAX_BLOCK_SIGOPS and one with one more sigop # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -231,14 +211,14 @@ def get_tests(self): self.log.info("Accept a block with lots of checksigs") lots_of_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS - 1)) self.move_tip(13) - self.next_block(15, spend=out[5], script=lots_of_checksigs) - yield self.accepted() + b15 = self.next_block(15, spend=out[5], script=lots_of_checksigs) self.save_spendable_output() + self.sync_blocks([b15], True) self.log.info("Reject a block with too many checksigs") too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS)) - self.next_block(16, spend=out[6], script=too_many_checksigs) - yield self.rejected(RejectResult(16, b'bad-blk-sigops')) + b16 = self.next_block(16, spend=out[6], script=too_many_checksigs) + self.sync_blocks([b16], False, 16, b'bad-blk-sigops', reconnect=True) # Attempt to spend a transaction created on a different fork # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -246,8 +226,8 @@ def get_tests(self): # \-> b3 (1) -> b4 (2) self.log.info("Reject a block with a spend from a re-org'ed out tx") self.move_tip(15) - self.next_block(17, spend=txout_b3) - yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + b17 = self.next_block(17, spend=txout_b3) + self.sync_blocks([b17], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) # Attempt to spend a transaction created on a different fork (on a fork this time) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -256,11 +236,11 @@ def get_tests(self): # \-> b3 (1) -> b4 (2) self.log.info("Reject a block with a spend from a re-org'ed out tx (on a forked chain)") self.move_tip(13) - self.next_block(18, spend=txout_b3) - yield self.rejected() + b18 = self.next_block(18, spend=txout_b3) + self.sync_blocks([b18], False) - self.next_block(19, spend=out[6]) - yield self.rejected() + b19 = self.next_block(19, spend=out[6]) + self.sync_blocks([b19], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) # Attempt to spend a coinbase at depth too low # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -268,8 +248,8 @@ def get_tests(self): # \-> b3 (1) -> b4 (2) self.log.info("Reject a block spending an immature coinbase.") self.move_tip(15) - self.next_block(20, spend=out[7]) - yield self.rejected(RejectResult(16, b'bad-txns-premature-spend-of-coinbase')) + b20 = self.next_block(20, spend=out[7]) + self.sync_blocks([b20], False, 16, b'bad-txns-premature-spend-of-coinbase') # Attempt to spend a coinbase at depth too low (on a fork this time) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -278,11 +258,11 @@ def get_tests(self): # \-> b3 (1) -> b4 (2) self.log.info("Reject a block spending an immature coinbase (on a forked chain)") self.move_tip(13) - self.next_block(21, spend=out[6]) - yield self.rejected() + b21 = self.next_block(21, spend=out[6]) + self.sync_blocks([b21], False) - self.next_block(22, spend=out[5]) - yield self.rejected() + b22 = self.next_block(22, spend=out[5]) + self.sync_blocks([b22], False, 16, b'bad-txns-premature-spend-of-coinbase') # Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -300,7 +280,7 @@ def get_tests(self): b23 = self.update_block(23, [tx]) # Make sure the math above worked out to produce a max-sized block assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE) - yield self.accepted() + self.sync_blocks([b23], True) self.save_spendable_output() self.log.info("Reject a block of size MAX_BLOCK_BASE_SIZE + 1") @@ -311,10 +291,10 @@ def get_tests(self): tx.vout = [CTxOut(0, script_output)] b24 = self.update_block(24, [tx]) assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1) - yield self.rejected(RejectResult(16, b'bad-blk-length')) + self.sync_blocks([b24], False, 16, b'bad-blk-length', reconnect=True) - self.next_block(25, spend=out[7]) - yield self.rejected() + b25 = self.next_block(25, spend=out[7]) + self.sync_blocks([b25], False) # Create blocks with a coinbase input script size out of range # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -329,11 +309,11 @@ def get_tests(self): # update_block causes the merkle root to get updated, even with no new # transactions, and updates the required state. b26 = self.update_block(26, []) - yield self.rejected(RejectResult(16, b'bad-cb-length')) + self.sync_blocks([b26], False, 16, b'bad-cb-length', reconnect=True) # Extend the b26 chain to make sure bitcoind isn't accepting b26 - self.next_block(27, spend=out[7]) - yield self.rejected(False) + b27 = self.next_block(27, spend=out[7]) + self.sync_blocks([b27], False) # Now try a too-large-coinbase script self.move_tip(15) @@ -341,11 +321,11 @@ def get_tests(self): b28.vtx[0].vin[0].scriptSig = b'\x00' * 101 b28.vtx[0].rehash() b28 = self.update_block(28, []) - yield self.rejected(RejectResult(16, b'bad-cb-length')) + self.sync_blocks([b28], False, 16, b'bad-cb-length', reconnect=True) # Extend the b28 chain to make sure bitcoind isn't accepting b28 - self.next_block(29, spend=out[7]) - yield self.rejected(False) + b29 = self.next_block(29, spend=out[7]) + self.sync_blocks([b29], False) # b30 has a max-sized coinbase scriptSig. self.move_tip(23) @@ -353,7 +333,7 @@ def get_tests(self): b30.vtx[0].vin[0].scriptSig = b'\x00' * 100 b30.vtx[0].rehash() b30 = self.update_block(30, []) - yield self.accepted() + self.sync_blocks([b30], True) self.save_spendable_output() # b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY @@ -369,7 +349,7 @@ def get_tests(self): lots_of_multisigs = CScript([OP_CHECKMULTISIG] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19) b31 = self.next_block(31, spend=out[8], script=lots_of_multisigs) assert_equal(get_legacy_sigopcount_block(b31), MAX_BLOCK_SIGOPS) - yield self.accepted() + self.sync_blocks([b31], True) self.save_spendable_output() # this goes over the limit because the coinbase has one sigop @@ -377,33 +357,33 @@ def get_tests(self): too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS // 20)) b32 = self.next_block(32, spend=out[9], script=too_many_multisigs) assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS + 1) - yield self.rejected(RejectResult(16, b'bad-blk-sigops')) + self.sync_blocks([b32], False, 16, b'bad-blk-sigops', reconnect=True) # CHECKMULTISIGVERIFY self.log.info("Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops") self.move_tip(31) lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS - 1) // 20) + [OP_CHECKSIG] * 19) - self.next_block(33, spend=out[9], script=lots_of_multisigs) - yield self.accepted() + b33 = self.next_block(33, spend=out[9], script=lots_of_multisigs) + self.sync_blocks([b33], True) self.save_spendable_output() self.log.info("Reject a block with too many OP_CHECKMULTISIGVERIFY sigops") too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS // 20)) - self.next_block(34, spend=out[10], script=too_many_multisigs) - yield self.rejected(RejectResult(16, b'bad-blk-sigops')) + b34 = self.next_block(34, spend=out[10], script=too_many_multisigs) + self.sync_blocks([b34], False, 16, b'bad-blk-sigops', reconnect=True) # CHECKSIGVERIFY self.log.info("Accept a block with the max number of OP_CHECKSIGVERIFY sigops") self.move_tip(33) lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS - 1)) b35 = self.next_block(35, spend=out[10], script=lots_of_checksigs) - yield self.accepted() + self.sync_blocks([b35], True) self.save_spendable_output() self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops") too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS)) - self.next_block(36, spend=out[11], script=too_many_checksigs) - yield self.rejected(RejectResult(16, b'bad-blk-sigops')) + b36 = self.next_block(36, spend=out[11], script=too_many_checksigs) + self.sync_blocks([b36], False, 16, b'bad-blk-sigops', reconnect=True) # Check spending of a transaction in a block which failed to connect # @@ -420,12 +400,12 @@ def get_tests(self): txout_b37 = PreviousSpendableOutput(b37.vtx[1], 0) tx = self.create_and_sign_transaction(out[11].tx, out[11].n, 0) b37 = self.update_block(37, [tx]) - yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + self.sync_blocks([b37], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) # attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid self.move_tip(35) - self.next_block(38, spend=txout_b37) - yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + b38 = self.next_block(38, spend=txout_b37) + self.sync_blocks([b38], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) # Check P2SH SigOp counting # @@ -475,7 +455,7 @@ def get_tests(self): b39_outputs += 1 b39 = self.update_block(39, []) - yield self.accepted() + self.sync_blocks([b39], True) self.save_spendable_output() # Test sigops in P2SH redeem scripts @@ -517,12 +497,12 @@ def get_tests(self): tx.rehash() new_txs.append(tx) self.update_block(40, new_txs) - yield self.rejected(RejectResult(16, b'bad-blk-sigops')) + self.sync_blocks([b40], False, 16, b'bad-blk-sigops', reconnect=True) # same as b40, but one less sigop self.log.info("Accept a block with the max number of P2SH sigops") self.move_tip(39) - self.next_block(41, spend=None) + b41 = self.next_block(41, spend=None) self.update_block(41, b40.vtx[1:-1]) b41_sigops_to_fill = b40_sigops_to_fill - 1 tx = CTransaction() @@ -530,7 +510,7 @@ def get_tests(self): tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill))) tx.rehash() self.update_block(41, [tx]) - yield self.accepted() + self.sync_blocks([b41], True) # Fork off of b39 to create a constant base again # @@ -538,13 +518,12 @@ def get_tests(self): # \-> b41 (12) # self.move_tip(39) - self.next_block(42, spend=out[12]) - yield self.rejected() + b42 = self.next_block(42, spend=out[12]) self.save_spendable_output() - self.next_block(43, spend=out[13]) - yield self.accepted() + b43 = self.next_block(43, spend=out[13]) self.save_spendable_output() + self.sync_blocks([b42, b43], True) # Test a number of really invalid scenarios # @@ -566,7 +545,7 @@ def get_tests(self): self.tip = b44 self.block_heights[b44.sha256] = height self.blocks[44] = b44 - yield self.accepted() + self.sync_blocks([b44], True) self.log.info("Reject a block with a non-coinbase as the first tx") non_coinbase = self.create_tx(out[15].tx, out[15].n, 1) @@ -581,7 +560,7 @@ def get_tests(self): self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256] + 1 self.tip = b45 self.blocks[45] = b45 - yield self.rejected(RejectResult(16, b'bad-cb-missing')) + self.sync_blocks([b45], False, 16, b'bad-cb-missing', reconnect=True) self.log.info("Reject a block with no transactions") self.move_tip(44) @@ -596,44 +575,44 @@ def get_tests(self): self.tip = b46 assert 46 not in self.blocks self.blocks[46] = b46 - yield self.rejected(RejectResult(16, b'bad-blk-length')) + self.sync_blocks([b46], False, 16, b'bad-blk-length', reconnect=True) self.log.info("Reject a block with invalid work") self.move_tip(44) b47 = self.next_block(47, solve=False) target = uint256_from_compact(b47.nBits) - while b47.sha256 < target: # changed > to < + while b47.sha256 < target: b47.nNonce += 1 b47.rehash() - yield self.rejected(RejectResult(16, b'high-hash')) + self.sync_blocks([b47], False, request_block=False) self.log.info("Reject a block with a timestamp >2 hours in the future") self.move_tip(44) b48 = self.next_block(48, solve=False) b48.nTime = int(self.mocktime) + 60 * 60 * 3 b48.solve() - yield self.rejected(RejectResult(16, b'time-too-new')) + self.sync_blocks([b48], False, request_block=False) self.log.info("Reject a block with invalid merkle hash") self.move_tip(44) b49 = self.next_block(49) b49.hashMerkleRoot += 1 b49.solve() - yield self.rejected(RejectResult(16, b'bad-txnmrklroot')) + self.sync_blocks([b49], False, 16, b'bad-txnmrklroot', reconnect=True) self.log.info("Reject a block with incorrect POW limit") self.move_tip(44) b50 = self.next_block(50) b50.nBits = b50.nBits - 1 b50.solve() - yield self.rejected(RejectResult(16, b'bad-diffbits')) + self.sync_blocks([b50], False, request_block=False, reconnect=True) self.log.info("Reject a block with two coinbase transactions") self.move_tip(44) - self.next_block(51) + b51 = self.next_block(51) cb2 = create_coinbase(51, self.coinbase_pubkey) - self.update_block(51, [cb2]) - yield self.rejected(RejectResult(16, b'bad-cb-multiple')) + b51 = self.update_block(51, [cb2]) + self.sync_blocks([b51], False, 16, b'bad-cb-multiple', reconnect=True) self.log.info("Reject a block with duplicate transactions") # Note: txns have to be in the right position in the merkle tree to trigger this error @@ -641,32 +620,32 @@ def get_tests(self): b52 = self.next_block(52, spend=out[15]) tx = self.create_tx(b52.vtx[1], 0, 1) b52 = self.update_block(52, [tx, tx]) - yield self.rejected(RejectResult(16, b'bad-txns-duplicate')) + self.sync_blocks([b52], False, 16, b'bad-txns-duplicate', reconnect=True) # Test block timestamps # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) # \-> b54 (15) # self.move_tip(43) - self.next_block(53, spend=out[14]) - yield self.rejected() # rejected since b44 is at same height + b53 = self.next_block(53, spend=out[14]) + self.sync_blocks([b53], False) self.save_spendable_output() self.log.info("Reject a block with timestamp before MedianTimePast") b54 = self.next_block(54, spend=out[15]) b54.nTime = b35.nTime - 1 b54.solve() - yield self.rejected(RejectResult(16, b'time-too-old')) + self.sync_blocks([b54], False, request_block=False) # valid timestamp self.move_tip(53) b55 = self.next_block(55, spend=out[15]) b55.nTime = b35.nTime self.update_block(55, []) - yield self.accepted() + self.sync_blocks([b55], True) self.save_spendable_output() - # Test CVE-2012-2459 + # Test Merkle tree malleability # # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16) # \-> b57 (16) @@ -708,7 +687,7 @@ def get_tests(self): assert_equal(len(b56.vtx), 3) b56 = self.update_block(56, [tx1]) assert_equal(b56.hash, b57.hash) - yield self.rejected(RejectResult(16, b'bad-txns-duplicate')) + self.sync_blocks([b56], False, 16, b'bad-txns-duplicate', reconnect=True) # b57p2 - a good block with 6 tx'es, don't submit until end self.move_tip(55) @@ -728,13 +707,13 @@ def get_tests(self): assert_equal(b56p2.hash, b57p2.hash) assert_equal(len(b56p2.vtx), 6) b56p2 = self.update_block("b56p2", [tx3, tx4]) - yield self.rejected(RejectResult(16, b'bad-txns-duplicate')) + self.sync_blocks([b56p2], False, 16, b'bad-txns-duplicate', reconnect=True) self.move_tip("57p2") - yield self.accepted() + self.sync_blocks([b57p2], True) self.move_tip(57) - yield self.rejected() # rejected because 57p2 seen first + self.sync_blocks([b57], False) # The tip is not updated because 57p2 seen first self.save_spendable_output() # Test a few invalid tx types @@ -746,27 +725,27 @@ def get_tests(self): # tx with prevout.n out of range self.log.info("Reject a block with a transaction with prevout.n out of range") self.move_tip(57) - self.next_block(58, spend=out[17]) + b58 = self.next_block(58, spend=out[17]) tx = CTransaction() assert(len(out[17].tx.vout) < 42) tx.vin.append(CTxIn(COutPoint(out[17].tx.sha256, 42), CScript([OP_TRUE]), 0xffffffff)) tx.vout.append(CTxOut(0, b"")) tx.calc_sha256() - self.update_block(58, [tx]) - yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + b58 = self.update_block(58, [tx]) + self.sync_blocks([b58], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) - # tx with output value > input value out of range + # tx with output value > input value self.log.info("Reject a block with a transaction with outputs > inputs") self.move_tip(57) - self.next_block(59) + b59 = self.next_block(59) tx = self.create_and_sign_transaction(out[17].tx, out[17].n, 510 * COIN) - self.update_block(59, [tx]) - yield self.rejected(RejectResult(16, b'bad-txns-in-belowout')) + b59 = self.update_block(59, [tx]) + self.sync_blocks([b59], False, 16, b'bad-txns-in-belowout', reconnect=True) # reset to good chain self.move_tip(57) b60 = self.next_block(60, spend=out[17]) - yield self.accepted() + self.sync_blocks([b60], True) self.save_spendable_output() # Test BIP30 @@ -781,11 +760,11 @@ def get_tests(self): self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)") self.move_tip(60) b61 = self.next_block(61, spend=out[18]) - b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # equalize the coinbases + b61.vtx[0].vin[0].scriptSig = b60.vtx[0].vin[0].scriptSig # Equalize the coinbases b61.vtx[0].rehash() b61 = self.update_block(61, []) assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize()) - yield self.rejected(RejectResult(16, b'bad-txns-BIP30')) + self.sync_blocks([b61], False, 16, b'bad-txns-BIP30', reconnect=True) # Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests) # @@ -794,7 +773,7 @@ def get_tests(self): # self.log.info("Reject a block with a transaction with a nonfinal locktime") self.move_tip(60) - self.next_block(62) + b62 = self.next_block(62) tx = CTransaction() tx.nLockTime = 0xffffffff # this locktime is non-final assert(out[18].n < len(out[18].tx.vout)) @@ -802,8 +781,8 @@ def get_tests(self): tx.vout.append(CTxOut(0, CScript([OP_TRUE]))) assert(tx.vin[0].nSequence < 0xffffffff) tx.calc_sha256() - self.update_block(62, [tx]) - yield self.rejected(RejectResult(16, b'bad-txns-nonfinal')) + b62 = self.update_block(62, [tx]) + self.sync_blocks([b62], False, 16, b'bad-txns-nonfinal') # Test a non-final coinbase is also rejected # @@ -817,7 +796,7 @@ def get_tests(self): b63.vtx[0].vin[0].nSequence = 0xDEADBEEF b63.vtx[0].rehash() b63 = self.update_block(63, []) - yield self.rejected(RejectResult(16, b'bad-txns-nonfinal')) + self.sync_blocks([b63], False, 16, b'bad-txns-nonfinal') # This checks that a block with a bloated VARINT between the block_header and the array of tx such that # the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint, @@ -851,10 +830,14 @@ def get_tests(self): tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0))) b64a = self.update_block("64a", [tx]) assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8) - yield TestInstance([[self.tip, None]]) + self.sync_blocks([b64a], False, 1, b'error parsing message') - # comptool workaround: to make sure b64 is delivered, manually erase b64a from blockstore - self.test.block_store.erase(b64a.sha256) + # 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 + # disconnect and reconnect and then call sync_blocks. + # TODO: improve this test to be less dependent on P2P DOS behaviour. + node.disconnect_p2ps() + self.reconnect_p2p() self.move_tip(60) b64 = CBlock(b64a) @@ -862,8 +845,8 @@ def get_tests(self): assert_equal(b64.hash, b64a.hash) assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE) self.blocks[64] = b64 - self.update_block(64, []) - yield self.accepted() + b64 = self.update_block(64, []) + self.sync_blocks([b64], True) self.save_spendable_output() # Spend an output created in the block itself @@ -872,11 +855,11 @@ def get_tests(self): # self.log.info("Accept a block with a transaction spending an output created in the same block") self.move_tip(64) - self.next_block(65) + b65 = self.next_block(65) tx1 = self.create_and_sign_transaction(out[19].tx, out[19].n, out[19].tx.vout[0].nValue) tx2 = self.create_and_sign_transaction(tx1, 0, 0) - self.update_block(65, [tx1, tx2]) - yield self.accepted() + b65 = self.update_block(65, [tx1, tx2]) + self.sync_blocks([b65], True) self.save_spendable_output() # Attempt to spend an output created later in the same block @@ -885,11 +868,11 @@ def get_tests(self): # \-> b66 (20) self.log.info("Reject a block with a transaction spending an output created later in the same block") self.move_tip(65) - self.next_block(66) + b66 = self.next_block(66) tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) tx2 = self.create_and_sign_transaction(tx1, 0, 1) - self.update_block(66, [tx2, tx1]) - yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + b66 = self.update_block(66, [tx2, tx1]) + self.sync_blocks([b66], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) # Attempt to double-spend a transaction created in a block # @@ -899,12 +882,12 @@ def get_tests(self): # self.log.info("Reject a block with a transaction double spending a transaction creted in the same block") self.move_tip(65) - self.next_block(67) + b67 = self.next_block(67) tx1 = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) tx2 = self.create_and_sign_transaction(tx1, 0, 1) tx3 = self.create_and_sign_transaction(tx1, 0, 2) - self.update_block(67, [tx1, tx2, tx3]) - yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + b67 = self.update_block(67, [tx1, tx2, tx3]) + self.sync_blocks([b67], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) # More tests of block subsidy # @@ -920,17 +903,17 @@ def get_tests(self): # self.log.info("Reject a block trying to claim too much subsidy in the coinbase transaction") self.move_tip(65) - self.next_block(68, additional_coinbase_value=10) + b68 = self.next_block(68, additional_coinbase_value=10) tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9) - self.update_block(68, [tx]) - yield self.rejected(RejectResult(16, b'bad-cb-amount')) + b68 = self.update_block(68, [tx]) + self.sync_blocks([b68], False, 16, b'bad-cb-amount', reconnect=True) self.log.info("Accept a block claiming the correct subsidy in the coinbase transaction") self.move_tip(65) b69 = self.next_block(69, additional_coinbase_value=10) tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 10) self.update_block(69, [tx]) - yield self.accepted() + self.sync_blocks([b69], True) self.save_spendable_output() # Test spending the outpoint of a non-existent transaction @@ -940,14 +923,14 @@ def get_tests(self): # self.log.info("Reject a block containing a transaction spending from a non-existent input") self.move_tip(69) - self.next_block(70, spend=out[21]) + b70 = self.next_block(70, spend=out[21]) bogus_tx = CTransaction() bogus_tx.sha256 = uint256_from_str(b"23c70ed7c0506e9178fc1a987f40a33946d4ad4c962b5ae3a52546da53af0c5c") tx = CTransaction() tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff)) tx.vout.append(CTxOut(1, b"")) - self.update_block(70, [tx]) - yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) + b70 = self.update_block(70, [tx]) + self.sync_blocks([b70], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) # Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) # @@ -955,7 +938,7 @@ def get_tests(self): # \-> b71 (21) # # b72 is a good block. - # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b71. + # b71 is a copy of 72, but re-adds one of its transactions. However, it has the same hash as b72. self.log.info("Reject a block containing a duplicate transaction but with the same Merkle root (Merkle tree malleability") self.move_tip(69) b72 = self.next_block(72) @@ -972,9 +955,10 @@ def get_tests(self): assert_equal(b72.sha256, b71.sha256) self.move_tip(71) - yield self.rejected(RejectResult(16, b'bad-txns-duplicate')) + self.sync_blocks([b71], False, 16, b'bad-txns-duplicate', reconnect=True) + self.move_tip(72) - yield self.accepted() + self.sync_blocks([b72], True) self.save_spendable_output() # Test some invalid scripts and MAX_BLOCK_SIGOPS @@ -1009,7 +993,7 @@ def get_tests(self): tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) b73 = self.update_block(73, [tx]) assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS + 1) - yield self.rejected(RejectResult(16, b'bad-blk-sigops')) + self.sync_blocks([b73], False, 16, b'bad-blk-sigops', reconnect=True) # b74/75 - if we push an invalid script element, all prevous sigops are counted, # but sigops after the element are not counted. @@ -1023,7 +1007,7 @@ def get_tests(self): # b75 succeeds because we put MAX_BLOCK_SIGOPS before the element self.log.info("Check sigops are counted correctly after an invalid script element") self.move_tip(72) - self.next_block(74) + b74 = self.next_block(74) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 # total = 20,561 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS] = 0x4e @@ -1032,11 +1016,11 @@ def get_tests(self): a[MAX_BLOCK_SIGOPS + 3] = 0xff a[MAX_BLOCK_SIGOPS + 4] = 0xff tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) - self.update_block(74, [tx]) - yield self.rejected(RejectResult(16, b'bad-blk-sigops')) + b74 = self.update_block(74, [tx]) + self.sync_blocks([b74], False, 16, b'bad-blk-sigops', reconnect=True) self.move_tip(72) - self.next_block(75) + b75 = self.next_block(75) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 42 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS - 1] = 0x4e @@ -1045,19 +1029,19 @@ def get_tests(self): a[MAX_BLOCK_SIGOPS + 2] = 0xff a[MAX_BLOCK_SIGOPS + 3] = 0xff tx = self.create_and_sign_transaction(out[22].tx, 0, 1, CScript(a)) - self.update_block(75, [tx]) - yield self.accepted() + b75 = self.update_block(75, [tx]) + self.sync_blocks([b75], True) self.save_spendable_output() # Check that if we push an element filled with CHECKSIGs, they are not counted self.move_tip(75) - self.next_block(76) + b76 = self.next_block(76) size = MAX_BLOCK_SIGOPS - 1 + MAX_SCRIPT_ELEMENT_SIZE + 1 + 5 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS - 1] = 0x4e # PUSHDATA4, but leave the following bytes as just checksigs tx = self.create_and_sign_transaction(out[23].tx, 0, 1, CScript(a)) - self.update_block(76, [tx]) - yield self.accepted() + b76 = self.update_block(76, [tx]) + self.sync_blocks([b76], True) self.save_spendable_output() # Test transaction resurrection @@ -1079,36 +1063,36 @@ def get_tests(self): # updated. (Perhaps to spend to a P2SH OP_TRUE script) self.log.info("Test transaction resurrection during a re-org") self.move_tip(76) - self.next_block(77) + b77 = self.next_block(77) tx77 = self.create_and_sign_transaction(out[24].tx, out[24].n, 10 * COIN) - self.update_block(77, [tx77]) - yield self.accepted() + b77 = self.update_block(77, [tx77]) + self.sync_blocks([b77], True) self.save_spendable_output() - self.next_block(78) + b78 = self.next_block(78) tx78 = self.create_tx(tx77, 0, 9 * COIN) - self.update_block(78, [tx78]) - yield self.accepted() + b78 = self.update_block(78, [tx78]) + self.sync_blocks([b78], True) - self.next_block(79) + b79 = self.next_block(79) tx79 = self.create_tx(tx78, 0, 8 * COIN) - self.update_block(79, [tx79]) - yield self.accepted() + b79 = self.update_block(79, [tx79]) + self.sync_blocks([b79], True) # mempool should be empty assert_equal(len(self.nodes[0].getrawmempool()), 0) self.move_tip(77) - self.next_block(80, spend=out[25]) - yield self.rejected() + b80 = self.next_block(80, spend=out[25]) + self.sync_blocks([b80], False, request_block=False) self.save_spendable_output() - self.next_block(81, spend=out[26]) - yield self.rejected() # other chain is same length + b81 = self.next_block(81, spend=out[26]) + self.sync_blocks([b81], False, request_block=False) # other chain is same length self.save_spendable_output() - self.next_block(82, spend=out[27]) - yield self.accepted() # now this chain is longer, triggers re-org + b82 = self.next_block(82, spend=out[27]) + self.sync_blocks([b82], True) # now this chain is longer, triggers re-org self.save_spendable_output() # now check that tx78 and tx79 have been put back into the peer's mempool @@ -1122,7 +1106,7 @@ def get_tests(self): # -> b81 (26) -> b82 (27) -> b83 (28) # self.log.info("Accept a block with invalid opcodes in dead execution paths") - self.next_block(83) + b83 = self.next_block(83) op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF] script = CScript(op_codes) tx1 = self.create_and_sign_transaction(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script) @@ -1131,8 +1115,8 @@ def get_tests(self): tx2.vin[0].scriptSig = CScript([OP_FALSE]) tx2.rehash() - self.update_block(83, [tx1, tx2]) - yield self.accepted() + b83 = self.update_block(83, [tx1, tx2]) + self.sync_blocks([b83], True) self.save_spendable_output() # Reorg on/off blocks that have OP_RETURN in them (and try to spend them) @@ -1141,7 +1125,7 @@ def get_tests(self): # \-> b85 (29) -> b86 (30) \-> b89a (32) # self.log.info("Test re-orging blocks with OP_RETURN in them") - self.next_block(84) + b84 = self.next_block(84) tx1 = self.create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN])) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) @@ -1158,37 +1142,37 @@ def get_tests(self): tx4.vout.append(CTxOut(0, CScript([OP_RETURN]))) tx5 = self.create_tx(tx1, 4, 0, CScript([OP_RETURN])) - self.update_block(84, [tx1, tx2, tx3, tx4, tx5]) - yield self.accepted() + b84 = self.update_block(84, [tx1, tx2, tx3, tx4, tx5]) + self.sync_blocks([b84], True) self.save_spendable_output() self.move_tip(83) - self.next_block(85, spend=out[29]) - yield self.rejected() + b85 = self.next_block(85, spend=out[29]) + self.sync_blocks([b85], False) # other chain is same length - self.next_block(86, spend=out[30]) - yield self.accepted() + b86 = self.next_block(86, spend=out[30]) + self.sync_blocks([b86], True) self.move_tip(84) - self.next_block(87, spend=out[30]) - yield self.rejected() + b87 = self.next_block(87, spend=out[30]) + self.sync_blocks([b87], False) # other chain is same length self.save_spendable_output() - self.next_block(88, spend=out[31]) - yield self.accepted() + b88 = self.next_block(88, spend=out[31]) + self.sync_blocks([b88], True) self.save_spendable_output() # trying to spend the OP_RETURN output is rejected - self.next_block("89a", spend=out[32]) + b89a = self.next_block("89a", spend=out[32]) tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE])) - self.update_block("89a", [tx]) - yield self.rejected() + b89a = self.update_block("89a", [tx]) + self.sync_blocks([b89a], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) self.log.info("Test a re-org of ~2 days' worth of blocks (1088 blocks)") self.move_tip(88) LARGE_REORG_SIZE = 1088 - test1 = TestInstance(sync_every_block=False) + blocks = [] spend = out[32] for i in range(89, LARGE_REORG_SIZE + 89): b = self.next_block(i, spend) @@ -1199,33 +1183,30 @@ def get_tests(self): tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0))) b = self.update_block(i, [tx]) assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE) - test1.blocks_and_transactions.append([self.tip, True]) + blocks.append(b) self.save_spendable_output() spend = self.get_spendable_output() - yield test1 + self.sync_blocks(blocks, True, timeout=180) chain1_tip = i # now create alt chain of same length self.move_tip(88) - test2 = TestInstance(sync_every_block=False) + blocks2 = [] for i in range(89, LARGE_REORG_SIZE + 89): - self.next_block("alt" + str(i)) - test2.blocks_and_transactions.append([self.tip, False]) - yield test2 + blocks2.append(self.next_block("alt" + str(i))) + self.sync_blocks(blocks2, False, request_block=False) # extend alt chain to trigger re-org - self.next_block("alt" + str(chain1_tip + 1)) - yield self.accepted() + block = self.next_block("alt" + str(chain1_tip + 1)) + self.sync_blocks([block], True, timeout=180) # ... and re-org back to the first chain self.move_tip(chain1_tip) - self.next_block(chain1_tip + 1) - yield self.rejected() - self.next_block(chain1_tip + 2) - yield self.accepted() - - chain1_tip += 2 + block = self.next_block(chain1_tip + 1) + self.sync_blocks([block], False, request_block=False) + block = self.next_block(chain1_tip + 2) + self.sync_blocks([block], True, timeout=180) # Helper methods ################ @@ -1236,8 +1217,7 @@ def add_transactions_to_block(self, block, 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 + return create_transaction(spend_tx, n, b"", value, script) # 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 @@ -1287,23 +1267,14 @@ def next_block(self, number, spend=None, additional_coinbase_value=0, script=CSc # save the current tip so it can be spent by a later block def save_spendable_output(self): + self.log.debug("saving spendable output %s" % self.tip.vtx[0]) self.spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(self): + self.log.debug("getting spendable output %s" % self.spendable_outputs[0].vtx[0]) return PreviousSpendableOutput(self.spendable_outputs.pop(0).vtx[0], 0) - # returns a test case that asserts that the current tip was accepted - def accepted(self): - return TestInstance([[self.tip, True]]) - - # returns a test case that asserts that the current tip was rejected - def rejected(self, 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 def move_tip(self, number): self.tip = self.blocks[number] @@ -1323,6 +1294,26 @@ def update_block(self, block_number, new_transactions): self.blocks[block_number] = block return block + def reconnect_p2p(self): + """Add a 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.""" + + network_thread_join() + self.nodes[0].disconnect_p2ps() + self.nodes[0].add_p2p_connection(P2PDataStore()) + network_thread_start() + self.nodes[0].p2p.wait_for_verack() + + def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60): + """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. + + Call with success = False if the tip shouldn't advance to the most recent block.""" + self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_code=reject_code, reject_reason=reject_reason, request_block=request_block, timeout=timeout) + + if reconnect: + self.reconnect_p2p() if __name__ == '__main__': FullBlockTest().main() From 07099ac791d78708fa32bbdf0b6e6941cb1c506e Mon Sep 17 00:00:00 2001 From: John Newbery Date: Wed, 13 Dec 2017 14:39:44 -0500 Subject: [PATCH 06/14] [tests] Improve assert message when wait_until() fails --- test/functional/test_framework/util.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index ada489e84cf8..3ba33c3de650 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -9,6 +9,7 @@ from binascii import hexlify, unhexlify from decimal import Decimal, ROUND_DOWN import hashlib +import inspect import json import logging import os @@ -206,9 +207,9 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), sleep= if attempts == float('inf') and timeout == float('inf'): timeout = 60 attempt = 0 - timeout += time.time() + time_end = time.time() + timeout - while attempt < attempts and time.time() < timeout: + while attempt < attempts and time.time() < time_end: try: if lock: with lock: @@ -225,8 +226,12 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), sleep= if do_assert: # Print the cause of the timeout - assert_greater_than(attempts, attempt) - assert_greater_than(timeout, time.time()) + predicate_source = inspect.getsourcelines(predicate) + logger.error("wait_until() failed. Predicate: {}".format(predicate_source)) + if attempt >= attempts: + raise AssertionError("Predicate {} not true after {} attempts".format(predicate_source, attempts)) + elif time.time() >= time_end: + raise AssertionError("Predicate {} not true after {} seconds".format(predicate_source, timeout)) raise RuntimeError('Unreachable') else: return False From 99748f093747c6a8d7f32566cbde965221f07188 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 4 Jan 2020 15:56:10 +0100 Subject: [PATCH 07/14] Rename MAX_BLOCK_BASE_SIZE back to MAX_BLOCK_SIZE --- test/functional/p2p-fullblocktest.py | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index 3d57f92c5832..fc3896d100cd 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -16,7 +16,7 @@ CTransaction, CTxIn, CTxOut, - MAX_BLOCK_BASE_SIZE, + MAX_BLOCK_SIZE, uint256_from_compact, uint256_from_str, ) @@ -264,33 +264,33 @@ def run_test(self): b22 = self.next_block(22, spend=out[5]) self.sync_blocks([b22], False, 16, b'bad-txns-premature-spend-of-coinbase') - # Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected + # Create a block on either side of MAX_BLOCK_SIZE and make sure its accepted/rejected # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) # \-> b24 (6) -> b25 (7) # \-> b3 (1) -> b4 (2) - self.log.info("Accept a block of size MAX_BLOCK_BASE_SIZE") + self.log.info("Accept a block of size MAX_BLOCK_SIZE") self.move_tip(15) b23 = self.next_block(23, spend=out[6]) tx = CTransaction() - script_length = MAX_BLOCK_BASE_SIZE - len(b23.serialize()) - 69 + script_length = MAX_BLOCK_SIZE - len(b23.serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0))) b23 = self.update_block(23, [tx]) # Make sure the math above worked out to produce a max-sized block - assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE) + assert_equal(len(b23.serialize()), MAX_BLOCK_SIZE) self.sync_blocks([b23], True) self.save_spendable_output() - self.log.info("Reject a block of size MAX_BLOCK_BASE_SIZE + 1") + self.log.info("Reject a block of size MAX_BLOCK_SIZE + 1") self.move_tip(15) b24 = self.next_block(24, spend=out[6]) - script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69 + script_length = MAX_BLOCK_SIZE - len(b24.serialize()) - 69 script_output = CScript([b'\x00' * (script_length + 1)]) tx.vout = [CTxOut(0, script_output)] b24 = self.update_block(24, [tx]) - assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1) + assert_equal(len(b24.serialize()), MAX_BLOCK_SIZE + 1) self.sync_blocks([b24], False, 16, b'bad-blk-length', reconnect=True) b25 = self.next_block(25, spend=out[7]) @@ -443,12 +443,12 @@ def run_test(self): tx_new = None tx_last = tx total_size = len(b39.serialize()) - while(total_size < MAX_BLOCK_BASE_SIZE): + while(total_size < MAX_BLOCK_SIZE): tx_new = self.create_tx(tx_last, 1, 1, p2sh_script) tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE]))) tx_new.rehash() total_size += len(tx_new.serialize()) - if total_size >= MAX_BLOCK_BASE_SIZE: + if total_size >= MAX_BLOCK_SIZE: break b39.vtx.append(tx_new) # add tx to block tx_last = tx_new @@ -799,7 +799,7 @@ def run_test(self): self.sync_blocks([b63], False, 16, b'bad-txns-nonfinal') # This checks that a block with a bloated VARINT between the block_header and the array of tx such that - # the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint, + # the block is > MAX_BLOCK_SIZE with the bloated varint, but <= MAX_BLOCK_SIZE without the bloated varint, # 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. # @@ -824,12 +824,12 @@ def run_test(self): tx = CTransaction() # use canonical serialization to calculate size - script_length = MAX_BLOCK_BASE_SIZE - len(b64a.normal_serialize()) - 69 + script_length = MAX_BLOCK_SIZE - len(b64a.normal_serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0))) b64a = self.update_block("64a", [tx]) - assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8) + assert_equal(len(b64a.serialize()), MAX_BLOCK_SIZE + 8) self.sync_blocks([b64a], False, 1, b'error parsing message') # bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently @@ -843,7 +843,7 @@ def run_test(self): b64 = CBlock(b64a) b64.vtx = copy.deepcopy(b64a.vtx) assert_equal(b64.hash, b64a.hash) - assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE) + assert_equal(len(b64.serialize()), MAX_BLOCK_SIZE) self.blocks[64] = b64 b64 = self.update_block(64, []) self.sync_blocks([b64], True) @@ -1177,12 +1177,12 @@ def run_test(self): for i in range(89, LARGE_REORG_SIZE + 89): b = self.next_block(i, spend) tx = CTransaction() - script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69 + script_length = MAX_BLOCK_SIZE - len(b.serialize()) - 69 script_output = CScript([b'\x00' * script_length]) tx.vout.append(CTxOut(0, script_output)) tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0))) b = self.update_block(i, [tx]) - assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE) + assert_equal(len(b.serialize()), MAX_BLOCK_SIZE) blocks.append(b) self.save_spendable_output() spend = self.get_spendable_output() From c037c238a8ccd0863b13d11137fb6a26fd282550 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 4 Jan 2020 16:28:47 +0100 Subject: [PATCH 08/14] Use DoS score of 100 for bad-blk-sigops This was accidently changed to 10 while backporting bitcoin#7287 and causes test failures in p2p-fullblocktest.py --- src/validation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index ef27759721bd..c29706e597df 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3357,7 +3357,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c // Size limits unsigned int nMaxBlockSize = MaxBlockSize(fDIP0001Active_context); if (block.vtx.empty() || block.vtx.size() > nMaxBlockSize || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > nMaxBlockSize) - return state.DoS(10, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); + return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); // Check that all transactions are finalized and not over-sized // Also count sigops @@ -3374,7 +3374,7 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c // Check sigops if (nSigOps > MaxBlockSigOps(fDIP0001Active_context)) - return state.DoS(10, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); + return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); // Enforce rule that the coinbase starts with serialized block height // After DIP3/DIP4 activation, we don't enforce the height in the input script anymore. From 6a9c9649f97f5b46f617bf210b619b6888ebf45c Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 4 Jan 2020 16:43:02 +0100 Subject: [PATCH 09/14] Use allowOptimisticSend=true when sending reject messages This fixes test failures in p2p-fullblocktest.py which expects reject messages to be sent/received before connections get closed. --- src/net_processing.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 9f01e5e13e20..d5c72ca0a90f 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -3186,7 +3186,9 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) CNodeState &state = *State(pnode->GetId()); for (const CBlockReject& reject : state.rejects) { - connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); + // Pass allowOptimisticSend=true here to allow sending the reject message immediately instead of waiting for the + // network thread to do it. This gives us a chance of actually sending out the reject before we close the connection. + connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock), true); } state.rejects.clear(); From 3c8149f84ca53dc7d1682c6b444c0c21260e6584 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sat, 4 Jan 2020 17:39:01 +0100 Subject: [PATCH 10/14] Fix p2p-fullblocktest.py - CBlock and friends are still in test_framework.mininode - "-whitelist" causes connections to not be dropped, which in turn causes sync_blocks with reconnect=True to fail - "bad-cb-amount" does not cause a ban in Dash, so reconnect must be False - Dash already bans when a header is received which is a child of an invalid header, causing block requests to never happen --- test/functional/p2p-fullblocktest.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index fc3896d100cd..b2f5e5e4d593 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -9,7 +9,7 @@ from test_framework.blocktools import create_block, create_coinbase, create_transaction, get_legacy_sigopcount_block from test_framework.key import CECKey -from test_framework.messages import ( +from test_framework.mininode import ( CBlock, COIN, COutPoint, @@ -75,7 +75,7 @@ def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True # Must set '-dip3params=2000:2000' to create pre-dip3 blocks only - self.extra_args = [['-whitelist=127.0.0.1', '-dip3params=2000:2000']] + self.extra_args = [['-dip3params=2000:2000']] def run_test(self): node = self.nodes[0] # convenience reference to the node @@ -174,7 +174,7 @@ def run_test(self): self.log.info("Reject a block where the miner creates too much coinbase reward") self.move_tip(6) b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1) - self.sync_blocks([b9], False, 16, b'bad-cb-amount', reconnect=True) + self.sync_blocks([b9], False, 16, b'bad-cb-amount', reconnect=False) # Create a fork that ends in a block with too much fee (the one that causes the reorg) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -186,7 +186,7 @@ def run_test(self): self.sync_blocks([b10], False) b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1) - self.sync_blocks([b11], False, 16, b'bad-cb-amount', reconnect=True) + self.sync_blocks([b11], False, 16, b'bad-cb-amount', reconnect=False) # Try again, but with a valid fork first # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -199,7 +199,7 @@ def run_test(self): b13 = self.next_block(13, spend=out[4]) self.save_spendable_output() b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1) - self.sync_blocks([b12, b13, b14], False, 16, b'bad-cb-amount', reconnect=True) + self.sync_blocks([b12, b13, b14], False, 16, b'bad-cb-amount', reconnect=False) # New tip should be b13. assert_equal(node.getbestblockhash(), b13.hash) @@ -294,7 +294,7 @@ def run_test(self): self.sync_blocks([b24], False, 16, b'bad-blk-length', reconnect=True) b25 = self.next_block(25, spend=out[7]) - self.sync_blocks([b25], False) + self.sync_blocks([b25], False, request_block=False, reconnect=True) # Create blocks with a coinbase input script size out of range # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -906,7 +906,7 @@ def run_test(self): b68 = self.next_block(68, additional_coinbase_value=10) tx = self.create_and_sign_transaction(out[20].tx, out[20].n, out[20].tx.vout[0].nValue - 9) b68 = self.update_block(68, [tx]) - self.sync_blocks([b68], False, 16, b'bad-cb-amount', reconnect=True) + self.sync_blocks([b68], False, 16, b'bad-cb-amount', reconnect=False) self.log.info("Accept a block claiming the correct subsidy in the coinbase transaction") self.move_tip(65) From 87d879d35e3ce9352b64d93b009ed24a4234cc78 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Sun, 5 Jan 2020 10:42:31 +0100 Subject: [PATCH 11/14] Backport missing changes from bitcoin#13003 bitcoin#13003 was backported out of order which causes missed changes. --- test/functional/p2p-fullblocktest.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index b2f5e5e4d593..4fff63d17f0d 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -80,10 +80,7 @@ def set_test_params(self): def run_test(self): node = self.nodes[0] # convenience reference to the node - # reconnect_p2p() expects the network thread to be running - network_thread_start() - - self.reconnect_p2p() + self.bootstrap_p2p() # Add one p2p connection to the node self.block_heights = {} self.coinbase_key = CECKey() @@ -1294,18 +1291,23 @@ def update_block(self, block_number, new_transactions): self.blocks[block_number] = block return block - def reconnect_p2p(self): + def bootstrap_p2p(self): """Add a 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.""" - - network_thread_join() - self.nodes[0].disconnect_p2ps() + Helper to connect and wait for version handshake.""" self.nodes[0].add_p2p_connection(P2PDataStore()) network_thread_start() self.nodes[0].p2p.wait_for_verack() + def reconnect_p2p(self): + """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.""" + self.nodes[0].disconnect_p2ps() + network_thread_join() + self.bootstrap_p2p() + def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60): """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. From 91fad6198a642fb15e5f7d57ff301a10e99e3565 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 21 Apr 2018 21:00:04 -0400 Subject: [PATCH 12/14] Merge #13048: [tests] Fix feature_block flakiness c1d742025c [tests] Fix feature_block flakiness (John Newbery) Pull request description: feature_block.py occasionally fails on Travis. I believe this is due to a a race condition when reconnecting to bitcoind after a subtest that expects disconnection. If the test runs ahead and sends the INV for the subsequent test before we've received the initial sync getheaders, then we may end up sending two headers messages - one as a response to the initial sync getheaders and one in response to the INV getheaders. If both of those headers fail validation with a DoS score of 50 or higher, then we'll unexpectedly be disconnected. There is only one validation failure that has a DoS score bewteen 50 and 100, which is high-hash. That's why the test is failing immediately after the "Reject a block with invalid work" subtest. Fix is to wait for the initial getheaders from the peer before we start populating our blockstore. That way we won't have any invalid headers to respond to it with. Tree-SHA512: dc17d795fcfaf0f8c0bf1e9732b5e11fbc8febbfafba4c231b7c13a5404a2c297dcd703a7a75bc7f353c893e12efc87f424f2201abd47ba5268af32d4d2e841f --- test/functional/p2p-fullblocktest.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index 4fff63d17f0d..8608560c6006 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -1297,7 +1297,13 @@ def bootstrap_p2p(self): Helper to connect and wait for version handshake.""" self.nodes[0].add_p2p_connection(P2PDataStore()) network_thread_start() - self.nodes[0].p2p.wait_for_verack() + # We need to wait for the initial getheaders from the peer before we + # start populating our blockstore. If we don't, then we may run ahead + # to the next subtest before we receive the getheaders. We'd then send + # an INV for the next block and receive two getheaders - one for the + # IBD and one for the INV. We'd respond to both and could get + # unexpectedly disconnected if the DoS score for that error is 50. + self.nodes[0].p2p.wait_for_getheaders(timeout=5) def reconnect_p2p(self): """Tear down and bootstrap the P2P connection to the node. From c75f09cf372adb84e488fe5196f07e24b7b24013 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Tue, 7 Jan 2020 13:57:44 +0100 Subject: [PATCH 13/14] Bump p2p-fullblocktest timeouts --- test/functional/p2p-fullblocktest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index 8608560c6006..4db328b6e39d 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -1184,7 +1184,7 @@ def run_test(self): self.save_spendable_output() spend = self.get_spendable_output() - self.sync_blocks(blocks, True, timeout=180) + self.sync_blocks(blocks, True, timeout=960) chain1_tip = i # now create alt chain of same length @@ -1196,14 +1196,14 @@ def run_test(self): # extend alt chain to trigger re-org block = self.next_block("alt" + str(chain1_tip + 1)) - self.sync_blocks([block], True, timeout=180) + self.sync_blocks([block], True, timeout=960) # ... and re-org back to the first chain self.move_tip(chain1_tip) block = self.next_block(chain1_tip + 1) self.sync_blocks([block], False, request_block=False) block = self.next_block(chain1_tip + 2) - self.sync_blocks([block], True, timeout=180) + self.sync_blocks([block], True, timeout=960) # Helper methods ################ From a300461f58d6a24848cf3cb4239338a4934be561 Mon Sep 17 00:00:00 2001 From: Alexander Block Date: Fri, 10 Jan 2020 12:25:56 +0100 Subject: [PATCH 14/14] Increase RPC timeout in p2p-fullblocktest.py --- test/functional/p2p-fullblocktest.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index 4db328b6e39d..251c80872223 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -77,6 +77,13 @@ def set_test_params(self): # Must set '-dip3params=2000:2000' to create pre-dip3 blocks only self.extra_args = [['-dip3params=2000:2000']] + def setup_nodes(self): + # Very large reorgs cause cs_main to be held for a very long time in ActivateBestChainStep, + # which causes RPC to hang, so we need to increase RPC timeouts + # TODO remove this when bitcoin#13837 gets backported and change it to use self.rpc_timeout + self.add_nodes(self.num_nodes, self.extra_args, timewait=180) + self.start_nodes() + def run_test(self): node = self.nodes[0] # convenience reference to the node