Skip to content

Commit 389658b

Browse files
partial sig verify and automatas
1 parent c728c2c commit 389658b

File tree

3 files changed

+223
-68
lines changed

3 files changed

+223
-68
lines changed

src/libMPC/SCL_Musig2.mjs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,38 @@ Psign(secnonce, sk, session_ctx){
430430
}
431431

432432
/********************************************************************************************/
433-
/* VERIFICATION*/
433+
/* VERIFICATIONS*/
434434
/********************************************************************************************/
435+
436+
//verify one of the partial signature provided by a participant
437+
Psig_verify(psig, pubnonce, pk, session_ctx){
438+
let sessionV=this.Get_session_values(session_ctx);//(Q, gacc, tacc, b, R, e)
439+
let s = int_from_bytes(psig);
440+
let R_s1 = this.curve.PointDecompress(pubnonce.slice(0,this.RawBytesSize));
441+
let R_s2 = this.curve.PointDecompress(pubnonce.slice(this.RawBytesSize,2*this.RawBytesSize));
442+
443+
let Re_s_ =R_s1.add(R_s2.multiply(b));
444+
445+
let Re_s=this.curve.PointCompressXonly(Re_s_);//forced to even point
446+
447+
448+
a=key_agg_coeff(session_ctx[1], pk);
449+
let g=BigInt(1);
450+
if(has_even_y(Q)==false)
451+
g=secp256k1.CURVE.n - g;//n-1
452+
let P=ProjectivePoint.fromHex(pk);//partial input public key
453+
454+
g=(g*gacc) % this.order;
455+
456+
let G= secp256k1.ProjectivePoint.BASE;
457+
let P1 = (G.multiply(s));
458+
let P2=Re_s.add(P.multiply((e*a*g)%this.order));
459+
460+
461+
return (P1.equals(P2));
462+
}
463+
464+
435465
//beware that this function take as input a msb representation of pubkey and signature
436466
//key is assumed to be even
437467
Schnorr_verify(msg, pubkey, sig){
@@ -464,6 +494,7 @@ Psign(secnonce, sk, session_ctx){
464494
}
465495

466496

497+
467498
}
468499
/********************************************************************************************/
469500
/* END OF CLASS MUSIG2 */

src/libMPC/SCL_atomic_swaps.mjs

Lines changed: 190 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

src/libMPC/SCL_ecc.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export class SCL_ecc
137137
}
138138
}
139139

140-
//takes as input a compressed key and return an even Xonly key
140+
//takes as input a msb compressed key and return an even Xonly key
141141
ForceXonly(bytePoint){
142142
if (this.curve === 'secp256k1') {
143143
return bytePoint.slice(1,33);//x-only version for noncegen

0 commit comments

Comments
 (0)