def get_tests(self): if self.tip is None: self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) self.block_time = int(time.time())+1 ''' Create a new block with an anyone-can-spend coinbase ''' height = 1 block = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block.solve() # Save the coinbase for later self.block1 = block self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) ''' Now we need that block to mature so we can spend the coinbase. ''' test = TestInstance(sync_every_block=False) for i in range(100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() self.tip = block.sha256 self.block_time += 1 test.blocks_and_transactions.append([block, True]) height += 1 yield test # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) tx1 = create_transaction(self.block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) yield TestInstance([[tx1, RejectResult(16, b'mandatory-script-verify-flag-failed')]])
def get_tests(self): # shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) # Create a new block block(0) self.chain.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(101): block(5000 + i) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(self.chain.get_spendable_output()) assert_equal(node.getblock(node.getbestblockhash())['height'], 102) # create block with height 103, where sig_pushonly is not yet mandatory block(1, spend=out[0]) transaction_op_add_accepted = create_transaction( out[1].tx, out[1].n, CScript([1, 1, OP_ADD]), 100000, CScript([OP_TRUE])) blk_accepted = self.chain.update_block(1, [transaction_op_add_accepted]) yield self.accepted() assert_equal(node.getblock(node.getbestblockhash())['height'], 103) # create block with height 104, where sig_pushonly is mandatory block(2, spend=out[2]) transaction_op_add_rejected = create_transaction( out[3].tx, out[3].n, CScript([1, 1, OP_ADD]), 100000, CScript([OP_TRUE])) self.chain.update_block(2, [transaction_op_add_rejected]) yield self.rejected(RejectResult(16, b'blk-bad-inputs')) assert_equal(node.getblock(node.getbestblockhash())['height'], 103) assert_equal(node.getbestblockhash(), blk_accepted.hash) # invalidate block with height 103 node.invalidateblock(format(blk_accepted.sha256, 'x')) # tip is now on height 102 assert_equal(node.getblock(node.getbestblockhash())['height'], 102) # transaction_op_add_accepted should not be in mempool (individual transactions are always checked against pushonly) assert (transaction_op_add_accepted.hash not in set(node.getrawmempool()))
def test_ltor_infringement_detection(self, sync_height): txns = self.create_chained_transactions() block = self.get_empty_block(sync_height=sync_height) # We ensure that the transactions are NOT sorted in the correct order block.vtx.extend(sorted(txns, key=lambda _tx: _tx.hash, reverse=True)) block.compute_merkle_trees() block.solve() yield TestInstance( [[block, RejectResult(16, b'bad-tx-ordering')]], test_name='test_ltor_infringement_detection')
def get_tests(self): # shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) # Now we need that block to mature so we can spend the coinbase. test = TestInstance(sync_every_block=False) for i in range(105): block(5000 + i) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(105): out.append(self.chain.get_spendable_output()) assert_equal(node.getblock(node.getbestblockhash())['height'], 105) block(1) redeem_script = CScript([OP_TRUE, OP_RETURN, b"a" * 5000]) spend_tx1 = CTransaction() spend_tx1.vin.append( CTxIn(COutPoint(out[2].tx.sha256, out[2].n), CScript(), 0xffffffff)) spend_tx1.vout.append(CTxOut(500, redeem_script)) spend_tx1.vout.append(CTxOut(500, redeem_script)) spend_tx1.calc_sha256() self.log.info(spend_tx1.hash) self.chain.update_block(1, [spend_tx1]) yield self.accepted() tx1 = CTransaction() tx1.vout = [CTxOut(499, CScript([OP_TRUE]))] tx1.vin.append( CTxIn(COutPoint(spend_tx1.sha256, 0), CScript(), 0xfffffff)) tx1.vin.append( CTxIn(COutPoint(spend_tx1.sha256, 1), CScript(), 0xfffffff)) tx1.calc_sha256() self.log.info(tx1.hash) yield TestInstance( [[tx1, RejectResult(16, b'bad-txns-inputs-too-large')]])
def get_tests(self): # shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) block(0) yield self.accepted() test, out, _ = prepare_init_chain(self.chain, 101, 100) yield test assert_equal(node.getblock(node.getbestblockhash())['height'], 102) # create block with height 103, where sig_pushonly is not yet mandatory block(1, spend=out[0]) transaction_op_add_accepted = create_transaction( out[1].tx, out[1].n, CScript([1, 1, OP_ADD]), 100000, CScript([OP_TRUE])) blk_accepted = self.chain.update_block(1, [transaction_op_add_accepted]) yield self.accepted() assert_equal(node.getblock(node.getbestblockhash())['height'], 103) # create block with height 104, where sig_pushonly is mandatory block(2, spend=out[2]) transaction_op_add_rejected = create_transaction( out[3].tx, out[3].n, CScript([1, 1, OP_ADD]), 100000, CScript([OP_TRUE])) self.chain.update_block(2, [transaction_op_add_rejected]) yield self.rejected(RejectResult(16, b'blk-bad-inputs')) assert_equal(node.getblock(node.getbestblockhash())['height'], 103) assert_equal(node.getbestblockhash(), blk_accepted.hash) # invalidate block with height 103 node.invalidateblock(hashToHex(blk_accepted.sha256)) # tip is now on height 102 assert_equal(node.getblock(node.getbestblockhash())['height'], 102) # transaction_op_add_accepted should not be in mempool (individual transactions are always checked against pushonly) assert (transaction_op_add_accepted.hash not in set(node.getrawmempool()))
def get_tests(self): # shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) test, out, _ = prepare_init_chain(self.chain, 105, 105, block_0=False) yield test assert_equal(node.getblock(node.getbestblockhash())['height'], 105) block(1) redeem_script = CScript([OP_TRUE, OP_RETURN, b"a" * 5000]) spend_tx1 = CTransaction() spend_tx1.vin.append( CTxIn(COutPoint(out[2].tx.sha256, out[2].n), CScript(), 0xffffffff)) spend_tx1.vout.append(CTxOut(500, redeem_script)) spend_tx1.vout.append(CTxOut(500, redeem_script)) spend_tx1.calc_sha256() self.log.info(spend_tx1.hash) self.chain.update_block(1, [spend_tx1]) yield self.accepted() tx1 = CTransaction() tx1.vout = [CTxOut(499, CScript([OP_TRUE]))] tx1.vin.append( CTxIn(COutPoint(spend_tx1.sha256, 0), CScript(), 0xfffffff)) tx1.vin.append( CTxIn(COutPoint(spend_tx1.sha256, 1), CScript(), 0xfffffff)) tx1.calc_sha256() self.log.info(tx1.hash) yield TestInstance( [[tx1, RejectResult(16, b'bad-txns-inputs-too-large')]])
def get_tests(self): node = self.nodes[0] self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # 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 a new block block(0) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Let's build some blocks and test them. for i in range(15): n = i + 1 block(n, spend=out[i], block_size=n * ONE_MEGABYTE // 2) yield accepted() # Start moving MTP forward bfork = block(5555, out[15], block_size=8 * ONE_MEGABYTE) bfork.nTime = MONOLITH_START_TIME - 1 update_block(5555, []) yield accepted() # Get to one block of the May 15, 2018 HF activation for i in range(5): block(5100 + i) test.blocks_and_transactions.append([self.tip, True]) yield test # Check that the MTP is just before the configured fork point. assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MONOLITH_START_TIME - 1) # Before we acivate the May 15, 2018 HF, 8MB is the limit. block(4444, spend=out[16], block_size=8 * ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-blk-length')) # Rewind bad block. tip(5104) # Actiavte the May 15, 2018 HF block(5556) yield accepted() # Now MTP is exactly the fork time. Bigger blocks are now accepted. assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MONOLITH_START_TIME) # block of maximal size block(17, spend=out[16], block_size=self.excessive_block_size) yield accepted() # Reject oversized blocks with bad-blk-length error block(18, spend=out[17], block_size=self.excessive_block_size + 1) yield rejected(RejectResult(16, b'bad-blk-length')) # Rewind bad block. tip(17) # Accept many sigops lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) block(19, spend=out[17], script=lots_of_checksigs, block_size=ONE_MEGABYTE) yield accepted() block(20, spend=out[18], script=lots_of_checksigs, block_size=ONE_MEGABYTE, extra_sigops=1) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(19) # Accept 40k sigops per block > 1MB and <= 2MB block(21, spend=out[18], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=ONE_MEGABYTE + 1) yield accepted() # Accept 40k sigops per block > 1MB and <= 2MB block(22, spend=out[19], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE) yield accepted() # Reject more than 40k sigops per block > 1MB and <= 2MB. block(23, spend=out[20], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(22) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(24, spend=out[20], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(22) # Accept 60k sigops per block > 2MB and <= 3MB block(25, spend=out[20], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE + 1) yield accepted() # Accept 60k sigops per block > 2MB and <= 3MB block(26, spend=out[21], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=3 * ONE_MEGABYTE) yield accepted() # Reject more than 40k sigops per block > 1MB and <= 2MB. block(27, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(26) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(28, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=3 * ONE_MEGABYTE) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(26) # Too many sigops in one txn too_many_tx_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB + 1)) block(29, spend=out[22], script=too_many_tx_checksigs, block_size=ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-txn-sigops')) # Rewind bad block tip(26) # Generate a key pair to test P2SH sigops count private_key = CECKey() private_key.set_secretbytes(b"fatstacks") public_key = private_key.get_pubkey() # P2SH # Build the redeem script, hash it, use hash to create the p2sh script redeem_script = CScript([public_key] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG]) redeem_script_hash = hash160(redeem_script) p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) # Create a p2sh transaction p2sh_tx = self.create_tx(out[22], 1, p2sh_script) # Add the transaction to the block block(30) update_block(30, [p2sh_tx]) yield accepted() # Creates a new transaction using the p2sh transaction included in the # last block def spend_p2sh_tx(output_script=CScript([OP_TRUE])): # Create the transaction spent_p2sh_tx = CTransaction() spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b'')) spent_p2sh_tx.vout.append(CTxOut(1, output_script)) # Sign the transaction using the redeem script sighash = SignatureHashForkId(redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx.vout[0].nValue) sig = private_key.sign(sighash) + \ bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) spent_p2sh_tx.rehash() return spent_p2sh_tx # Sigops p2sh limit p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \ redeem_script.GetSigOpCount(True) # Too many sigops in one p2sh txn too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1)) block(31, spend=out[23], block_size=ONE_MEGABYTE + 1) update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)]) yield rejected(RejectResult(16, b'bad-txn-sigops')) # Rewind bad block tip(30) # Max sigops in one p2sh txn max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit)) block(32, spend=out[23], block_size=ONE_MEGABYTE + 1) update_block(32, [spend_p2sh_tx(max_p2sh_sigops)]) yield accepted()
def get_tests(self): node = self.nodes[0] # First, we generate some coins to spend. node.generate(125) # Create various outputs using the OP_CHECKDATASIG # to check for activation. tx_hex = self.create_checkdatasig_tx(25) txid = node.sendrawtransaction(tx_hex) assert (txid in set(node.getrawmempool())) node.generate(1) assert (txid not in set(node.getrawmempool())) # register the spendable outputs. tx = FromHex(CTransaction(), tx_hex) tx.rehash() spendable_checkdatasigs = [ PreviousSpendableOutput(tx, i) for i in range(len(tx.vout)) ] def spend_checkdatasig(): outpoint = spendable_checkdatasigs.pop() out = outpoint.tx.vout[outpoint.n] tx = CTransaction() tx.vin = [CTxIn(COutPoint(outpoint.tx.sha256, outpoint.n))] tx.vout = [ CTxOut(out.nValue, CScript([])), CTxOut(0, CScript([random.getrandbits(800), OP_RETURN])) ] tx.vout[0].nValue -= min(tx.vout[0].nValue / 100, 1000) # node.calculate_fee(tx) tx.rehash() return tx # Check that transactions using checkdatasig are not accepted yet. logging.info("Try to use the checkdatasig opcodes before activation") tx0 = spend_checkdatasig() tx0_hex = ToHex(tx0) assert_raises_rpc_error(-26, RPC_BAD_OPCODE_ERROR, node.sendrawtransaction, tx0_hex) # Push MTP forward just before activation. logging.info("Pushing MTP just before the activation and check again") node.setmocktime(NOV152018_START_TIME) # returns a test case that asserts that the current tip was accepted def accepted(tip): return TestInstance([[tip, True]]) # returns a test case that asserts that the current tip was rejected def rejected(tip, reject=None): if reject is None: return TestInstance([[tip, False]]) else: return TestInstance([[tip, reject]]) def next_block(block_time): # get block height blockchaininfo = node.getblockchaininfo() height = int(blockchaininfo['blocks']) # create the block coinbase = create_coinbase(height) coinbase.rehash() block = create_block(int(node.getbestblockhash(), 16), coinbase, block_time) # Do PoW, which is cheap on regnet block.solve() return block for i in range(6): b = next_block(NOV152018_START_TIME + i - 1) yield accepted(b) # Check again just before the activation time assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], NOV152018_START_TIME - 1) assert_raises_rpc_error(-26, RPC_BAD_OPCODE_ERROR, node.sendrawtransaction, tx0_hex) def add_tx(block, tx): block.vtx.append(tx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() b = next_block(NOV152018_START_TIME + 6) add_tx(b, tx0) yield rejected(b, RejectResult(16, b'bad-blk-signatures')) logging.info("Activates checkdatasig") fork_block = next_block(NOV152018_START_TIME + 6) yield accepted(fork_block) assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], NOV152018_START_TIME) tx0id = node.sendrawtransaction(tx0_hex) assert (tx0id in set(node.getrawmempool())) # Transactions can also be included in blocks. nov152018forkblock = next_block(NOV152018_START_TIME + 7) add_tx(nov152018forkblock, tx0) yield accepted(nov152018forkblock) logging.info("Cause a reorg that deactivate the checkdatasig opcodes") # Invalidate the checkdatasig block, ensure tx0 gets back to the mempool. assert (tx0id not in set(node.getrawmempool())) node.invalidateblock(format(nov152018forkblock.sha256, 'x')) waitFor(3, lambda: tx0id in set(node.getrawmempool()), "Transaction shoud be included in the mempool") node.invalidateblock(format(fork_block.sha256, 'x')) waitFor(3, lambda: tx0id not in set(node.getrawmempool()), "Transaction should not be included in the memopool")
def get_tests(self): node = self.nodes[0] self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # 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 a new block block(0) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Let's build some blocks and test them. for i in range(15): n = i + 1 block(n) yield accepted() # Start moving MTP forward bfork = block(5555) bfork.nTime = MAGNETIC_ANOMALY_START_TIME - 1 update_block(5555) yield accepted() # Get to one block of the Nov 15, 2018 HF activation for i in range(5): block(5100 + i) test.blocks_and_transactions.append([self.tip, True]) yield test # Check that the MTP is just before the configured fork point. assert_equal(node.getblockheader(node.getbestblockhash())['mediantime'], MAGNETIC_ANOMALY_START_TIME - 1) # Check that block with small transactions, non push only signatures and # non clean stack are still accepted. small_tx_block = block( 4444, out[0], MIN_TX_SIZE - 1, pushonly=False, cleanstack=False) assert_equal(len(small_tx_block.vtx[1].serialize()), MIN_TX_SIZE - 1) yield accepted() # Now MTP is exactly the fork time. Small transaction are now rejected. assert_equal(node.getblockheader(node.getbestblockhash())['mediantime'], MAGNETIC_ANOMALY_START_TIME) # Now that the for activated, it is not possible to have # small transactions anymore. small_tx_block = block(4445, out[1], MIN_TX_SIZE - 1) assert_equal(len(small_tx_block.vtx[1].serialize()), MIN_TX_SIZE - 1) yield rejected(RejectResult(16, b'bad-txns-undersize')) # Rewind bad block. tip(4444) # Now that the for activated, it is not possible to have # non push only transactions. non_pushonly_tx_block = block( 4446, out[1], MIN_TX_SIZE, pushonly=False) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block. tip(4444) # Now that the for activated, it is not possible to have # non clean stack transactions. non_cleanstack_tx_block = block( 4447, out[1], MIN_TX_SIZE, cleanstack=False) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block. tip(4444) # But large transactions are still ok. large_tx_block = block(3333, out[1], MIN_TX_SIZE) assert_equal(len(large_tx_block.vtx[1].serialize()), MIN_TX_SIZE) yield accepted()
def get_tests(self): if self.tip is None: self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) self.block_time = int(time.time()) + 1 ''' Create a new block with an anyone-can-spend coinbase ''' height = 1 block = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block.solve() # Save the coinbase for later self.block1 = block self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) ''' Now we need that block to mature so we can spend the coinbase. ''' test = TestInstance(sync_every_block=False) for i in range(100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() self.tip = block.sha256 self.block_time += 1 test.blocks_and_transactions.append([block, True]) height += 1 yield test ''' Now we use merkle-root malleability to generate an invalid block with same blockheader. Manufacture a block with 3 transactions (coinbase, spend of prior coinbase, spend of that spend). Duplicate the 3rd transaction to leave merkle root and blockheader unchanged but invalidate the block. ''' block2 = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 # b'0x51' is OP_TRUE tx1 = create_transaction(self.block1.vtx[0], 0, b'\x51', 50 * COIN) tx2 = create_transaction(tx1, 0, b'\x51', 50 * COIN) block2.vtx.extend([tx1, tx2]) block2.hashMerkleRoot = block2.calc_merkle_root() block2.rehash() block2.solve() orig_hash = block2.sha256 block2_orig = copy.deepcopy(block2) # Mutate block 2 block2.vtx.append(tx2) assert_equal(block2.hashMerkleRoot, block2.calc_merkle_root()) assert_equal(orig_hash, block2.rehash()) assert (block2_orig.vtx != block2.vtx) self.tip = block2.sha256 yield TestInstance([[block2, RejectResult(16, b'bad-txns-duplicate')]]) # Check transactions for duplicate inputs self.log.info("Test duplicate input block.") block2_dup = copy.deepcopy(block2_orig) block2_dup.vtx[2].vin.append(block2_dup.vtx[2].vin[0]) block2_dup.vtx[2].rehash() block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root() block2_dup.rehash() block2_dup.solve() yield TestInstance( [[block2_dup, RejectResult(16, b'bad-txns-inputs-duplicate')], [block2_orig, True]]) height += 1 ''' Make sure that a totally screwed up block is not valid. ''' block3 = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block3.vtx[0].vout[0].nValue = 100 * COIN # Too high! block3.vtx[0].sha256 = None block3.vtx[0].calc_sha256() block3.hashMerkleRoot = block3.calc_merkle_root() block3.rehash() block3.solve() yield TestInstance([[block3, RejectResult(16, b'bad-cb-amount')]])
def get_tests(self): # shorthand for functions block = lambda *a, **kw: self.chain.next_block( *a, coinbase_key=self.coinbase_key, simple_output=True, **kw) create_and_sign_tx = lambda *a, **kw: create_and_sign_transaction( *a, private_key=self.coinbase_key, **({k: v for k, v in kw.items() if not k == 'private_key'})) update_block = self.chain.update_block tip = self.chain.set_tip accepted = self.accepted rejected = self.rejected self.chain.set_genesis_hash(int(self.nodes[0].getbestblockhash(), 16)) save_spendable_output = self.chain.save_spendable_output get_spendable_output = self.chain.get_spendable_output # Create a new block block(0) yield accepted() test, out, _ = prepare_init_chain(self.chain, 99, 33) yield test # 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() block(2, spend=out[1]) yield accepted() save_spendable_output() # so fork like this: # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) # # Nothing should happen at this point. We saw b2 first so it takes # priority. tip(1) b3 = block(3, spend=out[1]) 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) # \-> b3 (1) -> b4 (2) 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) tip(2) block(5, spend=out[2]) save_spendable_output() yield rejected() block(6, spend=out[3]) yield 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() block(8, spend=out[4]) yield 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')) # 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() 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) # (b12 added last) # \-> b3 (1) -> b4 (2) tip(5) b12 = block(12, spend=out[3]) save_spendable_output() b13 = 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() # 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() yield TestInstance([[b12, True, b13.sha256]]) # New tip should be b13. # Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop # 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 lots_of_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) tip(13) block(15, spend=out[5], script=lots_of_checksigs) yield accepted() save_spendable_output() # Test that a block with too many checksigs is rejected too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB)) 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]) # \-> b3 (1) -> b4 (2) tip(15) block(17, spend=txout_b3) yield 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() block(19, spend=out[6]) yield 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')) # 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() block(22, spend=out[5]) yield rejected() # Create a block on either side of LEGACY_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) tip(15) b23 = block(23, spend=out[6]) tx = CTransaction() script_length = LEGACY_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 = update_block(23, [tx]) # Make sure the math above worked out to produce a max-sized block assert_equal(len(b23.serialize()), LEGACY_MAX_BLOCK_SIZE) yield accepted() save_spendable_output() # 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]) 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')) # Extend the b26 chain to make sure bitcoind isn't accepting b26 b27 = block(27, spend=out[7]) yield rejected(False) # Now try a too-large-coinbase script tip(15) b28 = 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')) # Extend the b28 chain to make sure bitcoind isn't accepting b28 b29 = block(29, spend=out[7]) yield rejected(False) # b30 has a max-sized coinbase scriptSig. tip(23) b30 = block(30) b30.vtx[0].vin[0].scriptSig = b'\x00' * 100 b30.vtx[0].rehash() b30 = update_block(30, []) yield accepted() save_spendable_output() # b31 - b35 - check sigops of OP_CHECKMULTISIG / OP_CHECKMULTISIGVERIFY / OP_CHECKSIGVERIFY # # genesis -> ... -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) # \-> b36 (11) # \-> b34 (10) # \-> b32 (9) # # 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_PER_MB - 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_PER_MB) yield accepted() save_spendable_output() # this goes over the limit because the coinbase has one sigop too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) b32 = block(32, spend=out[9], script=too_many_multisigs) assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS_PER_MB + 1) yield rejected(RejectResult(16, b'bad-blk-sigops')) # CHECKMULTISIGVERIFY tip(31) lots_of_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * ((MAX_BLOCK_SIGOPS_PER_MB - 1) // 20) + [OP_CHECKSIG] * 19) block(33, spend=out[9], script=lots_of_multisigs) yield accepted() save_spendable_output() too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) 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_PER_MB - 1)) b35 = block(35, spend=out[10], script=lots_of_checksigs) yield accepted() save_spendable_output() too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB)) 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) # b12 (3) -> b13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) # \-> b37 (11) # \-> b38 (11/37) # # save 37's spendable output, but then double-spend out11 to invalidate # the block tip(35) b37 = 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')) # 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')) # Check P2SH SigOp counting # # # 13 (4) -> b15 (5) -> b23 (6) -> b30 (7) -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b41 (12) # \-> b40 (12) # # b39 - create some P2SH outputs that will require 6 sigops to spend: # # 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) b39_outputs = 0 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_hash = hash160(redeem_script) p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) # 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.vout.append( CTxOut(spend.tx.vout[spend.n].nValue - 1, CScript([OP_TRUE]))) sign_tx(tx, spend.tx, spend.n, self.coinbase_key) tx.rehash() b39 = 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 tx_new = None tx_last = tx total_size = len(b39.serialize()) while (total_size < LEGACY_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]))) tx_new.rehash() total_size += len(tx_new.serialize()) if total_size >= LEGACY_MAX_BLOCK_SIZE: break b39.vtx.append(tx_new) # add tx to block tx_last = tx_new b39_outputs += 1 b39 = update_block(39, []) 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. # The first tx has one sigop and then at the end we add 2 more to put us just over the max. # # b41 does the same, less one, so it has the maximum sigops permitted. # tip(39) b40 = block(40, spend=out[12]) sigops = get_legacy_sigopcount_block(b40) numTxes = (MAX_BLOCK_SIGOPS_PER_MB - sigops) // b39_sigops_per_output assert_equal(numTxes <= b39_outputs, True) lastOutpoint = COutPoint(b40.vtx[1].sha256, 0) lastAmount = b40.vtx[1].vout[0].nValue new_txs = [] for i in range(1, numTxes + 1): tx = CTransaction() tx.vout.append(CTxOut(1, CScript([OP_TRUE]))) tx.vin.append(CTxIn(lastOutpoint, b'')) # second input is corresponding P2SH output from b39 tx.vin.append(CTxIn(COutPoint(b39.vtx[i].sha256, 0), b'')) # Note: must pass the redeem_script (not p2sh_script) to the # signature hash function sighash = SignatureHashForkId(redeem_script, tx, 1, SIGHASH_ALL | SIGHASH_FORKID, lastAmount) sig = self.coinbase_key.sign(sighash) + bytes( bytearray([SIGHASH_ALL | SIGHASH_FORKID])) scriptSig = CScript([sig, redeem_script]) tx.vin[1].scriptSig = scriptSig tx.rehash() new_txs.append(tx) lastOutpoint = COutPoint(tx.sha256, 0) lastAmount = tx.vout[0].nValue b40_sigops_to_fill = MAX_BLOCK_SIGOPS_PER_MB - \ (numTxes * b39_sigops_per_output + sigops) + 1 tx = CTransaction() tx.vin.append(CTxIn(lastOutpoint, b'')) 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')) # same as b40, but one less sigop tip(39) b41 = block(41, spend=None) 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() # 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() block(43, spend=out[13]) 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) # \-> ??? (15) # 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. height = self.chain.block_heights[self.chain.tip.sha256] + 1 coinbase = create_coinbase(height, self.coinbase_pubkey) b44 = CBlock() b44.nTime = self.chain.tip.nTime + 1 b44.hashPrevBlock = self.chain.tip.sha256 b44.nBits = 0x207fffff b44.vtx.append(coinbase) b44.hashMerkleRoot = b44.calc_merkle_root() b44.solve() self.chain.tip = b44 self.chain.block_heights[b44.sha256] = height self.chain.blocks[44] = b44 yield accepted() # A block with a non-coinbase as the first tx non_coinbase = create_tx(out[15].tx, out[15].n, 1) b45 = CBlock() b45.nTime = self.chain.tip.nTime + 1 b45.hashPrevBlock = self.chain.tip.sha256 b45.nBits = 0x207fffff b45.vtx.append(non_coinbase) b45.hashMerkleRoot = b45.calc_merkle_root() b45.calc_sha256() b45.solve() self.chain.block_heights[ b45.sha256] = self.chain.block_heights[self.chain.tip.sha256] + 1 self.chain.tip = b45 self.chain.blocks[45] = b45 yield rejected(RejectResult(16, b'bad-cb-missing')) # A block with no txns tip(44) b46 = CBlock() b46.nTime = b44.nTime + 1 b46.hashPrevBlock = b44.sha256 b46.nBits = 0x207fffff b46.vtx = [] b46.hashMerkleRoot = 0 b46.solve() self.chain.block_heights[ b46.sha256] = self.chain.block_heights[b44.sha256] + 1 self.chain.tip = b46 assert 46 not in self.chain.blocks self.chain.blocks[46] = b46 s = ser_uint256(b46.hashMerkleRoot) yield rejected(RejectResult(16, b'bad-cb-missing')) # A block with invalid work tip(44) b47 = block(47, do_solve_block=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')) # A block with timestamp > 2 hrs in the future tip(44) b48 = block(48, do_solve_block=False) b48.nTime = int(time.time()) + 60 * 60 * 3 b48.solve() yield rejected(RejectResult(16, b'time-too-new')) # A block with an invalid merkle hash tip(44) b49 = block(49) b49.hashMerkleRoot += 1 b49.solve() yield rejected(RejectResult(16, b'bad-txnmrklroot')) # A block with an incorrect POW limit tip(44) b50 = block(50) b50.nBits = b50.nBits - 1 b50.solve() yield rejected(RejectResult(16, b'bad-diffbits')) # A block with two coinbase txns tip(44) b51 = block(51) cb2 = create_coinbase(51, self.coinbase_pubkey) b51 = update_block(51, [cb2]) yield rejected(RejectResult(16, b'bad-tx-coinbase')) # 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')) # 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() # invalid timestamp (b35 is 5 blocks back, so its time is # MedianTimePast) b54 = block(54, spend=out[15]) b54.nTime = b35.nTime - 1 b54.solve() yield rejected(RejectResult(16, b'time-too-old')) # valid timestamp tip(53) b55 = block(55, spend=out[15]) b55.nTime = b35.nTime update_block(55, []) yield accepted() save_spendable_output() # Test CVE-2012-2459 # # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57p2 (16) # \-> b57 (16) # \-> b56p2 (16) # \-> b56 (16) # # 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 # # b57 has three txns: coinbase, tx, tx1. The merkle root computation will duplicate tx. # Result: OK # # b56 copies b57 but duplicates tx1 and does not recalculate the block hash. So it has a valid merkle # root but duplicate transactions. # Result: Fails # # b57p2 has six transactions in its merkle tree: # - coinbase, tx, tx1, tx2, tx3, tx4 # Merkle root calculation will duplicate as necessary. # Result: OK. # # b56p2 copies b57p2 but adds both tx3 and tx4. The purpose of the test is to make sure the code catches # duplicate txns that are not next to one another with the "bad-txns-duplicate" error (which indicates # 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]) # b56 - copy b57, add a duplicate tx tip(55) b56 = copy.deepcopy(b57) self.chain.blocks[56] = b56 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')) # 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]) # b56p2 - copy b57p2, duplicate two non-consecutive tx's tip(55) b56p2 = copy.deepcopy(b57p2) self.chain.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')) tip("57p2") yield accepted() tip(57) yield rejected() # rejected because 57p2 seen first save_spendable_output() # Test a few invalid tx types # # -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> ??? (17) # # tx with prevout.n out of range tip(57) b58 = 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]) 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, 51 * COIN) b59 = update_block(59, [tx]) yield 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() # Test BIP30 # # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) # \-> b61 (18) # # Blocks are not allowed to contain a transaction whose id matches that of an earlier, # 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]) 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) tx = CTransaction() 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.vout.append(CTxOut(0, CScript([OP_TRUE]))) assert (tx.vin[0].nSequence < 0xffffffff) tx.calc_sha256() b62 = 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) # \-> b63 (-) # tip(60) b63 = 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')) # This checks that a block with a bloated VARINT between the block_header and the array of tx such that # the block is > LEGACY_MAX_BLOCK_SIZE with the bloated varint, but <= LEGACY_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. # # What matters is that the receiving node should not reject the bloated block, and then reject the canonical # block on the basis that it's the same as an already-rejected block (which would be a consensus failure.) # # -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) # \ # b64a (18) # 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]) # make it a "broken_block," with non-canonical serialization b64a = CBrokenBlock(regular_block) b64a.initialize(regular_block) self.chain.blocks["64a"] = b64a self.chain.tip = b64a tx = CTransaction() # use canonical serialization to calculate size script_length = LEGACY_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 = update_block("64a", [tx]) assert_equal(len(b64a.serialize()), LEGACY_MAX_BLOCK_SIZE + 8) yield TestInstance([[self.chain.tip, None]]) # comptool workaround: to make sure b64 is delivered, manually erase # b64a from blockstore self.test.block_store.erase(b64a.sha256) tip(60) b64 = CBlock(b64a) b64.vtx = copy.deepcopy(b64a.vtx) assert_equal(b64.hash, b64a.hash) assert_equal(len(b64.serialize()), LEGACY_MAX_BLOCK_SIZE) self.chain.blocks[64] = b64 update_block(64, []) yield accepted() 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) b65 = 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() # 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) b66 = 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')) # Attempt to double-spend a transaction created in a block # # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # \-> b67 (20) # # tip(65) b67 = 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')) # More tests of block subsidy # # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) # \-> b68 (20) # # b68 - coinbase with an extra 10 satoshis, # creates a tx that has 9 satoshis from out[20] go to fees # this fails because the coinbase is trying to claim 1 satoshi too much in fees # # b69 - coinbase with extra 10 satoshis, and a tx that gives a 10 satoshi fee # this succeeds # tip(65) b68 = 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')) 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() # 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]) 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')) # 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) # \-> 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. # 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 b71 = copy.deepcopy(b72) b71.vtx.append(tx2) # add duplicate tx2 self.chain.block_heights[b71.sha256] = self.chain.block_heights[ b69.sha256] + 1 # b71 builds off b69 self.chain.blocks[71] = b71 assert_equal(len(b71.vtx), 4) 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() # Test some invalid scripts and MAX_BLOCK_SIGOPS_PER_MB # # -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) -> b69 (20) -> b72 (21) # \-> b** (22) # # b73 - tx with excessive sigops that are placed after an excessively large script element. # The purpose of the test is to make sure those sigops are counted. # # script is a bytearray of size 20,526 # # bytearray[0-19,998] : OP_CHECKSIG # bytearray[19,999] : OP_PUSHDATA4 # bytearray[20,000-20,003]: 521 (max_script_element_size_before_genesis+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) # tip(72) b73 = block(73) size = MAX_BLOCK_SIGOPS_PER_MB - 1 + \ MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 1 + 5 + 1 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS_PER_MB - 1] = int("4e", 16) # OP_PUSHDATA4 element_size = MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 1 a[MAX_BLOCK_SIGOPS_PER_MB] = element_size % 256 a[MAX_BLOCK_SIGOPS_PER_MB + 1] = element_size // 256 a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0 a[MAX_BLOCK_SIGOPS_PER_MB + 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_PER_MB + 1) yield 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. # # The invalid script element is that the push_data indicates that # there will be a large amount of data (0xffffff bytes), but we only # provide a much smaller number. These bytes are CHECKSIGS so they would # cause b75 to fail for excessive sigops, if those bytes were counted. # # b74 fails because we put MAX_BLOCK_SIGOPS_PER_MB+1 before the element # b75 succeeds because we put MAX_BLOCK_SIGOPS_PER_MB before the element # # tip(72) b74 = block(74) size = MAX_BLOCK_SIGOPS_PER_MB - 1 + \ MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 42 # total = 20,561 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS_PER_MB] = 0x4e a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xfe a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 4] = 0xff tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a)) b74 = update_block(74, [tx]) yield rejected(RejectResult(16, b'bad-blk-sigops')) tip(72) b75 = block(75) size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 42 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS_PER_MB - 1] = 0x4e a[MAX_BLOCK_SIGOPS_PER_MB] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 1] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 2] = 0xff a[MAX_BLOCK_SIGOPS_PER_MB + 3] = 0xff tx = create_and_sign_tx(out[22].tx, 0, 1, CScript(a)) b75 = 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) size = MAX_BLOCK_SIGOPS_PER_MB - 1 + MAX_SCRIPT_ELEMENT_SIZE_BEFORE_GENESIS + 1 + 5 a = bytearray([OP_CHECKSIG] * size) a[MAX_BLOCK_SIGOPS_PER_MB - 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]) yield accepted() save_spendable_output() # Test transaction resurrection # # -> b77 (24) -> b78 (25) -> b79 (26) # \-> b80 (25) -> b81 (26) -> b82 (27) # # b78 creates a tx, which is spent in b79. After b82, both should be in mempool # # The tx'es must be unsigned and pass the node's mempool policy. It is unsigned for the # rather obscure reason that the Python signature code does not distinguish between # Low-S and High-S values (whereas the bitcoin code has custom code which does so); # as a result of which, the odds are 50% that the python code will use the right # value and the transaction will be accepted into the mempool. Until we modify the # test framework to support low-S signing, we are out of luck. # # 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) # 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() block(78) tx78 = create_tx(tx77, 0, 9 * COIN) update_block(78, [tx78]) yield accepted() block(79) tx79 = create_tx(tx78, 0, 8 * COIN) update_block(79, [tx79]) yield 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() block(81, spend=out[26]) yield rejected() # other chain is same length save_spendable_output() block(82, spend=out[27]) yield accepted() # now this chain is longer, triggers re-org save_spendable_output() # now check that tx78 and tx79 have been put back into the peer's # mempool mempool = self.nodes[0].getrawmempool() assert_equal(len(mempool), 2) assert (tx78.hash in mempool) assert (tx79.hash in mempool) # Test invalid opcodes in dead execution paths. # # -> b81 (26) -> b82 (27) -> b83 (28) # b83 = 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) tx2 = create_and_sign_tx(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() # 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) # \-> b85 (29) -> b86 (30) \-> b89a (32) # # b84 = block(84) tx1 = 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]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.calc_sha256() sign_tx(tx1, out[29].tx, out[29].n, self.coinbase_key) tx1.rehash() tx2 = 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.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx4 = 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])) update_block(84, [tx1, tx2, tx3, tx4, tx5]) yield accepted() save_spendable_output() tip(83) block(85, spend=out[29]) yield rejected() block(86, spend=out[30]) yield accepted() tip(84) block(87, spend=out[30]) yield rejected() save_spendable_output() block(88, spend=out[31]) yield accepted() 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 week's 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, sync_timeout=300, timeout_to_requested_block=600) spend = out[32] for i in range(89, LARGE_REORG_SIZE + 89): b = block(i, spend) tx = CTransaction() script_length = LEGACY_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 = update_block(i, [tx]) assert_equal(len(b.serialize()), LEGACY_MAX_BLOCK_SIZE) test1.blocks_and_transactions.append([self.chain.tip, True]) save_spendable_output() spend = self.chain.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.chain.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
def get_tests(self): node = self.nodes[0] self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 # returns a test case that asserts that the current tip was accepted def accepted(expectedTipHash=None): if expectedTipHash is None: return TestInstance([[self.tip, True]]) else: return TestInstance([[self.tip, True, expectedTipHash]]) # 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] # shorthand for functions block = self.next_block # Reference for blocks mined in this test: # # 11 21 -- 221 - 222 # / / / # 0 - 1 - 2 - 22 - 23 - 24 - 25 # \ # -- 12 - 13 - 14 # Generate some valid blocks block(0) yield accepted() block(1) yield accepted() block(2) yield accepted() # Explicitly invalidate blocks 1 and 2 # See below for why we do this node.invalidateblock(self.blocks[1].hash) assert_equal(self.blocks[0].hash, node.getbestblockhash()) node.invalidateblock(self.blocks[2].hash) assert_equal(self.blocks[0].hash, node.getbestblockhash()) # Mining on top of blocks 1 or 2 is rejected tip(1) block(11) yield rejected(RejectResult(16, b'bad-prevblk')) tip(2) block(21) yield rejected(RejectResult(16, b'bad-prevblk')) # Reconsider block 2 to remove invalid status from *both* 1 and 2 # The goal is to test that block 1 is not retaining any internal state # that prevents us from accepting blocks building on top of block 1 node.reconsiderblock(self.blocks[2].hash) assert_equal(self.blocks[2].hash, node.getbestblockhash()) # Mining on the block 1 chain should be accepted # (needs to mine two blocks because less-work chains are not processed) test = TestInstance(sync_every_block=False) tip(1) block(12) test.blocks_and_transactions.append([self.tip, None]) block(13) test.blocks_and_transactions.append([self.tip, None]) yield test # Mining on the block 2 chain should still be accepted # (needs to mine two blocks because less-work chains are not processed) test = TestInstance(sync_every_block=False) tip(2) block(22) test.blocks_and_transactions.append([self.tip, None]) # Mine block 221 for later block(221) test.blocks_and_transactions.append([self.tip, None]) yield test # Mine more blocks from block 22 to be longest chain test = TestInstance(sync_every_block=False) tip(22) block(23) test.blocks_and_transactions.append([self.tip, None]) block(24) test.blocks_and_transactions.append([self.tip, None]) yield test # Sanity checks assert_equal(self.blocks[24].hash, node.getbestblockhash()) assert any(self.blocks[221].hash == chaintip["hash"] for chaintip in node.getchaintips()) # Invalidating the block 2 chain should reject new blocks on that chain node.invalidateblock(self.blocks[2].hash) assert_equal(self.blocks[13].hash, node.getbestblockhash()) # Mining on the block 2 chain should be rejected tip(24) block(25) yield rejected(RejectResult(16, b'bad-prevblk')) # Continued mining on the block 1 chain is still ok tip(13) block(14) yield accepted() # Mining on a once-valid chain forking from block 2's longest chain, # which is now invalid, should also be rejected. tip(221) block(222) yield rejected(RejectResult(16, b'bad-prevblk'))
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 node = self.nodes[0] # Create a new block block(0, block_size=LEGACY_MAX_BLOCK_SIZE) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # block up to LEGACY_MAX_BLOCK_SIZE are accepted. block(1, spend=out[0], block_size=LEGACY_MAX_BLOCK_SIZE) yield accepted() # bigger block are reject as the fork isn't activated yet. block(2, spend=out[1], block_size=LEGACY_MAX_BLOCK_SIZE + 1) yield rejected(RejectResult(16, b'bad-blk-length')) # Rewind bad block tip(1) # Create a transaction that we will use to test SIGHASH_FORID script_forkid = CScript([self.forkid_pubkey, OP_CHECKSIG]) tx_forkid = self.create_and_sign_transaction(out[1].tx, out[1].n, 1, script_forkid) # Create a block that would activate the HF. We also add the # transaction that will allow us to test SIGHASH_FORKID b03 = block(3) b03.nTime = UAHF_START_TIME update_block(3, [tx_forkid]) yield accepted() # Pile up 4 blocks on top to get to the point just before activation. block(4, spend=out[2]) yield accepted() block(5, spend=out[3]) yield accepted() block(6, spend=out[4]) yield accepted() block(7, spend=out[5]) yield accepted() # bigger block are still rejected as the fork isn't activated yet. block(8, spend=out[6], block_size=LEGACY_MAX_BLOCK_SIZE + 1) yield rejected(RejectResult(16, b'bad-blk-length')) # Rewind bad block tip(7) # build a transaction using SIGHASH_FORKID tx_spend = self.create_tx(tx_forkid, 0, 1, CScript([OP_TRUE])) sighash_spend = SignatureHashForkId(script_forkid, tx_spend, 0, SIGHASH_FORKID | SIGHASH_ALL, 1) sig_forkid = self.forkid_key.sign(sighash_spend) tx_spend.vin[0].scriptSig = CScript( [sig_forkid + bytes(bytearray([SIGHASH_FORKID | SIGHASH_ALL]))]) tx_spend.rehash() # This transaction can't get into the mempool yet try: node.sendrawtransaction(ToHex(tx_spend)) except JSONRPCException as exp: assert_equal(exp.error["message"], RPC_SIGHASH_FORKID_ERROR) else: assert (False) # The transaction is rejected, so the mempool should still be empty assert_equal(set(node.getrawmempool()), set()) # check that SIGHASH_FORKID transaction are still rejected block(9) update_block(9, [tx_spend]) yield rejected(RejectResult(16, SIGHASH_INVALID_ERROR)) # Rewind bad block tip(7) # Pile up another block, to activate. OP_RETURN anti replay # outputs are still considered valid. antireplay_script = CScript([OP_RETURN, ANTI_REPLAY_COMMITMENT]) block(10, spend=out[6], script=antireplay_script) yield accepted() # Now that the HF is activated, replay protected tx are # accepted in the mempool tx_spend_id = node.sendrawtransaction(ToHex(tx_spend)) assert_equal(set(node.getrawmempool()), {tx_spend_id}) # Mark the HF self.uahfEnabled = True # HF is active now, we MUST create a big block. block(11, spend=out[7], block_size=LEGACY_MAX_BLOCK_SIZE) yield rejected(RejectResult(16, b'bad-blk-too-small')) # Rewind bad block tip(10) # HF is active, now we can create bigger blocks and use # SIGHASH_FORKID replay protection. block(12, spend=out[7], block_size=LEGACY_MAX_BLOCK_SIZE + 1) update_block(12, [tx_spend]) yield accepted() # We save this block id to test reorg fork_block_id = node.getbestblockhash() # The transaction has been mined, it's not in the mempool anymore assert_equal(set(node.getrawmempool()), set()) # Test OP_RETURN replay protection block(13, spend=out[8], script=antireplay_script) yield rejected(RejectResult(16, b'bad-txn-replay')) # Rewind bad block tip(12) # Check that only the first block has to be > 1MB block(14, spend=out[8]) yield accepted() # Now we reorg just when the HF activated. The # SIGHASH_FORKID transaction is back in the mempool node.invalidateblock(fork_block_id) assert (tx_spend_id in set(node.getrawmempool())) # And now just before when the HF activated. The # SIGHASH_FORKID should be kicked out the mempool node.invalidateblock(node.getbestblockhash()) assert (tx_spend_id not in set(node.getrawmempool()))
def get_tests(self): self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # shorthand block = self.next_block node_nonstd = self.nodes[0] node_std = self.nodes[1] # 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] block.vtx.extend(new_transactions) old_sha256 = block.sha256 make_conform_to_ctor(block) block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Update the internal state just like in next_block self.tip = block if block.sha256 != old_sha256: self.block_heights[ block.sha256] = self.block_heights[old_sha256] del self.block_heights[old_sha256] self.blocks[block_number] = block return block # Returns 2 transactions: # 1) txfund: create outputs in segwit addresses # 2) txspend: spends outputs from segwit addresses def create_segwit_fund_and_spend_tx(spend): # To make sure we'll be able to recover coins sent to segwit addresses, # we test using historical recoveries from btc.com: # Spending from a P2SH-P2WPKH coin, # txhash:a45698363249312f8d3d93676aa714be59b0bd758e62fa054fb1ea6218480691 redeem_script0 = bytearray.fromhex( '0014fcf9969ce1c98a135ed293719721fb69f0b686cb') # Spending from a P2SH-P2WSH coin, # txhash:6b536caf727ccd02c395a1d00b752098ec96e8ec46c96bee8582be6b5060fa2f redeem_script1 = bytearray.fromhex( '0020fc8b08ed636cb23afcb425ff260b3abd03380a2333b54cfa5d51ac52d803baf4' ) redeem_scripts = [redeem_script0, redeem_script1] # Fund transaction to segwit addresses txfund = CTransaction() txfund.vin = [CTxIn(COutPoint(spend.tx.sha256, spend.n))] amount = (50 * COIN - 1000) // len(redeem_scripts) for redeem_script in redeem_scripts: txfund.vout.append( CTxOut( amount, CScript([OP_HASH160, hash160(redeem_script), OP_EQUAL]))) txfund.rehash() # Segwit spending transaction # We'll test if a node that checks for standardness accepts this # txn. It should fail exclusively because of the restriction in # the scriptSig (non clean stack..), so all other characteristcs # must pass standardness checks. For this reason, we create # standard P2SH outputs. txspend = CTransaction() for i in range(len(redeem_scripts)): txspend.vin.append( CTxIn(COutPoint(txfund.sha256, i), CScript([redeem_scripts[i]]))) txspend.vout = [ CTxOut( 50 * COIN - 2000, CScript( [OP_HASH160, hash160(CScript([OP_TRUE])), OP_EQUAL])) ] txspend.rehash() return txfund, txspend # Check we are not banned when sending a txn that node_nonstd rejects. def check_for_no_ban_on_rejected_tx(tx, reject_code, reject_reason): # Check that our connection to node_std is open assert (node_std.p2p.state == 'connected') # The P2PConnection stores a public counter for each message type # and the last receive message of each type. We use this counter to # identify that we received a new reject message. with mininode_lock: rejects_count = node_std.p2p.message_count['reject'] # Send the transaction directly. We use a ping for synchronization: # if we have been banned, the pong message won't be received, a # timeout occurs and the test fails. node_std.p2p.send_message(msg_tx(tx)) node_std.p2p.sync_with_ping() # Check we haven't been disconnected assert (node_std.p2p.state == 'connected') # Check the reject message matches what we expected with mininode_lock: assert (node_std.p2p.message_count['reject'] == rejects_count + 1) reject_msg = node_std.p2p.last_message['reject'] assert (reject_msg.code == reject_code and reject_msg.reason == reject_reason and reject_msg.data == tx.sha256) # Create a new block block(0) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Create segwit funding and spending transactions txfund, txspend = create_segwit_fund_and_spend_tx(out[0]) # Create blocks to get closer to activate the fork. # Mine txfund, as it can't go into node_std mempool because it's # nonstandard. b = block(5555) b.nTime = GREAT_WALL_START_TIME - 1 update_block(5555, [txfund]) yield accepted() for i in range(5): block(5100 + i) test.blocks_and_transactions.append([self.tip, True]) yield test # Since the TestManager is not connected to node_std, we must check # both nodes are synchronized before continuing. sync_blocks(self.nodes) # Check we are just before the activation time assert_equal( node_nonstd.getblockheader( node_nonstd.getbestblockhash())['mediantime'], GREAT_WALL_START_TIME - 1) assert_equal( node_std.getblockheader(node_std.getbestblockhash())['mediantime'], GREAT_WALL_START_TIME - 1) # Before the fork, segwit spending txns are rejected. assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR, node_nonstd.sendrawtransaction, ToHex(txspend)) assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR, node_std.sendrawtransaction, ToHex(txspend)) # Blocks containing segwit spending txns are rejected as well. block(2) update_block(2, [txspend]) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block tip(5104) # Check that non-upgraded nodes checking for standardness are not # banning nodes sending segwit spending txns. check_for_no_ban_on_rejected_tx(txspend, 64, CLEANSTACK_ERROR) # Activate the fork in both nodes! forkblock = block(5556) yield accepted() sync_blocks(self.nodes) # Check we just activated the fork assert_equal( node_nonstd.getblockheader( node_nonstd.getbestblockhash())['mediantime'], GREAT_WALL_START_TIME) assert_equal( node_std.getblockheader(node_std.getbestblockhash())['mediantime'], GREAT_WALL_START_TIME) # Segwit spending txns are accepted in the mempool of nodes not checking # for standardness, but rejected in nodes that check. node_nonstd.sendrawtransaction(ToHex(txspend)) assert (txspend.hash in node_nonstd.getrawmempool()) assert_raises_rpc_error(-26, RPC_CLEANSTACK_ERROR, node_std.sendrawtransaction, ToHex(txspend)) # Check that upgraded nodes checking for standardness are not banning # nodes sending segwit spending txns. check_for_no_ban_on_rejected_tx(txspend, 64, CLEANSTACK_ERROR) # Blocks containing segwit spending txns are now accepted in both # nodes. block(5) postforkblock = update_block(5, [txspend]) yield accepted() sync_blocks(self.nodes) # Ok, now we check if a reorg work properly accross the activation. node_nonstd.invalidateblock(postforkblock.hash) assert (txspend.hash in node_nonstd.getrawmempool()) # Also check that nodes checking for standardness don't return a segwit # spending txn into the mempool when disconnecting a block. node_std.invalidateblock(postforkblock.hash) assert (txspend.hash not in node_std.getrawmempool()) # Deactivate the fork. The spending tx has been evicted from the # mempool node_nonstd.invalidateblock(forkblock.hash) assert (len(node_nonstd.getrawmempool()) == 0)
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] block.vtx.extend(new_transactions) old_sha256 = block.sha256 make_conform_to_ctor(block) block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Update the internal state just like in next_block self.tip = block if block.sha256 != old_sha256: self.block_heights[ block.sha256] = self.block_heights[old_sha256] del self.block_heights[old_sha256] self.blocks[block_number] = block return block # shorthand block = self.next_block node = self.nodes[0] # Create a new block block(0) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Generate a key pair to test P2SH sigops count private_key = CECKey() private_key.set_secretbytes(b"replayprotection") public_key = private_key.get_pubkey() # This is a little handier to use than the version in blocktools.py def create_fund_and_spend_tx(spend, forkvalue=0): # Fund transaction script = CScript([public_key, OP_CHECKSIG]) txfund = create_transaction(spend.tx, spend.n, b'', 50 * COIN, script) txfund.rehash() # Spend transaction txspend = CTransaction() txspend.vout.append(CTxOut(50 * COIN - 1000, CScript([OP_TRUE]))) txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b'')) # Sign the transaction sighashtype = (forkvalue << 8) | SIGHASH_ALL | SIGHASH_FORKID sighash = SignatureHashForkId(script, txspend, 0, sighashtype, 50 * COIN) sig = private_key.sign(sighash) + \ bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) txspend.vin[0].scriptSig = CScript([sig]) txspend.rehash() return [txfund, txspend] def send_transaction_to_mempool(tx): tx_id = node.sendrawtransaction(ToHex(tx)) assert (tx_id in set(node.getrawmempool())) return tx_id # Before the fork, no replay protection required to get in the mempool. txns = create_fund_and_spend_tx(out[0]) send_transaction_to_mempool(txns[0]) send_transaction_to_mempool(txns[1]) # And txns get mined in a block properly. block(1) update_block(1, txns) yield accepted() # Replay protected transactions are rejected. replay_txns = create_fund_and_spend_tx(out[1], 0xffdead) send_transaction_to_mempool(replay_txns[0]) assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, node.sendrawtransaction, ToHex(replay_txns[1])) # And block containing them are rejected as well. block(2) update_block(2, replay_txns) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block tip(1) # Create a block that would activate the replay protection. bfork = block(5555) bfork.nTime = REPLAY_PROTECTION_START_TIME - 1 update_block(5555, []) yield accepted() for i in range(5): block(5100 + i) test.blocks_and_transactions.append([self.tip, True]) yield test # Check we are just before the activation time assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], REPLAY_PROTECTION_START_TIME - 1) # We are just before the fork, replay protected txns still are rejected assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, node.sendrawtransaction, ToHex(replay_txns[1])) block(3) update_block(3, replay_txns) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block tip(5104) # Send some non replay protected txns in the mempool to check # they get cleaned at activation. txns = create_fund_and_spend_tx(out[2]) send_transaction_to_mempool(txns[0]) tx_id = send_transaction_to_mempool(txns[1]) # Activate the replay protection block(5556) yield accepted() # Check we just activated the replay protection assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], REPLAY_PROTECTION_START_TIME) # Non replay protected transactions are not valid anymore, # so they should be removed from the mempool. assert (tx_id not in set(node.getrawmempool())) # Good old transactions are now invalid. send_transaction_to_mempool(txns[0]) assert_raises_rpc_error(-26, RPC_INVALID_SIGNATURE_ERROR, node.sendrawtransaction, ToHex(txns[1])) # They also cannot be mined block(4) update_block(4, txns) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block tip(5556) # The replay protected transaction is now valid replay_tx0_id = send_transaction_to_mempool(replay_txns[0]) replay_tx1_id = send_transaction_to_mempool(replay_txns[1]) # Make sure the transaction are ready to be mined. tmpl = node.getblocktemplate() found_id0 = False found_id1 = False for txn in tmpl['transactions']: txid = txn['txid'] if txid == replay_tx0_id: found_id0 = True elif txid == replay_tx1_id: found_id1 = True assert (found_id0 and found_id1) # And the mempool is still in good shape. assert (replay_tx0_id in set(node.getrawmempool())) assert (replay_tx1_id in set(node.getrawmempool())) # They also can also be mined b5 = block(5) update_block(5, replay_txns) yield accepted() # Ok, now we check if a reorg work properly accross the activation. postforkblockid = node.getbestblockhash() node.invalidateblock(postforkblockid) assert (replay_tx0_id in set(node.getrawmempool())) assert (replay_tx1_id in set(node.getrawmempool())) # Deactivating replay protection. forkblockid = node.getbestblockhash() node.invalidateblock(forkblockid) # The funding tx is not evicted from the mempool, since it's valid in # both sides of the fork assert (replay_tx0_id in set(node.getrawmempool())) assert (replay_tx1_id not in set(node.getrawmempool())) # Check that we also do it properly on deeper reorg. node.reconsiderblock(forkblockid) node.reconsiderblock(postforkblockid) node.invalidateblock(forkblockid) assert (replay_tx0_id in set(node.getrawmempool())) assert (replay_tx1_id not in set(node.getrawmempool()))
def get_tests(self): # Test no excessiveblocksize parameter specified node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) # shorthand for functions block = self.chain.next_block # Create a new block block(0) self.chain.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) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(self.chain.get_spendable_output()) # Let's build some blocks and test them. for i in range(15): n = i + 1 block(n, spend=out[i], block_size=n * ONE_MEGABYTE) yield self.accepted() # Start moving MTP forward bfork = block(5555, out[15], block_size=32 * ONE_MEGABYTE) bfork.nTime = MAGNETIC_START_TIME - 1 self.chain.update_block(5555, []) yield self.accepted() # Get to one block of the Nov 15, 2018 HF activation for i in range(5): block(5100 + i) test.blocks_and_transactions.append([self.chain.tip, True]) yield test # Check that the MTP is just before the configured fork point. assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MAGNETIC_START_TIME - 1) # Before we acivate the Nov 15, 2018 HF, 32MB is the limit. block(4444, spend=out[16], block_size=32 * ONE_MEGABYTE + 1) yield self.rejected(RejectResult(16, b'bad-blk-length')) # Rewind bad block. self.chain.set_tip(5104) # Activate the Nov 15, 2018 HF block(5556) yield self.accepted() # Now MTP is exactly the fork time. Bigger blocks are now accepted. assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MAGNETIC_START_TIME) # block of maximal size block(17, spend=out[16], block_size=128 * ONE_MEGABYTE) yield self.accepted() # Oversized blocks will cause us to be disconnected assert (not self.test.test_nodes[0].closed) block(18, spend=out[17], block_size=128 * ONE_MEGABYTE + 1) self.test.connections[0].send_message(msg_block((self.chain.tip))) self.test.wait_for_disconnections() assert (self.test.test_nodes[0].closed) # Rewind bad block and remake connection to node self.chain.set_tip(17) self.restart_network() self.test.wait_for_verack() # Check we can still mine a good size block block(5557, spend=out[18], block_size=self.excessive_block_size) yield self.accepted()
def get_tests(self): self.genesis_hash = int(self.nodes[0].getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # shorthand block = self.next_block node = self.nodes[0] node_ban = self.nodes[1] # save the current tip so its coinbase can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get a coinbase 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] # Create a new block block(0) save_spendable_output() yield accepted() # Now we need that block to mature so we can spend the coinbase. test = TestInstance(sync_every_block=False) for i in range(199): block(5000 + i) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Generate a key pair to test P2SH sigops count privkeybytes = b"Schnorr!" * 4 private_key = CECKey() private_key.set_secretbytes(privkeybytes) # get uncompressed public key serialization public_key = private_key.get_pubkey() def create_fund_and_spend_tx(spend, multi=False, sig='schnorr'): if multi: script = CScript([OP_1, public_key, OP_1, OP_CHECKMULTISIG]) else: script = CScript([public_key, OP_CHECKSIG]) # Fund transaction txfund = create_transaction(spend.tx, spend.n, b'', 50 * COIN, script) txfund.rehash() # Spend transaction txspend = CTransaction() txspend.vout.append(CTxOut(50 * COIN - 1000, CScript([OP_TRUE]))) txspend.vin.append(CTxIn(COutPoint(txfund.sha256, 0), b'')) # Sign the transaction sighashtype = SIGHASH_ALL | SIGHASH_FORKID hashbyte = bytes([sighashtype & 0xff]) sighash = SignatureHashForkId(script, txspend, 0, sighashtype, 50 * COIN) if sig == 'schnorr': txsig = schnorr.sign(privkeybytes, sighash) + hashbyte elif sig == 'ecdsa': txsig = private_key.sign(sighash) + hashbyte elif isinstance(sig, bytes): txsig = sig + hashbyte if multi: txspend.vin[0].scriptSig = CScript([b'', txsig]) else: txspend.vin[0].scriptSig = CScript([txsig]) txspend.rehash() return txfund, txspend def send_transaction_to_mempool(tx): tx_id = node.sendrawtransaction(ToHex(tx)) assert (tx_id in set(node.getrawmempool())) return tx_id # Check we are disconnected when sending a txn that node_ban rejects. # (Can't actually get banned, since bitcoind won't ban local peers.) def check_for_ban_on_rejected_tx(tx): # Take a connection p2p = node_ban.p2ps.pop() assert (p2p.state == 'connected') # make sure we can ping p2p.sync_with_ping() # send the naughty transaction p2p.send_message(msg_tx(tx)) # if not "banned", this will timeout and raise exception. p2p.wait_for_disconnect() # Setup fundings fundings = [] fund, schnorrchecksigtx = create_fund_and_spend_tx(out[0]) fundings.append(fund) fund, schnorrmultisigtx = create_fund_and_spend_tx(out[1], multi=True) fundings.append(fund) fund, ecdsachecksigtx = create_fund_and_spend_tx(out[2], sig='ecdsa') fundings.append(fund) fund, sig64checksigtx = create_fund_and_spend_tx(out[5], sig=sig64) fundings.append(fund) fund, sig64multisigtx = create_fund_and_spend_tx(out[6], multi=True, sig=sig64) fundings.append(fund) for fund in fundings: send_transaction_to_mempool(fund) block(1, transactions=fundings) yield accepted() # we're now set up for the various spends; make sure the other node # is set up, too. sync_blocks(self.nodes) # Typical ECDSA and Schnorr CHECKSIG are valid schnorr_tx_id = send_transaction_to_mempool(schnorrchecksigtx) ecdsa_tx_id = send_transaction_to_mempool(ecdsachecksigtx) # It can also be mined block(2, transactions=[schnorrchecksigtx, ecdsachecksigtx]) yield accepted() assert schnorr_tx_id not in set(node.getrawmempool()) assert ecdsa_tx_id not in set(node.getrawmempool()) # Schnorr in multisig is rejected with mandatory error. assert_raises_rpc_error(-26, RPC_SCHNORR_MULTISIG_ERROR, node.sendrawtransaction, ToHex(schnorrmultisigtx)) # And it is banworthy. check_for_ban_on_rejected_tx(schnorrmultisigtx) # And it can't be mined block(13, transactions=[schnorrmultisigtx]) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block tip(2) # If we try to submit a bad 64-byte sig, we fail with mandatory errors. # In CHECKSIG it's invalid Schnorr and hence NULLFAIL. assert_raises_rpc_error(-26, RPC_NULLFAIL_ERROR, node.sendrawtransaction, ToHex(sig64checksigtx)) # In CHECKMULTISIG it's invalid length and hence BAD_LENGTH. assert_raises_rpc_error(-26, RPC_SCHNORR_MULTISIG_ERROR, node.sendrawtransaction, ToHex(sig64multisigtx)) # Getting sent these transactions is banworthy. check_for_ban_on_rejected_tx(sig64checksigtx) check_for_ban_on_rejected_tx(sig64multisigtx) # And they can't be mined either... block(14, transactions=[sig64checksigtx]) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block tip(2) block(15, transactions=[sig64multisigtx]) yield rejected(RejectResult(16, b'blk-bad-inputs')) # Rewind bad block tip(2)
def get_tests(self): node = self.nodes[0] self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"] # First, we generate some coins to spend. node.generate(125) # Create various outputs using the OP_AND to check for activation. tx_hex = self.create_and_tx(25) txid = node.sendrawtransaction(tx_hex) assert (txid in set(node.getrawmempool())) node.generate(1) assert (txid not in set(node.getrawmempool())) # register the spendable outputs. tx = FromHex(CTransaction(), tx_hex) tx.rehash() spendable_ands = [ PreviousSpendableOutput(tx, i) for i in range(len(tx.vout)) ] def spend_and(): outpoint = spendable_ands.pop() out = outpoint.tx.vout[outpoint.n] value = int(out.nValue - (self.relayfee * COIN)) tx = CTransaction() tx.vin = [CTxIn(COutPoint(outpoint.tx.sha256, outpoint.n))] tx.vout = [CTxOut(value, CScript([]))] tx.rehash() return tx # Check that large opreturn are not accepted yet. print("Try to use the monolith opcodes before activation") tx0 = spend_and() tx0_hex = ToHex(tx0) assert_raises_jsonrpc(-26, RPC_DISABLED_OPCODE_ERROR, node.sendrawtransaction, tx0_hex) # Push MTP forward just before activation. print("Pushing MTP just before the activation and check again") node.setmocktime(MONOLITH_START_TIME) # returns a test case that asserts that the current tip was accepted def accepted(tip): return TestInstance([[tip, True]]) # returns a test case that asserts that the current tip was rejected def rejected(tip, reject=None): if reject is None: return TestInstance([[tip, False]]) else: return TestInstance([[tip, reject]]) def next_block(block_time): # get block height blockchaininfo = node.getblockchaininfo() height = int(blockchaininfo['blocks']) + 1 # create the block coinbase = create_coinbase(absoluteHeight=height) coinbase.rehash() block = create_block(int(node.getbestblockhash(), 16), coinbase, block_time) block.nVersion = 4 # Do PoW, which is cheap on regnet block.solve() return block for i in range(6): b = next_block(MONOLITH_START_TIME + i - 1) yield accepted(b) # Check again just before the activation time assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MONOLITH_START_TIME - 1) assert_raises_jsonrpc(-26, RPC_DISABLED_OPCODE_ERROR, node.sendrawtransaction, tx0_hex) def add_tx(block, tx): block.vtx.append(tx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() b = next_block(MONOLITH_START_TIME + 6) add_tx(b, tx0) yield rejected(b, RejectResult(16, b'blk-bad-inputs')) print("Activates the new opcodes") fork_block = next_block(MONOLITH_START_TIME + 6) yield accepted(fork_block) assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MONOLITH_START_TIME) tx0id = node.sendrawtransaction(tx0_hex) assert (tx0id in set(node.getrawmempool())) # Transactions can also be included in blocks. monolithblock = next_block(MONOLITH_START_TIME + 7) add_tx(monolithblock, tx0) yield accepted(monolithblock) print("Cause a reorg that deactivate the monolith opcodes") # Invalidate the monolith block, ensure tx0 gets back to the mempool. assert (tx0id not in set(node.getrawmempool())) node.invalidateblock(format(monolithblock.sha256, 'x')) assert (tx0id in set(node.getrawmempool())) node.invalidateblock(format(fork_block.sha256, 'x')) assert (tx0id not in set(node.getrawmempool()))
def get_tests(self): # shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) # Create a new block block(0) self.chain.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(105): block(5000 + i) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(self.chain.get_spendable_output()) # Block with height 107. block(1, spend=out[0]) yield self.accepted() # Create block with height 108 (genesis NOT activated). block(2, spend=out[1]) tx0 = create_transaction(out[2].tx, out[2].n, b"", 100000, CScript([OP_RETURN])) self.chain.update_block(2, [tx0]) # \x51 is OP_TRUE tx1 = create_transaction(tx0, 0, b'\x51', 1, CScript([OP_TRUE])) b108_rejected = self.chain.update_block(2, [tx1]) self.log.info( "Created block %s on height %d that tries to spend from block on height %d.", b108_rejected.hash, self.genesisactivationheight - 1, self.genesisactivationheight - 1) yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # Rewind bad block (height is 107). self.chain.set_tip(1) # Create block on height 108 (genesis NOT activated). block(3, spend=out[3]) tx0 = create_transaction(out[4].tx, out[4].n, b"", 100000, CScript([OP_RETURN])) b108 = self.chain.update_block(3, [tx0]) self.log.info("Created block %s on height %d.", b108.hash, self.genesisactivationheight - 1) yield self.accepted() # Create block on height 109 and try to spend from block on height 108 block(4, spend=out[5]) # \x51 is OP_TRUE tx1 = create_transaction(tx0, 0, b'\x51', 1, CScript([OP_TRUE])) b109_rejected = self.chain.update_block(4, [tx1]) self.log.info( "Created block %s on height %d that tries to spend from block on height %d.", b109_rejected.hash, self.genesisactivationheight, self.genesisactivationheight - 1) yield self.rejected(RejectResult(16, b'bad-txns-inputs-missingorspent')) # Rewind bad block (height is 108). self.chain.set_tip(3) # Create block on height 109 and try to spend from block on height 109. block(5, spend=out[6]) tx0 = create_transaction(out[7].tx, out[7].n, b"", 100000, CScript([OP_RETURN])) self.chain.update_block(5, [tx0]) # \x51 is OP_TRUE tx1 = create_transaction(tx0, 0, b'\x51', 1, CScript([OP_TRUE])) b109_accepted = self.chain.update_block(5, [tx1]) self.log.info( "Created block %s on height %d that tries to spend from block on height %d.", b109_accepted.hash, self.genesisactivationheight, self.genesisactivationheight) yield self.accepted() ############# # At this point, we have tx0 and tx1 in cache script marked as valid. # Now, invalidate blocks 109 and 108 so that we are in state before genesis. node.invalidateblock(format(b109_accepted.sha256, 'x')) sleep(1) # tx0 and tx1 are in mempool (currently valid because it was sent after genesis) assert_equal(True, tx0.hash in node.getrawmempool()) assert_equal(True, tx1.hash in node.getrawmempool()) node.invalidateblock(format(b108.sha256, 'x')) # tx0 and tx1 are not in mempool (mempool is deleted when 108 is invalidated) assert_equal(False, tx0.hash in node.getrawmempool()) assert_equal(False, tx1.hash in node.getrawmempool()) # So we are at height 107. assert_equal(node.getblock(node.getbestblockhash())['height'], 107) self.nodes[0].generate(1) tx = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['tx'] assert_equal(False, tx0.hash in tx) assert_equal(False, tx1.hash in tx) # Now we are at height 108. assert_equal(node.getblock(node.getbestblockhash())['height'], 108)
def get_tests(self): if self.tip is None: self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) self.block_time = int(time.time()) + 1 ''' Create a new block with an anyone-can-spend coinbase ''' height = 1 block = create_block(self.tip, create_coinbase(height), self.block_time) self.block_time += 1 block.solve() # Save the coinbase for later self.block1 = block self.tip = block.sha256 height += 1 yield TestInstance([[block, True]]) ''' Now we need that block to mature so we can spend the coinbase. ''' test = TestInstance(sync_every_block=False) for i in range(100): block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() self.tip = block.sha256 self.block_time += 1 test.blocks_and_transactions.append([block, True]) height += 1 yield test # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) tx1 = create_transaction(self.block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) yield TestInstance([[ tx1, RejectResult(RejectInvalid, b'mandatory-script-verify-flag-failed') ]]) self.log.debug( "[tx_check 001] should reject coinbase tx -----------------------------------------------------" ) # Transaction will be rejected with code 16 (REJECT_INVALID) tx1 = create_coinbase(0) yield TestInstance( [[tx1, RejectResult(RejectInvalid, b'bad-tx-coinbase')]]) self.log.debug( "[tx_check 002] should reject non final tx ----------------------------------------------------" ) # Transaction will be rejected with code 16 (REJECT_INVALID) tx1 = create_transaction(self.block1.vtx[0], 0, b'', 50 * COIN - 200) tx1.nLockTime = 5000000 #high lock height tx1.vin[0].nSequence = 0 #not final sequence tx1.rehash() yield TestInstance( [[tx1, RejectResult(RejectNonstandard, b'bad-txns-nonfinal')]]) self.log.debug( "[tx_check 003] should reject tx whose input has already spent in mempool ---------------------" ) # Transaction will be rejected with code 16 (REJECT_INVALID) tx1 = create_transaction(self.block1.vtx[0], 0, b'\x51', 50 * COIN - 300) yield TestInstance([[ tx1, RejectResult( RejectNonstandard, b'non-mandatory-script-verify-flag (Script did not clean its stack)' ) ]]) self.log.debug( "[tx_check 004-0] should reject tx whose input has already spent in mempool ---------------------" ) self.log.debug( " 004-1 --------------------- create a transaction tx1 with output" ) tx1 = create_transaction(self.block1.vtx[0], 0, b'', 50 * COIN - 300, CScript([OP_TRUE])) yield TestInstance([[tx1, True]]) self.log.debug( " 004-2 --------------------- spend output of tx1") tx2 = create_transaction(tx1, 0, b'', 50 * COIN - 300 - 100) yield TestInstance([[tx2, True]]) self.log.debug( " 004-3 --------------------- try spend output of tx1 again" ) tx3 = create_transaction(tx1, 0, b'', 50 * COIN - 300 - 200) yield TestInstance([[tx3, False]]) self.log.debug( "[tx_check finished] --------------------------------------------------------------------------" )
def get_tests(self): # Shorthand for functions block = self.chain.next_block node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) block(0) yield self.accepted() test, out, _ = prepare_init_chain(self.chain, 580, 200) yield test # Create block on height 581 with some transactions for us to spend nLockTime = self.start_time block(1, spend=out[0]) spend_tx1 = self.create_transaction(out[1].tx, out[1].n, CScript(), 100000, CScript([OP_TRUE])) spend_tx2 = self.create_transaction(out[2].tx, out[2].n, CScript(), 100000, CScript([OP_TRUE])) self.chain.update_block(1, [spend_tx1, spend_tx2]) yield self.accepted() # At height 582, create a transaction with nSequence set to prevent mining until height 584 rej_tx = self.create_locked_transaction(spend_tx1, 0, CScript(), 1, CScript([OP_TRUE]), nLockTime, self.make_sequence(584)) # The transaction should be rejected because genesis has not yet activated and the transaction is not yet minable yield TestInstance([[rej_tx, RejectResult(64, b'non-BIP68-final')]]) # At height 582, create a transaction with nSequence set to prevent mining until height 583. tx1 = self.create_locked_transaction(spend_tx1, 0, CScript(), 1, CScript([OP_TRUE]), nLockTime, self.make_sequence(583)) # The transaction should be accepted because genesis has not yet activated but the transaction is minable in the next block yield TestInstance([[tx1, True]]) assert (tx1.hash in self.nodes[0].getrawmempool()) # Move height on to 583, tx1 is mined and removed from mempool self.nodes[0].generate(1) assert (tx1.hash not in self.nodes[0].getrawmempool()) # Create block with bip68 non-final txn in it. It will be rejected. self.build_on_tip(9) block(10, spend=out[5]) bip68_non_final_tx = self.create_locked_transaction( spend_tx2, 0, CScript(), 1, CScript([OP_TRUE]), nLockTime, self.make_sequence(587)) self.chain.update_block(10, [bip68_non_final_tx]) yield self.rejected(RejectResult(16, b'bad-txns-nonfinal')) # Move height on to block 600; genesis is activated. self.nodes[0].generate(self.genesisactivationheight - self.nodes[0].getblockcount()) self.log.info("Genesis activated, height {}".format( self.nodes[0].getblockcount())) # Get tip from bitcoind because it's further along than we are, and we want to build on it self.build_on_tip(2) # Create block on height 601 with some more transactions for us to spend nLockTime = self.start_time block(3, spend=out[10]) spend_tx2 = self.create_transaction(out[12].tx, out[12].n, CScript(), 100000, CScript([OP_TRUE])) spend_tx3 = self.create_transaction(out[13].tx, out[13].n, CScript(), 100000, CScript([OP_TRUE])) spend_tx4 = self.create_transaction(out[14].tx, out[14].n, CScript(), 100000, CScript([OP_TRUE])) spend_tx5 = self.create_transaction(out[15].tx, out[15].n, CScript(), 100000, CScript([OP_TRUE])) spend_tx6 = self.create_transaction(out[16].tx, out[16].n, CScript(), 100000, CScript([OP_TRUE])) spend_tx7 = self.create_transaction(out[17].tx, out[17].n, CScript(), 100000, CScript([OP_TRUE])) spend_tx8 = self.create_transaction(out[18].tx, out[18].n, CScript(), 100000, CScript([OP_TRUE])) self.chain.update_block( 3, [spend_tx2, spend_tx3, spend_tx4, spend_tx5, spend_tx6]) yield self.accepted() # Check txn with empty vin is rejected with the expected code (fix for CORE-430). tx2 = self.create_locked_transaction(spend_tx2, 0, CScript(), 1000, CScript([OP_TRUE]), nLockTime, 0x00000001) tx2.vin = [] tx2.rehash() yield TestInstance([[tx2, RejectResult(16, b'bad-txns-vin-empty')]]) # At height 601, create a transaction with nLockTime in the past & nSequence set to the value # that would have (pre-genesis) meant minable at height 605. tx2 = self.create_locked_transaction(spend_tx2, 0, CScript(), 1000, CScript([OP_TRUE]), nLockTime, self.make_sequence(605)) # The transaction should be accepted because this is now considered final. yield TestInstance([[tx2, True]]) mempool = self.nodes[0].getrawmempool() assert (tx2.hash in mempool) # At height 601, create a transaction with nLockTime in the future & nSequence not 0xFFFFFFFF. nLockTime = int(time.time()) + 1000 tx3 = self.create_locked_transaction(spend_tx3, 0, CScript(), 1000, CScript([OP_TRUE]), nLockTime, 0x00000003) tx4 = self.create_locked_transaction(spend_tx4, 0, CScript(), 1000, CScript([OP_TRUE]), nLockTime, 0x00000003) tx5 = self.create_locked_transaction(spend_tx5, 0, CScript(), 1000, CScript([OP_TRUE]), nLockTime + 30, 0x00000003) # The transactions should be accepted into the non-final-mempool but not the main mempool. yield TestInstance([[tx3, DiscardResult()], [tx4, DiscardResult()], [tx5, DiscardResult()]]) mempool = self.nodes[0].getrawmempool() nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert (tx3.hash not in mempool) assert (tx3.hash in nonfinalmempool) assert (tx4.hash not in mempool) assert (tx4.hash in nonfinalmempool) assert (tx5.hash not in mempool) assert (tx5.hash in nonfinalmempool) # At height 601, create a transaction with nLockTime in the future & nSequence equal to 0xFFFFFFFF. tx6 = self.create_locked_transaction(spend_tx6, 0, CScript(), 1000, CScript([OP_TRUE]), nLockTime, 0xFFFFFFFF) # The transaction will be considered final and accepted into the main mempool. yield TestInstance([[tx6, True]]) mempool = self.nodes[0].getrawmempool() assert (tx6.hash in mempool) # Create transaction that tries to double spend UTXOs locked by txn3. It will be discarded. tx3_double_spend = self.create_transaction(spend_tx3, 0, CScript(), 1000, CScript([OP_TRUE])) tx3_double_spend.vin.append(spend_tx7.vin[0]) yield TestInstance([[tx3_double_spend, DiscardResult()]]) # Move height on to 602. Txn2 and Txn6 will be mined and removed from mempool, txn3, 4 & 5 will not have been moved there. self.nodes[0].generate(1) mempool = self.nodes[0].getrawmempool() nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert (tx2.hash not in mempool) assert (tx3.hash not in mempool) assert (tx4.hash not in mempool) assert (tx5.hash not in mempool) assert (tx6.hash not in mempool) assert (tx3.hash in nonfinalmempool) assert (tx4.hash in nonfinalmempool) assert (tx5.hash in nonfinalmempool) # Create standard transaction which tries to spend non-final txn3. It will be an orphan. txn_spend = self.create_transaction(tx3, 0, CScript(), 100, CScript([OP_TRUE])) yield TestInstance([[txn_spend, DiscardResult()]]) # Create non-final transaction which tries to spend non-final txn3. It will be rejected. txn_spend = self.create_locked_transaction(tx3, 0, CScript(), 100, CScript([OP_TRUE]), nLockTime, 0x00000003) yield TestInstance( [[txn_spend, RejectResult(64, b'too-long-non-final-chain')]]) # Send update to txn3 with lower nSequence. It will be rejected. tx3.vin[0].nSequence -= 1 tx3.rehash() yield TestInstance([[tx3, RejectResult(16, b'bad-txn-update')]]) # Send update to txn3 with higher nSequence. It will be accepted. tx3.vin[0].nSequence += 2 tx3.rehash() yield TestInstance([[tx3, DiscardResult()]]) nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert_equal(len(nonfinalmempool), 3) assert (tx3.hash in nonfinalmempool) assert (tx4.hash in nonfinalmempool) assert (tx5.hash in nonfinalmempool) # Send multiple updates together for txn3 with higher and different nSequence numbers. # Just one of the updates will be accepted, but due to PTV we can't be sure which. tx3_update1 = copy.deepcopy(tx3) tx3_update2 = copy.deepcopy(tx3) tx3_update3 = copy.deepcopy(tx3) tx3_update1.vin[0].nSequence += 1 tx3_update2.vin[0].nSequence += 3 # update2 has the highest nSequence tx3_update3.vin[0].nSequence += 2 tx3_update1.rehash() tx3_update2.rehash() tx3_update3.rehash() updateHashes = {tx3_update1.hash, tx3_update2.hash, tx3_update3.hash} yield TestInstance([[tx3_update1, None], [tx3_update2, None], [tx3_update3, None]]) nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert_equal(len(nonfinalmempool), 3) assert (bool(tx3_update1.hash in nonfinalmempool) ^ bool(tx3_update2.hash in nonfinalmempool) ^ bool(tx3_update3.hash in nonfinalmempool)) assert (tx4.hash in nonfinalmempool) assert (tx5.hash in nonfinalmempool) # Remember which update got accepted tx3_update_accepted = None for update in updateHashes: if update in nonfinalmempool: tx3_update_accepted = update break # Send an invalid update in a single txn which wants to update both txn4 and txn5. It will be rejected. tx4_tx5_invalid = copy.deepcopy(tx4) tx4_tx5_invalid.vin.append(tx5.vin[0]) tx4_tx5_invalid.vin[0].nSequence += 1 tx4_tx5_invalid.vin[1].nSequence += 1 tx4_tx5_invalid.rehash() yield TestInstance( [[tx4_tx5_invalid, RejectResult(16, b'bad-txn-update')]]) nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert_equal(len(nonfinalmempool), 3) assert (tx4.hash in nonfinalmempool) assert (tx5.hash in nonfinalmempool) # Send an invalid update for txn4 which changes the number of inputs. It will be rejected. tx4_invalid = copy.deepcopy(tx4) tx4_invalid.vin.append(spend_tx8.vin[0]) tx4_invalid.vin[0].nSequence += 1 tx4_invalid.rehash() yield TestInstance([[tx4_invalid, RejectResult(16, b'bad-txn-update')]]) nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert_equal(len(nonfinalmempool), 3) assert (tx4.hash in nonfinalmempool) # Send an update for txn3 with nSequence = 0xFFFFFFFF. It will be finalised and moved # into the main mempool. nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert (tx3_update_accepted in nonfinalmempool) tx3.vin[0].nSequence = 0xFFFFFFFF tx3.rehash() yield TestInstance([[tx3, True]]) mempool = self.nodes[0].getrawmempool() nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert (tx3.hash in mempool) assert (tx3.hash not in nonfinalmempool) assert (tx3_update_accepted not in nonfinalmempool) # Move time on beyond the nLockTime for txn4. It will be finalised and moved into the # main mempool. nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert (tx4.hash in nonfinalmempool) assert (tx5.hash in nonfinalmempool) self.nodes[0].setmocktime(nLockTime + 1) self.nodes[0].generate(6) wait_until(lambda: tx4.hash in self.nodes[0].getrawmempool(), timeout=5) nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert (tx4.hash not in nonfinalmempool) assert (tx5.hash in nonfinalmempool) # Get tip from bitcoind because it's further along than we are, and we want to build on it self.build_on_tip(4) # Create block with some more transactions for us to spend block(5, spend=out[20]) spend_tx11 = self.create_transaction(out[21].tx, out[21].n, CScript(), 100000, CScript([OP_TRUE])) spend_tx12 = self.create_transaction(out[22].tx, out[22].n, CScript(), 100000, CScript([OP_TRUE])) self.chain.update_block(5, [spend_tx11, spend_tx12]) yield self.accepted() # Send txn time-locked for the current block height so that it can be immediately mined self.nodes[0].generate(1) currentHeight = self.nodes[0].getblockcount() tx7 = self.create_locked_transaction(spend_tx11, 0, CScript(), 1000, CScript([OP_TRUE]), currentHeight, 0x01) yield TestInstance([[tx7, True]]) # Mine it & spend it self.nodes[0].generate(1) tx7_spend = self.create_transaction(tx7, 0, CScript(), 100, CScript([OP_TRUE])) yield TestInstance([[tx7_spend, True]]) mempool = self.nodes[0].getrawmempool() nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert (tx7.hash not in nonfinalmempool) assert (tx7.hash not in mempool) assert (tx7_spend.hash not in nonfinalmempool) assert (tx7_spend.hash in mempool) # Reorg back so tx7 is no longer final self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) # Both txns will have been removed from the mempool mempool = self.nodes[0].getrawmempool() nonfinalmempool = self.nodes[0].getrawnonfinalmempool() assert (tx7.hash not in nonfinalmempool) assert (tx7.hash not in mempool) assert (tx7_spend.hash not in nonfinalmempool) assert (tx7_spend.hash not in mempool) # Send non-final txn currentHeight = self.nodes[0].getblockcount() tx7 = self.create_locked_transaction(spend_tx11, 0, CScript(), 1000, CScript([OP_TRUE]), currentHeight + 1, 0x01) yield TestInstance([[tx7, DiscardResult()]]) nonfinalmempool = self.nodes[0].getrawnonfinalmempool() mempool = self.nodes[0].getrawmempool() assert (tx7.hash in nonfinalmempool) # Create a block that contains a txn that conflicts with the previous non-final txn tx7_double_spend = self.create_transaction(spend_tx11, 0, CScript(), 1000, CScript([OP_TRUE])) block(6, spend=out[30]) self.chain.update_block(6, [tx7_double_spend]) yield self.accepted() nonfinalmempool = self.nodes[0].getrawnonfinalmempool() mempool = self.nodes[0].getrawmempool() assert (tx7.hash not in nonfinalmempool) assert (tx7.hash not in mempool) # Reorg back and check that the transaction from the block is the txn picked to be kept in the mempool mempool = self.nodes[0].getrawmempool() assert (tx7_double_spend.hash not in mempool) assert (tx7.hash not in mempool) self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) mempool = self.nodes[0].getrawmempool() assert (tx7_double_spend.hash in mempool) assert (tx7.hash not in mempool) # Check that post-genesis if we receive a block containing a txn that pre-genesis would have been BIP68 # non-final but now is final, we accept that block ok. self.nodes[0].generate(1) self.build_on_tip(11) currentHeight = self.nodes[0].getblockcount() block(12, spend=out[31]) bip68_non_final_tx = self.create_locked_transaction( spend_tx12, 0, CScript(), 1, CScript([OP_TRUE]), 0, self.make_sequence(currentHeight + 10)) self.chain.update_block(12, [bip68_non_final_tx]) yield self.accepted() # Check that post-genesis we will not accept a block containing a non-final transaction currentHeight = self.nodes[0].getblockcount() block(13, spend=out[32]) non_final_tx = self.create_locked_transaction(out[32].tx, out[32].n, CScript(), 1, CScript([OP_TRUE]), currentHeight + 1, 0x01) self.chain.update_block(13, [non_final_tx]) yield self.rejected(RejectResult(16, b'bad-txns-nonfinal'))
def get_tests(self): # shorthand for functions block = self.chain.next_block node0 = self.nodes[0] node1 = self.nodes[1] node2 = self.nodes[2] self.chain.set_genesis_hash(int(node1.getbestblockhash(), 16)) # Now we need that block to mature so we can spend the coinbase. test = TestInstance(sync_every_block=False) for i in range(0,100): block(i, coinbase_pubkey=self.coinbase_pubkey) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test # create two addresses on the node0 address1 = node0.getnewaddress() scriptPubKey1 = node0.validateaddress(address1)["scriptPubKey"] address2 = node0.getnewaddress() scriptPubKey2 = node0.validateaddress(address2)["scriptPubKey"] # import P2SH(P2PKH) on node1 and node2 # have to do in this way because it seems that we can't create P2PKH address and later add P2SH(P2PKH) to the same private key node1.importaddress(scriptPubKey1, "x", True, True) # importing script, not key node1.importprivkey(node0.dumpprivkey(address1)) node2.importaddress(scriptPubKey2, "x", True, True) # importing script, not key node2.importprivkey(node0.dumpprivkey(address2)) out = [self.chain.get_spendable_output() for _ in range(50)] # Create a p2sh transactions def new_P2SH_tx(scriptPubKey): output = out.pop(0) redeem_script = CScript(hex_str_to_bytes(scriptPubKey)) redeem_script_hash = hash160(redeem_script) p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) return create_and_sign_transaction(spend_tx=output.tx, n=output.n, value=output.tx.vout[0].nValue-100, coinbase_key=self.coinbase_key, script=p2sh_script) # Add the transactions to the block assert node0.getblockcount() < self.genesisactivationheight, "We must be before genesis" block(100) new_tx1 = new_P2SH_tx(scriptPubKey1) self.chain.update_block(100, [new_tx1]) # sending funds to P2SH address BEFORE genesis yield self.accepted() current_height = node1.getblockcount() for i in range(self.genesisactivationheight - current_height): block(101+i, coinbase_pubkey=self.coinbase_pubkey) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() yield test assert node0.getblockcount() >= self.genesisactivationheight, "We must be after genesis" block(150) new_tx2 = new_P2SH_tx(scriptPubKey2) self.chain.update_block(150, [new_tx2]) # sending funds to P2SH address AFTER genesis yield self.rejected(RejectResult(16, b'bad-txns-vout-p2sh')) self.chain.set_tip(149) balance1 = node1.getbalance("*", 1, False) assert balance1 * COIN == new_tx1.vout[0].nValue, "Wallet has registered pre genesis transaction." balance2 = node2.getbalance("*", 1, False) assert balance2 * COIN == 0, "No funds in wallet as transaction is not accepted." # Pre genesis P2SH transaction can be spent through wallet node1.sendtoaddress(node0.getnewaddress(), balance1 - 1) balance1_new = node1.getbalance("*", 1, False) assert balance1 > balance1_new, "Pre genesis P2SH is spent."
def get_tests(self): # move the tip back to a previous block def tip(number): self.chain.set_tip(number) # shorthand for functions block = self.chain.next_block node = self.nodes[0] gen_hash = int(node.getbestblockhash(), 16) self.chain.set_genesis_hash(gen_hash) spendable_outputs = [] # Create a new block block(0) # Test 1 self.chain.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(119): block(5000 + i) test.blocks_and_transactions.append([self.chain.tip, True]) self.chain.save_spendable_output() # Test 2 yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(119): out.append(self.chain.get_spendable_output()) logger.info( "Before Genesis, blocks 2, 4, 6 should be rejected because of too many sigops" ) # Test 3 # Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop # Test that a block with a lot of checksigs is okay #tip(0) - height = 120 lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) b1 = block(1, spend=out[0], script=lots_of_checksigs, block_size=ONE_MEGABYTE) self.chain.save_spendable_output() yield self.accepted() # Test 4 # Test that a block with too many checksigs is rejected b2 = block(2, spend=out[1], script=lots_of_checksigs, block_size=ONE_MEGABYTE, extra_sigops=1) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # Test 5 # Test that a block with a lot of checkmultisigs is ok tip(1) lots_of_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS_PER_MB // 20)) b3 = block(3, spend=out[3], script=lots_of_multisigs, block_size=ONE_MEGABYTE) assert_equal(get_legacy_sigopcount_block(b3), MAX_BLOCK_SIGOPS_PER_MB) yield self.accepted() # Test 6 # this goes over the limit because the coinbase has one sigop b4 = block(4, spend=out[4], script=lots_of_multisigs, block_size=ONE_MEGABYTE, extra_sigops=1) assert_equal(get_legacy_sigopcount_block(b4), MAX_BLOCK_SIGOPS_PER_MB + 1) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # CHECKSIGVERIFY tip(3) # Test 7 lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS_PER_MB)) b5 = block(5, spend=out[5], script=lots_of_checksigs, block_size=ONE_MEGABYTE) assert_equal(get_legacy_sigopcount_block(b5), MAX_BLOCK_SIGOPS_PER_MB) self.chain.save_spendable_output() yield self.accepted() # Test 8 b6 = block(6, spend=out[6], script=lots_of_checksigs, block_size=ONE_MEGABYTE, extra_sigops=1) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) tip(5) b7 = block(7) yield self.accepted() logger.info( "Genesis is enabled, all the following blocks should be accepted") # Test 10 # Add a block with MAX_BLOCK_SIGOPS_PER_MB and one with one more sigop # Test that a block with a lot of checksigs is okay lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) b9 = block(9, spend=out[9], script=lots_of_checksigs, block_size=ONE_MEGABYTE) self.chain.save_spendable_output() yield self.accepted() # Test 11 # Test that a block with 'too many' checksigs is now accepted b10 = block(10, spend=out[10], script=lots_of_checksigs, block_size=ONE_MEGABYTE, extra_sigops=1) yield self.accepted() self.chain.save_spendable_output() # Test 12 # this goes over the limit because the coinbase has one sigop, but should pass bcs genesis threshold was reached b11 = block(11, spend=out[11], script=lots_of_multisigs, block_size=ONE_MEGABYTE, extra_sigops=1) yield self.accepted() # CHECKSIGVERIFY # Test 13 lots_of_checksigs = CScript([OP_CHECKSIGVERIFY] * MAX_BLOCK_SIGOPS_PER_MB) b12 = block(12, spend=out[12], script=lots_of_checksigs, block_size=ONE_MEGABYTE) self.chain.save_spendable_output() yield self.accepted() # Test 14 b13 = block(13, spend=out[13], script=lots_of_checksigs, block_size=ONE_MEGABYTE, extra_sigops=1) self.chain.save_spendable_output() yield self.accepted() # Test 15 # Test that a block with MAX_BLOCK_SIGOPS_PER_MB_POST_GENESIS checksigs is OK MAX_BLOCK_SIGOPS_PER_MB_POST_GENESIS = 1000000 lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) b14 = block(14, spend=out[14], script=lots_of_checksigs, block_size=ONE_MEGABYTE + 500, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB_POST_GENESIS - MAX_BLOCK_SIGOPS_PER_MB) yield self.accepted()
def get_tests(self): node = self.nodes[0] self.chain.set_genesis_hash(int(node.getbestblockhash(), 16)) # shorthand for functions block = self.chain.next_block block(0) yield self.accepted() test, out, _ = prepare_init_chain(self.chain, 99, 100) yield test # Let's build some blocks and test them. for i in range(16): n = i + 1 block(n, spend=out[i], block_size=n * ONE_MEGABYTE // 2) yield self.accepted() # block of maximal size block(17, spend=out[16], block_size=self.excessive_block_size) yield self.accepted() # Oversized blocks will cause us to be disconnected assert (not self.test.test_nodes[0].closed) block(18, spend=out[17], block_size=self.excessive_block_size + 1) self.test.connections[0].send_message(msg_block((self.chain.tip))) self.test.wait_for_disconnections() assert (self.test.test_nodes[0].closed) # Rewind bad block and remake connection to node self.chain.set_tip(17) self.restart_network() self.test.wait_for_verack() # Accept many sigops lots_of_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) block(19, spend=out[17], script=lots_of_checksigs, block_size=ONE_MEGABYTE) yield self.accepted() block(20, spend=out[18], script=lots_of_checksigs, block_size=ONE_MEGABYTE, extra_sigops=1) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block self.chain.set_tip(19) # Accept 40k sigops per block > 1MB and <= 2MB block(21, spend=out[18], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=ONE_MEGABYTE + 1) yield self.accepted() # Accept 40k sigops per block > 1MB and <= 2MB block(22, spend=out[19], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE) yield self.accepted() # Reject more than 40k sigops per block > 1MB and <= 2MB. block(23, spend=out[20], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=ONE_MEGABYTE + 1) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block self.chain.set_tip(22) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(24, spend=out[20], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block self.chain.set_tip(22) # Accept 60k sigops per block > 2MB and <= 3MB block(25, spend=out[20], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE + 1) yield self.accepted() # Accept 60k sigops per block > 2MB and <= 3MB block(26, spend=out[21], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=3 * ONE_MEGABYTE) yield self.accepted() # Reject more than 40k sigops per block > 1MB and <= 2MB. block(27, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE + 1) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block self.chain.set_tip(26) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(28, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=3 * ONE_MEGABYTE) yield self.rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block self.chain.set_tip(26) # Too many sigops in one txn too_many_tx_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB + 1)) block(29, spend=out[22], script=too_many_tx_checksigs, block_size=ONE_MEGABYTE + 1) yield self.rejected(RejectResult(16, b'bad-txn-sigops')) # Rewind bad block self.chain.set_tip(26) # Generate a key pair to test P2SH sigops count private_key = CECKey() private_key.set_secretbytes(b"fatstacks") public_key = private_key.get_pubkey() # P2SH # Build the redeem script, hash it, use hash to create the p2sh script redeem_script = CScript([public_key] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG]) redeem_script_hash = hash160(redeem_script) p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) # Create a p2sh transaction p2sh_tx = self.chain.create_tx_with_script(out[22], 1, p2sh_script) # Add the transaction to the block block(30) self.chain.update_block(30, [p2sh_tx]) yield self.accepted() # Creates a new transaction using the p2sh transaction included in the # last block def spend_p2sh_tx(output_script=CScript([OP_TRUE])): # Create the transaction spent_p2sh_tx = CTransaction() spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b'')) spent_p2sh_tx.vout.append(CTxOut(1, output_script)) # Sign the transaction using the redeem script sighash = SignatureHashForkId(redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx.vout[0].nValue) sig = private_key.sign(sighash) + \ bytes(bytearray([SIGHASH_ALL | SIGHASH_FORKID])) spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) spent_p2sh_tx.rehash() return spent_p2sh_tx # Sigops p2sh limit p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \ redeem_script.GetSigOpCount(True) # Too many sigops in one p2sh txn too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1)) block(31, spend=out[23], block_size=ONE_MEGABYTE + 1) self.chain.update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)]) yield self.rejected(RejectResult(16, b'bad-txn-sigops')) # Rewind bad block self.chain.set_tip(30) # Max sigops in one p2sh txn max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit)) block(32, spend=out[23], block_size=ONE_MEGABYTE + 1) self.chain.update_block(32, [spend_p2sh_tx(max_p2sh_sigops)]) yield self.accepted() # Submit a very large block via RPC large_block = block(33, spend=out[24], block_size=self.excessive_block_size) node.submitblock(ToHex(large_block))
def get_tests(self): # 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 # Test no excessiveblocksize parameter specified node = self.nodes[0] self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # shorthand for functions block = self.next_block # Create a new block block(0) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Let's build some blocks and test them. for i in range(15): n = i + 1 block(n, spend=out[i], block_size=n * ONE_MEGABYTE) yield accepted() # Start moving MTP forward bfork = block(5555, out[15], block_size=32 * ONE_MEGABYTE) bfork.nTime = MAGNETIC_START_TIME - 1 update_block(5555, []) yield accepted() # Get to one block of the Nov 15, 2018 HF activation for i in range(5): block(5100 + i) test.blocks_and_transactions.append([self.tip, True]) yield test # Check that the MTP is just before the configured fork point. assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MAGNETIC_START_TIME - 1) # Before we acivate the Nov 15, 2018 HF, 32MB is the limit. block(4444, spend=out[16], block_size=32 * ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-blk-length')) # Rewind bad block. tip(5104) # Activate the Nov 15, 2018 HF block(5556) yield accepted() # Now MTP is exactly the fork time. Bigger blocks are now accepted. assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MAGNETIC_START_TIME) # block of maximal size block(17, spend=out[16], block_size=128 * ONE_MEGABYTE) yield accepted() # Reject oversized blocks with bad-blk-length error block(18, spend=out[17], block_size=128 * ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-blk-length'))
def get_tests(self): node = self.nodes[0] self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # 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 a new block block(0) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Let's build some blocks and test them. for i in range(17): n = i + 1 block(n) yield accepted() block(5556) yield accepted() # Block with regular ordering are now rejected. block(5557, out[17], tx_count=16) yield rejected(RejectResult(16, b'tx-ordering')) # Rewind bad block. tip(5556) # After we activate the Nov 15, 2018 HF, transaction order is enforced. def ordered_block(block_number, spend): b = block(block_number, spend=spend, tx_count=16) make_conform_to_ctor(b) update_block(block_number) return b # Now that the fork activated, we need to order transaction per txid. ordered_block(4445, out[17]) yield accepted() ordered_block(4446, out[18]) yield accepted() # Generate a block with a duplicated transaction. double_tx_block = ordered_block(4447, out[19]) assert_equal(len(double_tx_block.vtx), 16) double_tx_block.vtx = double_tx_block.vtx[:8] + \ [double_tx_block.vtx[8]] + double_tx_block.vtx[8:] update_block(4447) yield rejected(RejectResult(16, b'bad-txns-duplicate')) # Rewind bad block. tip(4446) # Check over two blocks. proper_block = ordered_block(4448, out[20]) yield accepted() replay_tx_block = ordered_block(4449, out[21]) assert_equal(len(replay_tx_block.vtx), 16) replay_tx_block.vtx.append(proper_block.vtx[5]) replay_tx_block.vtx = [replay_tx_block.vtx[0]] + \ sorted(replay_tx_block.vtx[1:], key=lambda tx: tx.get_id()) update_block(4449) yield rejected(RejectResult(16, b'bad-txns-BIP30'))
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 previous 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] # add transactions to a block produced by next_block def update_block(block_number, new_transactions): block = self.blocks[block_number] old_hash = block.sha256 self.add_transactions_to_block(block, new_transactions) block.solve() # Update the internal state just like in next_block self.tip = block self.block_heights[block.sha256] = self.block_heights[old_hash] del self.block_heights[old_hash] self.blocks[block_number] = block return block # creates a new block and advances the tip to that block block = self.next_block # Create a new block block(0) save_spendable_output() yield 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(1000 + i) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # Start by building a couple of blocks on top (which output is spent is # in parentheses): # genesis -> b1 (0) -> b2 (1) out0 = get_spendable_output() block(1, spend=out0) save_spendable_output() yield accepted() out1 = get_spendable_output() b2 = block(2, spend=out1) yield accepted() # so fork like this: # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) # # Nothing should happen at this point. We saw b2 first so it takes priority. tip(1) b3 = block(3, spend=out1) txout_b3 = PreviousSpendableOutput(b3.vtx[1], 1) yield rejected() # Now we add another block to make the alternative chain longer. # # genesis -> b1 (0) -> b2 (1) # \-> b3 (1) -> b4 (2) out2 = get_spendable_output() block(4, spend=out2) yield 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=out2) save_spendable_output() yield rejected() out3 = get_spendable_output() block(6, spend=out3) yield 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=out2) yield rejected() out4 = get_spendable_output() block(8, spend=out4) yield 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=out4, additional_coinbase_value=1) yield 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=out3) yield rejected() block(11, spend=out4, 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) # (b12 added last) # \-> b3 (1) -> b4 (2) tip(5) b12 = block(12, spend=out3) save_spendable_output() #yield TestInstance([[b12, False]]) b13 = block(13, spend=out4) # 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() out5 = get_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=out5, additional_coinbase_value=1) yield rejected() 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) # \-> b12 (3) -> b13 (4) -> b15 (5) -> b16 (6) # \-> b3 (1) -> b4 (2) # Test that a block with a lot of checksigs is okay lots_of_checksigs = CScript([OP_CHECKSIG] * (1000000 // 50 - 1)) tip(13) block(15, spend=out5, script=lots_of_checksigs) yield accepted() # Test that a block with too many checksigs is rejected out6 = get_spendable_output() too_many_checksigs = CScript([OP_CHECKSIG] * (1000000 // 50)) block(16, spend=out6, 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]) # \-> b3 (1) -> b4 (2) tip(15) block(17, spend=txout_b3) yield 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() block(19, spend=out6) yield 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) out7 = get_spendable_output() block(20, spend=out7) yield 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=out6) yield rejected() block(22, spend=out5) yield 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) tip(15) b23 = block(23, spend=out6) old_hash = b23.sha256 tx = CTransaction() 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, 1))) 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) yield accepted() # Make the next block one byte bigger and check that it fails tip(15) b24 = block(24, spend=out6) script_length = MAX_BLOCK_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) yield rejected(RejectResult(16, b'bad-blk-length')) b25 = block(25, spend=out7) yield 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=out6) 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')) # Extend the b26 chain to make sure bitcoind isn't accepting b26 b27 = block(27, spend=out7) yield rejected() # Now try a too-large-coinbase script tip(15) b28 = block(28, spend=out6) 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')) # Extend the b28 chain to make sure bitcoind isn't accepted b28 b29 = block(29, spend=out7) # TODO: Should get a reject message back with "bad-prevblk", except # there's a bug that prevents this from being detected. Just note # failure for now, and add the reject result later. yield rejected() # b30 has a max-sized coinbase scriptSig. tip(23) b30 = block(30) b30.vtx[0].vin[0].scriptSig = b'\x00' * 100 b30.vtx[0].rehash() b30 = update_block(30, []) yield accepted()
def get_tests(self): node = self.nodes[0] self.genesis_hash = int(node.getbestblockhash(), 16) self.block_heights[self.genesis_hash] = 0 spendable_outputs = [] # save the current tip so it can be spent by a later block def save_spendable_output(): spendable_outputs.append(self.tip) # get an output that we previously marked as spendable def get_spendable_output(): return PreviousSpendableOutput(spendable_outputs.pop(0).vtx[0], 0) # 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 a new block block(0) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Let's build some blocks and test them. for i in range(15): n = i + 1 block(n) yield accepted() # Start moving MTP forward bfork = block(5555) bfork.nTime = MAGNETIC_ANOMALY_START_TIME - 1 update_block(5555) yield accepted() # Get to one block of the Nov 15, 2018 HF activation for i in range(5): block(5100 + i) test.blocks_and_transactions.append([self.tip, True]) yield test # Check that the MTP is just before the configured fork point. assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MAGNETIC_ANOMALY_START_TIME - 1) # Activate the Nov 15, 2018 HF block(5556, out[16], tx_count=16) yield accepted() # Now MTP is exactly the fork time. Transactions are expected to be ordered now. assert_equal( node.getblockheader(node.getbestblockhash())['mediantime'], MAGNETIC_ANOMALY_START_TIME) # Block with regular ordering are now rejected. block(5557, out[17], tx_count=16) yield rejected(RejectResult(16, b'tx-ordering')) # Rewind bad block. tip(5556) # After we activate the Nov 15, 2018 HF, transaction order is enforced. def ordered_block(block_number, spend): b = block(block_number, spend=spend, tx_count=16) b.vtx = [b.vtx[0]] + sorted(b.vtx[1:], key=lambda tx: tx.get_id()) update_block(block_number) return b # Now that the fork activated, we need to order transaction per txid. ordered_block(4445, out[17]) yield accepted() # Invalidate the best block and make sure we are back at the fork point. ctorblockhash = node.getbestblockhash() node.invalidateblock(ctorblockhash) forkblockhash = node.getbestblockhash() assert (forkblockhash != ctorblockhash) assert_equal( node.getblockheader(forkblockhash)['mediantime'], MAGNETIC_ANOMALY_START_TIME) assert_equal(len(node.getrawmempool()), 15) node.generate(1) generatedblockhash = node.getbestblockhash() assert (forkblockhash != generatedblockhash) # Reconstruct tip. tip_hash = node.getbestblockhash() self.tip = CBlock() self.tip.sha256 = int(tip_hash, 16) self.tip.nTime = timestamp = node.getblock(tip_hash)['time'] self.block_heights[self.tip.sha256] = node.getblock(tip_hash)['height'] ordered_block(4446, out[18]) yield accepted() # Generate a block with a duplicated transaction. double_tx_block = ordered_block(4447, out[19]) assert_equal(len(double_tx_block.vtx), 16) double_tx_block.vtx = double_tx_block.vtx[:8] + \ [double_tx_block.vtx[8]] + double_tx_block.vtx[8:] update_block(4447) yield rejected(RejectResult(16, b'bad-txns-duplicate')) # Rewind bad block. tip(4446) # Check over two blocks. proper_block = ordered_block(4448, out[20]) yield accepted() replay_tx_block = ordered_block(4449, out[21]) assert_equal(len(replay_tx_block.vtx), 16) replay_tx_block.vtx.append(proper_block.vtx[5]) replay_tx_block.vtx = [replay_tx_block.vtx[0]] + \ sorted(replay_tx_block.vtx[1:], key=lambda tx: tx.get_id()) update_block(4449) yield rejected(RejectResult(16, b'bad-txns-BIP30'))
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 a new block block(0) save_spendable_output() yield 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) test.blocks_and_transactions.append([self.tip, True]) save_spendable_output() yield test # collect spendable outputs now to avoid cluttering the code later on out = [] for i in range(100): out.append(get_spendable_output()) # Let's build some blocks and test them. for i in range(16): n = i + 1 block(n, spend=out[i], block_size=n * ONE_MEGABYTE) yield accepted() # block of maximal size block(17, spend=out[16], block_size=self.excessive_block_size) yield accepted() # Reject oversized blocks with bad-blk-length error block(18, spend=out[17], block_size=self.excessive_block_size + 1) yield rejected(RejectResult(16, b'bad-blk-length')) # Rewind bad block. tip(17) # Accept many sigops lots_of_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB - 1)) block(19, spend=out[17], script=lots_of_checksigs, block_size=ONE_MEGABYTE) yield accepted() too_many_blk_checksigs = CScript([OP_CHECKSIG] * MAX_BLOCK_SIGOPS_PER_MB) block(20, spend=out[18], script=too_many_blk_checksigs, block_size=ONE_MEGABYTE) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(19) # Accept 40k sigops per block > 1MB and <= 2MB block(21, spend=out[18], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=ONE_MEGABYTE + 1) yield accepted() # Accept 40k sigops per block > 1MB and <= 2MB block(22, spend=out[19], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE) yield accepted() # Reject more than 40k sigops per block > 1MB and <= 2MB. block(23, spend=out[20], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(22) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(24, spend=out[20], script=lots_of_checksigs, extra_sigops=MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(22) # Accept 60k sigops per block > 2MB and <= 3MB block(25, spend=out[20], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=2 * ONE_MEGABYTE + 1) yield accepted() # Accept 60k sigops per block > 2MB and <= 3MB block(26, spend=out[21], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB, block_size=3 * ONE_MEGABYTE) yield accepted() # Reject more than 40k sigops per block > 1MB and <= 2MB. block(27, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=2 * ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(26) # Reject more than 40k sigops per block > 1MB and <= 2MB. block(28, spend=out[22], script=lots_of_checksigs, extra_sigops=2 * MAX_BLOCK_SIGOPS_PER_MB + 1, block_size=3 * ONE_MEGABYTE) yield rejected(RejectResult(16, b'bad-blk-sigops')) # Rewind bad block tip(26) # Too many sigops in one txn too_many_tx_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS_PER_MB + 1)) block(29, spend=out[22], script=too_many_tx_checksigs, block_size=ONE_MEGABYTE + 1) yield rejected(RejectResult(16, b'bad-txn-sigops')) # Rewind bad block tip(26) # P2SH # Build the redeem script, hash it, use hash to create the p2sh script redeem_script = CScript([self.coinbase_pubkey] + [OP_2DUP, OP_CHECKSIGVERIFY] * 5 + [OP_CHECKSIG]) redeem_script_hash = hash160(redeem_script) p2sh_script = CScript([OP_HASH160, redeem_script_hash, OP_EQUAL]) # Create a p2sh transaction p2sh_tx = self.create_and_sign_transaction(out[22].tx, out[22].n, 1, p2sh_script) # Add the transaction to the block block(30) update_block(30, [p2sh_tx]) yield accepted() # Creates a new transaction using the p2sh transaction included in the # last block def spend_p2sh_tx(output_script=CScript([OP_TRUE])): # Create the transaction spent_p2sh_tx = CTransaction() spent_p2sh_tx.vin.append(CTxIn(COutPoint(p2sh_tx.sha256, 0), b'')) spent_p2sh_tx.vout.append(CTxOut(1, output_script)) # Sign the transaction using the redeem script sighash = SignatureHashForkId(redeem_script, spent_p2sh_tx, 0, SIGHASH_ALL | SIGHASH_FORKID, p2sh_tx.vout[0].nValue) sig = self.coinbase_key.sign(sighash) + bytes( bytearray([SIGHASH_ALL | SIGHASH_FORKID])) spent_p2sh_tx.vin[0].scriptSig = CScript([sig, redeem_script]) spent_p2sh_tx.rehash() return spent_p2sh_tx # Sigops p2sh limit p2sh_sigops_limit = MAX_BLOCK_SIGOPS_PER_MB - \ redeem_script.GetSigOpCount(True) # Too many sigops in one p2sh txn too_many_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit + 1)) block(31, spend=out[23], block_size=ONE_MEGABYTE + 1) update_block(31, [spend_p2sh_tx(too_many_p2sh_sigops)]) yield rejected(RejectResult(16, b'bad-txn-sigops')) # Rewind bad block tip(30) # Max sigops in one p2sh txn max_p2sh_sigops = CScript([OP_CHECKSIG] * (p2sh_sigops_limit)) block(32, spend=out[23], block_size=ONE_MEGABYTE + 1) update_block(32, [spend_p2sh_tx(max_p2sh_sigops)]) yield accepted() # Check that compact block also work for big blocks node = self.nodes[0] peer = TestNode() peer.add_connection(NodeConn('127.0.0.1', p2p_port(0), node, peer)) # Start up network handling in another thread and wait for connection # to be etablished NetworkThread().start() peer.wait_for_verack() # Wait for SENDCMPCT def received_sendcmpct(): return (peer.last_sendcmpct != None) got_sendcmpt = wait_until(received_sendcmpct, timeout=30) assert (got_sendcmpt) sendcmpct = msg_sendcmpct() sendcmpct.version = 1 sendcmpct.announce = True peer.send_and_ping(sendcmpct) # Exchange headers def received_getheaders(): return (peer.last_getheaders != None) got_getheaders = wait_until(received_getheaders, timeout=30) assert (got_getheaders) # Return the favor peer.send_message(peer.last_getheaders) # Wait for the header list def received_headers(): return (peer.last_headers != None) got_headers = wait_until(received_headers, timeout=30) assert (got_headers) # It's like we know about the same headers ! peer.send_message(peer.last_headers) # Send a block b33 = block(33, spend=out[24], block_size=ONE_MEGABYTE + 1) yield accepted() # Checks the node to forward it via compact block def received_block(): return (peer.last_cmpctblock != None) got_cmpctblock = wait_until(received_block, timeout=30) assert (got_cmpctblock) # Was it our block ? cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header cmpctblk_header.calc_sha256() assert (cmpctblk_header.sha256 == b33.sha256) # Send a bigger block peer.clear_block_data() b34 = block(34, spend=out[25], block_size=8 * ONE_MEGABYTE) yield accepted() # Checks the node to forward it via compact block got_cmpctblock = wait_until(received_block, timeout=30) assert (got_cmpctblock) # Was it our block ? cmpctblk_header = peer.last_cmpctblock.header_and_shortids.header cmpctblk_header.calc_sha256() assert (cmpctblk_header.sha256 == b34.sha256) # Let's send a compact block and see if the node accepts it. # First, we generate the block and send all transaction to the mempool b35 = block(35, spend=out[26], block_size=8 * ONE_MEGABYTE) for i in range(1, len(b35.vtx)): node.sendrawtransaction(ToHex(b35.vtx[i]), True) # Now we create the compact block and send it comp_block = HeaderAndShortIDs() comp_block.initialize_from_block(b35) peer.send_and_ping(msg_cmpctblock(comp_block.to_p2p())) # Check that compact block is received properly assert (int(node.getbestblockhash(), 16) == b35.sha256)
def get_tests(self): node = self.nodes[0] # First, we generate some coins to spend. node.generate(125) # Create various outputs using the OP_CHECKDATASIG # to check for activation. tx_hex = self.create_checkdatasig_tx(25) txid = node.sendrawtransaction(tx_hex) assert (txid in set(node.getrawmempool())) node.generate(1) assert (txid not in set(node.getrawmempool())) # register the spendable outputs. tx = FromHex(CTransaction(), tx_hex) tx.rehash() spendable_checkdatasigs = [ PreviousSpendableOutput(tx, i) for i in range(len(tx.vout)) ] def spend_checkdatasig(): outpoint = spendable_checkdatasigs.pop() out = outpoint.tx.vout[outpoint.n] tx = CTransaction() tx.vin = [CTxIn(COutPoint(outpoint.tx.sha256, outpoint.n))] tx.vout = [ CTxOut(out.nValue, CScript([])), CTxOut(0, CScript([random.getrandbits(800), OP_RETURN])) ] tx.vout[0].nValue -= get_relay_fee(node, unit="sat") tx.rehash() return tx # Check that transactions using checkdatasig are not accepted yet. self.log.info("Try to use the checkdatasig opcodes before activation") tx0 = spend_checkdatasig() tx0_hex = ToHex(tx0) assert_raises_rpc_error(-26, RPC_BAD_OPCODE_ERROR, node.sendrawtransaction, tx0_hex) assert_equal( get_bip135_status(node, 'bip135test0')['status'], 'started') # CDSV regtest start happened at height 99, activation should be at height 299 self.log.info( "Advance to height 298, just before activation, and check again") node.generate(172) # returns a test case that asserts that the current tip was accepted def accepted(tip): return TestInstance([[tip, True]]) # returns a test case that asserts that the current tip was rejected def rejected(tip, reject=None): if reject is None: return TestInstance([[tip, False]]) else: return TestInstance([[tip, reject]]) def next_block(): # get block height tiphash = node.getbestblockhash() tipheader = node.getblockheader(tiphash) height = int(tipheader['height']) # create the block coinbase = create_coinbase(absoluteHeight=height + 1) coinbase.rehash() version = 0x20000001 # Signal for CDSV on bit 0 block = create_block(int(tiphash, 16), coinbase, int(tipheader['time']) + 600, nVersion=version) # Do PoW, which is cheap on regnet block.solve() return block assert_raises_rpc_error(-26, RPC_BAD_OPCODE_ERROR, node.sendrawtransaction, tx0_hex) def add_tx(block, tx): block.vtx.append(tx) block.hashMerkleRoot = block.calc_merkle_root() block.solve() b = next_block() add_tx(b, tx0) yield rejected(b, RejectResult(16, b'blk-bad-inputs')) #1 assert_equal( get_bip135_status(node, 'bip135test0')['status'], 'locked_in') self.log.info("Activates checkdatasig") fork_block = next_block() yield accepted(fork_block) #2 assert_equal( get_bip135_status(node, 'bip135test0')['status'], 'active') tx0id = node.sendrawtransaction(tx0_hex) assert (tx0id in set(node.getrawmempool())) # Transactions can also be included in blocks. nextblock = next_block() add_tx(nextblock, tx0) yield accepted(nextblock) #3 self.log.info("Cause a reorg that deactivate the checkdatasig opcodes") # Invalidate the checkdatasig block, ensure tx0 gets back to the mempool. assert (tx0id not in set(node.getrawmempool())) node.invalidateblock(format(nextblock.sha256, 'x')) assert (tx0id in set(node.getrawmempool())) node.invalidateblock(format(fork_block.sha256, 'x')) assert (tx0id not in set(node.getrawmempool()))