From fd520079a191f1ab6afb2ed1a512c56a3a86f44b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 9 Sep 2021 20:11:44 +0000 Subject: [PATCH 1/2] claimpegin: sanity-check fee in case fallbackfee is disabled, throw explicit exception Core's fee logic has a special case where it will give a zero fee estimate if fallbackfee is disabled and it has no good data from estimatesmartfee. If it gets a nonzero value below the min relay fee, it'll instead use the min relay fee ... but zero is special. I think this is a bug. Regardless, it was causing confusing error messages in `claimpegin`, especially as the fallbackfee is no longer set by default as of 0.21 --- src/wallet/rpcwallet.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9eca39489d..ed3dc8d83f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5875,7 +5875,12 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef unsigned int nBytes = GetVirtualTransactionSize(CTransaction(mtx)) + (1+1+72+1+33/WITNESS_SCALE_FACTOR); CCoinControl coin_control; - CAmount nFeeNeeded = GetMinimumFee(*pwallet, nBytes, coin_control, nullptr); + FeeCalculation feeCalc; + CAmount nFeeNeeded = GetMinimumFee(*pwallet, nBytes, coin_control, &feeCalc); + + if (nFeeNeeded == CAmount{0} && feeCalc.reason == FeeReason::FALLBACK) { + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee."); + } mtx.vout[0].nValue = mtx.vout[0].nValue.GetAmount() - nFeeNeeded; mtx.vout[1].nValue = mtx.vout[1].nValue.GetAmount() + nFeeNeeded; From fc8bfc1f2cda0844adbf1d831a1a5e1888f8949b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 9 Sep 2021 19:49:36 +0000 Subject: [PATCH 2/2] claimpegin: add regression test for behavior when fallbackfee is disabled --- test/functional/wallet_fallbackfee.py | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py index b128d10384..fd99769e9b 100755 --- a/test/functional/wallet_fallbackfee.py +++ b/test/functional/wallet_fallbackfee.py @@ -5,10 +5,11 @@ """Test wallet replace-by-fee capabilities in conjunction with the fallbackfee.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_raises_rpc_error +from test_framework.util import rpc_port ## ELEMENTS class WalletRBFTest(BitcoinTestFramework): def set_test_params(self): - self.num_nodes = 1 + self.num_nodes = 2 self.setup_clean_chain = True def skip_test_if_missing_module(self): @@ -26,5 +27,32 @@ class WalletRBFTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "Fee estimation failed", lambda: self.nodes[0].fundrawtransaction(self.nodes[0].createrawtransaction([], [{self.nodes[0].getnewaddress(): 1}]))) assert_raises_rpc_error(-6, "Fee estimation failed", lambda: self.nodes[0].sendmany("", {self.nodes[0].getnewaddress(): 1})) + ## ELEMENTS: test claimpegin with fallback fee set to zero + # getpeginaddress does not work with descriptor wallets yet + if not self.options.descriptors: + extra_args = [ + '-fallbackfee=0', + '-mainchainrpchost=127.0.0.1', + '-mainchainrpcport=%s' % rpc_port(0), + '-parentgenesisblockhash=%s' % self.nodes[0].getblockhash(0), + '-con_parent_chain_signblockscript=51', + '-parentscriptprefix=75', + ] + self.restart_node(0) + self.restart_node(1, extra_args) + + addrs = self.nodes[1].getpeginaddress() + txid = self.nodes[0].sendtoaddress(addrs["mainchain_address"], 5) + raw = self.nodes[0].getrawtransaction(txid) + self.nodes[0].generate(12) + proof = self.nodes[0].gettxoutproof([txid]) + assert_raises_rpc_error(-6, "Fee estimation failed", lambda: self.nodes[1].claimpegin(raw, proof)) + + # Try again with fallbackfee below the min relay fee. It should just work + # (will let the relay fee override the fallbackfee) + extra_args[0] = '-fallbackfee=0.00000001' + self.restart_node(1, extra_args) + self.nodes[1].claimpegin(raw, proof) + if __name__ == '__main__': WalletRBFTest().main()