Skip to content

Commit 221c789

Browse files
committed
rpc: include verbose reject-details field in testmempoolaccept response
1 parent ae69fc3 commit 221c789

File tree

6 files changed

+36
-13
lines changed

6 files changed

+36
-13
lines changed

src/rpc/mempool.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ static RPCHelpMan testmempoolaccept()
146146
{RPCResult{RPCResult::Type::STR_HEX, "", "transaction wtxid in hex"},
147147
}},
148148
}},
149-
{RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection string (only present when 'allowed' is false)"},
149+
{RPCResult::Type::STR, "reject-reason", /*optional=*/true, "Rejection reason (only present when 'allowed' is false)"},
150+
{RPCResult::Type::STR, "reject-details", /*optional=*/true, "Rejection details (only present when 'allowed' is false and rejection details exist)"},
150151
}},
151152
}
152153
},
@@ -245,6 +246,7 @@ static RPCHelpMan testmempoolaccept()
245246
result_inner.pushKV("reject-reason", "missing-inputs");
246247
} else {
247248
result_inner.pushKV("reject-reason", state.GetRejectReason());
249+
result_inner.pushKV("reject-details", state.ToString());
248250
}
249251
}
250252
rpc_result.push_back(std::move(result_inner));

test/functional/feature_cltv.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ def run_test(self):
148148
# create and test one invalid tx per CLTV failure reason (5 in total)
149149
for i in range(5):
150150
spendtx = wallet.create_self_transfer()['tx']
151+
assert_equal(len(spendtx.vin), 1)
152+
coin = spendtx.vin[0]
153+
coin_txid = format(coin.prevout.hash, '064x')
154+
coin_vout = coin.prevout.n
151155
cltv_invalidate(spendtx, i)
152156

153157
expected_cltv_reject_reason = [
@@ -159,12 +163,15 @@ def run_test(self):
159163
][i]
160164
# First we show that this tx is valid except for CLTV by getting it
161165
# rejected from the mempool for exactly that reason.
166+
spendtx_txid = spendtx.hash
167+
spendtx_wtxid = spendtx.getwtxid()
162168
assert_equal(
163169
[{
164-
'txid': spendtx.hash,
165-
'wtxid': spendtx.getwtxid(),
170+
'txid': spendtx_txid,
171+
'wtxid': spendtx_wtxid,
166172
'allowed': False,
167173
'reject-reason': expected_cltv_reject_reason,
174+
'reject-details': expected_cltv_reject_reason + f", input 0 of {spendtx_txid} (wtxid {spendtx_wtxid}), spending {coin_txid}:{coin_vout}"
168175
}],
169176
self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0),
170177
)

test/functional/feature_dersig.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,23 @@ def run_test(self):
109109
self.log.info("Test that transactions with non-DER signatures cannot appear in a block")
110110
block.nVersion = 4
111111

112-
spendtx = self.create_tx(self.coinbase_txids[1])
112+
coin_txid = self.coinbase_txids[1]
113+
spendtx = self.create_tx(coin_txid)
113114
unDERify(spendtx)
114115
spendtx.rehash()
115116

116117
# First we show that this tx is valid except for DERSIG by getting it
117118
# rejected from the mempool for exactly that reason.
119+
spendtx_txid = spendtx.hash
120+
spendtx_wtxid = spendtx.getwtxid()
118121
assert_equal(
119122
[{
120-
'txid': spendtx.hash,
121-
'wtxid': spendtx.getwtxid(),
123+
'txid': spendtx_txid,
124+
'wtxid': spendtx_wtxid,
122125
'allowed': False,
123126
'reject-reason': 'mandatory-script-verify-flag-failed (Non-canonical DER signature)',
127+
'reject-details': 'mandatory-script-verify-flag-failed (Non-canonical DER signature), ' +
128+
f"input 0 of {spendtx_txid} (wtxid {spendtx_wtxid}), spending {coin_txid}:0"
124129
}],
125130
self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0),
126131
)

test/functional/mempool_accept.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ def check_mempool_result(self, result_expected, *args, **kwargs):
6767
if "fees" in r:
6868
r["fees"].pop("effective-feerate")
6969
r["fees"].pop("effective-includes")
70+
if "reject-details" in r:
71+
r.pop("reject-details")
7072
assert_equal(result_expected, result_test)
7173
assert_equal(self.nodes[0].getmempoolinfo()['size'], self.mempool_size) # Must not change mempool state
7274

test/functional/mempool_accept_wtxid.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,15 @@ def run_test(self):
100100
"txid": child_one_txid,
101101
"wtxid": child_one_wtxid,
102102
"allowed": False,
103-
"reject-reason": "txn-already-in-mempool"
103+
"reject-reason": "txn-already-in-mempool",
104+
"reject-details": "txn-already-in-mempool"
104105
}])
105106
assert_equal(node.testmempoolaccept([child_two.serialize().hex()])[0], {
106107
"txid": child_two_txid,
107108
"wtxid": child_two_wtxid,
108109
"allowed": False,
109-
"reject-reason": "txn-same-nonwitness-data-in-mempool"
110+
"reject-reason": "txn-same-nonwitness-data-in-mempool",
111+
"reject-details": "txn-same-nonwitness-data-in-mempool"
110112
})
111113

112114
# sendrawtransaction will not throw but quits early when the exact same transaction is already in mempool

test/functional/rpc_packages.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,21 @@ def test_independent(self, coin):
110110
self.assert_testres_equal(package_bad, testres_bad)
111111

112112
self.log.info("Check testmempoolaccept tells us when some transactions completed validation successfully")
113-
tx_bad_sig_hex = node.createrawtransaction([{"txid": coin["txid"], "vout": 0}],
113+
tx_bad_sig_hex = node.createrawtransaction([{"txid": coin["txid"], "vout": coin["vout"]}],
114114
{address : coin["amount"] - Decimal("0.0001")})
115115
tx_bad_sig = tx_from_hex(tx_bad_sig_hex)
116116
testres_bad_sig = node.testmempoolaccept(self.independent_txns_hex + [tx_bad_sig_hex])
117117
# By the time the signature for the last transaction is checked, all the other transactions
118118
# have been fully validated, which is why the node returns full validation results for all
119119
# transactions here but empty results in other cases.
120+
tx_bad_sig_txid = tx_bad_sig.rehash()
121+
tx_bad_sig_wtxid = tx_bad_sig.getwtxid()
120122
assert_equal(testres_bad_sig, self.independent_txns_testres + [{
121-
"txid": tx_bad_sig.rehash(),
122-
"wtxid": tx_bad_sig.getwtxid(), "allowed": False,
123-
"reject-reason": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)"
123+
"txid": tx_bad_sig_txid,
124+
"wtxid": tx_bad_sig_wtxid, "allowed": False,
125+
"reject-reason": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)",
126+
"reject-details": "mandatory-script-verify-flag-failed (Operation not valid with the current stack size), " +
127+
f"input 0 of {tx_bad_sig_txid} (wtxid {tx_bad_sig_wtxid}), spending {coin['txid']}:{coin['vout']}"
124128
}])
125129

126130
self.log.info("Check testmempoolaccept reports txns in packages that exceed max feerate")
@@ -304,7 +308,8 @@ def test_rbf(self):
304308
assert testres_rbf_single[0]["allowed"]
305309
testres_rbf_package = self.independent_txns_testres_blank + [{
306310
"txid": replacement_tx["txid"], "wtxid": replacement_tx["wtxid"], "allowed": False,
307-
"reject-reason": "bip125-replacement-disallowed"
311+
"reject-reason": "bip125-replacement-disallowed",
312+
"reject-details": "bip125-replacement-disallowed"
308313
}]
309314
self.assert_testres_equal(self.independent_txns_hex + [replacement_tx["hex"]], testres_rbf_package)
310315

0 commit comments

Comments
 (0)