diff --git a/typescript/infra/src/agents/aws/s3.ts b/typescript/infra/src/agents/aws/s3.ts deleted file mode 100644 index 2c12cd00c3..0000000000 --- a/typescript/infra/src/agents/aws/s3.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; -import { Readable } from 'stream'; - -import { streamToString } from '@hyperlane-xyz/utils'; - -export const S3_BUCKET_REGEX = - /^(?:https?:\/\/)?(.*)\.s3\.(.*)\.amazonaws.com\/?$/; - -export interface S3Receipt { - data: T; - modified: Date; -} - -export class S3Wrapper { - private readonly client: S3Client; - readonly bucket: string; - readonly region: string; - readonly folder: string | undefined; - - static fromBucketUrl(bucketUrl: string): S3Wrapper { - const match = bucketUrl.match(S3_BUCKET_REGEX); - if (!match) throw new Error('Could not parse bucket url'); - return new S3Wrapper(match[1], match[2], undefined); - } - - constructor(bucket: string, region: string, folder: string | undefined) { - this.bucket = bucket; - this.region = region; - this.folder = folder; - this.client = new S3Client({ region }); - } - - async getS3Obj(key: string): Promise | undefined> { - const Key = this.folder ? `${this.folder}/${key}` : key; - const command = new GetObjectCommand({ - Bucket: this.bucket, - Key, - }); - try { - const response = await this.client.send(command); - const body: string = await streamToString(response.Body as Readable); - return { - data: JSON.parse(body), - modified: response.LastModified!, - }; - } catch (e: any) { - if (e.message.includes('The specified key does not exist.')) { - return; - } - throw e; - } - } -} diff --git a/typescript/infra/src/agents/aws/validator.ts b/typescript/infra/src/agents/aws/validator.ts index 2c6b48c607..7417b5d2e9 100644 --- a/typescript/infra/src/agents/aws/validator.ts +++ b/typescript/infra/src/agents/aws/validator.ts @@ -1,5 +1,5 @@ +import { S3Receipt, S3Validator } from '@hyperlane-xyz/sdk'; import { - BaseValidator, Checkpoint, HexString, S3Checkpoint, @@ -8,8 +8,6 @@ import { isS3CheckpointWithId, } from '@hyperlane-xyz/utils'; -import { S3Receipt, S3Wrapper } from './s3.js'; - export enum CheckpointStatus { EXTRA = '➕', MISSING = '❓', @@ -35,71 +33,29 @@ type S3CheckpointReceipt = S3Receipt; const checkpointWithMessageIdKey = (checkpointIndex: number) => `checkpoint_${checkpointIndex}_with_id.json`; const LATEST_KEY = 'checkpoint_latest_index.json'; -const ANNOUNCEMENT_KEY = 'announcement.json'; -const LOCATION_PREFIX = 's3://'; /** * Extension of BaseValidator that includes AWS S3 utilities. */ -export class S3Validator extends BaseValidator { - s3Bucket: S3Wrapper; - - constructor( - address: string, - localDomain: number, - mailbox: string, - s3Bucket: string, - s3Region: string, - s3Folder: string | undefined, - ) { - super(address, localDomain, mailbox); - this.s3Bucket = new S3Wrapper(s3Bucket, s3Region, s3Folder); - } - +export class InfraS3Validator extends S3Validator { static async fromStorageLocation( storageLocation: string, - ): Promise { - if (storageLocation.startsWith(LOCATION_PREFIX)) { - const suffix = storageLocation.slice(LOCATION_PREFIX.length); - const pieces = suffix.split('/'); - if (pieces.length >= 2) { - const s3Bucket = new S3Wrapper(pieces[0], pieces[1], pieces[2]); - const announcement = await s3Bucket.getS3Obj(ANNOUNCEMENT_KEY); - const address = announcement?.data.value.validator; - const mailbox = announcement?.data.value.mailbox_address; - const localDomain = announcement?.data.value.mailbox_domain; - - return new S3Validator( - address, - localDomain, - mailbox, - pieces[0], - pieces[1], - pieces[2], - ); - } - } - throw new Error(`Unable to parse location ${storageLocation}`); - } - - async getAnnouncement(): Promise { - const data = await this.s3Bucket.getS3Obj(ANNOUNCEMENT_KEY); - if (data) { - return data.data; - } - } - - async getLatestCheckpointIndex() { - const latestCheckpointIndex = await this.s3Bucket.getS3Obj( - LATEST_KEY, + ): Promise { + const s3Validator = await S3Validator.fromStorageLocation(storageLocation); + return new InfraS3Validator( + s3Validator.address, + s3Validator.localDomain, + s3Validator.mailbox_address, + s3Validator.s3Bucket.bucket, + s3Validator.s3Bucket.region, + s3Validator.s3Bucket.folder, ); - - if (!latestCheckpointIndex) return -1; - - return latestCheckpointIndex.data; } - async compare(other: S3Validator, count = 5): Promise { + async compare( + other: InfraS3Validator, + count = 5, + ): Promise { const latestCheckpointIndex = await this.s3Bucket.getS3Obj( LATEST_KEY, ); @@ -196,10 +152,6 @@ export class S3Validator extends BaseValidator { return checkpointMetrics.slice(-1 * count); } - storageLocation(): string { - return `${LOCATION_PREFIX}/${this.s3Bucket.bucket}/${this.s3Bucket.region}`; - } - private async getCheckpointReceipt( index: number, ): Promise {