The original impetus of this suggestion was for the reader to avoid reading redundant code, but having the sighash
calculated only once during MuSig2 signing flow for a script_pubkey
seems better from performance POV as well, even though this redundant calculation is inside wallet signing and not in the more latency sensitive operations of the node. This also made the participant derivation info filling in a separate loop before the sighash
calculation.
The nonce and partial sig calculation is done for every participant pubkey that internally calculates the same sighash every time. Consider this redundancy removal before the first MuSig2 flow is checked in.
0diff --git a/src/script/sign.cpp b/src/script/sign.cpp
1index e4e5859de0..8aa9aea12c 100644
2--- a/src/script/sign.cpp
3+++ b/src/script/sign.cpp
4@@ -102,7 +102,7 @@ bool MutableTransactionSignatureCreator::CreateSchnorrSig(const SigningProvider&
5 return true;
6 }
7
8-std::vector<uint8_t> MutableTransactionSignatureCreator::CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const
9+std::vector<uint8_t> MutableTransactionSignatureCreator::CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const
10 {
11 assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
12
13@@ -116,21 +116,17 @@ std::vector<uint8_t> MutableTransactionSignatureCreator::CreateMuSig2Nonce(const
14 const std::vector<CPubKey>& pubkeys = it->second;
15 if (std::find(pubkeys.begin(), pubkeys.end(), part_pubkey) == pubkeys.end()) return {};
16
17- // Compute sighash
18- std::optional<uint256> sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion);
19- if (!sighash.has_value()) return {};
20-
21 MuSig2SecNonce secnonce;
22- std::vector<uint8_t> out = key.CreateMuSig2Nonce(secnonce, *sighash, aggregate_pubkey, pubkeys);
23+ std::vector<uint8_t> out = key.CreateMuSig2Nonce(secnonce, sighash, aggregate_pubkey, pubkeys);
24 if (out.empty()) return {};
25
26 // Store the secnonce in the SigningProvider
27- provider.SetMuSig2SecNonce(MuSig2SessionID(script_pubkey, part_pubkey, *sighash), std::move(secnonce));
28+ provider.SetMuSig2SecNonce(MuSig2SessionID(script_pubkey, part_pubkey, sighash), std::move(secnonce));
29
30 return out;
31 }
32
33-bool MutableTransactionSignatureCreator::CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const
34+bool MutableTransactionSignatureCreator::CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const
35 {
36 assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
37
38@@ -153,17 +149,13 @@ bool MutableTransactionSignatureCreator::CreateMuSig2PartialSig(const SigningPro
39 // Check if enough pubnonces
40 if (pubnonces.size() != pubkeys.size()) return false;
41
42- // Compute sighash
43- std::optional<uint256> sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion);
44- if (!sighash.has_value()) return false;
45-
46 // Retrieve the secnonce
47- uint256 session_id = MuSig2SessionID(script_pubkey, part_pubkey, *sighash);
48+ uint256 session_id = MuSig2SessionID(script_pubkey, part_pubkey, sighash);
49 std::optional<std::reference_wrapper<MuSig2SecNonce>> secnonce = provider.GetMuSig2SecNonce(session_id);
50 if (!secnonce || !secnonce->get().IsValid()) return false;
51
52 // Compute the sig
53- std::optional<uint256> sig = key.CreateMuSig2PartialSig(*sighash, aggregate_pubkey, pubkeys, pubnonces, *secnonce, tweaks);
54+ std::optional<uint256> sig = key.CreateMuSig2PartialSig(sighash, aggregate_pubkey, pubkeys, pubnonces, *secnonce, tweaks);
55 if (!sig) return false;
56 partial_sig = std::move(*sig);
57
58@@ -174,7 +166,7 @@ bool MutableTransactionSignatureCreator::CreateMuSig2PartialSig(const SigningPro
59 return true;
60 }
61
62-bool MutableTransactionSignatureCreator::CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const
63+bool MutableTransactionSignatureCreator::CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const
64 {
65 assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
66 if (!participants.size()) return false;
67@@ -192,11 +184,7 @@ bool MutableTransactionSignatureCreator::CreateMuSig2AggregateSig(const std::vec
68 if (pubnonces.size() != participants.size()) return false;
69 if (partial_sigs.size() != participants.size()) return false;
70
71- // Compute sighash
72- std::optional<uint256> sighash = ComputeSchnorrSignatureHash(leaf_hash, sigversion);
73- if (!sighash.has_value()) return false;
74-
75- std::optional<std::vector<uint8_t>> res = ::CreateMuSig2AggregateSig(participants, aggregate_pubkey, tweaks, *sighash, pubnonces, partial_sigs);
76+ std::optional<std::vector<uint8_t>> res = ::CreateMuSig2AggregateSig(participants, aggregate_pubkey, tweaks, sighash, pubnonces, partial_sigs);
77 if (!res) return false;
78 sig = res.value();
79 if (nHashType) sig.push_back(nHashType);
80@@ -269,10 +257,10 @@ static bool SignMuSig2(const BaseSignatureCreator& creator, SignatureData& sigda
81 {
82 Assert(sigversion == SigVersion::TAPROOT || sigversion == SigVersion::TAPSCRIPT);
83
84+ // Fill participant derivation path info
85 for (const auto& [agg_pub, part_pks] : sigdata.musig2_pubkeys) {
86 if (part_pks.empty()) continue;
87
88- // Fill participant derivation path info
89 for (const auto& part_pk : part_pks) {
90 KeyOriginInfo part_info;
91 if (provider.GetKeyOrigin(part_pk.GetID(), part_info)) {
92@@ -284,6 +272,15 @@ static bool SignMuSig2(const BaseSignatureCreator& creator, SignatureData& sigda
93 if (leaf_hash) it->second.first.insert(*leaf_hash);
94 }
95 }
96+ }
97+
98+ // Compute sighash
99+ std::optional<uint256> sighash = creator.ComputeSchnorrSignatureHash(leaf_hash, sigversion);
100+ if (!sighash.has_value()) return false;
101+
102+ // Execute signing flow
103+ for (const auto& [agg_pub, part_pks] : sigdata.musig2_pubkeys) {
104+ if (part_pks.empty()) continue;
105
106 // The pubkey in the script may not be the actual aggregate of the participants, but derived from it.
107 // Check the derivation, and compute the BIP 32 derivation tweaks
108@@ -318,7 +315,7 @@ static bool SignMuSig2(const BaseSignatureCreator& creator, SignatureData& sigda
109 }
110
111 // First try to aggregate
112- if (creator.CreateMuSig2AggregateSig(part_pks, sig_out, agg_pub, plain_pub, leaf_hash, tweaks, sigversion, sigdata)) {
113+ if (creator.CreateMuSig2AggregateSig(part_pks, sig_out, agg_pub, plain_pub, leaf_hash, tweaks, sigversion, sigdata, *sighash)) {
114 if (sigversion == SigVersion::TAPROOT) {
115 sigdata.taproot_key_path_sig = sig_out;
116 } else {
117@@ -331,7 +328,7 @@ static bool SignMuSig2(const BaseSignatureCreator& creator, SignatureData& sigda
118 auto pub_key_leaf_hash = std::make_pair(plain_pub, leaf_hash ? *leaf_hash : uint256());
119 for (const CPubKey& part_pk : part_pks) {
120 uint256 partial_sig;
121- if (creator.CreateMuSig2PartialSig(provider, partial_sig, agg_pub, plain_pub, part_pk, leaf_hash, tweaks, sigversion, sigdata) && Assume(!partial_sig.IsNull())) {
122+ if (creator.CreateMuSig2PartialSig(provider, partial_sig, agg_pub, plain_pub, part_pk, leaf_hash, tweaks, sigversion, sigdata, *sighash) && Assume(!partial_sig.IsNull())) {
123 sigdata.musig2_partial_sigs[pub_key_leaf_hash].emplace(part_pk, partial_sig);
124 }
125 }
126@@ -344,7 +341,7 @@ static bool SignMuSig2(const BaseSignatureCreator& creator, SignatureData& sigda
127 std::map<CPubKey, std::vector<uint8_t>>& pubnonces = sigdata.musig2_pubnonces[pub_key_leaf_hash];
128 for (const CPubKey& part_pk : part_pks) {
129 if (pubnonces.contains(part_pk)) continue;
130- std::vector<uint8_t> pubnonce = creator.CreateMuSig2Nonce(provider, agg_pub, plain_pub, part_pk, leaf_hash, merkle_root, sigversion, sigdata);
131+ std::vector<uint8_t> pubnonce = creator.CreateMuSig2Nonce(provider, agg_pub, plain_pub, part_pk, leaf_hash, merkle_root, sigversion, sigdata, *sighash);
132 if (pubnonce.empty()) continue;
133 pubnonces[part_pk] = std::move(pubnonce);
134 }
135@@ -969,18 +966,22 @@ public:
136 sig.assign(64, '\000');
137 return true;
138 }
139- std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const override
140+ std::optional<uint256> ComputeSchnorrSignatureHash(const uint256* leaf_hash, SigVersion sigversion) const override
141+ {
142+ return uint256::ONE;
143+ }
144+ std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const override
145 {
146 std::vector<uint8_t> out;
147 out.assign(MUSIG2_PUBNONCE_SIZE, '\000');
148 return out;
149 }
150- bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override
151+ bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const override
152 {
153 partial_sig = uint256::ONE;
154 return true;
155 }
156- bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override
157+ bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const override
158 {
159 sig.assign(64, '\000');
160 return true;
161diff --git a/src/script/sign.h b/src/script/sign.h
162index dd86a8066a..009d70b9ac 100644
163--- a/src/script/sign.h
164+++ b/src/script/sign.h
165@@ -34,9 +34,10 @@ public:
166 /** Create a singular (non-script) signature. */
167 virtual bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const =0;
168 virtual bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const =0;
169- virtual std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const =0;
170- virtual bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0;
171- virtual bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const =0;
172+ virtual std::optional<uint256> ComputeSchnorrSignatureHash(const uint256* leaf_hash, SigVersion sigversion) const =0;
173+ virtual std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const =0;
174+ virtual bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const =0;
175+ virtual bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const =0;
176 };
177
178 /** A signature creator for transactions. */
179@@ -49,17 +50,16 @@ class MutableTransactionSignatureCreator : public BaseSignatureCreator
180 const MutableTransactionSignatureChecker checker;
181 const PrecomputedTransactionData* m_txdata;
182
183- std::optional<uint256> ComputeSchnorrSignatureHash(const uint256* leaf_hash, SigVersion sigversion) const;
184-
185 public:
186 MutableTransactionSignatureCreator(const CMutableTransaction& tx LIFETIMEBOUND, unsigned int input_idx, const CAmount& amount, int hash_type);
187 MutableTransactionSignatureCreator(const CMutableTransaction& tx LIFETIMEBOUND, unsigned int input_idx, const CAmount& amount, const PrecomputedTransactionData* txdata, int hash_type);
188 const BaseSignatureChecker& Checker() const override { return checker; }
189 bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override;
190 bool CreateSchnorrSig(const SigningProvider& provider, std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion) const override;
191- std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata) const override;
192- bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override;
193- bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata) const override;
194+ std::optional<uint256> ComputeSchnorrSignatureHash(const uint256* leaf_hash, SigVersion sigversion) const override;
195+ std::vector<uint8_t> CreateMuSig2Nonce(const SigningProvider& provider, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const uint256* merkle_root, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const override;
196+ bool CreateMuSig2PartialSig(const SigningProvider& provider, uint256& partial_sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const CPubKey& part_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const override;
197+ bool CreateMuSig2AggregateSig(const std::vector<CPubKey>& participants, std::vector<uint8_t>& sig, const CPubKey& aggregate_pubkey, const CPubKey& script_pubkey, const uint256* leaf_hash, const std::vector<std::pair<uint256, bool>>& tweaks, SigVersion sigversion, const SignatureData& sigdata, const uint256& sighash) const override;
198 };
199
200 /** A signature checker that accepts all signatures */