Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
251utxo = wallet.listunspent()[0]252else:253assert_equal(utxo, wallet.listunspent()[0])254psbt = wallets[0].walletcreatefundedpsbt(outputs=[{self.def_wallet.getnewaddress(): 5}], inputs=[utxo], change_type="bech32m", changePosition=1, locktime=self.nodes[0].getblockcount())["psbt"]256dec_psbt = self.nodes[0].decodepsbt(psbt)257assert_equal(len(dec_psbt["inputs"]), 1)258assert_equal(len(dec_psbt["inputs"][0]["musig2_participant_pubkeys"]), expected_participant_maps)259if has_internal:260assert_equal(len(dec_psbt["outputs"][1]["musig2_participant_pubkeys"]), expected_participant_maps)262# Check all participant pubkeys in the input and change output263psbt_maps = [dec_psbt["inputs"][0]]264if has_internal:265psbt_maps.append(dec_psbt["outputs"][1])266origin_paths = {ORIGIN_PATH_RE.search(pub).group(1) for _, pub in keys}267for psbt_map in psbt_maps:268part_pks = set()269for agg in psbt_map["musig2_participant_pubkeys"]:270for part_pub in agg["participant_pubkeys"]:271part_pks.add(part_pub[2:])272# Check that there are as many participants as we expected273assert_equal(len(part_pks), len(keys))274# Check that each participant has a derivation path, and that its origin appears in275# that path just once no matter how many musig() expressions the participant is in276for deriv_path in psbt_map["taproot_bip32_derivs"]:277if deriv_path["pubkey"] in part_pks:278origin = next((o for o in origin_paths if deriv_path["path"].startswith(f"m{o}")), None)279assert origin is not None, deriv_path["path"]280assert_equal(deriv_path["path"].count(origin), 1)281part_pks.remove(deriv_path["pubkey"])282assert_equal(len(part_pks), 0)284# Run 2 signing sessions simultaneously to verify no nonce reuse285# Add pubnonces286nonce_psbts = []287nonce_psbts2 = []288for i, wallet in enumerate(wallets):289if nosign_wallets and i in nosign_wallets:290continue291for psbt_list in [nonce_psbts, nonce_psbts2]:292proc = wallet.walletprocesspsbt(psbt=psbt, sighashtype=sighash_type)293assert_equal(proc["complete"], False)294psbt_list.append(proc["psbt"])296comb_nonce_psbt = self.nodes[0].combinepsbt(nonce_psbts)297comb_nonce_psbt2 = self.nodes[0].combinepsbt(nonce_psbts2)299dec_psbt = self.nodes[0].decodepsbt(comb_nonce_psbt)300dec_psbt2 = self.nodes[0].decodepsbt(comb_nonce_psbt2)301assert_equal(len(dec_psbt["inputs"][0]["musig2_pubnonces"]), len(dec_psbt2["inputs"][0]["musig2_pubnonces"]), expected_pubnonces)302for pn, pn2 in zip(dec_psbt["inputs"][0]["musig2_pubnonces"], dec_psbt2["inputs"][0]["musig2_pubnonces"]):303self.assert_musig_signer_data(pn, pn2, "pubnonce")304self.assert_musig_aggregate_in_script(pn, pattern, dec_psbt["inputs"][0])306# Add partial sigs307psig_psbts = []308psig_psbts2 = []309for i, wallet in enumerate(wallets):310if nosign_wallets and i in nosign_wallets:311continue312for psbt, psbt_list in [(comb_nonce_psbt, psig_psbts), (comb_nonce_psbt2, psig_psbts2)]:313proc = wallet.walletprocesspsbt(psbt=psbt, sighashtype=sighash_type)314assert_equal(proc["complete"], False)315psbt_list.append(proc["psbt"])317comb_psig_psbt = self.nodes[0].combinepsbt(psig_psbts)318comb_psig_psbt2 = self.nodes[0].combinepsbt(psig_psbts2)320dec_psbt = self.nodes[0].decodepsbt(comb_psig_psbt)321dec_psbt2 = self.nodes[0].decodepsbt(comb_psig_psbt2)322assert_equal(len(dec_psbt["inputs"][0]["musig2_partial_sigs"]), len(dec_psbt2["inputs"][0]["musig2_partial_sigs"]), expected_partial_sigs)323for ps, ps2 in zip(dec_psbt["inputs"][0]["musig2_partial_sigs"], dec_psbt2["inputs"][0]["musig2_partial_sigs"]):324self.assert_musig_signer_data(ps, ps2, "partial_sig")325self.assert_musig_aggregate_in_script(ps, pattern, dec_psbt["inputs"][0])327# Non-participant aggregates partial sigs and send328finalized = self.nodes[0].finalizepsbt(psbt=comb_psig_psbt, extract=False)329finalized2 = self.nodes[0].finalizepsbt(psbt=comb_psig_psbt2, extract=False)330assert_equal(finalized["complete"], finalized2["complete"], True)331witness = self.nodes[0].decodepsbt(finalized["psbt"])["inputs"][0]["final_scriptwitness"]332assert_not_equal(witness, self.nodes[0].decodepsbt(finalized2["psbt"])["inputs"][0]["final_scriptwitness"])333if scriptpath:334assert_greater_than(len(witness), 1)335else:336assert_equal(len(witness), 1)337finalized = self.nodes[0].finalizepsbt(comb_psig_psbt)338assert "hex" in finalized339self.nodes[0].sendrawtransaction(finalized["hex"])341def run_test(self):342self.def_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)344self.test_success_case("rawtr(musig(keys/*))", "rawtr(musig($0/<0;1>/*,$1/<1;2>/*,$2/<2;3>/*))")345self.test_success_case("rawtr(musig(keys/*)) with ALL|ANYONECANPAY", "rawtr(musig($0/<0;1>/*,$1/<1;2>/*,$2/<2;3>/*))", "ALL|ANYONECANPAY")346self.test_success_case("tr(musig(keys/*)) no multipath", "tr(musig($0/0/*,$1/1/*,$2/2/*))")347self.test_success_case("tr(musig(keys/*)) 2 index multipath", "tr(musig($0/<0;1>/*,$1/<1;2>/*,$2/<2;3>/*))")348self.test_success_case("tr(musig(keys/*)) 3 index multipath", "tr(musig($0/<0;1;2>/*,$1/<1;2;3>/*,$2/<2;3;4>/*))")349self.test_success_case("rawtr(musig/*)", "rawtr(musig($0,$1,$2)/<0;1>/*)")350self.test_success_case("tr(musig/*)", "tr(musig($0,$1,$2)/<0;1>/*)")351self.test_success_case("rawtr(musig(keys/*)) without all wallets importing", "rawtr(musig($0/<0;1>/*,$1/<0;1>/*,$2/<0;1>/*))", only_one_musig_wallet=True)352self.test_success_case("tr(musig(keys/*)) without all wallets importing", "tr(musig($0/<0;1>/*,$1/<0;1>/*,$2/<0;1>/*))", only_one_musig_wallet=True)353self.test_success_case("tr(H, pk(musig(keys/*)))", "tr($H,pk(musig($0/<0;1>/*,$1/<1;2>/*,$2/<2;3>/*)))", scriptpath=True)354self.test_success_case("tr(H,pk(musig/*))", "tr($H,pk(musig($0,$1,$2)/<0;1>/*))", scriptpath=True)355self.test_success_case("tr(H,{pk(musig/*), pk(musig/*)})", "tr($H,{pk(musig($0,$1,$2)/<0;1>/*),pk(musig($3,$4,$5)/0/*)})", scriptpath=True)356self.test_success_case("tr(H,{pk(musig/*), pk(same keys different musig/*)})", "tr($H,{pk(musig($0,$1,$2)/<0;1>/*),pk(musig($1,$2)/0/*)})", scriptpath=True)357self.test_success_case("tr(H,and(pk(musig/*),pk(same musig, other derivation/*)))", "tr($H,and_v(v:pk(musig($0,$1,$2)/<0;1>/*),pk(musig($0,$1,$2)/<2;3>/*)))", scriptpath=True)358self.test_success_case("tr(musig/*,{pk(partial keys diff musig-1/*),pk(partial keys diff musig-2/*)})}", "tr(musig($0,$1,$2)/<3;4>/*,{pk(musig($0,$1)/<5;6>/*),pk(musig($1,$2)/7/*)})")359self.test_success_case("tr(musig/*,{pk(partial keys diff musig-1/*),pk(partial keys diff musig-2/*)})} script-path", "tr(musig($0,$1,$2)/<3;4>/*,{pk(musig($0,$1)/<5;6>/*),pk(musig($1,$2)/7/*)})", scriptpath=True, nosign_wallets=[0])360self.test_success_case("tr(H,and(pk(musig/*),after(1)))", "tr($H,and_v(v:pk(musig($0,$1,$2)/<0;1>/*),after(1)))", scriptpath=True)361self.test_success_case("tr(H,and(pk_k(musig/*),after(1)))", "tr($H,and_v(vc:pk_k(musig($0,$1,$2)/<0;1>/*),after(1)))", scriptpath=True)362self.test_success_case("tr(H,and(pkh(musig/*),after(1)))", "tr($H,and_v(v:pkh(musig($0,$1,$2)/<0;1>/*),after(1)))", scriptpath=True)363self.test_success_case("tr(H,and(pk_h(musig/*),after(1)))", "tr($H,and_v(vc:pk_h(musig($0,$1,$2)/<0;1>/*),after(1)))", scriptpath=True)364self.test_success_case("tr(H,{and(pk(musig/*),after(1)),and(pk(musig/*),after(1))})", "tr($H,{and_v(v:pk(musig($0,$2)/0/*),after(1)),and_v(v:pk(musig($1,$2)/0/*),after(1))})", scriptpath=True)366self.test_failure_case_1("missing participant nonce", "tr(musig($0/<0;1>/*,$1/<1;2>/*,$2/<2;3>/*))")367self.test_failure_case_2("insufficient partial signatures", "rawtr(musig($0/<0;1>/*,$1/<1;2>/*,$2/<2;3>/*))")368self.test_failure_case_3("finalize without partial sigs", "rawtr(musig($0/<0;1>/*,$1/<1;2>/*))")370if __name__ == '__main__':371WalletMuSigTest(__file__).main()
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.