wallet/rpc: add maxfeerate parameter to testmempoolaccept

This commit is contained in:
Karl-Johan Alm 2019-02-14 17:07:29 +09:00
parent 6c0a6f73e3
commit 7abd2e697c
No known key found for this signature in database
GPG key ID: 57AF762DB3353322
5 changed files with 32 additions and 11 deletions

View file

@ -95,6 +95,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendrawtransaction", 1, "maxfeerate" }, { "sendrawtransaction", 1, "maxfeerate" },
{ "testmempoolaccept", 0, "rawtxs" }, { "testmempoolaccept", 0, "rawtxs" },
{ "testmempoolaccept", 1, "allowhighfees" }, { "testmempoolaccept", 1, "allowhighfees" },
{ "testmempoolaccept", 1, "maxfeerate" },
{ "combinerawtransaction", 0, "txs" }, { "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" }, { "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" }, { "fundrawtransaction", 2, "iswitness" },

View file

@ -1108,7 +1108,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""}, {"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{ RPCResult{
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n" "[ (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()); }.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) { if (request.params[0].get_array().size() != 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now"); 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))); CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
const uint256& tx_hash = tx->GetHash(); const uint256& tx_hash = tx->GetHash();
CAmount max_raw_tx_fee = ::maxTxFee; CAmount max_raw_tx_fee = maxTxFee;
if (!request.params[1].isNull() && request.params[1].get_bool()) { // TODO: temporary migration code for old clients. Remove in v0.20
max_raw_tx_fee = 0; 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); UniValue result(UniValue::VARR);
@ -2068,7 +2082,7 @@ static const CRPCCommand commands[] =
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
{ "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, { "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} },
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, { "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
{ "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} }, { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees|maxfeerate"} },
{ "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} }, { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} },
{ "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} }, { "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} },
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} }, { "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },

View file

@ -113,7 +113,7 @@ class BIP65Test(BitcoinTestFramework):
# rejected from the mempool for exactly that reason. # rejected from the mempool for exactly that reason.
assert_equal( assert_equal(
[{'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Negative locktime)'}], [{'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. # Now we verify that a block with this transaction is also invalid.

View file

@ -102,7 +102,7 @@ class BIP66Test(BitcoinTestFramework):
# rejected from the mempool for exactly that reason. # rejected from the mempool for exactly that reason.
assert_equal( assert_equal(
[{'txid': spendtx.hash, 'allowed': False, 'reject-reason': '64: non-mandatory-script-verify-flag (Non-canonical DER signature)'}], [{'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. # Now we verify that a block with this transaction is also invalid.

View file

@ -426,7 +426,7 @@ class RawTransactionsTest(BitcoinTestFramework):
decrawtx = self.nodes[0].decoderawtransaction(rawtx) decrawtx = self.nodes[0].decoderawtransaction(rawtx)
assert_equal(decrawtx['version'], 0x7fffffff) 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) txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
rawTx = self.nodes[0].getrawtransaction(txId, True) rawTx = self.nodes[0].getrawtransaction(txId, True)
@ -439,9 +439,15 @@ class RawTransactionsTest(BitcoinTestFramework):
rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx) rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx)
assert_equal(rawTxSigned['complete'], True) assert_equal(rawTxSigned['complete'], True)
# 1000 sat fee, ~200 b transaction, fee rate should land around 5 sat/b = 0.00005000 BTC/kB # 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) 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) self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate=0.00007000)