diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3351,8 +3351,8 @@ if (fIsMagneticAnomalyEnabled) { if (prevTx && (tx.GetId() <= prevTx->GetId())) { if (tx.GetId() == prevTx->GetId()) { - return state.DoS(100, false, REJECT_INVALID, "tx-duplicate", - false, + return state.DoS(100, false, REJECT_INVALID, + "bad-txns-duplicate", false, strprintf("Duplicated transaction %s", tx.GetId().ToString())); } 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]) + anomalyate_block(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 + anomalyate_block(block) block.hashMerkleRoot = block.calc_merkle_root() block.solve() # Update the internal state just like in next_block @@ -555,60 +558,59 @@ # # b41 does the same, less one, so it has the maximum sigops permitted. # + def generate_long_chain(start_tx, numTxes): + lastOutpoint = COutPoint(start_tx.sha256, 0) + lastAmount = start_tx.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 + 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 - + new_txs, lastOutpoint, lastAmount = generate_long_chain( + b40.vtx[1], numTxes) 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))) - 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], numTxes) 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]) - yield accepted() + update_block(41, new_txs + [tx]) + yield accepted() # bad-txns-inputs-missingorspent # 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 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,17 @@ return block +def anomalyate_block(block): + """ + Make a block compatible with magnetic anomaly + """ + 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: