mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-13 03:30:42 +02:00
Merge 20391 via rpc_setfeerate-28+knots
This commit is contained in:
commit
a22e586743
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
class CBlock;
|
class CBlock;
|
||||||
class CBlockHeader;
|
class CBlockHeader;
|
||||||
|
class CFeeRate;
|
||||||
class CScript;
|
class CScript;
|
||||||
class CTransaction;
|
class CTransaction;
|
||||||
struct CMutableTransaction;
|
struct CMutableTransaction;
|
||||||
@ -41,6 +42,7 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
|
|||||||
|
|
||||||
// core_write.cpp
|
// core_write.cpp
|
||||||
UniValue ValueFromAmount(const CAmount amount);
|
UniValue ValueFromAmount(const CAmount amount);
|
||||||
|
UniValue ValueFromFeeRate(const CFeeRate& fee_rate);
|
||||||
std::string FormatScript(const CScript& script);
|
std::string FormatScript(const CScript& script);
|
||||||
std::string EncodeHexTx(const CTransaction& tx);
|
std::string EncodeHexTx(const CTransaction& tx);
|
||||||
std::string SighashToStr(unsigned char sighash_type);
|
std::string SighashToStr(unsigned char sighash_type);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <consensus/consensus.h>
|
#include <consensus/consensus.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
#include <key_io.h>
|
#include <key_io.h>
|
||||||
|
#include <policy/feerate.h>
|
||||||
#include <script/descriptor.h>
|
#include <script/descriptor.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
#include <script/solver.h>
|
#include <script/solver.h>
|
||||||
@ -36,6 +37,11 @@ UniValue ValueFromAmount(const CAmount amount)
|
|||||||
strprintf("%s%d.%08d", amount < 0 ? "-" : "", quotient, remainder));
|
strprintf("%s%d.%08d", amount < 0 ? "-" : "", quotient, remainder));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue ValueFromFeeRate(const CFeeRate& fee_rate)
|
||||||
|
{
|
||||||
|
return UniValue(UniValue::VNUM, fee_rate.SatsToString());
|
||||||
|
}
|
||||||
|
|
||||||
std::string FormatScript(const CScript& script)
|
std::string FormatScript(const CScript& script)
|
||||||
{
|
{
|
||||||
std::string ret;
|
std::string ret;
|
||||||
|
@ -43,3 +43,7 @@ std::string CFeeRate::ToString(const FeeEstimateMode& fee_estimate_mode) const
|
|||||||
default: return strprintf("%d.%08d %s/kvB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
|
default: return strprintf("%d.%08d %s/kvB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CFeeRate::SatsToString() const {
|
||||||
|
return strprintf("%d.%03d", nSatoshisPerK / 1000, nSatoshisPerK % 1000);
|
||||||
|
}
|
||||||
|
@ -68,7 +68,10 @@ public:
|
|||||||
friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
|
friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
|
||||||
friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; }
|
friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; }
|
||||||
CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
|
CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
|
||||||
|
/** Return the fee rate in sat/vB or BTC/kvB, with units, as a string. */
|
||||||
std::string ToString(const FeeEstimateMode& fee_estimate_mode = FeeEstimateMode::BTC_KVB) const;
|
std::string ToString(const FeeEstimateMode& fee_estimate_mode = FeeEstimateMode::BTC_KVB) const;
|
||||||
|
/** Return the fee rate in sat/vB, without units, as a string. */
|
||||||
|
std::string SatsToString() const;
|
||||||
friend CFeeRate operator*(const CFeeRate& f, int a) { return CFeeRate(a * f.nSatoshisPerK); }
|
friend CFeeRate operator*(const CFeeRate& f, int a) { return CFeeRate(a * f.nSatoshisPerK); }
|
||||||
friend CFeeRate operator*(int a, const CFeeRate& f) { return CFeeRate(a * f.nSatoshisPerK); }
|
friend CFeeRate operator*(int a, const CFeeRate& f) { return CFeeRate(a * f.nSatoshisPerK); }
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "sendtoaddress", 8, "avoid_reuse" },
|
{ "sendtoaddress", 8, "avoid_reuse" },
|
||||||
{ "sendtoaddress", 9, "fee_rate"},
|
{ "sendtoaddress", 9, "fee_rate"},
|
||||||
{ "sendtoaddress", 10, "verbose"},
|
{ "sendtoaddress", 10, "verbose"},
|
||||||
|
{ "setfeerate", 0, "amount" },
|
||||||
{ "settxfee", 0, "amount" },
|
{ "settxfee", 0, "amount" },
|
||||||
{ "sethdseed", 0, "newkeypool" },
|
{ "sethdseed", 0, "newkeypool" },
|
||||||
{ "getreceivedbyaddress", 1, "minconf" },
|
{ "getreceivedbyaddress", 1, "minconf" },
|
||||||
|
@ -139,4 +139,12 @@ BOOST_AUTO_TEST_CASE(ToStringTest)
|
|||||||
BOOST_CHECK_EQUAL(feeRate.ToString(FeeEstimateMode::SAT_VB), "0.001 sat/vB");
|
BOOST_CHECK_EQUAL(feeRate.ToString(FeeEstimateMode::SAT_VB), "0.001 sat/vB");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(SatsToStringTest)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(CFeeRate(1).SatsToString(), "0.001");
|
||||||
|
BOOST_CHECK_EQUAL(CFeeRate(70).SatsToString(), "0.070");
|
||||||
|
BOOST_CHECK_EQUAL(CFeeRate(3141).SatsToString(), "3.141");
|
||||||
|
BOOST_CHECK_EQUAL(CFeeRate(10002).SatsToString(), "10.002");
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
@ -311,6 +311,17 @@ BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values)
|
|||||||
BOOST_CHECK_THROW(AmountFromValue(ValueFromString("93e+9")), UniValue); //overflow error
|
BOOST_CHECK_THROW(AmountFromValue(ValueFromString("93e+9")), UniValue); //overflow error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(rpc_parse_fee_rate_values)
|
||||||
|
{
|
||||||
|
// Test ValueFromFeeRate() and CFeeRate()
|
||||||
|
// ...using default CFeeRate constructor
|
||||||
|
BOOST_CHECK_EQUAL(ValueFromFeeRate(CFeeRate(AmountFromValue(0.00001234))).get_real(), 1.234);
|
||||||
|
BOOST_CHECK_EQUAL(ValueFromFeeRate(CFeeRate(AmountFromValue(0.1234))).get_real(), 12340.000);
|
||||||
|
BOOST_CHECK_EQUAL(ValueFromFeeRate(CFeeRate(AmountFromValue(1234))).get_real(), 123400000.000);
|
||||||
|
// ...using CFeeRate constructor with bytes 1000
|
||||||
|
BOOST_CHECK_EQUAL(ValueFromFeeRate(CFeeRate(AmountFromValue(0.00001234), 1000)).get_real(), 1.234);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(rpc_ban)
|
BOOST_AUTO_TEST_CASE(rpc_ban)
|
||||||
{
|
{
|
||||||
BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned")));
|
BOOST_CHECK_NO_THROW(CallRPC(std::string("clearbanned")));
|
||||||
|
@ -433,6 +433,80 @@ RPCHelpMan sendmany()
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RPCHelpMan setfeerate()
|
||||||
|
{
|
||||||
|
return RPCHelpMan{
|
||||||
|
"setfeerate",
|
||||||
|
"\nSet the transaction fee rate in " + CURRENCY_ATOM + "/vB for this wallet.\n"
|
||||||
|
"Overrides the global -paytxfee configuration option. Like -paytxfee, it is not persisted after bitcoind shutdown/restart.\n"
|
||||||
|
"Can be deactivated by passing 0 as the fee rate, in which case automatic fee selection will be used by default.\n",
|
||||||
|
{
|
||||||
|
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee rate in " + CURRENCY_ATOM + "/vB to set (0 to unset)"},
|
||||||
|
},
|
||||||
|
RPCResult{
|
||||||
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::STR, "wallet_name", "Name of the wallet the fee rate setting applies to"},
|
||||||
|
{RPCResult::Type::NUM, "fee_rate", "Fee rate in " + CURRENCY_ATOM + "/vB for the wallet after this operation"},
|
||||||
|
{RPCResult::Type::STR, "result", /* optional */ true, "Description of result, if successful"},
|
||||||
|
{RPCResult::Type::STR, "error", /* optional */ true, "Description of error, if any"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RPCExamples{
|
||||||
|
""
|
||||||
|
"\nSet a fee rate of 1 " + CURRENCY_ATOM + "/vB\n"
|
||||||
|
+ HelpExampleCli("setfeerate", "1") +
|
||||||
|
"\nSet a fee rate of 3.141 " + CURRENCY_ATOM + "/vB\n"
|
||||||
|
+ HelpExampleCli("setfeerate", "3.141") +
|
||||||
|
"\nSet a fee rate of 7.75 " + CURRENCY_ATOM + "/vB with named arguments\n"
|
||||||
|
+ HelpExampleCli("-named setfeerate", "amount=\"7.75\"") +
|
||||||
|
"\nSet a fee rate of 25 " + CURRENCY_ATOM + "/vB with the RPC\n"
|
||||||
|
+ HelpExampleRpc("setfeerate", "25")
|
||||||
|
},
|
||||||
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
|
||||||
|
std::shared_ptr<CWallet> const rpc_wallet{GetWalletForJSONRPCRequest(request)};
|
||||||
|
if (!rpc_wallet) return NullUniValue;
|
||||||
|
CWallet& wallet = *rpc_wallet;
|
||||||
|
|
||||||
|
LOCK(wallet.cs_wallet);
|
||||||
|
const CFeeRate amount{AmountFromValue(request.params[0]), COIN /* sat/vB */};
|
||||||
|
const CFeeRate relay_min_feerate{wallet.chain().relayMinFee().GetFeePerK()};
|
||||||
|
const CFeeRate wallet_min_feerate{wallet.m_min_fee.GetFeePerK()};
|
||||||
|
const CFeeRate wallet_max_feerate{wallet.m_default_max_tx_fee, 1000 /* BTC/kvB */};
|
||||||
|
const CFeeRate zero{CFeeRate{0}};
|
||||||
|
const std::string amount_str{amount.ToString(FeeEstimateMode::SAT_VB)};
|
||||||
|
const std::string current_setting{strprintf("The current setting of %s for this wallet remains unchanged.", wallet.m_pay_tx_fee == zero ? "0 (unset)" : wallet.m_pay_tx_fee.ToString(FeeEstimateMode::SAT_VB))};
|
||||||
|
std::string result, error;
|
||||||
|
|
||||||
|
if (amount == zero) {
|
||||||
|
if (request.params[0].get_real() != 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
|
||||||
|
wallet.m_pay_tx_fee = amount;
|
||||||
|
result = "Fee rate for transactions with this wallet successfully unset. By default, automatic fee selection will be used.";
|
||||||
|
} else if (amount < relay_min_feerate) {
|
||||||
|
error = strprintf("The requested fee rate of %s cannot be less than the minimum relay fee rate of %s. %s", amount_str, relay_min_feerate.ToString(FeeEstimateMode::SAT_VB), current_setting);
|
||||||
|
} else if (amount < wallet_min_feerate) {
|
||||||
|
error = strprintf("The requested fee rate of %s cannot be less than the wallet min fee rate of %s. %s", amount_str, wallet_min_feerate.ToString(FeeEstimateMode::SAT_VB), current_setting);
|
||||||
|
} else if (amount > wallet_max_feerate) {
|
||||||
|
error = strprintf("The requested fee rate of %s cannot be greater than the wallet max fee rate of %s. %s", amount_str, wallet_max_feerate.ToString(FeeEstimateMode::SAT_VB), current_setting);
|
||||||
|
} else {
|
||||||
|
wallet.m_pay_tx_fee = amount;
|
||||||
|
result = "Fee rate for transactions with this wallet successfully set to " + amount_str;
|
||||||
|
}
|
||||||
|
CHECK_NONFATAL(result.empty() != error.empty());
|
||||||
|
|
||||||
|
UniValue obj{UniValue::VOBJ};
|
||||||
|
obj.pushKV("wallet_name", wallet.GetName());
|
||||||
|
obj.pushKV("fee_rate", ValueFromFeeRate(wallet.m_pay_tx_fee));
|
||||||
|
if (error.empty()) {
|
||||||
|
obj.pushKV("result", result);
|
||||||
|
} else {
|
||||||
|
obj.pushKV("error", error);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
RPCHelpMan settxfee()
|
RPCHelpMan settxfee()
|
||||||
{
|
{
|
||||||
return RPCHelpMan{"settxfee",
|
return RPCHelpMan{"settxfee",
|
||||||
|
@ -1075,6 +1075,7 @@ RPCHelpMan encryptwallet();
|
|||||||
// spend
|
// spend
|
||||||
RPCHelpMan sendtoaddress();
|
RPCHelpMan sendtoaddress();
|
||||||
RPCHelpMan sendmany();
|
RPCHelpMan sendmany();
|
||||||
|
RPCHelpMan setfeerate();
|
||||||
RPCHelpMan settxfee();
|
RPCHelpMan settxfee();
|
||||||
RPCHelpMan fundrawtransaction();
|
RPCHelpMan fundrawtransaction();
|
||||||
RPCHelpMan bumpfee();
|
RPCHelpMan bumpfee();
|
||||||
@ -1156,6 +1157,7 @@ Span<const CRPCCommand> GetWalletRPCCommands()
|
|||||||
{"wallet", &sendtoaddress},
|
{"wallet", &sendtoaddress},
|
||||||
{"wallet", &sethdseed},
|
{"wallet", &sethdseed},
|
||||||
{"wallet", &setlabel},
|
{"wallet", &setlabel},
|
||||||
|
{"wallet", &setfeerate},
|
||||||
{"wallet", &settxfee},
|
{"wallet", &settxfee},
|
||||||
{"wallet", &setwalletflag},
|
{"wallet", &setwalletflag},
|
||||||
{"wallet", &signmessage},
|
{"wallet", &signmessage},
|
||||||
|
@ -19,10 +19,12 @@ from test_framework.blocktools import (
|
|||||||
COINBASE_MATURITY,
|
COINBASE_MATURITY,
|
||||||
)
|
)
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
|
COIN,
|
||||||
MAX_BIP125_RBF_SEQUENCE,
|
MAX_BIP125_RBF_SEQUENCE,
|
||||||
)
|
)
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import (
|
from test_framework.util import (
|
||||||
|
assert_approx,
|
||||||
assert_equal,
|
assert_equal,
|
||||||
assert_fee_amount,
|
assert_fee_amount,
|
||||||
assert_greater_than,
|
assert_greater_than,
|
||||||
@ -62,6 +64,7 @@ class BumpFeeTest(BitcoinTestFramework):
|
|||||||
"-mintxfee=0.00002",
|
"-mintxfee=0.00002",
|
||||||
"-addresstype=bech32",
|
"-addresstype=bech32",
|
||||||
] for i in range(self.num_nodes)]
|
] for i in range(self.num_nodes)]
|
||||||
|
self.wallet_names = [self.default_wallet_name, "RBF wallet"]
|
||||||
|
|
||||||
def skip_test_if_missing_module(self):
|
def skip_test_if_missing_module(self):
|
||||||
self.skip_if_no_wallet()
|
self.skip_if_no_wallet()
|
||||||
@ -106,6 +109,7 @@ class BumpFeeTest(BitcoinTestFramework):
|
|||||||
test_bumpfee_metadata(self, rbf_node, dest_address)
|
test_bumpfee_metadata(self, rbf_node, dest_address)
|
||||||
test_locked_wallet_fails(self, rbf_node, dest_address)
|
test_locked_wallet_fails(self, rbf_node, dest_address)
|
||||||
test_change_script_match(self, rbf_node, dest_address)
|
test_change_script_match(self, rbf_node, dest_address)
|
||||||
|
test_setfeerate(self, rbf_node, dest_address)
|
||||||
test_settxfee(self, rbf_node, dest_address)
|
test_settxfee(self, rbf_node, dest_address)
|
||||||
test_maxtxfee_fails(self, rbf_node, dest_address)
|
test_maxtxfee_fails(self, rbf_node, dest_address)
|
||||||
# These tests wipe out a number of utxos that are expected in other tests
|
# These tests wipe out a number of utxos that are expected in other tests
|
||||||
@ -532,6 +536,44 @@ def test_dust_to_fee(self, rbf_node, dest_address):
|
|||||||
self.clear_mempool()
|
self.clear_mempool()
|
||||||
|
|
||||||
|
|
||||||
|
def test_setfeerate(self, rbf_node, dest_address):
|
||||||
|
self.log.info("Test setfeerate")
|
||||||
|
|
||||||
|
def test_response(*, wallet="RBF wallet", requested=0, expected=0, error=None, msg):
|
||||||
|
assert_equal(rbf_node.setfeerate(requested), {"wallet_name": wallet, "fee_rate": expected, ("error" if error else "result"): msg})
|
||||||
|
|
||||||
|
# Test setfeerate with too high/low values returns expected errors
|
||||||
|
new = Decimal("10000.001")
|
||||||
|
test_response(requested=new, error=True, msg=f"The requested fee rate of {new} sat/vB cannot be greater than the wallet max fee rate of 10000.000 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
|
||||||
|
new = Decimal("0.999")
|
||||||
|
test_response(requested=new, error=True, msg=f"The requested fee rate of {new} sat/vB cannot be less than the minimum relay fee rate of 1.000 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
|
||||||
|
fee_rate = Decimal("2.001")
|
||||||
|
test_response(requested=fee_rate, expected=fee_rate, msg=f"Fee rate for transactions with this wallet successfully set to {fee_rate} sat/vB")
|
||||||
|
new = Decimal("1.999")
|
||||||
|
test_response(requested=new, expected=fee_rate, error=True, msg=f"The requested fee rate of {new} sat/vB cannot be less than the wallet min fee rate of 2.000 sat/vB. The current setting of {fee_rate} sat/vB for this wallet remains unchanged.")
|
||||||
|
|
||||||
|
# Test setfeerate with valid values returns expected results
|
||||||
|
rbfid = spend_one_input(rbf_node, dest_address)
|
||||||
|
fee_rate = 25
|
||||||
|
test_response(requested=fee_rate, expected=fee_rate, msg="Fee rate for transactions with this wallet successfully set to 25.000 sat/vB")
|
||||||
|
bumped_tx = rbf_node.bumpfee(rbfid)
|
||||||
|
bumped_txdetails = rbf_node.getrawtransaction(bumped_tx["txid"], True)
|
||||||
|
allow_for_bytes_offset = len(bumped_txdetails['vout']) * 2 # potentially up to 2 bytes per output
|
||||||
|
actual_fee = bumped_tx["fee"] * COIN
|
||||||
|
assert_approx(actual_fee, fee_rate * bumped_txdetails['vsize'], fee_rate * allow_for_bytes_offset)
|
||||||
|
test_response(msg="Fee rate for transactions with this wallet successfully unset. By default, automatic fee selection will be used.")
|
||||||
|
|
||||||
|
# Test setfeerate with a different -maxtxfee
|
||||||
|
self.restart_node(1, ["-maxtxfee=0.000025"] + self.extra_args[1])
|
||||||
|
new = "2.501"
|
||||||
|
test_response(requested=new, error=True, msg=f"The requested fee rate of {new} sat/vB cannot be greater than the wallet max fee rate of 2.500 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
|
||||||
|
|
||||||
|
self.restart_node(1, self.extra_args[1])
|
||||||
|
rbf_node.walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT)
|
||||||
|
self.connect_nodes(1, 0)
|
||||||
|
self.clear_mempool()
|
||||||
|
|
||||||
|
|
||||||
def test_settxfee(self, rbf_node, dest_address):
|
def test_settxfee(self, rbf_node, dest_address):
|
||||||
self.log.info('Test settxfee')
|
self.log.info('Test settxfee')
|
||||||
assert_raises_rpc_error(-8, "txfee cannot be less than min relay tx fee", rbf_node.settxfee, Decimal('0.000005'))
|
assert_raises_rpc_error(-8, "txfee cannot be less than min relay tx fee", rbf_node.settxfee, Decimal('0.000005'))
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from test_framework.messages import (
|
from test_framework.messages import (
|
||||||
tx_from_hex,
|
tx_from_hex,
|
||||||
)
|
)
|
||||||
@ -37,6 +39,7 @@ class CreateTxWalletTest(BitcoinTestFramework):
|
|||||||
self.test_tx_size_too_large()
|
self.test_tx_size_too_large()
|
||||||
self.test_create_too_long_mempool_chain()
|
self.test_create_too_long_mempool_chain()
|
||||||
self.test_version3()
|
self.test_version3()
|
||||||
|
self.test_setfeerate()
|
||||||
|
|
||||||
def test_anti_fee_sniping(self):
|
def test_anti_fee_sniping(self):
|
||||||
self.log.info('Check that we have some (old) blocks and that anti-fee-sniping is disabled')
|
self.log.info('Check that we have some (old) blocks and that anti-fee-sniping is disabled')
|
||||||
@ -55,6 +58,7 @@ class CreateTxWalletTest(BitcoinTestFramework):
|
|||||||
# More than 10kB of outputs, so that we hit -maxtxfee with a high feerate
|
# More than 10kB of outputs, so that we hit -maxtxfee with a high feerate
|
||||||
outputs = {self.nodes[0].getnewaddress(address_type='bech32'): 0.000025 for _ in range(400)}
|
outputs = {self.nodes[0].getnewaddress(address_type='bech32'): 0.000025 for _ in range(400)}
|
||||||
raw_tx = self.nodes[0].createrawtransaction(inputs=[], outputs=outputs)
|
raw_tx = self.nodes[0].createrawtransaction(inputs=[], outputs=outputs)
|
||||||
|
msg = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)"
|
||||||
|
|
||||||
for fee_setting in ['-minrelaytxfee=0.01', '-mintxfee=0.01', '-paytxfee=0.01']:
|
for fee_setting in ['-minrelaytxfee=0.01', '-mintxfee=0.01', '-paytxfee=0.01']:
|
||||||
self.log.info('Check maxtxfee in combination with {}'.format(fee_setting))
|
self.log.info('Check maxtxfee in combination with {}'.format(fee_setting))
|
||||||
@ -85,6 +89,12 @@ class CreateTxWalletTest(BitcoinTestFramework):
|
|||||||
)
|
)
|
||||||
self.nodes[0].settxfee(0)
|
self.nodes[0].settxfee(0)
|
||||||
|
|
||||||
|
self.log.info('Check maxtxfee in combination with setfeerate (sat/vB)')
|
||||||
|
self.nodes[0].setfeerate(1000)
|
||||||
|
assert_raises_rpc_error(-6, msg, self.nodes[0].sendmany, dummy="", amounts=outputs)
|
||||||
|
assert_raises_rpc_error(-4, msg, self.nodes[0].fundrawtransaction, hexstring=raw_tx)
|
||||||
|
self.nodes[0].setfeerate(0)
|
||||||
|
|
||||||
def test_create_too_long_mempool_chain(self):
|
def test_create_too_long_mempool_chain(self):
|
||||||
self.log.info('Check too-long mempool chain error')
|
self.log.info('Check too-long mempool chain error')
|
||||||
df_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
|
df_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
|
||||||
@ -127,6 +137,51 @@ class CreateTxWalletTest(BitcoinTestFramework):
|
|||||||
assert_equal(tx_current_version.version, 2)
|
assert_equal(tx_current_version.version, 2)
|
||||||
wallet_v3.unloadwallet()
|
wallet_v3.unloadwallet()
|
||||||
|
|
||||||
|
def test_setfeerate(self):
|
||||||
|
self.log.info("Test setfeerate")
|
||||||
|
self.restart_node(0, extra_args=["-mintxfee=0.00003141"]) # 3.141 sat/vB
|
||||||
|
node = self.nodes[0]
|
||||||
|
|
||||||
|
def test_response(*, requested=0, expected=0, error=None, msg):
|
||||||
|
assert_equal(node.setfeerate(requested), {"wallet_name": self.default_wallet_name, "fee_rate": expected, ("error" if error else "result"): msg})
|
||||||
|
|
||||||
|
# Test setfeerate with 10.0001 (CFeeRate rounding), "10.001" and "4" sat/vB
|
||||||
|
test_response(requested=10.0001, expected=10, msg="Fee rate for transactions with this wallet successfully set to 10.000 sat/vB")
|
||||||
|
assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00010000"))
|
||||||
|
test_response(requested="10.001", expected=Decimal("10.001"), msg="Fee rate for transactions with this wallet successfully set to 10.001 sat/vB")
|
||||||
|
assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00010001"))
|
||||||
|
test_response(requested="4", expected=4, msg="Fee rate for transactions with this wallet successfully set to 4.000 sat/vB")
|
||||||
|
assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00004000"))
|
||||||
|
|
||||||
|
# Test setfeerate with too-high/low values returns expected errors
|
||||||
|
test_response(requested=Decimal("10000.001"), expected=4, error=True, msg="The requested fee rate of 10000.001 sat/vB cannot be greater than the wallet max fee rate of 10000.000 sat/vB. The current setting of 4.000 sat/vB for this wallet remains unchanged.")
|
||||||
|
test_response(requested=Decimal("0.999"), expected=4, error=True, msg="The requested fee rate of 0.999 sat/vB cannot be less than the minimum relay fee rate of 1.000 sat/vB. The current setting of 4.000 sat/vB for this wallet remains unchanged.")
|
||||||
|
test_response(requested=Decimal("3.140"), expected=4, error=True, msg="The requested fee rate of 3.140 sat/vB cannot be less than the wallet min fee rate of 3.141 sat/vB. The current setting of 4.000 sat/vB for this wallet remains unchanged.")
|
||||||
|
assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00004000"))
|
||||||
|
|
||||||
|
# Test setfeerate to 3.141 sat/vB
|
||||||
|
test_response(requested=3.141, expected=Decimal("3.141"), msg="Fee rate for transactions with this wallet successfully set to 3.141 sat/vB")
|
||||||
|
assert_equal(node.getwalletinfo()["paytxfee"], Decimal("0.00003141"))
|
||||||
|
|
||||||
|
# Test setfeerate with values non-representable by CFeeRate
|
||||||
|
for invalid_value in [0.00000001, 0.0009, 0.00099999]:
|
||||||
|
assert_raises_rpc_error(-3, "Invalid amount", node.setfeerate, amount=invalid_value)
|
||||||
|
|
||||||
|
# Test setfeerate with values rejected by ParseFixedPoint() called in AmountFromValue()
|
||||||
|
for invalid_value in ["", 0.000000001, "1.111111111", 11111111111]:
|
||||||
|
assert_raises_rpc_error(-3, "Invalid amount", node.setfeerate, amount=invalid_value)
|
||||||
|
|
||||||
|
# Test deactivating setfeerate
|
||||||
|
test_response(msg="Fee rate for transactions with this wallet successfully unset. By default, automatic fee selection will be used.")
|
||||||
|
assert_equal(node.getwalletinfo()["paytxfee"], 0)
|
||||||
|
|
||||||
|
# Test currently-unset setfeerate with too-high/low values returns expected errors
|
||||||
|
test_response(requested=Decimal("10000.001"), error=True, msg="The requested fee rate of 10000.001 sat/vB cannot be greater than the wallet max fee rate of 10000.000 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
|
||||||
|
assert_equal(node.getwalletinfo()["paytxfee"], 0)
|
||||||
|
test_response(requested=Decimal("0.999"), error=True, msg="The requested fee rate of 0.999 sat/vB cannot be less than the minimum relay fee rate of 1.000 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
|
||||||
|
test_response(requested=Decimal("3.140"), error=True, msg="The requested fee rate of 3.140 sat/vB cannot be less than the wallet min fee rate of 3.141 sat/vB. The current setting of 0 (unset) for this wallet remains unchanged.")
|
||||||
|
assert_equal(node.getwalletinfo()["paytxfee"], 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
CreateTxWalletTest(__file__).main()
|
CreateTxWalletTest(__file__).main()
|
||||||
|
@ -250,12 +250,18 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||||||
assert_equal(batch[0]["result"]["chain"], self.chain)
|
assert_equal(batch[0]["result"]["chain"], self.chain)
|
||||||
assert_equal(batch[1]["result"]["walletname"], "w1")
|
assert_equal(batch[1]["result"]["walletname"], "w1")
|
||||||
|
|
||||||
self.log.info('Check for per-wallet settxfee call')
|
self.log.info('Test per-wallet setfeerate and settxfee calls')
|
||||||
assert_equal(w1.getwalletinfo()['paytxfee'], 0)
|
assert_equal(w1.getwalletinfo()['paytxfee'], 0)
|
||||||
assert_equal(w2.getwalletinfo()['paytxfee'], 0)
|
assert_equal(w2.getwalletinfo()['paytxfee'], 0)
|
||||||
|
w2.setfeerate(200)
|
||||||
|
assert_equal(w1.getwalletinfo()['paytxfee'], 0)
|
||||||
|
assert_equal(w2.getwalletinfo()['paytxfee'], Decimal('0.00200000'))
|
||||||
w2.settxfee(0.001)
|
w2.settxfee(0.001)
|
||||||
assert_equal(w1.getwalletinfo()['paytxfee'], 0)
|
assert_equal(w1.getwalletinfo()['paytxfee'], 0)
|
||||||
assert_equal(w2.getwalletinfo()['paytxfee'], Decimal('0.00100000'))
|
assert_equal(w2.getwalletinfo()['paytxfee'], Decimal('0.00100000'))
|
||||||
|
w1.setfeerate(30)
|
||||||
|
assert_equal(w1.getwalletinfo()['paytxfee'], Decimal('0.00030000'))
|
||||||
|
assert_equal(w2.getwalletinfo()['paytxfee'], Decimal('0.00100000'))
|
||||||
|
|
||||||
self.log.info("Test dynamic wallet loading")
|
self.log.info("Test dynamic wallet loading")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user