Skip to content

Commit 27d12cf

Browse files
committed
Merge bitcoin#31043: rpc: getorphantxs follow-up
0ea84bc test: explicitly check boolean verbosity is disallowed (tdb3) 7a2e6b6 doc: add rpc guidance for boolean verbosity avoidance (tdb3) 698f302 rpc: disallow boolean verbosity in getorphantxs (tdb3) 63f5e6e test: add entry and expiration time checks (tdb3) 808a708 rpc: add entry time to getorphantxs (tdb3) 56bf302 refactor: rename rpc_getorphantxs to rpc_orphans (tdb3) 7824f6b test: check that getorphantxs is hidden (tdb3) ac68fcc rpc: disallow undefined verbosity in getorphantxs (tdb3) Pull request description: Implements follow-up suggestions from bitcoin#30793. - Now disallows undefined verbosity levels (below and above valid values) (bitcoin#30793 (comment)) - Disallows boolean verbosity (bitcoin#30793 (comment)) and adds guidance to developer-notes - Checks that `getorphantxs` is a hidden rpc (bitcoin#30793 (comment)) - Adds a test for `expiration` time - Adds `entry` time to the returned orphan objects (verbosity >=1) to relieve the user from having to calculate it from `expiration`. Also adds associated test. (bitcoin#30793 (comment)) - Minor cleanup (blank line removal and log message move) (bitcoin#30793 (comment)) Included a commit to rename the test to a more generic `get_orphans` to better accommodate future orphanage-related RPCs (e.g. `getorphanangeinfo`). Can drop the refactor commit from this PR if people feel strongly about it. ACKs for top commit: achow101: ACK 0ea84bc glozow: utACK 0ea84bc rkrux: tACK 0ea84bc itornaza: tACK 0ea84bc Tree-SHA512: e48a088f333ebde132923072da58e970461e74362d0acebbc799c3043d5727cdf5f28e82b43cb38bbed27c603df6710695dba91ff0695e623ad168e985dce08e
2 parents 7b66815 + 0ea84bc commit 27d12cf

File tree

10 files changed

+60
-21
lines changed

10 files changed

+60
-21
lines changed

doc/developer-notes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,12 @@ A few guidelines for introducing and reviewing new RPC interfaces:
13971397
to a multi-value, or due to other historical reasons. **Always** have false map to 0 and
13981398
true to 1 in this case.
13991399

1400+
- For new RPC methods, if implementing a `verbosity` argument, use integer verbosity rather than boolean.
1401+
Disallow usage of boolean verbosity (see `ParseVerbosity()` in [util.h](/src/rpc/util.h)).
1402+
1403+
- *Rationale*: Integer verbosity allows for multiple values. Undocumented boolean verbosity is deprecated
1404+
and new RPC methods should prevent its use.
1405+
14001406
- Don't forget to fill in the argument names correctly in the RPC command table.
14011407

14021408
- *Rationale*: If not, the call cannot be used with name-based arguments.

src/rpc/blockchain.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ static RPCHelpMan getblock()
766766
{
767767
uint256 hash(ParseHashV(request.params[0], "blockhash"));
768768

769-
int verbosity{ParseVerbosity(request.params[1], /*default_verbosity=*/1)};
769+
int verbosity{ParseVerbosity(request.params[1], /*default_verbosity=*/1, /*allow_bool=*/true)};
770770

771771
const CBlockIndex* pblockindex;
772772
const CBlockIndex* tip;

src/rpc/client.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,6 @@ static const CRPCConvertParam vRPCConvertParams[] =
255255
{ "getrawmempool", 0, "verbose" },
256256
{ "getrawmempool", 1, "mempool_sequence" },
257257
{ "getorphantxs", 0, "verbosity" },
258-
{ "getorphantxs", 0, "verbose" },
259258
{ "estimatesmartfee", 0, "conf_target" },
260259
{ "estimaterawfee", 0, "conf_target" },
261260
{ "estimaterawfee", 1, "threshold" },

src/rpc/mempool.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,7 @@ static std::vector<RPCResult> OrphanDescription()
823823
RPCResult{RPCResult::Type::NUM, "bytes", "The serialized transaction size in bytes"},
824824
RPCResult{RPCResult::Type::NUM, "vsize", "The virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted."},
825825
RPCResult{RPCResult::Type::NUM, "weight", "The transaction weight as defined in BIP 141."},
826+
RPCResult{RPCResult::Type::NUM_TIME, "entry", "The entry time into the orphanage expressed in " + UNIX_EPOCH_TIME},
826827
RPCResult{RPCResult::Type::NUM_TIME, "expiration", "The orphan expiration time expressed in " + UNIX_EPOCH_TIME},
827828
RPCResult{RPCResult::Type::ARR, "from", "",
828829
{
@@ -839,6 +840,7 @@ static UniValue OrphanToJSON(const TxOrphanage::OrphanTxBase& orphan)
839840
o.pushKV("bytes", orphan.tx->GetTotalSize());
840841
o.pushKV("vsize", GetVirtualTransactionSize(*orphan.tx));
841842
o.pushKV("weight", GetTransactionWeight(*orphan.tx));
843+
o.pushKV("entry", int64_t{TicksSinceEpoch<std::chrono::seconds>(orphan.nTimeExpire - ORPHAN_TX_EXPIRE_TIME)});
842844
o.pushKV("expiration", int64_t{TicksSinceEpoch<std::chrono::seconds>(orphan.nTimeExpire)});
843845
UniValue from(UniValue::VARR);
844846
from.push_back(orphan.fromPeer); // only one fromPeer for now
@@ -852,7 +854,7 @@ static RPCHelpMan getorphantxs()
852854
"\nShows transactions in the tx orphanage.\n"
853855
"\nEXPERIMENTAL warning: this call may be changed in future releases.\n",
854856
{
855-
{"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for an array of txids (may contain duplicates), 1 for an array of objects with tx details, and 2 for details from (1) and tx hex",
857+
{"verbosity", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for an array of txids (may contain duplicates), 1 for an array of objects with tx details, and 2 for details from (1) and tx hex",
856858
RPCArgOptions{.skip_type_check = true}},
857859
},
858860
{
@@ -887,25 +889,26 @@ static RPCHelpMan getorphantxs()
887889
PeerManager& peerman = EnsurePeerman(node);
888890
std::vector<TxOrphanage::OrphanTxBase> orphanage = peerman.GetOrphanTransactions();
889891

890-
int verbosity{ParseVerbosity(request.params[0], /*default_verbosity=*/0)};
892+
int verbosity{ParseVerbosity(request.params[0], /*default_verbosity=*/0, /*allow_bool*/false)};
891893

892894
UniValue ret(UniValue::VARR);
893895

894-
if (verbosity <= 0) {
896+
if (verbosity == 0) {
895897
for (auto const& orphan : orphanage) {
896898
ret.push_back(orphan.tx->GetHash().ToString());
897899
}
898900
} else if (verbosity == 1) {
899901
for (auto const& orphan : orphanage) {
900902
ret.push_back(OrphanToJSON(orphan));
901903
}
902-
} else {
903-
// >= 2
904+
} else if (verbosity == 2) {
904905
for (auto const& orphan : orphanage) {
905906
UniValue o{OrphanToJSON(orphan)};
906907
o.pushKV("hex", EncodeHexTx(*orphan.tx));
907908
ret.push_back(o);
908909
}
910+
} else {
911+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid verbosity value " + ToString(verbosity));
909912
}
910913

911914
return ret;

src/rpc/rawtransaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ static RPCHelpMan getrawtransaction()
338338
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved");
339339
}
340340

341-
int verbosity{ParseVerbosity(request.params[1], /*default_verbosity=*/0)};
341+
int verbosity{ParseVerbosity(request.params[1], /*default_verbosity=*/0, /*allow_bool=*/true)};
342342

343343
if (!request.params[2].isNull()) {
344344
LOCK(cs_main);

src/rpc/util.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,13 @@ void RPCTypeCheckObj(const UniValue& o,
8181
}
8282
}
8383

84-
int ParseVerbosity(const UniValue& arg, int default_verbosity)
84+
int ParseVerbosity(const UniValue& arg, int default_verbosity, bool allow_bool)
8585
{
8686
if (!arg.isNull()) {
8787
if (arg.isBool()) {
88+
if (!allow_bool) {
89+
throw JSONRPCError(RPC_TYPE_ERROR, "Verbosity was boolean but only integer allowed");
90+
}
8891
return arg.get_bool(); // true = 1
8992
} else {
9093
return arg.getInt<int>();

src/rpc/util.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ std::vector<unsigned char> ParseHexO(const UniValue& o, std::string_view strKey)
103103
/**
104104
* Parses verbosity from provided UniValue.
105105
*
106-
* @param[in] arg The verbosity argument as a bool (true) or int (0, 1, 2,...)
106+
* @param[in] arg The verbosity argument as an int (0, 1, 2,...) or bool if allow_bool is set to true
107107
* @param[in] default_verbosity The value to return if verbosity argument is null
108+
* @param[in] allow_bool If true, allows arg to be a bool and parses it
108109
* @returns An integer describing the verbosity level (e.g. 0, 1, 2, etc.)
110+
* @throws JSONRPCError if allow_bool is false but arg provided is boolean
109111
*/
110-
int ParseVerbosity(const UniValue& arg, int default_verbosity);
112+
int ParseVerbosity(const UniValue& arg, int default_verbosity, bool allow_bool);
111113

112114
/**
113115
* Validate and return a CAmount from a UniValue number or string.

test/functional/rpc_getorphantxs.py renamed to test/functional/rpc_orphans.py

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,33 @@
22
# Copyright (c) 2014-2024 The Bitcoin Core developers
33
# Distributed under the MIT software license, see the accompanying
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5-
"""Test the getorphantxs RPC."""
5+
"""Tests for orphan related RPCs."""
66

7-
from test_framework.mempool_util import tx_in_orphanage
7+
import time
8+
9+
from test_framework.mempool_util import (
10+
ORPHAN_TX_EXPIRE_TIME,
11+
tx_in_orphanage,
12+
)
813
from test_framework.messages import msg_tx
914
from test_framework.p2p import P2PInterface
10-
from test_framework.util import assert_equal
15+
from test_framework.util import (
16+
assert_equal,
17+
assert_raises_rpc_error,
18+
)
1119
from test_framework.test_framework import BitcoinTestFramework
1220
from test_framework.wallet import MiniWallet
1321

1422

15-
class GetOrphanTxsTest(BitcoinTestFramework):
23+
class OrphanRPCsTest(BitcoinTestFramework):
1624
def set_test_params(self):
1725
self.num_nodes = 1
1826

1927
def run_test(self):
2028
self.wallet = MiniWallet(self.nodes[0])
2129
self.test_orphan_activity()
2230
self.test_orphan_details()
31+
self.test_misc()
2332

2433
def test_orphan_activity(self):
2534
self.log.info("Check that orphaned transactions are returned with getorphantxs")
@@ -37,13 +46,13 @@ def test_orphan_activity(self):
3746
self.log.info("Check that neither parent is in the mempool")
3847
assert_equal(node.getmempoolinfo()["size"], 0)
3948

40-
self.log.info("Check that both children are in the orphanage")
41-
4249
orphanage = node.getorphantxs(verbosity=0)
4350
self.log.info("Check the size of the orphanage")
4451
assert_equal(len(orphanage), 2)
45-
self.log.info("Check that negative verbosity is treated as 0")
46-
assert_equal(orphanage, node.getorphantxs(verbosity=-1))
52+
self.log.info("Check that undefined verbosity is disallowed")
53+
assert_raises_rpc_error(-8, "Invalid verbosity value -1", node.getorphantxs, verbosity=-1)
54+
assert_raises_rpc_error(-8, "Invalid verbosity value 3", node.getorphantxs, verbosity=3)
55+
self.log.info("Check that both children are in the orphanage")
4756
assert tx_in_orphanage(node, tx_child_1["tx"])
4857
assert tx_in_orphanage(node, tx_child_2["tx"])
4958

@@ -86,6 +95,8 @@ def test_orphan_details(self):
8695
tx_child_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_parent_2["new_utxo"])
8796
peer_1 = node.add_p2p_connection(P2PInterface())
8897
peer_2 = node.add_p2p_connection(P2PInterface())
98+
entry_time = int(time.time())
99+
node.setmocktime(entry_time)
89100
peer_1.send_and_ping(msg_tx(tx_child_1["tx"]))
90101
peer_2.send_and_ping(msg_tx(tx_child_2["tx"]))
91102

@@ -105,6 +116,9 @@ def test_orphan_details(self):
105116
assert_equal(len(node.getorphantxs()), 1)
106117
orphan_1 = orphanage[0]
107118
self.orphan_details_match(orphan_1, tx_child_1, verbosity=1)
119+
self.log.info("Checking orphan entry/expiration times")
120+
assert_equal(orphan_1["entry"], entry_time)
121+
assert_equal(orphan_1["expiration"], entry_time + ORPHAN_TX_EXPIRE_TIME)
108122

109123
self.log.info("Checking orphan details (verbosity 2)")
110124
orphanage = node.getorphantxs(verbosity=2)
@@ -125,5 +139,15 @@ def orphan_details_match(self, orphan, tx, verbosity):
125139
self.log.info("Check the transaction hex of orphan")
126140
assert_equal(orphan["hex"], tx["hex"])
127141

142+
def test_misc(self):
143+
node = self.nodes[0]
144+
assert_raises_rpc_error(-3, "Verbosity was boolean but only integer allowed", node.getorphantxs, verbosity=True)
145+
assert_raises_rpc_error(-3, "Verbosity was boolean but only integer allowed", node.getorphantxs, verbosity=False)
146+
help_output = node.help()
147+
self.log.info("Check that getorphantxs is a hidden RPC")
148+
assert "getorphantxs" not in help_output
149+
assert "unknown command: getorphantxs" not in node.help("getorphantxs")
150+
151+
128152
if __name__ == '__main__':
129-
GetOrphanTxsTest(__file__).main()
153+
OrphanRPCsTest(__file__).main()

test/functional/test_framework/mempool_util.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
MiniWallet,
2020
)
2121

22+
ORPHAN_TX_EXPIRE_TIME = 1200
23+
2224

2325
def fill_mempool(test_framework, node, *, tx_sync_fun=None):
2426
"""Fill mempool until eviction.

test/functional/test_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@
160160
'wallet_importmulti.py --legacy-wallet',
161161
'mempool_limit.py',
162162
'rpc_txoutproof.py',
163-
'rpc_getorphantxs.py',
163+
'rpc_orphans.py',
164164
'wallet_listreceivedby.py --legacy-wallet',
165165
'wallet_listreceivedby.py --descriptors',
166166
'wallet_abandonconflict.py --legacy-wallet',

0 commit comments

Comments
 (0)