mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-10 11:57:28 -03:00
Merge bitcoin/bitcoin#22513: rpc: Allow walletprocesspsbt to sign without finalizing
a99ed89865
psbt: sign without finalizing (Andrew Chow) Pull request description: It can be useful to sign an input with `walletprocesspsbt` but not finalize that input if it is complete. This PR adds another option to `walletprocesspsbt` to be able to do that. We will still finalize by default. This does not materially change the PSBT workflow since `finalizepsbt` needs to be called in order to extract the tx for broadcast. ACKs for top commit: meshcollider: utACKa99ed89865
Sjors: utACKa99ed89
Tree-SHA512: c88e5d3222109c5f4e763b1b9d97ce4655f68f2985a4509caab2d4e7f5bac5047328fd69696e82a330f5c5a333e0312568ae293515689b77a4747ca2f17caca6
This commit is contained in:
commit
383d350bd5
11 changed files with 32 additions and 20 deletions
|
@ -247,7 +247,7 @@ PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction&
|
|||
return txdata;
|
||||
}
|
||||
|
||||
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata)
|
||||
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata, bool finalize)
|
||||
{
|
||||
PSBTInput& input = psbt.inputs.at(index);
|
||||
const CMutableTransaction& tx = *psbt.tx;
|
||||
|
@ -295,6 +295,10 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
|
|||
}
|
||||
// Verify that a witness signature was produced in case one was required.
|
||||
if (require_witness_sig && !sigdata.witness) return false;
|
||||
|
||||
// If we are not finalizing, set sigdata.complete to false to not set the scriptWitness
|
||||
if (!finalize && sigdata.complete) sigdata.complete = false;
|
||||
|
||||
input.FromSignatureData(sigdata);
|
||||
|
||||
// If we have a witness signature, put a witness UTXO.
|
||||
|
@ -324,7 +328,7 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
|
|||
bool complete = true;
|
||||
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
|
||||
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
|
||||
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL);
|
||||
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL, nullptr, true);
|
||||
}
|
||||
|
||||
return complete;
|
||||
|
|
|
@ -578,7 +578,7 @@ bool PSBTInputSigned(const PSBTInput& input);
|
|||
* txdata should be the output of PrecomputePSBTData (which can be shared across
|
||||
* multiple SignPSBTInput calls). If it is nullptr, a dummy signature will be created.
|
||||
**/
|
||||
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr);
|
||||
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool finalize = true);
|
||||
|
||||
/** Counts the unsigned inputs of a PSBT. */
|
||||
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
|
||||
|
|
|
@ -115,6 +115,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "walletcreatefundedpsbt", 4, "bip32derivs" },
|
||||
{ "walletprocesspsbt", 1, "sign" },
|
||||
{ "walletprocesspsbt", 3, "bip32derivs" },
|
||||
{ "walletprocesspsbt", 4, "finalize" },
|
||||
{ "createpsbt", 0, "inputs" },
|
||||
{ "createpsbt", 1, "outputs" },
|
||||
{ "createpsbt", 2, "locktime" },
|
||||
|
|
|
@ -60,10 +60,10 @@ bool ExternalSignerScriptPubKeyMan::DisplayAddress(const CScript scriptPubKey, c
|
|||
}
|
||||
|
||||
// If sign is true, transaction must previously have been filled
|
||||
TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
|
||||
TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
{
|
||||
if (!sign) {
|
||||
return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed);
|
||||
return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed, finalize);
|
||||
}
|
||||
|
||||
// Already complete if every input is now signed
|
||||
|
@ -79,6 +79,6 @@ TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransact
|
|||
tfm::format(std::cerr, "Failed to sign: %s\n", strFailReason);
|
||||
return TransactionError::EXTERNAL_SIGNER_FAILED;
|
||||
}
|
||||
FinalizePSBT(psbt); // This won't work in a multisig setup
|
||||
if (finalize) FinalizePSBT(psbt); // This won't work in a multisig setup
|
||||
return TransactionError::OK;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,6 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
|
|||
|
||||
bool DisplayAddress(const CScript scriptPubKey, const ExternalSigner &signer) const;
|
||||
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
};
|
||||
#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
|
||||
|
|
|
@ -4551,6 +4551,7 @@ static RPCHelpMan walletprocesspsbt()
|
|||
" \"NONE|ANYONECANPAY\"\n"
|
||||
" \"SINGLE|ANYONECANPAY\""},
|
||||
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
|
||||
{"finalize", RPCArg::Type::BOOL, RPCArg::Default{true}, "Also finalize inputs if possible"},
|
||||
},
|
||||
RPCResult{
|
||||
RPCResult::Type::OBJ, "", "",
|
||||
|
@ -4572,7 +4573,7 @@ static RPCHelpMan walletprocesspsbt()
|
|||
// the user could have gotten from another RPC command prior to now
|
||||
wallet.BlockUntilSyncedToCurrentChain();
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL, UniValue::VSTR});
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR});
|
||||
|
||||
// Unserialize the transaction
|
||||
PartiallySignedTransaction psbtx;
|
||||
|
@ -4587,11 +4588,12 @@ static RPCHelpMan walletprocesspsbt()
|
|||
// Fill transaction with our data and also sign
|
||||
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
|
||||
bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
|
||||
bool finalize = request.params[4].isNull() ? true : request.params[4].get_bool();
|
||||
bool complete = true;
|
||||
|
||||
if (sign) EnsureWalletIsUnlocked(*pwallet);
|
||||
|
||||
const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs)};
|
||||
const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs, nullptr, finalize)};
|
||||
if (err != TransactionError::OK) {
|
||||
throw JSONRPCTransactionError(err);
|
||||
}
|
||||
|
|
|
@ -610,7 +610,7 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
|
|||
return SigningResult::SIGNING_FAILED;
|
||||
}
|
||||
|
||||
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
|
||||
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
{
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
|
@ -639,7 +639,7 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
|
|||
}
|
||||
SignatureData sigdata;
|
||||
input.FillSignatureData(sigdata);
|
||||
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
|
||||
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize);
|
||||
|
||||
bool signed_one = PSBTInputSigned(input);
|
||||
if (n_signed && (signed_one || !sign)) {
|
||||
|
@ -2074,7 +2074,7 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
|
|||
return SigningResult::OK;
|
||||
}
|
||||
|
||||
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
|
||||
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
{
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
|
@ -2124,7 +2124,7 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
|
|||
}
|
||||
}
|
||||
|
||||
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type);
|
||||
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize);
|
||||
|
||||
bool signed_one = PSBTInputSigned(input);
|
||||
if (n_signed && (signed_one || !sign)) {
|
||||
|
|
|
@ -224,7 +224,7 @@ public:
|
|||
/** Sign a message with the given script */
|
||||
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
|
||||
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
|
||||
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
|
||||
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const { return TransactionError::INVALID_PSBT; }
|
||||
|
||||
virtual uint256 GetID() const { return uint256(); }
|
||||
|
||||
|
@ -388,7 +388,7 @@ public:
|
|||
|
||||
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
|
||||
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
|
||||
uint256 GetID() const override;
|
||||
|
||||
|
@ -593,7 +593,7 @@ public:
|
|||
|
||||
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
|
||||
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
|
||||
uint256 GetID() const override;
|
||||
|
||||
|
|
|
@ -1849,7 +1849,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint,
|
|||
return false;
|
||||
}
|
||||
|
||||
TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed) const
|
||||
TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed, bool finalize) const
|
||||
{
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
|
@ -1881,7 +1881,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
// Fill in information from ScriptPubKeyMans
|
||||
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
|
||||
int n_signed_this_spkm = 0;
|
||||
TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
|
||||
TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm, finalize);
|
||||
if (res != TransactionError::OK) {
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -555,6 +555,8 @@ public:
|
|||
* @param[in] sighash_type the sighash type to use when signing (if PSBT does not specify)
|
||||
* @param[in] sign whether to sign or not
|
||||
* @param[in] bip32derivs whether to fill in bip32 derivation information if available
|
||||
* @param[out] n_signed the number of inputs signed by this wallet
|
||||
* @param[in] finalize whether to create the final scriptSig or scriptWitness if possible
|
||||
* return error
|
||||
*/
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbtx,
|
||||
|
@ -562,7 +564,8 @@ public:
|
|||
int sighash_type = 1 /* SIGHASH_ALL */,
|
||||
bool sign = true,
|
||||
bool bip32derivs = true,
|
||||
size_t* n_signed = nullptr) const;
|
||||
size_t* n_signed = nullptr,
|
||||
bool finalize = true) const;
|
||||
|
||||
/**
|
||||
* Submit the transaction to the node's mempool and then relay to peers.
|
||||
|
|
|
@ -120,7 +120,9 @@ class PSBTTest(BitcoinTestFramework):
|
|||
self.nodes[0].walletpassphrase(passphrase="password", timeout=1000000)
|
||||
|
||||
# Sign the transaction and send
|
||||
signed_tx = self.nodes[0].walletprocesspsbt(psbtx)['psbt']
|
||||
signed_tx = self.nodes[0].walletprocesspsbt(psbt=psbtx, finalize=False)['psbt']
|
||||
finalized_tx = self.nodes[0].walletprocesspsbt(psbt=psbtx, finalize=True)['psbt']
|
||||
assert signed_tx != finalized_tx
|
||||
final_tx = self.nodes[0].finalizepsbt(signed_tx)['hex']
|
||||
self.nodes[0].sendrawtransaction(final_tx)
|
||||
|
||||
|
|
Loading…
Reference in a new issue