diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -134,7 +134,9 @@ tx = create_transaction(spend.tx, spend.n, b"", 1, script) self.sign_tx(tx, spend.tx, spend.n) self.add_transactions_to_block(block, [tx]) + make_conform_to_ctor(block) block.hashMerkleRoot = block.calc_merkle_root() + if solve: block.solve() self.tip = block @@ -176,6 +178,7 @@ block = self.blocks[block_number] self.add_transactions_to_block(block, 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 @@ -555,59 +558,58 @@ # # b41 does the same, less one, so it has the maximum sigops permitted. # + def generate_long_chain(start_tx, numTxs): + lastOutpoint = COutPoint(start_tx.sha256, 0) + lastAmount = start_tx.vout[0].nValue + new_txs = [] + for i in range(1, numTxs + 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 + pad_tx(tx) + tx.rehash() + new_txs.append(tx) + lastOutpoint = COutPoint(tx.sha256, 0) + lastAmount = tx.vout[0].nValue + return new_txs, lastOutpoint, lastAmount + 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 - pad_tx(tx) - tx.rehash() - new_txs.append(tx) - lastOutpoint = COutPoint(tx.sha256, 0) - lastAmount = tx.vout[0].nValue - + numTxs = (MAX_BLOCK_SIGOPS_PER_MB - sigops) // b39_sigops_per_output + assert_equal(numTxs <= b39_outputs, True) + new_txs, lastOutpoint, lastAmount = generate_long_chain( + b40.vtx[1], numTxs) b40_sigops_to_fill = MAX_BLOCK_SIGOPS_PER_MB - \ - (numTxes * b39_sigops_per_output + sigops) + 1 + (numTxs * 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))) - pad_tx(tx) - 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 = block(41, spend=out[12]) b41_sigops_to_fill = b40_sigops_to_fill - 1 + new_txs, lastOutpoint, lastAmount = generate_long_chain( + b41.vtx[1], numTxs) tx = CTransaction() tx.vin.append(CTxIn(lastOutpoint, b'')) tx.vout.append(CTxOut(1, CScript([OP_CHECKSIG] * b41_sigops_to_fill))) - pad_tx(tx) - tx.rehash() - update_block(41, [tx]) + update_block(41, new_txs + [tx]) yield accepted() # Fork off of b39 to create a constant base again @@ -787,8 +789,8 @@ b56 = copy.deepcopy(b57) self.blocks[56] = b56 assert_equal(len(b56.vtx), 3) - b56 = update_block(56, [tx1]) - assert_equal(b56.hash, b57.hash) + b56 = update_block(56, [b57.vtx[2]]) + assert_equal(b56.hashMerkleRoot, b57.hashMerkleRoot) yield rejected(RejectResult(16, b'bad-txns-duplicate')) # b57p2 - a good block with 6 tx'es, don't submit until end @@ -930,6 +932,7 @@ 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.tip, None]]) # comptool workaround: to make sure b64 is delivered, manually erase @@ -959,18 +962,6 @@ 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) @@ -1045,7 +1036,7 @@ 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 + b71.vtx.append(b72.vtx[-1]) # add duplicate last transaction self.block_heights[b71.sha256] = self.block_heights[ b69.sha256] + 1 # b71 builds off b69 self.blocks[71] = b71 @@ -1316,6 +1307,8 @@ block(chain1_tip + 1) yield rejected() block(chain1_tip + 2) + yield rejected() + block(chain1_tip + 3) yield accepted() chain1_tip += 2 diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -28,6 +28,14 @@ return block +def make_conform_to_ctor(block): + for tx in block.vtx: + pad_tx(tx) + tx.rehash() + block.vtx = [block.vtx[0]] + \ + sorted(block.vtx[1:], key=lambda tx: tx.get_id()) + + def serialize_script_num(value): r = bytearray(0) if value == 0: