@@ -21,6 +21,10 @@ import { randomBytes } from 'crypto'; // Use Node.js's crypto module
2121
2222
2323
24+ /********************************************************************************************/
25+ /* ATOMIC SWAP PRIMITIVES */
26+ /********************************************************************************************/
27+
2428//the Alice adaptator signature
2529//sk_A is Alice secret Key
2630//rA is the secnonce
@@ -126,114 +130,234 @@ export function atomic_example(){
126130
127131}
128132
129- export class SCL_Atomic_Swap
130- {
131- constructor ( curve ) {
132- this . signer = new SCL_Musig2 ( curve ) ;
133-
134- this . curve = signer . curve ;
135- }
136-
137- Psign_adapt ( psig , t ) {
133+ function Psign_adapt ( psig , t ) {
138134
139-
140135 let sprime = ( int_from_bytes ( psig ) + t ) % this . curve . order ;
141- let G = this . curve . GetBase ( t ) ;
142-
143- return [ sprime , G . multiply ( t ) ] ;
136+
137+ return sprime ;
144138 }
145139
146- Untweak ( t , psigA_adapt , psigB ) {
140+ function Untweak ( t , psigA_adapt , psigB ) {
147141 const sABp = partial_sig_agg ( [ psigA_adapt , psigB ] ) ;
148142
149143 const sAB = ( sABp - t ) % secp256k1 . CURVE . n ;
150144
151145 return sAB ;
152146 }
153147
154- }
155148
156149
157- function test_full_atomic_session ( curve ) {
158- const swapper = new SCL_Atomic_Swap ( curve ) ;
159- const signer = swapper . signer ;
150+ /********************************************************************************************/
151+ /* INITIATOR AUTOMATA*/
152+ /********************************************************************************************/
153+ export class SCL_Atomic_Initiator {
154+
155+ constructor ( curve , pubkey , sk ) {
156+
157+ this . signer = new SCL_Musig2 ( curve ) ;
158+ this . pubkey = pubkey ;
159+ this . sk = sk ;
160+ this . state = "idle" ;
160161
161- console . log ( "/*************************** " ) ;
162- console . log ( "Test full Atomic session on curve" , Curve ) ;
162+ this . nonceA1 = 0 ;
163+ this . nonceA2 = 0 ;
164+
165+ this . nonceB1 = 0 ;
166+ this . nonceB2 = 0 ;
167+
168+ this . t = 0 ;
169+
170+ this . tx1 = 0 ;
171+ this . tx2 = 0 ;
172+ this . tG = 0 ;
173+ }
163174
164- console . log ( " -Generate random keys" ) ;
175+ ResetSession ( ) {
176+ this . state = "idle" ;
165177
166- const sk1 = signer . curve . Get_Random_privateKey ( ) ; //this provides a 32 bytes array
167- const sk2 = signer . curve . Get_Random_privateKey ( ) ;
178+ this . nonceA1 = 0 ;
179+ this . nonceA2 = 0 ;
168180
169- console . log ( "sk1=" , sk1 ) ;
170- console . log ( "sk2=" , sk2 ) ;
171- let seckeys = [ sk1 , sk2 ] ;
181+ this . nonceB1 = 0 ;
182+ this . nonceB2 = 0 ;
183+
184+ }
172185
173- const pubK1 = signer . IndividualPubKey_array ( sk1 ) ;
174- const pubK2 = signer . IndividualPubKey_array ( sk2 ) ;
186+ InitSession ( tx1 , tx2 )
187+ {
175188
176- console . log ( "pubK1=" , pubK1 ) ;
177- console . log ( "pubK2=" , pubK2 ) ;
189+ this . nonceA1 = signer . Nonce_gen ( this . sk , this . pubkey , x_aggpk , tx1 , extra_in1 ) ;
190+ this . nonceA2 = signer . Nonce_gen ( this . sk , this . pubkey , x_aggpk , tx2 , extra_in2 ) ;
191+ this . tx1 = tx1 ;
192+ this . tx2 = tx2 ;
178193
179- const pubkeys = [ pubK1 , pubK2 ] ;
194+ let Message_I1 = [ tx1 , tx2 , nonceA1 [ 1 ] , nonceA2 [ 1 ] ] ;
180195
181- let aggpk = signer . Key_agg ( pubkeys ) [ 0 ] ; //here aggpk is a 32 or 33 bytes compressed public key
182- let x_aggpk = signer . curve . ForceXonly ( aggpk ) ; //x-only version for noncegen, allways 32
196+ this . state = "waitR1"
197+ return Message_I1 ; //this message is broadcast offchain
198+ }
183199
184- console . log ( "Aggregated Pubkey:" , aggpk ) ;
200+ PartialSign_Tweaked ( Message_R1 ) {
201+ let Message_I2 = [ ] ;
185202
186- const msg1 = Buffer . from ( "Unlock 1strkBTC on Starknet to Alice" , 'utf-8' ) ;
187- console . log ( "msg1=" , msg1 ) ;
188- const msg2 = Buffer . from ( "Unlock 1WBTC on Ethereum to Bob" , 'utf-8' ) ;
189- console . log ( "msg2=" , msg2 ) ;
203+ this . nonceB1 = Message_R1 [ 2 ] ;
204+ this . nonceB2 = Message_R1 [ 3 ] ;
190205
206+ let aggnonce1 = signer . Nonce_agg ( [ nonceA1 [ 1 ] . toString ( 'hex' ) , nonceB1 . toString ( 'hex' ) ] ) ;
207+ let aggnonce2 = signer . Nonce_agg ( [ nonceA2 [ 1 ] . toString ( 'hex' ) , nonceB2 . toString ( 'hex' ) ] ) ;
208+
209+ const session_ctx1 = [ aggnonce1 , [ this . pubkey , Message_R1 [ 4 ] ] , [ ] , [ ] , this . tx1 ] ;
210+ const session_ctx2 = [ aggnonce2 , [ this . pubkey , Message_R1 [ 4 ] ] , [ ] , [ ] , this . tx2 ] ;
191211
192- //nonce generation
193- console . log ( " -Generate random Nonces with commitment" , aggpk ) ;
194- //diversification chain
195- const extra_in1 = Buffer . from ( randomBytes ( 32 ) ) ;
196- const extra_in2 = Buffer . from ( randomBytes ( 32 ) ) ;
197-
198212
199- let nonceA1 = signer . Nonce_gen ( seckeys [ 0 ] , pubkeys [ 0 ] , x_aggpk , msg1 , extra_in1 ) ;
200- let nonceB1 = signer . Nonce_gen ( seckeys [ 1 ] , pubkeys [ 1 ] , x_aggpk , msg2 , extra_in1 ) ;
213+ let psigI1 = signer . Psign ( nonceA1 [ 0 ] , this . sk , session_ctx1 ) ;
214+ let psigI2 = signer . Psign ( nonceA2 [ 0 ] , this . sk , session_ctx2 ) ;
215+
216+
217+ this . t = int_from_bytes ( signer . Get_Random_privateKey ( ) ) ;
218+ let G = this . curve . GetBase ( t ) ;
219+ this . tG = G . multiply ( t ) ;
220+
221+
222+ let psigI1p = Psign_adapt ( psigI1 , t )
223+ let psigI2p = Psign_adapt ( psigI2 , t )
224+
225+ Message_I2 = [ psigI1p , psigI2p , this . tG ] ;
226+
227+ this . state = "waitR2"
228+ return Message_I2 ; //this message is broadcast offchain
229+ }
230+
231+ //here it is assumed that Initiator checked that deposit has been made and locked by signature of tx1 on Chain1
232+ FinalUnlock ( Message_R2 ) {
233+ let Message_I3 = [ ] ;
234+
201235
202- let nonceA2 = signer . Nonce_gen ( seckeys [ 0 ] , pubkeys [ 0 ] , x_aggpk , msg1 , extra_in2 ) ;
203- let nonceB2 = signer . Nonce_gen ( seckeys [ 1 ] , pubkeys [ 1 ] , x_aggpk , msg2 , extra_in2 ) ;
236+ this . state = "idle" ;
237+ return Message_I3 ; //this message is broadcast onchain to unlock initiator exit liquidity
238+
239+ }
204240
241+
242+ }
243+
244+ /********************************************************************************************/
245+ /* RESPONDER AUTOMATA*/
246+ /********************************************************************************************/
247+ export class SCL_Atomic_Responder {
205248
206- //aggregation of public nonces
207- let aggnonce1 = signer . Nonce_agg ( [ nonceA1 [ 1 ] . toString ( 'hex' ) , nonceB1 [ 1 ] . toString ( 'hex' ) ] ) ;
208- let aggnonce2 = signer . Nonce_agg ( [ nonceA2 [ 1 ] . toString ( 'hex' ) , nonceB2 [ 1 ] . toString ( 'hex' ) ] ) ;
249+ constructor ( curve , pubkey , sk ) {
250+
251+ this . signer = new SCL_Musig2 ( curve ) ;
252+ this . pubkey = pubkey ;
253+ this . sk = sk ;
254+ this . state = "idle" ;
255+
256+ this . nonceA1 = 0 ;
257+ this . nonceA2 = 0 ;
258+
259+ this . nonceB1 = 0 ;
260+ this . nonceB2 = 0 ;
261+
262+ this . tx1 = 0 ;
263+ this . tx2 = 0 ;
264+ }
265+
266+
267+ ResetSession ( ) {
268+ this . state = "idle" ;
209269
270+ this . nonceA1 = 0 ;
271+ this . nonceA2 = 0 ;
272+
273+ this . nonceB1 = 0 ;
274+ this . nonceB2 = 0 ;
275+
276+ }
210277
211- const session_ctx1 = [ aggnonce1 , pubkeys , [ ] , [ ] , msg ] ;
212- const session_ctx2 = [ aggnonce2 , pubkeys , [ ] , [ ] , msg ] ;
278+ RespondInit ( Message_I1 )
279+ {
280+ let tx1 = Message_I1 [ 0 ] ;
281+ let tx2 = Message_I1 [ 1 ] ;
213282
214- let pA1 = signer . Psign ( nonceA1 [ 0 ] , seckeys [ 0 ] , session_ctx1 ) ;
215- let pA2 = signer . Psign ( nonceA2 [ 0 ] , seckeys [ 0 ] , session_ctx2 ) ;
283+ let nonceA1 = Message_I1 [ 2 ] ;
284+ let nonceA2 = Message_I1 [ 3 ] ;
216285
286+ let nonceB1 = signer . Nonce_gen ( this . sk , this . pubkey , x_aggpk , tx1 , extra_in1 ) ;
287+ let nonceB2 = signer . Nonce_gen ( this . sk , this . pubkey , x_aggpk , tx2 , extra_in2 ) ;
217288
218- let pB1 = signer . Psign ( nonceB1 [ 0 ] , seckeys [ 0 ] , session_ctx1 ) ;
219- let pB2 = signer . Psign ( nonceB2 [ 0 ] , seckeys [ 0 ] , session_ctx2 ) ;
289+ let aggnonce1 = signer . Nonce_agg ( [ nonceA1 . toString ( 'hex' ) , nonceB1 [ 1 ] . toString ( 'hex' ) ] ) ;
290+ let aggnonce2 = signer . Nonce_agg ( [ nonceA2 . toString ( 'hex' ) , nonceB2 [ 1 ] . toString ( 'hex' ) ] ) ;
291+
292+ let Message_R1 = [ aggnonce1 , aggnonce2 , nonceB1 [ 1 ] , nonceB2 [ 1 ] , this . pubkey ] ;
220293
221- //Alice tweaks signatures
222- let t = int_from_bytes ( signer . curve . Get_Random_privateKey ( ) ) ;
223- let atomic_ctx1 = swapper . Psign_adapt ( pA1 , t ) ;
224- let atomic_ctx2 = swapper . Psign_adapt ( pA2 , t ) ;
294+ this . state = "waitI2" ;
225295
226- //todo:Bob checks compliance of tG, pA2p, pA1p, msg1, msg2
296+ return Message_R1 ; //this message is broadcast offchain
297+ }
227298
299+ PartialSign ( Message_I2 ) {
300+ let Message_R2 = [ ] ;
228301
229- //Bob compute sAB', then substract to obtain sAB
230- let sAB1 = swapper . Untweak ( t , atomic_ctx1 ) ;
302+ //Prior to release PsigB, check compliance of transmitted elements
231303
232- //
304+ //Compute partial sig
305+ let psigI1 = signer . Psign ( nonceB1 [ 0 ] , this . sk , session_ctx1 ) ;
306+ let psigI2 = signer . Psign ( nonceB2 [ 0 ] , this . sk , session_ctx2 ) ;
307+
233308
234309
310+ this . state = "waitI3" ;
311+ return Message_R2 ; //this message is broadcast onchain to unlock responder exit liquidity
235312
313+ }
236314
237- console . log ( "p1=" , p1 ) ;
315+ FinalUnlock ( Message_I3 ) {
316+ let Message_R3 = [ ] ;
317+
318+ this . state = "idle" ;
319+ return Message_R3 ; //this message is broadcast onchain to unlock responder exit liquidity
320+
321+ }
322+
323+ }
324+
325+
326+ //example of full session with automata
327+ //note that worst case is assumed (Bob read tweak from Alice's signature)
328+ function test_full_atomic_session_automatas ( curve ) {
329+ let signer = new SCL_Musig2 ( curve ) ;
330+
331+ //generating keypairs
332+ let Initiator = new SCL_Atomic_Initiator ( curve , signer . curve . Get_Random_privateKey ( ) ) ;
333+ let Responder = new SCL_Atomic_Responder ( curve , signer . curve . Get_Random_privateKey ( ) ) ;
334+
335+ //the transaction unlocking tokens for Alice and Bob, must be multisigned with Musig2
336+ //Alice want to compute msg1 signed by AB
337+ //Bob wants to compute msg2 signed by AB
338+ const msg1 = Buffer . from ( "Unlock 1strkBTC on Starknet to Alice" , 'utf-8' ) ;
339+ const msg2 = Buffer . from ( "Unlock 1WBTC on Ethereum to Bob" , 'utf-8' ) ;
340+
341+
342+ console . log ( "Initiator Start session" ) ;
343+ let Message_I1 = Initiator . InitSession ( msg1 , msg2 ) ; //Initiator sends I1 to responder offchain
344+
345+ console . log ( "Responder Start session" ) ;
346+ let Message_R1 = Responder . RespondInit ( Message_I1 ) ; //Respondeur sends R1 to Initiator offchain
347+
348+ console . log ( "Initiator Partial Sign and tweak" ) ;
349+ let Message_I2 = Initiator . PartialSign_Tweaked ( Message_R1 ) ; //Initiator sends I2 to responder offchain
350+ //At this Point Alice and Bob locks the funds to multisig address on chain 1 and chain 2
351+
352+ console . log ( "Responder Check and Partial Sign" ) ;
353+ let Message_R2 = Responder . PartialSign ( Message_I2 ) ; //Respondeur sends R2 to Initiator offchain
354+
355+ console . log ( "Initiator Signature Aggregation and Unlock" ) ;
356+ let UnlockSigAlice = Initiator . FinalUnlock ( Message_R2 ) ; //final signature to Unlock chain1 token by Initiator
357+
358+ console . log ( "Responder Signature Aggregation and Unlock" ) ;
359+ let UnlockSigBob = Initiator . FinalUnlock ( UnlockSigAlice ) ; //final signature to Unlock chain2 token by Responder
360+
361+ //todo: result is ok if UnlockSigBob is equal to classic multisig
238362
239363}
0 commit comments