Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move ceremony command to R2 #3648

Merged
merged 2 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions ironfish-cli/src/commands/ceremony.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,6 @@ export default class Ceremony extends IronfishCommand {
let refreshEtaInterval: NodeJS.Timeout | null = null
let etaDate: Date | null = null

// Trusted setup has ended but this command may still be needed in case of future
// setups / network upgrades. So for now, just exit the command with some information
this.log(
`The trusted setup ceremony was completed on Mar 3, 2023. For more information on the trusted setup process and its completion please read https://setup.ironfish.network.`,
)
this.exit(0)

// Prompt for randomness
let randomness: string | null = await CliUx.ux.prompt(
`If you'd like to contribute your own randomness to the ceremony, type it here, then press Enter. For more information on where this should come from and its importance, please read https://setup.ironfish.network. If you'd like the command to generate some randomness for you, just press Enter`,
Expand Down
23 changes: 20 additions & 3 deletions ironfish-cli/src/commands/service/ceremony.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { S3Utils } from '../../utils'
const CONTRIBUTE_TIMEOUT_MS = 5 * 60 * 1000
const UPLOAD_TIMEOUT_MS = 5 * 60 * 1000
const PRESIGNED_EXPIRATION_SEC = 5 * 60
const START_DATE = 1676318400000 // Mon Feb 13 2023 12:00:00 GMT-0800 (Pacific Standard Time)
const START_DATE = 1681146000000 // Monday, April 10, 2023 10:00:00 AM GMT-07:00 (Pacific Daylight Time)

export default class Ceremony extends IronfishCommand {
static hidden = true
Expand All @@ -29,6 +29,14 @@ export default class Ceremony extends IronfishCommand {
description: 'S3 bucket to download and upload params to',
default: 'ironfish-contributions',
}),
downloadPrefix: Flags.string({
char: 'b',
parse: (input: string) => Promise.resolve(input.trim()),
required: false,
description: 'Prefix for contribution download URLs',
// TODO: update this to non-dev endpoint to avoid rate limiting
default: 'https://pub-6a239e04e140459087cf392ffc3245b1.r2.dev',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't the bucket, right? I assume it's like an account name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its the dev testing url. We will replace it with ironfish.network domain name when close to launch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that is basically a link to the bucket

}),
contributionTimeoutMs: Flags.integer({
required: false,
description: 'Allowable milliseconds for a contributor to run the contribution script',
Expand Down Expand Up @@ -65,7 +73,15 @@ export default class Ceremony extends IronfishCommand {
const DEFAULT_HOST = '0.0.0.0'
const DEFAULT_PORT = 9040

const s3Client = S3Utils.getS3Client(true)
const r2Credentials = await S3Utils.getR2Credentials()

if (r2Credentials === undefined) {
this.logger.log('Failed getting R2 credentials from AWS')
this.exit(0)
return
}

const r2Client = S3Utils.getR2S3Client(r2Credentials)

setLogPrefixFromConfig(`[%tag%]`)

Expand All @@ -74,7 +90,8 @@ export default class Ceremony extends IronfishCommand {
port: DEFAULT_PORT,
host: DEFAULT_HOST,
s3Bucket: flags.bucket,
s3Client: s3Client,
downloadPrefix: flags.downloadPrefix,
s3Client: r2Client,
tempDir: this.sdk.config.tempDir,
contributionTimeoutMs: flags.contributionTimeoutMs,
uploadTimeoutMs: flags.uploadTimeoutMs,
Expand Down
19 changes: 11 additions & 8 deletions ironfish-cli/src/trusted-setup/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export class CeremonyServer {
readonly host: string

readonly s3Bucket: string
readonly downloadPrefix: string
private s3Client: S3Client

readonly tempDir: string
Expand All @@ -102,6 +103,7 @@ export class CeremonyServer {
port: number
host: string
s3Bucket: string
downloadPrefix: string
s3Client: S3Client
tempDir: string
contributionTimeoutMs: number
Expand All @@ -121,6 +123,7 @@ export class CeremonyServer {
this.tempDir = options.tempDir

this.s3Bucket = options.s3Bucket
this.downloadPrefix = options.downloadPrefix
this.s3Client = options.s3Client

this.contributionTimeoutMs = options.contributionTimeoutMs
Expand Down Expand Up @@ -188,13 +191,7 @@ export class CeremonyServer {

nextClient.send({
method: 'initiate-contribution',
// S3Client doesn't support unauthenticated downloads, so we can build the URL to download for the client:
downloadLink: S3Utils.getDownloadUrl(
this.s3Bucket,
latestParamName,
{ accelerated: true },
{ dualStack: true },
),
downloadLink: `${this.downloadPrefix}/${latestParamName}`,
contributionNumber: nextParamNumber,
})
}
Expand Down Expand Up @@ -403,14 +400,20 @@ export class CeremonyServer {

client.logger.info(`Uploading verified contribution`)
const destFile = 'params_' + nextParamNumber.toString().padStart(5, '0')

const metadata = {
...(client.name && { contributorName: encodeURIComponent(client.name) }),
...(client.socket.remoteAddress && { remoteAddress: client.socket.remoteAddress }),
}

await S3Utils.uploadToBucket(
this.s3Client,
newParamsDownloadPath,
'application/octet-stream',
this.s3Bucket,
destFile,
client.logger,
client.name ? { contributorName: encodeURIComponent(client.name) } : undefined,
metadata,
)

client.logger.info(`Cleaning up local files`)
Expand Down
34 changes: 34 additions & 0 deletions ironfish-cli/src/utils/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
S3Client,
UploadPartCommand,
} from '@aws-sdk/client-s3'
import { GetSecretValueCommand, SecretsManagerClient } from '@aws-sdk/client-secrets-manager'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { Credentials } from '@aws-sdk/types/dist-types/credentials'
import { Assert, ErrorUtils, Logger } from '@ironfish/sdk'
Expand All @@ -42,6 +43,14 @@ class UploadLastMultipartError extends UploadToBucketError {}
class UploadReadFileError extends UploadToBucketError {}
class UploadFailedError extends UploadToBucketError {}

const R2_SECRET_NAME = 'r2-prod-access-key'
const R2_ENDPOINT = `https://a93bebf26da4c2fe205f71c896afcf89.r2.cloudflarestorage.com`

export type R2Secret = {
r2AccessKeyId: string
r2SecretAccessKey: string
}

export async function uploadToBucket(
s3: S3Client,
filePath: string,
Expand Down Expand Up @@ -309,6 +318,31 @@ export function getS3Client(
})
}

export function getR2S3Client(credentials: {
r2AccessKeyId: string
r2SecretAccessKey: string
}): S3Client {
return new S3Client({
region: 'auto',
endpoint: R2_ENDPOINT,
credentials: {
accessKeyId: credentials.r2AccessKeyId,
secretAccessKey: credentials.r2SecretAccessKey,
},
})
}
Copy link
Contributor

@NullSoldier NullSoldier Mar 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are binary compatible with S3, so I wouldnt expect this to exist. There is no difference in the client. I would just expect these to be parameters that you pass into getS3Client but we can refactor this later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which things would be parameters?


export async function getR2Credentials(): Promise<R2Secret | undefined> {
const client = new SecretsManagerClient({ region: 'us-east-1' })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this region config. If the ceremony server has terraform config, it prob wont need it.
Maybe try it without region on the github workflow

const command = new GetSecretValueCommand({ SecretId: R2_SECRET_NAME })
const response = await client.send(command)
if (response.SecretString === undefined) {
return
} else {
return JSON.parse(response.SecretString) as R2Secret
}
}

export async function getCognitoIdentityCredentials(): Promise<Credentials> {
const identityPoolId = 'us-east-1:3ebc542a-6ac4-4c5d-9558-1621eadd2382'

Expand Down