Skip to content

Commit 7e02a33

Browse files
committed
rpc: bumpfee signer support
1 parent 304ece9 commit 7e02a33

File tree

3 files changed

+42
-3
lines changed

3 files changed

+42
-3
lines changed

src/wallet/feebumper.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,22 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
239239

240240
bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx) {
241241
LOCK(wallet.cs_wallet);
242-
return wallet.SignTransaction(mtx);
242+
243+
if (wallet.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
244+
// Make a blank psbt
245+
PartiallySignedTransaction psbtx(mtx);
246+
247+
// First fill transaction with our data without signing,
248+
// so external signers are not asked to sign more than once.
249+
bool complete;
250+
wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
251+
const TransactionError err = wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true /* sign */, false /* bip32derivs */);
252+
if (err != TransactionError::OK) return false;
253+
complete = FinalizeAndExtractPSBT(psbtx, mtx);
254+
return complete;
255+
} else {
256+
return wallet.SignTransaction(mtx);
257+
}
243258
}
244259

245260
Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransaction&& mtx, std::vector<bilingual_str>& errors, uint256& bumped_txid)

src/wallet/rpc/spend.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
10101010
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
10111011
if (!pwallet) return NullUniValue;
10121012

1013-
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !want_psbt) {
1013+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER) && !want_psbt) {
10141014
throw JSONRPCError(RPC_WALLET_ERROR, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.");
10151015
}
10161016

@@ -1088,6 +1088,9 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
10881088
// For psbtbumpfee, return the base64-encoded unsigned PSBT of the new transaction.
10891089
if (!want_psbt) {
10901090
if (!feebumper::SignTransaction(*pwallet, mtx)) {
1091+
if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
1092+
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction incomplete. Try psbtbumpfee instead.");
1093+
}
10911094
throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
10921095
}
10931096

test/functional/wallet_signer.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from test_framework.test_framework import BitcoinTestFramework
1414
from test_framework.util import (
1515
assert_equal,
16+
assert_greater_than,
1617
assert_raises_rpc_error,
1718
)
1819

@@ -150,7 +151,7 @@ def test_valid_signer(self):
150151
assert_equal(result[1], {'success': True})
151152
assert_equal(mock_wallet.getwalletinfo()["txcount"], 1)
152153
dest = self.nodes[0].getnewaddress(address_type='bech32')
153-
mock_psbt = mock_wallet.walletcreatefundedpsbt([], {dest:0.5}, 0, {}, True)['psbt']
154+
mock_psbt = mock_wallet.walletcreatefundedpsbt([], {dest:0.5}, 0, {'replaceable': True}, True)['psbt']
154155
mock_psbt_signed = mock_wallet.walletprocesspsbt(psbt=mock_psbt, sign=True, sighashtype="ALL", bip32derivs=True)
155156
mock_psbt_final = mock_wallet.finalizepsbt(mock_psbt_signed["psbt"])
156157
mock_tx = mock_psbt_final["hex"]
@@ -190,6 +191,7 @@ def test_valid_signer(self):
190191

191192
self.log.info('Test send using hww1')
192193

194+
# Don't broadcast transaction yet so the RPC returns the raw hex
193195
res = hww.send(outputs={dest:0.5},options={"add_to_wallet": False})
194196
assert(res["complete"])
195197
assert_equal(res["hex"], mock_tx)
@@ -199,6 +201,25 @@ def test_valid_signer(self):
199201
res = hww.sendall(recipients=[{dest:0.5}, hww.getrawchangeaddress()],options={"add_to_wallet": False})
200202
assert(res["complete"])
201203
assert_equal(res["hex"], mock_tx)
204+
# Broadcast transaction so we can bump the fee
205+
hww.sendrawtransaction(res["hex"])
206+
207+
self.log.info('Prepare fee bumped mock PSBT')
208+
209+
# Now that the transaction is broadcast, bump fee in mock wallet:
210+
orig_tx_id = res["txid"]
211+
mock_psbt_bumped = mock_wallet.psbtbumpfee(orig_tx_id)["psbt"]
212+
mock_psbt_bumped_signed = mock_wallet.walletprocesspsbt(psbt=mock_psbt_bumped, sign=True, sighashtype="ALL", bip32derivs=True)
213+
214+
with open(os.path.join(self.nodes[1].cwd, "mock_psbt"), "w", encoding="utf8") as f:
215+
f.write(mock_psbt_bumped_signed["psbt"])
216+
217+
self.log.info('Test bumpfee using hww1')
218+
219+
# Bump fee
220+
res = hww.bumpfee(orig_tx_id)
221+
assert_greater_than(res["fee"], res["origfee"])
222+
assert_equal(res["errors"], [])
202223

203224
# # Handle error thrown by script
204225
# self.set_mock_result(self.nodes[4], "2")

0 commit comments

Comments
 (0)