mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-06-16 11:53:07 +02:00
Add psbtbumpfee RPC
This commit is contained in:
parent
f32f7e907a
commit
4638224f64
@ -151,6 +151,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||||||
{ "getmempoolancestors", 1, "verbose" },
|
{ "getmempoolancestors", 1, "verbose" },
|
||||||
{ "getmempooldescendants", 1, "verbose" },
|
{ "getmempooldescendants", 1, "verbose" },
|
||||||
{ "bumpfee", 1, "options" },
|
{ "bumpfee", 1, "options" },
|
||||||
|
{ "psbtbumpfee", 1, "options" },
|
||||||
{ "logging", 0, "include" },
|
{ "logging", 0, "include" },
|
||||||
{ "logging", 1, "exclude" },
|
{ "logging", 1, "exclude" },
|
||||||
{ "disconnectnode", 1, "nodeid" },
|
{ "disconnectnode", 1, "nodeid" },
|
||||||
|
@ -3245,8 +3245,11 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
|
|||||||
|
|
||||||
static UniValue bumpfee(const JSONRPCRequest& request)
|
static UniValue bumpfee(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
RPCHelpMan{"bumpfee",
|
bool want_psbt = request.strMethod == "psbtbumpfee";
|
||||||
|
|
||||||
|
RPCHelpMan{request.strMethod,
|
||||||
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
|
"\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
|
||||||
|
+ std::string(want_psbt ? "Returns a PSBT instead of creating and signing a new transaction.\n" : "") +
|
||||||
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
|
"An opt-in RBF transaction with the given txid must be in the wallet.\n"
|
||||||
"The command will pay the additional fee by reducing change outputs or adding inputs when necessary. It may add a new change output if one does not already exist.\n"
|
"The command will pay the additional fee by reducing change outputs or adding inputs when necessary. It may add a new change output if one does not already exist.\n"
|
||||||
"All inputs in the original transaction will be included in the replacement transaction.\n"
|
"All inputs in the original transaction will be included in the replacement transaction.\n"
|
||||||
@ -3277,20 +3280,24 @@ static UniValue bumpfee(const JSONRPCRequest& request)
|
|||||||
"options"},
|
"options"},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
RPCResult::Type::OBJ, "", "", {
|
RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
|
||||||
{RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled."},
|
{
|
||||||
{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."},
|
{RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction." + std::string(want_psbt ? "" : " Only returned when wallet private keys are disabled. (DEPRECATED)")},
|
||||||
|
},
|
||||||
|
want_psbt ? std::vector<RPCResult>{} : std::vector<RPCResult>{{RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."}}
|
||||||
|
),
|
||||||
|
{
|
||||||
{RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
|
{RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
|
||||||
{RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
|
{RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
|
||||||
{RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
|
{RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::STR, "", ""},
|
{RPCResult::Type::STR, "", ""},
|
||||||
}},
|
}},
|
||||||
}
|
})
|
||||||
},
|
},
|
||||||
RPCExamples{
|
RPCExamples{
|
||||||
"\nBump the fee, get the new transaction\'s txid\n" +
|
"\nBump the fee, get the new transaction\'s" + std::string(want_psbt ? "psbt" : "txid") + "\n" +
|
||||||
HelpExampleCli("bumpfee", "<txid>")
|
HelpExampleCli(request.strMethod, "<txid>")
|
||||||
},
|
},
|
||||||
}.Check(request);
|
}.Check(request);
|
||||||
|
|
||||||
@ -3298,6 +3305,10 @@ static UniValue bumpfee(const JSONRPCRequest& request)
|
|||||||
if (!wallet) return NullUniValue;
|
if (!wallet) return NullUniValue;
|
||||||
CWallet* const pwallet = wallet.get();
|
CWallet* const pwallet = wallet.get();
|
||||||
|
|
||||||
|
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !want_psbt) {
|
||||||
|
want_psbt = true;
|
||||||
|
}
|
||||||
|
|
||||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
|
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
|
||||||
uint256 hash(ParseHashV(request.params[0], "txid"));
|
uint256 hash(ParseHashV(request.params[0], "txid"));
|
||||||
|
|
||||||
@ -3382,7 +3393,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
|
|||||||
|
|
||||||
// If wallet private keys are enabled, return the new transaction id,
|
// If wallet private keys are enabled, return the new transaction id,
|
||||||
// otherwise return the base64-encoded unsigned PSBT of the new transaction.
|
// otherwise return the base64-encoded unsigned PSBT of the new transaction.
|
||||||
if (!pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
if (!want_psbt) {
|
||||||
if (!feebumper::SignTransaction(*pwallet, mtx)) {
|
if (!feebumper::SignTransaction(*pwallet, mtx)) {
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
|
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
|
||||||
}
|
}
|
||||||
@ -3415,6 +3426,11 @@ static UniValue bumpfee(const JSONRPCRequest& request)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UniValue psbtbumpfee(const JSONRPCRequest& request)
|
||||||
|
{
|
||||||
|
return bumpfee(request);
|
||||||
|
}
|
||||||
|
|
||||||
UniValue rescanblockchain(const JSONRPCRequest& request)
|
UniValue rescanblockchain(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
RPCHelpMan{"rescanblockchain",
|
RPCHelpMan{"rescanblockchain",
|
||||||
@ -4160,6 +4176,7 @@ static const CRPCCommand commands[] =
|
|||||||
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} },
|
{ "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} },
|
||||||
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
|
{ "wallet", "backupwallet", &backupwallet, {"destination"} },
|
||||||
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
|
{ "wallet", "bumpfee", &bumpfee, {"txid", "options"} },
|
||||||
|
{ "wallet", "psbtbumpfee", &psbtbumpfee, {"txid", "options"} },
|
||||||
{ "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors"} },
|
{ "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys", "blank", "passphrase", "avoid_reuse", "descriptors"} },
|
||||||
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
|
{ "wallet", "dumpprivkey", &dumpprivkey, {"address"} },
|
||||||
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
|
{ "wallet", "dumpwallet", &dumpwallet, {"filename"} },
|
||||||
|
@ -123,13 +123,19 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
|
|||||||
self.sync_mempools((rbf_node, peer_node))
|
self.sync_mempools((rbf_node, peer_node))
|
||||||
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
|
assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
|
||||||
if mode == "fee_rate":
|
if mode == "fee_rate":
|
||||||
|
bumped_psbt = rbf_node.psbtbumpfee(rbfid, {"fee_rate": NORMAL})
|
||||||
bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": NORMAL})
|
bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": NORMAL})
|
||||||
else:
|
else:
|
||||||
|
bumped_psbt = rbf_node.psbtbumpfee(rbfid)
|
||||||
bumped_tx = rbf_node.bumpfee(rbfid)
|
bumped_tx = rbf_node.bumpfee(rbfid)
|
||||||
assert_equal(bumped_tx["errors"], [])
|
assert_equal(bumped_tx["errors"], [])
|
||||||
assert bumped_tx["fee"] > -rbftx["fee"]
|
assert bumped_tx["fee"] > -rbftx["fee"]
|
||||||
assert_equal(bumped_tx["origfee"], -rbftx["fee"])
|
assert_equal(bumped_tx["origfee"], -rbftx["fee"])
|
||||||
assert "psbt" not in bumped_tx
|
assert "psbt" not in bumped_tx
|
||||||
|
assert_equal(bumped_psbt["errors"], [])
|
||||||
|
assert bumped_psbt["fee"] > -rbftx["fee"]
|
||||||
|
assert_equal(bumped_psbt["origfee"], -rbftx["fee"])
|
||||||
|
assert "psbt" in bumped_psbt
|
||||||
# check that bumped_tx propagates, original tx was evicted and has a wallet conflict
|
# check that bumped_tx propagates, original tx was evicted and has a wallet conflict
|
||||||
self.sync_mempools((rbf_node, peer_node))
|
self.sync_mempools((rbf_node, peer_node))
|
||||||
assert bumped_tx["txid"] in rbf_node.getrawmempool()
|
assert bumped_tx["txid"] in rbf_node.getrawmempool()
|
||||||
@ -391,7 +397,7 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
|
|||||||
assert_equal(len(watcher.decodepsbt(psbt)["tx"]["vin"]), 1)
|
assert_equal(len(watcher.decodepsbt(psbt)["tx"]["vin"]), 1)
|
||||||
|
|
||||||
# Bump fee, obnoxiously high to add additional watchonly input
|
# Bump fee, obnoxiously high to add additional watchonly input
|
||||||
bumped_psbt = watcher.bumpfee(original_txid, {"fee_rate": HIGH})
|
bumped_psbt = watcher.psbtbumpfee(original_txid, {"fee_rate": HIGH})
|
||||||
assert_greater_than(len(watcher.decodepsbt(bumped_psbt['psbt'])["tx"]["vin"]), 1)
|
assert_greater_than(len(watcher.decodepsbt(bumped_psbt['psbt'])["tx"]["vin"]), 1)
|
||||||
assert "txid" not in bumped_psbt
|
assert "txid" not in bumped_psbt
|
||||||
assert_equal(bumped_psbt["origfee"], -watcher.gettransaction(original_txid)["fee"])
|
assert_equal(bumped_psbt["origfee"], -watcher.gettransaction(original_txid)["fee"])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user