Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 0 additions & 38 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,6 @@ class PeerManagerImpl final : public PeerManager
bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state,
bool via_compact_block, const std::string& message = "");

/**
* Potentially disconnect and discourage a node based on the contents of a TxValidationState object
*
* @return Returns true if the peer was punished (probably disconnected)
*/
bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message = "");

/** Maybe disconnect a peer and discourage future connections from its address.
*
* @param[in] pnode The node to check.
Expand Down Expand Up @@ -1437,34 +1430,6 @@ bool PeerManagerImpl::MaybePunishNodeForBlock(NodeId nodeid, const BlockValidati
return false;
}

bool PeerManagerImpl::MaybePunishNodeForTx(NodeId nodeid, const TxValidationState& state, const std::string& message)
{
switch (state.GetResult()) {
case TxValidationResult::TX_RESULT_UNSET:
break;
// The node is providing invalid data:
case TxValidationResult::TX_CONSENSUS:
Misbehaving(nodeid, 100, message);
return true;
// Conflicting (but not necessarily invalid) data or different policy:
case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE:
case TxValidationResult::TX_INPUTS_NOT_STANDARD:
case TxValidationResult::TX_NOT_STANDARD:
case TxValidationResult::TX_MISSING_INPUTS:
case TxValidationResult::TX_PREMATURE_SPEND:
case TxValidationResult::TX_WITNESS_MUTATED:
case TxValidationResult::TX_WITNESS_STRIPPED:
case TxValidationResult::TX_CONFLICT:
case TxValidationResult::TX_MEMPOOL_POLICY:
case TxValidationResult::TX_NO_MEMPOOL:
break;
}
if (message != "") {
LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message);
}
return false;
}

bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
{
AssertLockHeld(cs_main);
Expand Down Expand Up @@ -2392,8 +2357,6 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
orphanHash.ToString(),
from_peer,
state.ToString());
// Maybe punish peer that gave us an invalid orphan tx
MaybePunishNodeForTx(from_peer, state);
}
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
Expand Down Expand Up @@ -3559,7 +3522,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(),
pfrom.GetId(),
state.ToString());
MaybePunishNodeForTx(pfrom.GetId(), state);
}
return;
}
Expand Down
25 changes: 10 additions & 15 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1793,21 +1793,15 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
CCheck* check = new CScriptCheck(txdata.m_spent_outputs[i], tx, i, flags, cacheSigStore, &txdata);
ScriptError serror = QueueCheck(pvChecks, check);
if (serror != SCRIPT_ERR_OK) {
// Tx failures never trigger disconnections/bans.
// This is so that network splits aren't triggered
// either due to non-consensus relay policies (such as
// non-standard DER encodings or non-null dummy
// arguments) or due to new consensus rules introduced in
// soft forks.
if (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) {
// Check whether the failure was caused by a
// non-mandatory script verification check, such as
// non-standard DER encodings or non-null dummy
// arguments; if so, ensure we return NOT_STANDARD
// instead of CONSENSUS to avoid downstream users
// splitting the network between upgraded and
// non-upgraded nodes by banning CONSENSUS-failing
// data providers.
CScriptCheck check2(txdata.m_spent_outputs[i], tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
if (check2()) {
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(serror)));
}
}
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("mempool-script-verify-flag-failed (%s)", ScriptErrorString(serror)));
} else {
// MANDATORY flag failures correspond to
// TxValidationResult::TX_CONSENSUS. Because CONSENSUS
// failures are the most serious case of validation
Expand All @@ -1817,7 +1811,8 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state,
// support, to avoid splitting the network (but this
// depends on the details of how net_processing handles
// such errors).
return state.Invalid(TxValidationResult::TX_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(serror)));
return state.Invalid(TxValidationResult::TX_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(serror)));
}
}
}

Expand Down
18 changes: 1 addition & 17 deletions test/functional/data/invalid_txs.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ class BadTxTemplate:
# Only specified if it differs from mempool acceptance error.
block_reject_reason = ""

# Do we expect to be disconnected after submitting this tx?
expect_disconnect = False

# Is this tx considered valid when included in a block, but not for acceptance into
# the mempool (i.e. does it violate policy but not consensus)?
valid_in_block = False
Expand All @@ -89,7 +86,6 @@ def get_tx(self, *args, **kwargs):

class OutputMissing(BadTxTemplate):
reject_reason = "bad-txns-vout-empty"
expect_disconnect = True

def get_tx(self):
tx = CTransaction()
Expand All @@ -100,7 +96,6 @@ def get_tx(self):

class InputMissing(BadTxTemplate):
reject_reason = "bad-txns-vin-empty"
expect_disconnect = True

# We use a blank transaction here to make sure
# it is interpreted as a non-witness transaction.
Expand Down Expand Up @@ -134,7 +129,6 @@ class BadInputOutpointIndex(BadTxTemplate):
# Won't be rejected - nonexistent outpoint index is treated as an orphan since the coins
# database can't distinguish between spent outpoints and outpoints which never existed.
reject_reason = None
expect_disconnect = False

def get_tx(self):
num_indices = len(self.spend_tx.vin)
Expand All @@ -149,7 +143,6 @@ def get_tx(self):

class DuplicateInput(BadTxTemplate):
reject_reason = 'bad-txns-inputs-duplicate'
expect_disconnect = True

def get_tx(self):
tx = CTransaction()
Expand All @@ -162,7 +155,6 @@ def get_tx(self):

class PrevoutNullInput(BadTxTemplate):
reject_reason = 'bad-txns-prevout-null'
expect_disconnect = True

def get_tx(self):
tx = CTransaction()
Expand All @@ -175,7 +167,6 @@ def get_tx(self):

class NonexistentInput(BadTxTemplate):
reject_reason = None # Added as an orphan tx.
expect_disconnect = False

def get_tx(self):
tx = CTransaction()
Expand All @@ -189,7 +180,6 @@ def get_tx(self):
class SpendTooMuch(BadTxTemplate):
reject_reason = 'bad-txns-in-ne-out'
block_reject_reason = 'block-validation-failed'
expect_disconnect = True

def get_tx(self):
return create_tx_with_script(
Expand All @@ -198,23 +188,20 @@ def get_tx(self):

class CreateNegative(BadTxTemplate):
reject_reason = 'bad-txns-vout-negative'
expect_disconnect = True

def get_tx(self):
return create_tx_with_script(self.spend_tx, 0, amount=-1)


class CreateTooLarge(BadTxTemplate):
reject_reason = 'bad-txns-vout-toolarge'
expect_disconnect = True

def get_tx(self):
return create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY + 1)


class CreateSumTooLarge(BadTxTemplate):
reject_reason = 'bad-txns-txouttotal-toolarge'
expect_disconnect = True

def get_tx(self):
tx = create_tx_with_script(self.spend_tx, 0, amount=MAX_MONEY)
Expand All @@ -224,8 +211,7 @@ def get_tx(self):


class InvalidOPIFConstruction(BadTxTemplate):
reject_reason = "mandatory-script-verify-flag-failed (Invalid OP_IF construction)"
expect_disconnect = True
reject_reason = "mempool-script-verify-flag-failed (Invalid OP_IF construction)"
valid_in_block = True

def get_tx(self):
Expand All @@ -237,7 +223,6 @@ def get_tx(self):
class TooManySigops(BadTxTemplate):
reject_reason = "bad-txns-too-many-sigops"
block_reject_reason = "bad-blk-sigops, out-of-bounds SigOpCount"
expect_disconnect = False

def get_tx(self):
lotsa_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS))
Expand All @@ -260,7 +245,6 @@ def get_tx(self):

return type('DisabledOpcode_' + str(opcode), (BadTxTemplate,), {
'reject_reason': "disabled opcode",
'expect_disconnect': True,
'get_tx': get_tx,
'valid_in_block' : True
})
Expand Down
5 changes: 4 additions & 1 deletion test/functional/feature_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,12 @@ def run_test(self):
self.sign_tx(badtx, attempt_spend_tx)
badtx.rehash()
badblock = self.update_block(blockname, [badtx])
reject_reason = (template.block_reject_reason or template.reject_reason)
if reject_reason == "mempool-script-verify-flag-failed":
reject_reason = "mandatory-script-verify-flag-failed" + reject_reason[33:]
self.send_blocks(
[badblock], success=False,
reject_reason=(template.block_reject_reason or template.reject_reason),
reject_reason=reject_reason,
reconnect=True, timeout=2)

self.move_tip(2)
Expand Down
15 changes: 8 additions & 7 deletions test/functional/feature_cltv.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,13 @@ def run_test(self):
spendtx = wallet.create_self_transfer()['tx']
cltv_invalidate(spendtx, i)

tx_rej = "mempool-script-verify-flag-failed"
expected_cltv_reject_reason = [
"non-mandatory-script-verify-flag (Operation not valid with the current stack size)",
"non-mandatory-script-verify-flag (Negative locktime)",
"non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
"non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
"non-mandatory-script-verify-flag (Locktime requirement not satisfied)",
" (Operation not valid with the current stack size)",
" (Negative locktime)",
" (Locktime requirement not satisfied)",
" (Locktime requirement not satisfied)",
" (Locktime requirement not satisfied)",
][i]
# First we show that this tx is valid except for CLTV by getting it
# rejected from the mempool for exactly that reason.
Expand All @@ -164,7 +165,7 @@ def run_test(self):
'txid': spendtx.hash,
'wtxid': spendtx.getwtxid(),
'allowed': False,
'reject-reason': expected_cltv_reject_reason,
'reject-reason': tx_rej + expected_cltv_reject_reason,
}],
self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0),
)
Expand All @@ -174,7 +175,7 @@ def run_test(self):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()

with self.nodes[0].assert_debug_log(expected_msgs=[f'CheckInputScripts on {block.vtx[-1].hash} failed with {expected_cltv_reject_reason}']):
with self.nodes[0].assert_debug_log(expected_msgs=[f'CheckInputScripts on {block.vtx[-1].hash} failed with {tx_rej + expected_cltv_reject_reason}']):
peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
peer.sync_with_ping()
Expand Down
4 changes: 2 additions & 2 deletions test/functional/feature_dersig.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def run_test(self):
'txid': spendtx.hash,
'wtxid': spendtx.getwtxid(),
'allowed': False,
'reject-reason': 'non-mandatory-script-verify-flag (Non-canonical DER signature)',
'reject-reason': 'mempool-script-verify-flag-failed (Non-canonical DER signature)',
}],
self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0),
)
Expand All @@ -131,7 +131,7 @@ def run_test(self):
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()

with self.nodes[0].assert_debug_log(expected_msgs=[f'CheckInputScripts on {block.vtx[-1].hash} failed with non-mandatory-script-verify-flag (Non-canonical DER signature)']):
with self.nodes[0].assert_debug_log(expected_msgs=[f'CheckInputScripts on {block.vtx[-1].hash} failed with mempool-script-verify-flag-failed (Non-canonical DER signature)']):
peer.send_and_ping(msg_block(block))
assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)
peer.sync_with_ping()
Expand Down
3 changes: 1 addition & 2 deletions test/functional/feature_nulldummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@
assert_raises_rpc_error,
)

NULLDUMMY_ERROR = "non-mandatory-script-verify-flag (Dummy CHECKMULTISIG argument must be zero)"

NULLDUMMY_ERROR = "mempool-script-verify-flag-failed (Dummy CHECKMULTISIG argument must be zero)"

def invalidate_nulldummy_tx(tx):
"""Transform a NULLDUMMY compliant tx (i.e. scriptSig starts with OP_0)
Expand Down
24 changes: 12 additions & 12 deletions test/functional/feature_segwit.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ def run_test(self):
self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][P2WSH][0], True) # block 427

self.log.info("Verify unsigned p2sh witness txs without a redeem script are invalid")
self.fail_accept(self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WPKH][1], sign=False)
self.fail_accept(self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WSH][1], sign=False)
self.fail_accept(self.nodes[2], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WPKH][1], sign=False)
self.fail_accept(self.nodes[2], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WSH][1], sign=False)

self.generate(self.nodes[2], 4) # blocks 428-431

Expand All @@ -221,13 +221,13 @@ def run_test(self):

self.log.info("Verify default node can't accept txs with missing witness")
# unsigned, no scriptsig
self.fail_accept(self.nodes[0], "non-mandatory-script-verify-flag (Witness program hash mismatch)", wit_ids[NODE_0][P2WPKH][0], sign=False)
self.fail_accept(self.nodes[0], "non-mandatory-script-verify-flag (Witness program was passed an empty witness)", wit_ids[NODE_0][P2WSH][0], sign=False)
self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WPKH][0], sign=False)
self.fail_accept(self.nodes[0], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WSH][0], sign=False)
self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program hash mismatch)", wit_ids[NODE_0][P2WPKH][0], sign=False)
self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program was passed an empty witness)", wit_ids[NODE_0][P2WSH][0], sign=False)
self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WPKH][0], sign=False)
self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WSH][0], sign=False)
# unsigned with redeem script
self.fail_accept(self.nodes[0], "non-mandatory-script-verify-flag (Witness program hash mismatch)", p2sh_ids[NODE_0][P2WPKH][0], sign=False, redeem_script=witness_script(False, self.pubkey[0]))
self.fail_accept(self.nodes[0], "non-mandatory-script-verify-flag (Witness program was passed an empty witness)", p2sh_ids[NODE_0][P2WSH][0], sign=False, redeem_script=witness_script(True, self.pubkey[0]))
self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program hash mismatch)", p2sh_ids[NODE_0][P2WPKH][0], sign=False, redeem_script=witness_script(False, self.pubkey[0]))
self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program was passed an empty witness)", p2sh_ids[NODE_0][P2WSH][0], sign=False, redeem_script=witness_script(True, self.pubkey[0]))

self.log.info("Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag")
assert self.nodes[2].getblock(blockhash, False) != self.nodes[0].getblock(blockhash, False)
Expand All @@ -250,10 +250,10 @@ def run_test(self):
assert_equal(witnesses[0], '00' * 32)

self.log.info("Verify witness txs without witness data are invalid after the fork")
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', wit_ids[NODE_2][P2WPKH][2], sign=False)
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', wit_ids[NODE_2][P2WSH][2], sign=False)
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program hash mismatch)', p2sh_ids[NODE_2][P2WPKH][2], sign=False, redeem_script=witness_script(False, self.pubkey[2]))
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', p2sh_ids[NODE_2][P2WSH][2], sign=False, redeem_script=witness_script(True, self.pubkey[2]))
self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program hash mismatch)', wit_ids[NODE_2][P2WPKH][2], sign=False)
self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program was passed an empty witness)', wit_ids[NODE_2][P2WSH][2], sign=False)
self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program hash mismatch)', p2sh_ids[NODE_2][P2WPKH][2], sign=False, redeem_script=witness_script(False, self.pubkey[2]))
self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program was passed an empty witness)', p2sh_ids[NODE_2][P2WSH][2], sign=False, redeem_script=witness_script(True, self.pubkey[2]))

self.log.info("Verify default node can now use witness txs")
self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WPKH][0], True) # block 432
Expand Down
6 changes: 0 additions & 6 deletions test/functional/p2p_invalid_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,9 @@ def run_test(self):
tx = template.get_tx()
node.p2ps[0].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)
Expand Down Expand Up @@ -148,7 +143,6 @@ def run_test(self):
# tx_orphan_2_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
# tx_orphan_2_invalid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)

self.wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected
assert_equal(expected_mempool, set(node.getrawmempool()))

self.log.info('Test orphan pool overflow')
Expand Down
Loading