Skip to content

Commit 3edc738

Browse files
aba9370 test: strip `vin.scriptWitness` from more PSBT test vectors (Kittywhiskers Van Gogh) e4530e6 merge bitcoin#23718: hash preimages fields (Kittywhiskers Van Gogh) dc9342b test: strip `vin.scriptWitness` from PSBT test vectors (Kittywhiskers Van Gogh) c076d02 merge bitcoin#17034: PSBT version, proprietary, and xpub fields (BIP 174) (Kittywhiskers Van Gogh) f1ba319 partial bitcoin#22514: Actually use SIGHASH_DEFAULT for PSBT signing (Kittywhiskers Van Gogh) 0c52bfe merge bitcoin#22513: Allow walletprocesspsbt to sign without finalizing (Kittywhiskers Van Gogh) 193765b merge bitcoin#23403: Fix segfault in the psbt_wallet_tests/psbt_updater_test (Kittywhiskers Van Gogh) ba85b4c merge bitcoin#23303: Fix wallet_multisig_descriptor_psbt.py (Kittywhiskers Van Gogh) c7a69cc merge bitcoin#22067: Test and document a basic M-of-N multisig using descriptor wallets and PSBTs (Kittywhiskers Van Gogh) 3219855 merge bitcoin#23106: Ensure wallet is unlocked before signing PSBT with walletprocesspsbt and GUI (Kittywhiskers Van Gogh) b7c7c7b partial bitcoin#21365: Basic Taproot signing support for descriptor wallets (Kittywhiskers Van Gogh) 4a08920 partial bitcoin#21330: Deal with missing data in signature hashes more consistently (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Even though Dash Core does not implement Taproot, [bitcoin#21365](bitcoin#21365) has been partially backported due to changes related to transaction precomputation, required by later PSBT backports. * Some test vectors introduced in [bitcoin#17034](bitcoin#17034) and [bitcoin#23718](bitcoin#23718) have `psbtx.tx->vin[i].scriptWitness` populated and as Dash Core does not support SegWit, attempting to read the raw data results in serialization errors. The offending field was stripped out by forking Bitcoin Core v24 and modifying `decodepsbt` to strip out the field, reencode the modified data and return it. These changes were made in separate commits. ## Breaking Changes None expected. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)** - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK aba9370 PastaPastaPasta: utACK aba9370 Tree-SHA512: 439e009da032dffaa7b7048984ed27fefb7ce0854240e280a898d20c50132197d5575d8a289391d50f2527ab4f26326481ce78200c3014b6223d9c661e5c9377
2 parents f051036 + aba9370 commit 3edc738

39 files changed

+1054
-174
lines changed

doc/descriptors.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,47 @@ Key order does not matter for `sortedmulti()`. `sortedmulti()` behaves in the sa
117117
as `multi()` does but the keys are reordered in the resulting script such that they
118118
are lexicographically ordered as described in BIP67.
119119

120+
#### Basic multisig example
121+
122+
For a good example of a basic M-of-N multisig between multiple participants using descriptor
123+
wallets and PSBTs, as well as a signing flow, see [this functional test](/test/functional/wallet_multisig_descriptor_psbt.py).
124+
125+
Disclaimers: It is important to note that this example serves as a quick-start and is kept basic for readability. A downside of the approach
126+
outlined here is that each participant must maintain (and backup) two separate wallets: a signer and the corresponding multisig.
127+
It should also be noted that privacy best-practices are not "by default" here - participants should take care to only use the signer to sign
128+
transactions related to the multisig. Lastly, it is not recommended to use anything other than a Bitcoin Core descriptor wallet to serve as your
129+
signer(s). Other wallets, whether hardware or software, likely impose additional checks and safeguards to prevent users from signing transactions that
130+
could lead to loss of funds, or are deemed security hazards. Conforming to various 3rd-party checks and verifications is not in the scope of this example.
131+
132+
The basic steps are:
133+
134+
1. Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet which we will refer to as
135+
the participant's signer wallet. Avoid reusing this wallet for any purpose other than signing transactions from the
136+
corresponding multisig we are about to create. Hint: extract the wallet's xpubs using `listdescriptors` and pick the one from the
137+
`pkh` descriptor since it's least likely to be accidentally reused (legacy addresses)
138+
2. Create a watch-only descriptor wallet (blank, private keys disabled). Now the multisig is created by importing the two descriptors:
139+
`wsh(sortedmulti(<M>,XPUB1/0/*,XPUB2/0/*,…,XPUBN/0/*))` and `wsh(sortedmulti(<M>,XPUB1/1/*,XPUB2/1/*,…,XPUBN/1/*))`
140+
(one descriptor w/ `0` for receiving addresses and another w/ `1` for change). Every participant does this
141+
3. A receiving address is generated for the multisig. As a check to ensure step 2 was done correctly, every participant
142+
should verify they get the same addresses
143+
4. Funds are sent to the resulting address
144+
5. A sending transaction from the multisig is created using `walletcreatefundedpsbt` (anyone can initiate this). It is simple to do
145+
this in the GUI by going to the `Send` tab in the multisig wallet and creating an unsigned transaction (PSBT)
146+
6. At least `M` participants check the PSBT with their multisig using `decodepsbt` to verify the transaction is OK before signing it.
147+
7. (If OK) the participant signs the PSBT with their signer wallet using `walletprocesspsbt`. It is simple to do this in the GUI by
148+
loading the PSBT from file and signing it
149+
8. The signed PSBTs are collected with `combinepsbt`, finalized w/ `finalizepsbt`, and then the resulting transaction is broadcasted
150+
to the network. Note that any wallet (eg one of the signers or multisig) is capable of doing this.
151+
9. Checks that balances are correct after the transaction has been included in a block
152+
153+
You may prefer a daisy chained signing flow where each participant signs the PSBT one after another until
154+
the PSBT has been signed `M` times and is "complete." For the most part, the steps above remain the same, except (6, 7)
155+
change slightly from signing the original PSBT in parallel to signing it in series. `combinepsbt` is not necessary with
156+
this signing flow and the last (`m`th) signer can just broadcast the PSBT after signing. Note that a parallel signing flow may be
157+
preferable in cases where there are more signers. This signing flow is also included in the test / Python example.
158+
[The test](/test/functional/wallet_multisig_descriptor_psbt.py) is meant to be documentation as much as it is a functional test, so
159+
it is kept as simple and readable as possible.
160+
120161
### BIP32 derived keys and chains
121162

122163
Most modern wallet software and hardware uses keys that are derived using

doc/psbt.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ hardware implementations will typically implement multiple roles simultaneously.
9292

9393
#### Multisig with multiple Bitcoin Core instances
9494

95+
For a quick start see [Basic M-of-N multisig example using descriptor wallets and PSBTs](./descriptors.md#basic-multisig-example).
96+
If you are using legacy wallets feel free to continue with the example provided here.
97+
9598
Alice, Bob, and Carol want to create a 2-of-3 multisig address. They're all using
9699
Bitcoin Core. We assume their wallets only contain the multisig funds. In case
97100
they also have a personal wallet, this can be accomplished through the

src/coinjoin/server.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ bool CCoinJoinServer::IsInputScriptSigValid(const CTxIn& txin) const
556556
txNew.vin[nTxInIndex].scriptSig = txin.scriptSig;
557557
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::IsInputScriptSigValid -- verifying scriptSig %s\n", ScriptToAsmStr(txin.scriptSig).substr(0, 24));
558558
// TODO we're using amount=0 here but we should use the correct amount. This works because Dash ignores the amount while signing/verifying (only used in Bitcoin/Segwit)
559-
if (!VerifyScript(txNew.vin[nTxInIndex].scriptSig, sigPubKey, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, MutableTransactionSignatureChecker(&txNew, nTxInIndex, 0))) {
559+
if (!VerifyScript(txNew.vin[nTxInIndex].scriptSig, sigPubKey, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, MutableTransactionSignatureChecker(&txNew, nTxInIndex, 0, MissingDataBehavior::ASSERT_FAIL))) {
560560
LogPrint(BCLog::COINJOIN, "CCoinJoinServer::IsInputScriptSigValid -- VerifyScript() failed on input %d\n", nTxInIndex);
561561
return false;
562562
}

src/node/psbt.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
2222

2323
result.inputs.resize(psbtx.tx->vin.size());
2424

25+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
26+
2527
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
2628
PSBTInput& input = psbtx.inputs[i];
2729
PSBTInputAnalysis& input_analysis = result.inputs[i];
@@ -60,7 +62,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
6062

6163
// Figure out what is missing
6264
SignatureData outdata;
63-
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, &outdata);
65+
bool complete = SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, 1, &outdata);
6466

6567
// Things are missing
6668
if (!complete) {
@@ -119,7 +121,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
119121
PSBTInput& input = psbtx.inputs[i];
120122
Coin newcoin;
121123

122-
if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, 1, nullptr, true) || !psbtx.GetInputUTXO(newcoin.out, i)) {
124+
if (!SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, nullptr, 1) || !psbtx.GetInputUTXO(newcoin.out, i)) {
123125
success = false;
124126
break;
125127
} else {

src/psbt.cpp

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
3737
for (unsigned int i = 0; i < outputs.size(); ++i) {
3838
outputs[i].Merge(psbt.outputs[i]);
3939
}
40+
for (auto& xpub_pair : psbt.m_xpubs) {
41+
if (m_xpubs.count(xpub_pair.first) == 0) {
42+
m_xpubs[xpub_pair.first] = xpub_pair.second;
43+
} else {
44+
m_xpubs[xpub_pair.first].insert(xpub_pair.second.begin(), xpub_pair.second.end());
45+
}
46+
}
4047
unknown.insert(psbt.unknown.begin(), psbt.unknown.end());
4148

4249
return true;
@@ -63,12 +70,15 @@ bool PartiallySignedTransaction::AddOutput(const CTxOut& txout, const PSBTOutput
6370

6471
bool PartiallySignedTransaction::GetInputUTXO(CTxOut& utxo, int input_index) const
6572
{
66-
PSBTInput input = inputs[input_index];
73+
const PSBTInput& input = inputs[input_index];
6774
uint32_t prevout_index = tx->vin[input_index].prevout.n;
6875
if (input.non_witness_utxo) {
6976
if (prevout_index >= input.non_witness_utxo->vout.size()) {
7077
return false;
7178
}
79+
if (input.non_witness_utxo->GetHash() != tx->vin[input_index].prevout.hash) {
80+
return false;
81+
}
7282
utxo = input.non_witness_utxo->vout[prevout_index];
7383
} else {
7484
return false;
@@ -127,6 +137,10 @@ void PSBTInput::Merge(const PSBTInput& input)
127137
if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
128138

129139
partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
140+
ripemd160_preimages.insert(input.ripemd160_preimages.begin(), input.ripemd160_preimages.end());
141+
sha256_preimages.insert(input.sha256_preimages.begin(), input.sha256_preimages.end());
142+
hash160_preimages.insert(input.hash160_preimages.begin(), input.hash160_preimages.end());
143+
hash256_preimages.insert(input.hash256_preimages.begin(), input.hash256_preimages.end());
130144
hd_keypaths.insert(input.hd_keypaths.begin(), input.hd_keypaths.end());
131145
unknown.insert(input.unknown.begin(), input.unknown.end());
132146

@@ -202,7 +216,24 @@ bool PSBTInputSigned(const PSBTInput& input)
202216
return !input.final_script_sig.empty();
203217
}
204218

205-
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash, SignatureData* out_sigdata, bool use_dummy)
219+
PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt)
220+
{
221+
const CMutableTransaction& tx = *psbt.tx;
222+
bool have_all_spent_outputs = true;
223+
std::vector<CTxOut> utxos(tx.vin.size());
224+
for (size_t idx = 0; idx < tx.vin.size(); ++idx) {
225+
if (!psbt.GetInputUTXO(utxos[idx], idx)) have_all_spent_outputs = false;
226+
}
227+
PrecomputedTransactionData txdata;
228+
if (have_all_spent_outputs) {
229+
txdata.Init(tx, std::move(utxos), true);
230+
} else {
231+
txdata.Init(tx, {}, true);
232+
}
233+
return txdata;
234+
}
235+
236+
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, int sighash, SignatureData* out_sigdata, bool finalize)
206237
{
207238
PSBTInput& input = psbt.inputs.at(index);
208239
const CMutableTransaction& tx = *psbt.tx;
@@ -233,12 +264,16 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
233264
}
234265

235266
bool sig_complete;
236-
if (use_dummy) {
267+
if (txdata == nullptr) {
237268
sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
238269
} else {
239-
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, sighash);
270+
MutableTransactionSignatureCreator creator(&tx, index, utxo.nValue, txdata, sighash);
240271
sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
241272
}
273+
274+
// If we are not finalizing, set sigdata.complete to false to not set the scriptSig
275+
if (!finalize && sigdata.complete) sigdata.complete = false;
276+
242277
input.FromSignatureData(sigdata);
243278

244279
// Fill in the missing info
@@ -258,8 +293,9 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx)
258293
// PartiallySignedTransaction did not understand them), this will combine them into a final
259294
// script.
260295
bool complete = true;
296+
const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx);
261297
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
262-
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, SIGHASH_ALL);
298+
complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, SIGHASH_ALL, nullptr, true);
263299
}
264300

265301
return complete;
@@ -331,3 +367,11 @@ bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data,
331367
}
332368
return true;
333369
}
370+
371+
uint32_t PartiallySignedTransaction::GetVersion() const
372+
{
373+
if (m_version != std::nullopt) {
374+
return *m_version;
375+
}
376+
return 0;
377+
}

0 commit comments

Comments
 (0)