diff --git a/ironfish-cli/src/commands/ceremony.ts b/ironfish-cli/src/commands/ceremony.ts index 448a6db93b..b2c80d3cd2 100644 --- a/ironfish-cli/src/commands/ceremony.ts +++ b/ironfish-cli/src/commands/ceremony.ts @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { contribute } from '@ironfish/rust-nodejs' -import { ErrorUtils, PromiseUtils } from '@ironfish/sdk' +import { ErrorUtils, PromiseUtils, TimeUtils } from '@ironfish/sdk' import { CliUx, Flags } from '@oclif/core' import axios from 'axios' import fsAsync from 'fs/promises' @@ -41,6 +41,8 @@ export default class Ceremony extends IronfishCommand { const outputPath = path.join(tempDir, 'newParams') let localHash: string | null = null + let refreshEtaInterval: NodeJS.Timeout | null = null + let etaDate: Date | null = null // Prompt for randomness let randomness: string | null = await CliUx.ux.prompt( @@ -56,12 +58,22 @@ export default class Ceremony extends IronfishCommand { logger: this.logger.withTag('ceremonyClient'), }) - client.onJoined.on(({ queueLocation }) => { + client.onJoined.on(({ queueLocation, estimate }) => { + refreshEtaInterval && clearInterval(refreshEtaInterval) + + etaDate = new Date(Date.now() + estimate) + + CliUx.ux.action.status = renderStatus(queueLocation, etaDate) + refreshEtaInterval = setInterval(() => { + CliUx.ux.action.status = renderStatus(queueLocation, etaDate) + }, 10 * 1000) + CliUx.ux.action.status = `Current position: ${queueLocation}` }) client.onInitiateContribution.on(async ({ downloadLink, contributionNumber }) => { CliUx.ux.action.stop() + refreshEtaInterval && clearInterval(refreshEtaInterval) this.log(`Starting contribution. You are contributor #${contributionNumber}`) @@ -98,6 +110,7 @@ export default class Ceremony extends IronfishCommand { client.onInitiateUpload.on(async ({ uploadLink }) => { CliUx.ux.action.stop() + refreshEtaInterval && clearInterval(refreshEtaInterval) CliUx.ux.action.start(`Uploading your contribution`) @@ -126,6 +139,7 @@ export default class Ceremony extends IronfishCommand { client.onContributionVerified.on(({ hash, downloadLink, contributionNumber }) => { CliUx.ux.action.stop() + refreshEtaInterval && clearInterval(refreshEtaInterval) if (!localHash) { this.log( @@ -187,9 +201,17 @@ export default class Ceremony extends IronfishCommand { } } +const renderStatus = (queueLocation: number, etaDate: Date | null): string => { + return `Current position: ${queueLocation} ${ + etaDate + ? `(Estimated time remaining: ${TimeUtils.renderSpan(etaDate.getTime() - Date.now())})` + : '' + }` +} + const display256CharacterHash = (hash: string): string => { // split string every 8 characters - let slices = hash.match(/.{1,8}/g) ?? [] + let slices: string[] = hash.match(/.{1,8}/g) ?? [] const output = [] for (let i = 0; i < 4; i++) { diff --git a/ironfish-cli/src/trusted-setup/client.ts b/ironfish-cli/src/trusted-setup/client.ts index 79bae9da88..832ac88aa0 100644 --- a/ironfish-cli/src/trusted-setup/client.ts +++ b/ironfish-cli/src/trusted-setup/client.ts @@ -14,7 +14,7 @@ export class CeremonyClient { private stopPromise: Promise<{ success: boolean }> | null = null private stopResolve: ((params: { success: boolean }) => void) | null = null - readonly onJoined = new Event<[{ queueLocation: number }]>() + readonly onJoined = new Event<[{ queueLocation: number; estimate: number }]>() readonly onInitiateUpload = new Event<[{ uploadLink: string }]>() readonly onInitiateContribution = new Event< [{ downloadLink: string; contributionNumber: number }] @@ -92,7 +92,10 @@ export class CeremonyClient { } if (parsedMessage.method === 'joined') { - this.onJoined.emit({ queueLocation: parsedMessage.queueLocation }) + this.onJoined.emit({ + queueLocation: parsedMessage.queueLocation, + estimate: parsedMessage.estimate, + }) } else if (parsedMessage.method === 'initiate-upload') { this.onInitiateUpload.emit({ uploadLink: parsedMessage.uploadLink }) } else if (parsedMessage.method === 'initiate-contribution') { diff --git a/ironfish-cli/src/trusted-setup/schema.ts b/ironfish-cli/src/trusted-setup/schema.ts index 256c9d855a..9e5f707938 100644 --- a/ironfish-cli/src/trusted-setup/schema.ts +++ b/ironfish-cli/src/trusted-setup/schema.ts @@ -8,6 +8,7 @@ export type CeremonyServerMessage = | { method: 'joined' queueLocation: number + estimate: number } | { method: 'initiate-contribution' diff --git a/ironfish-cli/src/trusted-setup/server.ts b/ironfish-cli/src/trusted-setup/server.ts index 533d701126..929c1f8e98 100644 --- a/ironfish-cli/src/trusted-setup/server.ts +++ b/ironfish-cli/src/trusted-setup/server.ts @@ -195,7 +195,8 @@ export class CeremonyServer { } this.queue.push(client) - client.send({ method: 'joined', queueLocation: this.queue.length }) + const estimate = this.queue.length * (this.contributionTimeoutMs + this.uploadTimeoutMs) + client.send({ method: 'joined', queueLocation: this.queue.length, estimate }) client.logger.info(`Connected ${this.queue.length} total`) void this.startNextContributor() @@ -277,7 +278,9 @@ export class CeremonyServer { private sendUpdatedLocationsToClients() { for (const [i, client] of this.queue.entries()) { - client.send({ method: 'joined', queueLocation: i + 1 }) + const queueLocation = i + 1 + const estimate = queueLocation * (this.uploadTimeoutMs + this.contributionTimeoutMs) + client.send({ method: 'joined', queueLocation, estimate }) } }