mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
4aa197dbdb
fa4632c41714dfaa699bacc6a947d72668a4deef test: Move boost/stdlib includes last (MarcoFalke) fa488f131fd4f5bab0d01376c5a5013306f1abcd scripted-diff: Bump copyright headers (MarcoFalke) fac5c373006a9e4bcbb56843bb85f1aca4d87599 scripted-diff: Sort test includes (MarcoFalke) Pull request description: When writing tests, often includes need to be added or removed. Currently the list of includes is not sorted, so developers that write tests and have `clang-format` installed will either have an unrelated change (sorting) included in their commit or they will have to manually undo the sort. This pull preempts both issues by just sorting all includes in one commit. Please be aware that this is **NOT** a change to policy to enforce clang-format or any other developer guideline or process. Developers are free to use whatever tool they want, see also #18651. Edit: Also includes a commit to bump the copyright headers, so that the touched files don't need to be touched again for that. ACKs for top commit: practicalswift: ACK fa4632c41714dfaa699bacc6a947d72668a4deef jonatack: ACK fa4632c41714dfaa, light review and sanity checks with gcc build and clang fuzz build Tree-SHA512: 130a8d073a379ba556b1e64104d37c46b671425c0aef0ed725fd60156a95e8dc83fb6f0b5330b2f8152cf5daaf3983b4aca5e75812598f2626c39fd12b88b180
188 lines
8.1 KiB
Python
Executable File
188 lines
8.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2015-2020 The Bitcoin Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
"""Test node responses to invalid transactions.
|
|
|
|
In this test we connect to one node over p2p, and test tx requests."""
|
|
|
|
from test_framework.blocktools import create_block, create_coinbase
|
|
from test_framework.messages import (
|
|
COIN,
|
|
COutPoint,
|
|
CTransaction,
|
|
CTxIn,
|
|
CTxOut,
|
|
)
|
|
from test_framework.mininode import P2PDataStore
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
from test_framework.util import (
|
|
assert_equal,
|
|
wait_until,
|
|
)
|
|
from data import invalid_txs
|
|
|
|
|
|
class InvalidTxRequestTest(BitcoinTestFramework):
|
|
|
|
def set_test_params(self):
|
|
self.num_nodes = 1
|
|
self.extra_args = [[
|
|
"-acceptnonstdtxn=1",
|
|
]]
|
|
self.setup_clean_chain = True
|
|
|
|
def bootstrap_p2p(self, *, num_connections=1):
|
|
"""Add a P2P connection to the node.
|
|
|
|
Helper to connect and wait for version handshake."""
|
|
for _ in range(num_connections):
|
|
self.nodes[0].add_p2p_connection(P2PDataStore())
|
|
|
|
def reconnect_p2p(self, **kwargs):
|
|
"""Tear down and bootstrap the P2P connection to the node.
|
|
|
|
The node gets disconnected several times in this test. This helper
|
|
method reconnects the p2p and restarts the network thread."""
|
|
self.nodes[0].disconnect_p2ps()
|
|
self.bootstrap_p2p(**kwargs)
|
|
|
|
def run_test(self):
|
|
node = self.nodes[0] # convenience reference to the node
|
|
|
|
self.bootstrap_p2p() # Add one p2p connection to the node
|
|
|
|
best_block = self.nodes[0].getbestblockhash()
|
|
tip = int(best_block, 16)
|
|
best_block_time = self.nodes[0].getblock(best_block)['time']
|
|
block_time = best_block_time + 1
|
|
|
|
self.log.info("Create a new block with an anyone-can-spend coinbase.")
|
|
height = 1
|
|
block = create_block(tip, create_coinbase(height), block_time)
|
|
block.solve()
|
|
# Save the coinbase for later
|
|
block1 = block
|
|
tip = block.sha256
|
|
|
|
# Create a second one to test orphan resolution via block receival
|
|
height += 1
|
|
block_time += 1
|
|
block = create_block(tip, create_coinbase(height), block_time)
|
|
block.solve()
|
|
# Save the coinbase for later
|
|
block2 = block
|
|
tip = block.sha256
|
|
node.p2p.send_blocks_and_test([block1, block2], node, success=True)
|
|
|
|
self.log.info("Mature the block.")
|
|
self.nodes[0].generatetoaddress(100, self.nodes[0].get_deterministic_priv_key().address)
|
|
|
|
# Iterate through a list of known invalid transaction types, ensuring each is
|
|
# rejected. Some are consensus invalid and some just violate policy.
|
|
for BadTxTemplate in invalid_txs.iter_all_templates():
|
|
self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__)
|
|
template = BadTxTemplate(spend_block=block1)
|
|
tx = template.get_tx()
|
|
node.p2p.send_txs_and_test(
|
|
[tx], node, success=False,
|
|
expect_disconnect=template.expect_disconnect,
|
|
reject_reason=template.reject_reason,
|
|
)
|
|
|
|
if template.expect_disconnect:
|
|
self.log.info("Reconnecting to peer")
|
|
self.reconnect_p2p()
|
|
|
|
# Make two p2p connections to provide the node with orphans
|
|
# * p2ps[0] will send valid orphan txs (one with low fee)
|
|
# * p2ps[1] will send an invalid orphan tx (and is later disconnected for that)
|
|
self.reconnect_p2p(num_connections=2)
|
|
|
|
self.log.info('Test orphan transaction handling ... ')
|
|
self.test_orphan_tx_handling(block1.vtx[0].sha256, False)
|
|
|
|
self.log.info('Test orphan transaction handling, resolve via block')
|
|
self.restart_node(0, ["-acceptnonstdtxn=1", '-persistmempool=0'])
|
|
self.reconnect_p2p(num_connections=2)
|
|
self.test_orphan_tx_handling(block2.vtx[0].sha256, True)
|
|
|
|
def test_orphan_tx_handling(self, base_tx, resolve_via_block):
|
|
node = self.nodes[0] # convenience reference to the node
|
|
|
|
# Create a root transaction that we withhold until all dependent transactions
|
|
# are sent out and in the orphan cache
|
|
SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'
|
|
tx_withhold = CTransaction()
|
|
tx_withhold.vin.append(CTxIn(outpoint=COutPoint(base_tx, 0)))
|
|
tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
|
|
tx_withhold.calc_sha256()
|
|
|
|
# Our first orphan tx with some outputs to create further orphan txs
|
|
tx_orphan_1 = CTransaction()
|
|
tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0)))
|
|
tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
|
|
tx_orphan_1.calc_sha256()
|
|
|
|
# A valid transaction with low fee
|
|
tx_orphan_2_no_fee = CTransaction()
|
|
tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0)))
|
|
tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
|
|
|
|
# A valid transaction with sufficient fee
|
|
tx_orphan_2_valid = CTransaction()
|
|
tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1)))
|
|
tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
|
|
tx_orphan_2_valid.calc_sha256()
|
|
|
|
# An invalid transaction with negative fee
|
|
tx_orphan_2_invalid = CTransaction()
|
|
tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2)))
|
|
tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
|
|
|
|
self.log.info('Send the orphans ... ')
|
|
# Send valid orphan txs from p2ps[0]
|
|
node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
|
|
# Send invalid tx from p2ps[1]
|
|
node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False)
|
|
|
|
assert_equal(0, node.getmempoolinfo()['size']) # Mempool should be empty
|
|
assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected
|
|
|
|
self.log.info('Send the withhold tx ... ')
|
|
if resolve_via_block:
|
|
# Test orphan handling/resolution by publishing the withhold TX via a mined block
|
|
prev_block = node.getblockheader(node.getbestblockhash())
|
|
block = create_block(int(prev_block['hash'], 16), create_coinbase(prev_block['height'] + 1), prev_block["time"] + 1)
|
|
block.vtx.append(tx_withhold)
|
|
block.hashMerkleRoot = block.calc_merkle_root()
|
|
block.solve()
|
|
node.p2p.send_blocks_and_test([block], node, success=True)
|
|
else:
|
|
with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]):
|
|
# Test orphan handling/resolution by publishing the withhold TX via the mempool
|
|
node.p2p.send_txs_and_test([tx_withhold], node, success=True)
|
|
|
|
# Transactions that should end up in the mempool
|
|
expected_mempool = {
|
|
t.hash
|
|
for t in [
|
|
tx_withhold, # The transaction that is the root for all orphans
|
|
tx_orphan_1, # The orphan transaction that splits the coins
|
|
tx_orphan_2_valid, # The valid transaction (with sufficient fee)
|
|
]
|
|
}
|
|
# Transactions that do not end up in the mempool
|
|
# tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
|
|
# tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)
|
|
if resolve_via_block:
|
|
# This TX has appeared in a block instead of being broadcasted via the mempool
|
|
expected_mempool.remove(tx_withhold.hash)
|
|
|
|
wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected
|
|
assert_equal(expected_mempool, set(node.getrawmempool()))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
InvalidTxRequestTest().main()
|