@@ -52,14 +52,51 @@ int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_s
52
52
return 1 ;
53
53
}
54
54
55
+ /* Tweak the pubkey corresponding to the provided keyagg cache, update the cache
56
+ * and return the tweaked aggregate pk. */
57
+ int tweak (const secp256k1_context * ctx , secp256k1_xonly_pubkey * agg_pk , secp256k1_musig_keyagg_cache * cache ) {
58
+ secp256k1_pubkey output_pk ;
59
+ unsigned char ordinary_tweak [32 ] = "this could be a BIP32 tweak...." ;
60
+ unsigned char xonly_tweak [32 ] = "this could be a taproot tweak.." ;
61
+
62
+
63
+ /* Ordinary tweaking which, for example, allows deriving multiple child
64
+ * public keys from a single aggregate key using BIP32 */
65
+ if (!secp256k1_musig_pubkey_ec_tweak_add (ctx , NULL , cache , ordinary_tweak )) {
66
+ return 0 ;
67
+ }
68
+ /* Note that we did not provided an output_pk argument, because the
69
+ * resulting pk is also saved in the cache and so if one is just interested
70
+ * in signing the output_pk argument is unnecessary. On the other hand, if
71
+ * one is not interested in signing, the same output_pk can be obtained by
72
+ * calling `secp256k1_musig_pubkey_get` right after key aggregation to get
73
+ * the full pubkey and then call `secp256k1_ec_pubkey_tweak_add`. */
74
+
75
+ /* Xonly tweaking which, for example, allows creating taproot commitments */
76
+ if (!secp256k1_musig_pubkey_xonly_tweak_add (ctx , & output_pk , cache , xonly_tweak )) {
77
+ return 0 ;
78
+ }
79
+ /* Note that if we wouldn't care about signing, we can arrive at the same
80
+ * output_pk by providing the untweaked public key to
81
+ * `secp256k1_xonly_pubkey_tweak_add` (after converting it to an xonly pubkey
82
+ * if necessary with `secp256k1_xonly_pubkey_from_pubkey`). */
83
+
84
+ /* Now we convert the output_pk to an xonly pubkey to allow to later verify
85
+ * the Schnorr signature against it. For this purpose we can ignore the
86
+ * `pk_parity` output argument; we would need it if we would have to open
87
+ * the taproot commitment. */
88
+ if (!secp256k1_xonly_pubkey_from_pubkey (ctx , agg_pk , NULL , & output_pk )) {
89
+ return 0 ;
90
+ }
91
+ return 1 ;
92
+ }
93
+
55
94
/* Sign a message hash with the given key pairs and store the result in sig */
56
- int sign (const secp256k1_context * ctx , struct signer_secrets * signer_secrets , struct signer * signer , const unsigned char * msg32 , unsigned char * sig64 ) {
95
+ int sign (const secp256k1_context * ctx , struct signer_secrets * signer_secrets , struct signer * signer , const secp256k1_musig_keyagg_cache * cache , const unsigned char * msg32 , unsigned char * sig64 ) {
57
96
int i ;
58
- const secp256k1_xonly_pubkey * pubkeys [N_SIGNERS ];
59
97
const secp256k1_musig_pubnonce * pubnonces [N_SIGNERS ];
60
98
const secp256k1_musig_partial_sig * partial_sigs [N_SIGNERS ];
61
99
/* The same for all signers */
62
- secp256k1_musig_keyagg_cache cache ;
63
100
secp256k1_musig_session session ;
64
101
65
102
for (i = 0 ; i < N_SIGNERS ; i ++ ) {
@@ -86,29 +123,25 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
86
123
if (!secp256k1_musig_nonce_gen (ctx , & signer_secrets [i ].secnonce , & signer [i ].pubnonce , session_id , seckey , msg32 , NULL , NULL )) {
87
124
return 0 ;
88
125
}
89
- pubkeys [i ] = & signer [i ].pubkey ;
90
126
pubnonces [i ] = & signer [i ].pubnonce ;
91
127
}
92
128
/* Communication round 1: A production system would exchange public nonces
93
129
* here before moving on. */
94
130
for (i = 0 ; i < N_SIGNERS ; i ++ ) {
95
131
secp256k1_musig_aggnonce agg_pubnonce ;
96
132
97
- /* Create aggregate pubkey, aggregate nonce and initialize signer data */
98
- if (!secp256k1_musig_pubkey_agg (ctx , NULL , NULL , & cache , pubkeys , N_SIGNERS )) {
99
- return 0 ;
100
- }
133
+ /* Create aggregate nonce and initialize the session */
101
134
if (!secp256k1_musig_nonce_agg (ctx , & agg_pubnonce , pubnonces , N_SIGNERS )) {
102
135
return 0 ;
103
136
}
104
- if (!secp256k1_musig_nonce_process (ctx , & session , & agg_pubnonce , msg32 , & cache , NULL )) {
137
+ if (!secp256k1_musig_nonce_process (ctx , & session , & agg_pubnonce , msg32 , cache , NULL )) {
105
138
return 0 ;
106
139
}
107
140
/* partial_sign will clear the secnonce by setting it to 0. That's because
108
141
* you must _never_ reuse the secnonce (or use the same session_id to
109
142
* create a secnonce). If you do, you effectively reuse the nonce and
110
143
* leak the secret key. */
111
- if (!secp256k1_musig_partial_sign (ctx , & signer [i ].partial_sig , & signer_secrets [i ].secnonce , & signer_secrets [i ].keypair , & cache , & session )) {
144
+ if (!secp256k1_musig_partial_sign (ctx , & signer [i ].partial_sig , & signer_secrets [i ].secnonce , & signer_secrets [i ].keypair , cache , & session )) {
112
145
return 0 ;
113
146
}
114
147
partial_sigs [i ] = & signer [i ].partial_sig ;
@@ -127,7 +160,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
127
160
* fine to first verify the aggregate sig, and only verify the individual
128
161
* sigs if it does not work.
129
162
*/
130
- if (!secp256k1_musig_partial_sig_verify (ctx , & signer [i ].partial_sig , & signer [i ].pubnonce , & signer [i ].pubkey , & cache , & session )) {
163
+ if (!secp256k1_musig_partial_sig_verify (ctx , & signer [i ].partial_sig , & signer [i ].pubnonce , & signer [i ].pubkey , cache , & session )) {
131
164
return 0 ;
132
165
}
133
166
}
@@ -141,6 +174,7 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
141
174
struct signer signers [N_SIGNERS ];
142
175
const secp256k1_xonly_pubkey * pubkeys_ptr [N_SIGNERS ];
143
176
secp256k1_xonly_pubkey agg_pk ;
177
+ secp256k1_musig_keyagg_cache cache ;
144
178
unsigned char msg [32 ] = "this_could_be_the_hash_of_a_msg!" ;
145
179
unsigned char sig [64 ];
146
180
@@ -156,13 +190,21 @@ int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, st
156
190
}
157
191
printf ("ok\n" );
158
192
printf ("Combining public keys..." );
159
- if (!secp256k1_musig_pubkey_agg (ctx , NULL , & agg_pk , NULL , pubkeys_ptr , N_SIGNERS )) {
193
+ /* If you just want to aggregate and not sign the cache can be NULL */
194
+ if (!secp256k1_musig_pubkey_agg (ctx , NULL , & agg_pk , & cache , pubkeys_ptr , N_SIGNERS )) {
195
+ printf ("FAILED\n" );
196
+ return 1 ;
197
+ }
198
+ printf ("ok\n" );
199
+ printf ("Tweaking................" );
200
+ /* Optionally tweak the aggregate key */
201
+ if (!tweak (ctx , & agg_pk , & cache )) {
160
202
printf ("FAILED\n" );
161
203
return 1 ;
162
204
}
163
205
printf ("ok\n" );
164
206
printf ("Signing message........." );
165
- if (!sign (ctx , signer_secrets , signers , msg , sig )) {
207
+ if (!sign (ctx , signer_secrets , signers , & cache , msg , sig )) {
166
208
printf ("FAILED\n" );
167
209
return 1 ;
168
210
}
0 commit comments