From 7abd2e697c0f8e93245e09ac853bae05d0b48bee Mon Sep 17 00:00:00 2001 From: Karl-Johan Alm Date: Thu, 14 Feb 2019 17:07:29 +0900 Subject: [PATCH] wallet/rpc: add maxfeerate parameter to testmempoolaccept --- src/rpc/client.cpp | 1 + src/rpc/rawtransaction.cpp | 26 ++++++++++++++++++++------ test/functional/feature_cltv.py | 2 +- test/functional/feature_dersig.py | 2 +- test/functional/rpc_rawtransaction.py | 12 +++++++++--- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index c19160d32..4144a17bc 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -95,6 +95,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendrawtransaction", 1, "maxfeerate" }, { "testmempoolaccept", 0, "rawtxs" }, { "testmempoolaccept", 1, "allowhighfees" }, + { "testmempoolaccept", 1, "maxfeerate" }, { "combinerawtransaction", 0, "txs" }, { "fundrawtransaction", 1, "options" }, { "fundrawtransaction", 2, "iswitness" }, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index fedc30dc3..27fb53654 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1108,7 +1108,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""}, }, }, - {"allowhighfees", RPCArg::Type::BOOL, /* default */ "false", "Allow high fees"}, + {"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(maxTxFee), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"}, }, RPCResult{ "[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n" @@ -1133,7 +1133,11 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) }.ToString()); } - RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL}); + RPCTypeCheck(request.params, { + UniValue::VARR, + UniValueType(), // NUM or BOOL, checked later + }); + if (request.params[0].get_array().size() != 1) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now"); } @@ -1145,9 +1149,19 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) CTransactionRef tx(MakeTransactionRef(std::move(mtx))); const uint256& tx_hash = tx->GetHash(); - CAmount max_raw_tx_fee = ::maxTxFee; - if (!request.params[1].isNull() && request.params[1].get_bool()) { - max_raw_tx_fee = 0; + CAmount max_raw_tx_fee = maxTxFee; + // TODO: temporary migration code for old clients. Remove in v0.20 + if (request.params[1].isBool()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0."); + } else if (request.params[1].isNum()) { + size_t weight = GetTransactionWeight(*tx); + CFeeRate fr(AmountFromValue(request.params[1])); + // the +3/4 part rounds the value up, and is the same formula used when + // calculating the fee for a transaction + // (see GetVirtualTransactionSize) + max_raw_tx_fee = fr.GetFee((weight+3)/4); + } else if (!request.params[1].isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric"); } UniValue result(UniValue::VARR); @@ -2068,7 +2082,7 @@ static const CRPCCommand commands[] = { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, { "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, { "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, - { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} }, + { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees|maxfeerate"} }, { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} }, { "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} }, { "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} }, diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 070242c1d..b16eafccc 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -113,7 +113,7 @@ class BIP65Test(BitcoinTestFramework): # rejected from the mempool for exactly that reason. assert_equal( [{'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Negative locktime)'}], - self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], allowhighfees=True) + self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0) ) # Now we verify that a block with this transaction is also invalid. diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 4ddfd80b0..7480e5c5b 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -102,7 +102,7 @@ class BIP66Test(BitcoinTestFramework): # rejected from the mempool for exactly that reason. assert_equal( [{'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Non-canonical DER signature)'}], - self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], allowhighfees=True) + self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0) ) # Now we verify that a block with this transaction is also invalid. diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index a5a563335..8c82b0ae4 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -426,7 +426,7 @@ class RawTransactionsTest(BitcoinTestFramework): decrawtx = self.nodes[0].decoderawtransaction(rawtx) assert_equal(decrawtx['version'], 0x7fffffff) - self.log.info('sendrawtransaction with maxfeerate') + self.log.info('sendrawtransaction/testmempoolaccept with maxfeerate') txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0) rawTx = self.nodes[0].getrawtransaction(txId, True) @@ -439,9 +439,15 @@ class RawTransactionsTest(BitcoinTestFramework): rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx) assert_equal(rawTxSigned['complete'], True) # 1000 sat fee, ~200 b transaction, fee rate should land around 5 sat/b = 0.00005000 BTC/kB - # Thus, below call should fail + # Thus, testmempoolaccept should reject + testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']], 0.00001000)[0] + assert_equal(testres['allowed'], False) + assert_equal(testres['reject-reason'], '256: absurdly-high-fee') + # and sendrawtransaction should throw assert_raises_rpc_error(-26, "absurdly-high-fee", self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000) - # And below call should succeed + # And below calls should both succeed + testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']], maxfeerate=0.00007000)[0] + assert_equal(testres['allowed'], True) self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate=0.00007000)