Skip to content

Commit cdb70ef

Browse files
committed
Merge branch 'feat/tss-copayer-ready' of github.com:gabrielbazan7/bitcore
2 parents 8b47106 + ab6f7dd commit cdb70ef

4 files changed

Lines changed: 48 additions & 11 deletions

File tree

packages/bitcore-wallet-client/src/lib/tsssign.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class TssSign extends EventEmitter {
4242
#credentials: Credentials;
4343
#subscriptionId: ReturnType<typeof setInterval>;
4444
#subscriptionRunning: boolean;
45+
#emittedParticipants: Set<string>;
4546
id: string;
4647

4748

@@ -136,6 +137,8 @@ export class TssSign extends EventEmitter {
136137
const msg = await this.#sign.initJoin();
137138
const m = this.#tssKey.metadata.m;
138139
await this.#request.post('/v1/tss/sign/' + this.id, { message: msg, m });
140+
this.#emittedParticipants = new Set([this.#credentials.copayerId]);
141+
this.emit('copayerReady', this.#credentials.copayerId);
139142
return this;
140143
}
141144

@@ -180,6 +183,7 @@ export class TssSign extends EventEmitter {
180183
* - `signature` => ISignature: The signature is ready. Emits the signature object
181184
* - `complete` => void: The signature generation process is complete
182185
* - `error` => Error: An error occurred during the process. Emits the error. Note that this will not stop the subscription.
186+
* - `copayerReady` => string: A copayer has joined the session. Emits the copayer ID.
183187
* @returns {NodeJS.Timeout} Subscription ID
184188
*/
185189
subscribe(params: {
@@ -203,6 +207,16 @@ export class TssSign extends EventEmitter {
203207
const prevRound = thisRound - 1; // Get previous round's messages
204208
const { body } = await this.#request.get(`/v1/tss/sign/${this.id}/${prevRound}`) as RequestResponse;
205209

210+
if (body.participants?.length) {
211+
for (const copayerId of body.participants as string[]) {
212+
if (!this.#emittedParticipants?.has(copayerId)) {
213+
this.#emittedParticipants ??= new Set();
214+
this.#emittedParticipants.add(copayerId);
215+
this.emit('copayerReady', copayerId);
216+
}
217+
}
218+
}
219+
206220
const hasEveryoneSubmitted = body.messages?.length === this.#tssKey.metadata.m - 1; // subtract yourself
207221
if (hasEveryoneSubmitted && !this.#sign.isSignatureReady()) {
208222
this.emit('roundready', thisRound);
@@ -270,6 +284,7 @@ export class TssSign extends EventEmitter {
270284
}
271285
this.#subscriptionId = null;
272286
this.#subscriptionRunning = false;
287+
this.#emittedParticipants = null;
273288
}
274289

275290
/**

packages/bitcore-wallet-client/test/tss.test.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,22 @@ describe('TSS', function() {
634634
sig0.id.should.equal(sig1.id);
635635
});
636636

637+
it(happyPath('should emit copayerReady for self on start() and for remote on subscribe()'), async function() {
638+
const copayerReadyIds: string[] = [];
639+
const sigA = new TssSign({ baseUrl: '/bws/api', request: request(app), credentials: party0Creds, tssKey: party0TssKey });
640+
const sigB = new TssSign({ baseUrl: '/bws/api', request: request(app), credentials: party1Creds, tssKey: party1TssKey });
641+
sigA.on('copayerReady', (id) => copayerReadyIds.push(id));
642+
await sigA.start({ id: 'copayer-ready-test', messageHash, derivationPath });
643+
copayerReadyIds.should.deep.equal([party0Creds.copayerId]);
644+
await sigB.start({ id: 'copayer-ready-test', messageHash, derivationPath });
645+
sigA.on('error', (e) => { should.not.exist(e?.message ?? e); });
646+
const roundsubmitted = new Promise(r => sigA.once('roundsubmitted', r));
647+
sigA.subscribe({ timeout: 10, iterHandler: () => sigA.unsubscribe() });
648+
await roundsubmitted;
649+
copayerReadyIds.should.include(party0Creds.copayerId);
650+
copayerReadyIds.should.include(party1Creds.copayerId);
651+
});
652+
637653
it('should reject too many participants', async function() {
638654
const sig2 = new TssSign({
639655
baseUrl: '/bws/api',
@@ -715,11 +731,12 @@ describe('TSS', function() {
715731
const error = new Promise<Error>(r => sig0.on('error', r));
716732
sig0.subscribe({ timeout: 10, iterHandler: () => sig0.unsubscribe() });
717733
const e = await error;
718-
emitSpy.callCount.should.equal(3);
719-
emitSpy.args[0][0].should.equal('roundready');
720-
emitSpy.args[1][0].should.equal('roundprocessed');
721-
emitSpy.args[2][0].should.equal('error');
722-
emitSpy.args[2][1].should.equal(e);
734+
emitSpy.callCount.should.equal(4);
735+
emitSpy.args[0][0].should.equal('copayerReady');
736+
emitSpy.args[1][0].should.equal('roundready');
737+
emitSpy.args[2][0].should.equal('roundprocessed');
738+
emitSpy.args[3][0].should.equal('error');
739+
emitSpy.args[3][1].should.equal(e);
723740
e.message.should.include('TSS_ROUND_ALREADY_DONE');
724741
});
725742

packages/bitcore-wallet-service/src/lib/routes/tss.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ export class TssRouter {
100100
try {
101101
const { id, round } = req.params as { [key: string]: string };
102102
const copayerId = req.headers['x-identity'];
103-
const { messages, signature } = await TssSign.getMessagesForParty({ id, round: parseInt(round), copayerId });
104-
return res.json({ messages, signature });
103+
const { messages, signature, participants } = await TssSign.getMessagesForParty({ id, round: parseInt(round), copayerId });
104+
return res.json({ messages, signature, participants });
105105
} catch (err) {
106106
return returnError(err ?? 'unknown', res, req);
107107
}

packages/bitcore-wallet-service/src/lib/tss.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,9 @@ class TssKeyGenClass {
279279
export const TssKeyGen = new TssKeyGenClass();
280280

281281
class TssSignClass {
282-
async getMessagesForParty(params: { id: string; round: number; copayerId: string }): Promise<{ messages?: ITssSigMessageObject[]; signature?: ITssSigMessageObject['signature'] }> {
282+
async getMessagesForParty(params: { id: string; round: number; copayerId: string }): Promise<{ messages?: ITssSigMessageObject[]; signature?: ITssSigMessageObject['signature']; participants?: string[] }> {
283283
const { id, round, copayerId } = params;
284-
284+
285285
const storage = WalletService.getStorage();
286286
const session = await storage.fetchTssSigSession({ id });
287287
if (!session) {
@@ -297,14 +297,19 @@ class TssSignClass {
297297
}
298298

299299
const otherPartyMsgs = session.rounds[round].filter(m => m.fromPartyId != party.partyId);
300+
const participants = otherPartyMsgs.map(m => {
301+
const p = session.participants.find(p => p.partyId === m.fromPartyId);
302+
return p?.copayerId;
303+
}).filter(Boolean) as string[];
304+
300305
if (otherPartyMsgs.length === session.m - 1) {
301306
const messages = otherPartyMsgs.map(m => m.messages);
302307
for (const m of messages) {
303308
m.p2pMessages = m.p2pMessages.filter(m => m.to == party.partyId);
304309
}
305-
return { messages, signature: session.signature };
310+
return { messages, signature: session.signature, participants };
306311
}
307-
return {};
312+
return { participants };
308313
}
309314

310315
async processMessage(params: { id: string; message: ITssSigMessageObject; m?: string | number; copayerId: string }) {

0 commit comments

Comments
 (0)