diff --git a/doc/multisig-tutorial.md b/doc/multisig-tutorial.md index fd74e62ed28f..0a9379d9caf7 100644 --- a/doc/multisig-tutorial.md +++ b/doc/multisig-tutorial.md @@ -20,15 +20,27 @@ This tutorial also uses the default PKH derivation path to get the xpubs and doe For a 2-of-3 multisig, create 3 wallets. These wallets contain HD seed and private keys, which will be used to sign the PSBTs and derive the xpub. -These three wallets should not be used directly for privacy reasons (public key reuse). They should only be used to sign transactions for the (watch-only) multisig wallet. +These three wallets should not be used directly for privacy reasons (public key reuse). They should only be used to sign transactions for the (watch-only) multisig wallet. To make that less likely to happen by accident, participant wallets only have singlesig legacy addresses, and no singlesig Bech32 addresses. + +`addhdkey` adds an HD key to the blank wallet, which `createwalletdescriptor` then uses: ```bash for ((n=1;n<=3;n++)) do - ./build/bin/bitcoin rpc -signet createwallet "participant_${n}" + ./build/bin/bitcoin rpc -signet -named createwallet wallet_name="participant_${n}" blank=true + ./build/bin/bitcoin rpc -signet -rpcwallet="participant_${n}" addhdkey + ./build/bin/bitcoin rpc -signet -rpcwallet="participant_${n}" createwalletdescriptor legacy done ``` +The `legacy` descriptor is only there to hold the private keys: it derives from `m/44h/1h/0h/<0;1>/*`, the same paths as the multisig descriptor defined below, and a wallet can only sign for a derivation path that one of its descriptors uses. + +A later step spends from the multisig wallet, so create one more wallet to receive that payment. This one is an ordinary singlesig wallet, standing in for whoever is being paid: + +```bash +./build/bin/bitcoin rpc -signet createwallet "recipient" +``` + Extract the xpub of each wallet. To do this, the `derivehdkey` RPC is used. Note that previously at least two descriptors were usually used, one for external derivation paths and one for internal ones. Since https://github.com/bitcoin/bitcoin/pull/22838 this redundancy has been eliminated by a multipath descriptor with <0;1> at the [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#change) change level expanding to external and internal descriptors when imported. @@ -100,6 +112,7 @@ Once the wallets have already been created and this tutorial needs to be repeate ```bash for ((n=1;n<=3;n++)); do ./build/bin/bitcoin rpc -signet loadwallet "participant_${n}"; done ./build/bin/bitcoin rpc -signet loadwallet "multisig_wallet_01" +./build/bin/bitcoin rpc -signet loadwallet "recipient" ``` ### 1.4 Fund the wallet @@ -138,7 +151,7 @@ PSBT is a data format that allows wallets and other tools to exchange informatio The current PSBT version (v0) is defined in [BIP 174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki). -For simplicity, the destination address is taken from the `participant_1` wallet in the code above, but it can be any valid bitcoin address. +The destination address is taken from the `recipient` wallet. The `walletcreatefundedpsbt` RPC is used to create and fund a transaction in the PSBT format. It is the first step in creating the PSBT. @@ -147,7 +160,7 @@ balance=$(./build/bin/bitcoin rpc -signet -rpcwallet="multisig_wallet_01" getbal amount=$(echo "$balance * 0.8" | bc -l | sed -e 's/^\./0./' -e 's/^-\./-0./') -destination_addr=$(./build/bin/bitcoin rpc -signet -rpcwallet="participant_1" getnewaddress) +destination_addr=$(./build/bin/bitcoin rpc -signet -rpcwallet="recipient" getnewaddress "" bech32m) funded_psbt=$(./build/bin/bitcoin rpc -signet -rpcwallet="multisig_wallet_01" walletcreatefundedpsbt outputs="{\"$destination_addr\": $amount}" | jq -r '.psbt') ``` diff --git a/doc/release-notes-32857.md b/doc/release-notes-32857.md new file mode 100644 index 000000000000..24cdb811e794 --- /dev/null +++ b/doc/release-notes-32857.md @@ -0,0 +1,8 @@ +Updated RPCs +------------ + +- The `send`, `sendall`, `walletprocesspsbt`, `walletcreatefundedpsbt`, and + `descriptorprocesspsbt` RPCs now accept a `keypath_only` option. When enabled, + they do not add new Taproot script-path data, sign script paths, or finalize + script-path spends. Existing script-path data and signatures remain in a + supplied PSBT. (#32857) diff --git a/doc/release-notes-32861.md b/doc/release-notes-32861.md new file mode 100644 index 000000000000..9030ee57f9f9 --- /dev/null +++ b/doc/release-notes-32861.md @@ -0,0 +1,9 @@ +Updated RPCs +------------ + +- `createwalletdescriptor` no longer requires the `hdkey` argument when the + wallet has no active descriptors but does have exactly one HD key added with + `addhdkey`. Previously the user had to look the key up with `listdescriptors` + and pass it in. This makes it easier to set up a blank wallet that only has + the descriptors it actually needs, which `doc/multisig-tutorial.md` now + demonstrates. (#32861) diff --git a/src/common/types.h b/src/common/types.h index 1ffcd392d609..efcebc9062df 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -48,6 +48,12 @@ struct PSBTFillOptions { * Whether to fill in bip32 derivation information if available. */ bool bip32_derivs{true}; + + /** + * Only add new Taproot key-path data, and only sign and finalize Taproot + * inputs using the key path. Existing script-path data is left intact. + */ + bool taproot_keypath_only{false}; }; } // namespace common diff --git a/src/core_io.cpp b/src/core_io.cpp index 3650d70810a9..cbaf24171237 100644 --- a/src/core_io.cpp +++ b/src/core_io.cpp @@ -338,9 +338,13 @@ const std::map mapSigHashTypes = { {static_cast(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY), std::string("SINGLE|ANYONECANPAY")}, }; -std::string SighashToStr(unsigned char sighash_type) +std::string SighashToStr(int32_t sighash_type) { - const auto& it = mapSigHashTypes.find(sighash_type); + // Signatures encode the sighash type in a single byte, but the PSBT field + // for it is a 32 bit unsigned integer in BIP 174 (signed in PSBTInput) + if (sighash_type < 0 || sighash_type > 0xff) return ""; + const uint8_t sighash_byte(sighash_type); + const auto& it = mapSigHashTypes.find(sighash_byte); if (it == mapSigHashTypes.end()) return ""; return it->second; } diff --git a/src/core_io.h b/src/core_io.h index 904f5a8643b9..d78706216c20 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -42,7 +43,7 @@ bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header); UniValue ValueFromAmount(CAmount amount); std::string FormatScript(const CScript& script); std::string EncodeHexTx(const CTransaction& tx); -std::string SighashToStr(unsigned char sighash_type); +std::string SighashToStr(int32_t sighash_type); void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex = true, bool include_address = false, const SigningProvider* provider = nullptr); void TxToUniv(const CTransaction& tx, const uint256& block_hash, UniValue& entry, bool include_hex = true, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_DETAILS, std::function is_change_func = {}); diff --git a/src/external_signer.cpp b/src/external_signer.cpp index cb49cedc69c2..c3e989466891 100644 --- a/src/external_signer.cpp +++ b/src/external_signer.cpp @@ -8,10 +8,13 @@ #include #include #include +#include