From 1cb21b95d1a18f426166c0fa78b73329c538cf16 Mon Sep 17 00:00:00 2001 From: Hugh Cunningham Date: Thu, 3 Oct 2024 08:57:47 -0700 Subject: [PATCH] automatically retries messages to multisig broker adds a server 'ack' message that the server sends to the client upon receiving and successfully parsing the client message. each 'ack' contains the client's message id client sets an interval to retry sending each message. when it receives the 'ack' for its message it clears the interval for that message id --- .../src/multisigBroker/clients/client.ts | 31 ++++++++++++++++++- ironfish-cli/src/multisigBroker/messages.ts | 10 ++++++ ironfish-cli/src/multisigBroker/server.ts | 8 +++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/ironfish-cli/src/multisigBroker/clients/client.ts b/ironfish-cli/src/multisigBroker/clients/client.ts index 07b030e4de..cad3033e57 100644 --- a/ironfish-cli/src/multisigBroker/clients/client.ts +++ b/ironfish-cli/src/multisigBroker/clients/client.ts @@ -22,6 +22,7 @@ import { IdentityMessage, IdentitySchema, JoinSessionMessage, + MultisigBrokerAckSchema, MultisigBrokerMessage, MultisigBrokerMessageSchema, MultisigBrokerMessageWithError, @@ -40,6 +41,7 @@ import { SigningStatusSchema, } from '../messages' +const RETRY_INTERVAL = 5000 export abstract class MultisigClient { readonly logger: Logger readonly version: number @@ -68,6 +70,8 @@ export abstract class MultisigClient { sessionId: string | null = null passphrase: string + retries: Map = new Map() + constructor(options: { passphrase: string; logger: Logger }) { this.logger = options.logger this.version = 3 @@ -147,6 +151,10 @@ export abstract class MultisigClient { if (this.connectTimeout) { clearTimeout(this.connectTimeout) } + + for (const retryInterval of this.retries.values()) { + clearInterval(retryInterval) + } } isConnected(): boolean { @@ -215,14 +223,23 @@ export abstract class MultisigClient { return } + const messageId = this.nextMessageId++ + const message: MultisigBrokerMessage = { - id: this.nextMessageId++, + id: messageId, method, sessionId: this.sessionId, body: this.encryptMessageBody(body), } this.writeData(JSON.stringify(message) + '\n') + + this.retries.set( + messageId, + setInterval(() => { + this.writeData(JSON.stringify(message) + '\n') + }, RETRY_INTERVAL), + ) } protected onConnect(): void { @@ -275,6 +292,18 @@ export abstract class MultisigClient { header.result.body = this.decryptMessageBody(header.result.body) switch (header.result.method) { + case 'ack': { + const body = await YupUtils.tryValidate(MultisigBrokerAckSchema, header.result.body) + + if (body.error) { + throw new ServerMessageMalformedError(body.error, header.result.method) + } + + const retryInterval = this.retries.get(body.result.messageId) + clearInterval(retryInterval) + this.retries.delete(body.result.messageId) + break + } case 'identity': { const body = await YupUtils.tryValidate(IdentitySchema, header.result.body) diff --git a/ironfish-cli/src/multisigBroker/messages.ts b/ironfish-cli/src/multisigBroker/messages.ts index bcdab7997b..8ec4d9c441 100644 --- a/ironfish-cli/src/multisigBroker/messages.ts +++ b/ironfish-cli/src/multisigBroker/messages.ts @@ -18,6 +18,10 @@ export interface MultisigBrokerMessageWithError } } +export type MultisigBrokerAckMessage = { + messageId: number +} + export type DkgStartSessionMessage = { minSigners: number maxSigners: number @@ -94,6 +98,12 @@ export const MultisigBrokerMessageWithErrorSchema: yup.ObjectSchema = yup + .object({ + messageId: yup.number().required(), + }) + .required() + export const DkgStartSessionSchema: yup.ObjectSchema = yup .object({ minSigners: yup.number().defined(), diff --git a/ironfish-cli/src/multisigBroker/server.ts b/ironfish-cli/src/multisigBroker/server.ts index ba2b0dee68..3e7f07b548 100644 --- a/ironfish-cli/src/multisigBroker/server.ts +++ b/ironfish-cli/src/multisigBroker/server.ts @@ -12,6 +12,7 @@ import { DkgStatusMessage, IdentityMessage, IdentitySchema, + MultisigBrokerAckMessage, MultisigBrokerMessage, MultisigBrokerMessageSchema, MultisigBrokerMessageWithError, @@ -179,6 +180,7 @@ export class MultisigServer { } this.logger.debug(`Client ${client.id} sent ${message.method} message`) + this.send(client.socket, 'ack', message.sessionId, { messageId: message.id }) if (message.method === 'dkg.start_session') { await this.handleDkgStartSessionMessage(client, message) @@ -320,6 +322,12 @@ export class MultisigServer { body: SigningStatusMessage, ): void send(socket: net.Socket, method: 'connected', sessionId: string, body: ConnectedMessage): void + send( + socket: net.Socket, + method: 'ack', + sessionId: string, + body: MultisigBrokerAckMessage, + ): void send(socket: net.Socket, method: string, sessionId: string, body?: unknown): void { const message: MultisigBrokerMessage = { id: this.nextMessageId++,