Skip to content

Commit

Permalink
feat: improve create-key logging (#3207)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalaji authored Feb 1, 2024
1 parent 3ea5dd7 commit 166e9d3
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 31 deletions.
8 changes: 6 additions & 2 deletions typescript/infra/scripts/announce-validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,19 @@ async function main() {
const announced = announcedLocations[0].includes(location);
if (!announced) {
const signature = ethers.utils.joinSignature(announcement.signature);
console.log(`Announcing ${address} checkpoints at ${location}`);
console.log(
`[${chain}] Announcing ${address} checkpoints at ${location}`,
);
await validatorAnnounce.announce(
address,
location,
signature,
multiProvider.getTransactionOverrides(chain),
);
} else {
console.log(`Already announced ${address} checkpoints at ${location}`);
console.log(
`[${chain}] Already announced ${address} checkpoints at ${location}`,
);
}
}
}
Expand Down
19 changes: 16 additions & 3 deletions typescript/infra/scripts/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import debug from 'debug';
import path from 'path';
import yargs from 'yargs';

Expand Down Expand Up @@ -29,6 +30,8 @@ import { EnvironmentNames, deployEnvToSdkEnv } from '../src/config/environment';
import { Role } from '../src/roles';
import { assertContext, assertRole, readJSON } from '../src/utils/utils';

const debugLog = debug('infra:scripts:utils');

export enum Modules {
// TODO: change
PROXY_FACTORY = 'ism',
Expand Down Expand Up @@ -130,6 +133,7 @@ export function assertEnvironment(env: string): DeployEnvironment {
}

export function getEnvironmentConfig(environment: DeployEnvironment) {
debugLog(`Getting environment config for ${environment}`);
return environments[environment];
}

Expand All @@ -154,12 +158,17 @@ export function getAgentConfig(
typeof environment == 'string'
? getEnvironmentConfig(environment)
: environment;

debugLog(
`Getting agent config for ${context} context in ${coreConfig.environment} environment`,
);

const agentConfig = coreConfig.agents[context];
if (!agentConfig)
throw Error(
`Invalid context ${context} for environment, must be one of ${Object.keys(
coreConfig.agents,
)}.`,
`Invalid context ${context} for ${
coreConfig.environment
} environment, must be one of ${Object.keys(coreConfig.agents)}.`,
);
return agentConfig;
}
Expand All @@ -171,6 +180,7 @@ export function getKeyForRole(
role: Role,
index?: number,
): CloudAgentKey {
debugLog(`Getting key for ${role} role`);
const environmentConfig = environments[environment];
const agentConfig = getAgentConfig(context, environmentConfig);
return getCloudAgentKey(agentConfig, role, chain, index);
Expand All @@ -185,7 +195,9 @@ export async function getMultiProviderForRole(
// TODO: rename to consensusType?
connectionType?: RpcConsensusType,
): Promise<MultiProvider> {
debugLog(`Getting multiprovider for ${role} role`);
if (process.env.CI === 'true') {
debugLog('Returning multiprovider with default RPCs in CI');
return new MultiProvider(); // use default RPCs
}
const multiProvider = new MultiProvider(txConfigs);
Expand All @@ -212,6 +224,7 @@ export async function getKeysForRole(
index?: number,
): Promise<ChainMap<CloudAgentKey>> {
if (process.env.CI === 'true') {
debugLog('No keys to return in CI');
return {};
}

Expand Down
50 changes: 45 additions & 5 deletions typescript/infra/src/agents/aws/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
UpdateAliasCommand,
} from '@aws-sdk/client-kms';
import { KmsEthersSigner } from 'aws-kms-ethers-signer';
import { Debugger, debug } from 'debug';
import { ethers } from 'ethers';

import { AgentSignerKeyType, ChainName } from '@hyperlane-xyz/sdk';
Expand All @@ -41,6 +42,7 @@ export class AgentAwsKey extends CloudAgentKey {
private client: KMSClient | undefined;
private region: string;
public remoteKey: RemoteKey = { fetched: false };
protected logger: Debugger;

constructor(
agentConfig: AgentContextConfig,
Expand All @@ -53,16 +55,22 @@ export class AgentAwsKey extends CloudAgentKey {
throw new Error('Not configured as AWS');
}
this.region = agentConfig.aws.region;
this.logger = debug(`infra:agents:key:aws:${this.identifier}`);
}

get privateKey(): string {
this.logger(
'Attempting to access private key, which is unavailable for AWS keys',
);
throw new Error('Private key unavailable for AWS keys');
}

async getClient(): Promise<KMSClient> {
if (this.client) {
this.logger('Returning existing KMSClient instance');
return this.client;
}
this.logger('Creating new KMSClient instance');
this.client = new KMSClient({
region: this.region,
});
Expand Down Expand Up @@ -94,6 +102,7 @@ export class AgentAwsKey extends CloudAgentKey {
}

async fetch() {
this.logger('Fetching key');
const address = await this.fetchAddressFromAws();
this.remoteKey = {
fetched: true,
Expand All @@ -102,24 +111,28 @@ export class AgentAwsKey extends CloudAgentKey {
}

async createIfNotExists() {
this.logger('Checking if key exists and creating if not');
const keyId = await this.getId();
// If it doesn't exist, create it
if (!keyId) {
// TODO should this be awaited? create is async
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.create();
this.logger('Key does not exist, creating new key');
await this.create();
// It can take a moment for the change to propagate
await sleep(1000);
} else {
this.logger('Key already exists');
}
await this.fetch();
}

async delete() {
this.logger('Delete operation called, but not implemented');
throw Error('Not implemented yet');
}

// Allows the `userArn` to use the key
async putKeyPolicy(userArn: string) {
this.logger(`Putting key policy for user ARN: ${userArn}`);
const client = await this.getClient();
const policy = {
Version: '2012-10-17',
Expand Down Expand Up @@ -151,22 +164,29 @@ export class AgentAwsKey extends CloudAgentKey {
PolicyName: 'default', // This is the only accepted name
});
await client.send(cmd);
this.logger('Key policy put successfully');
}

// Gets the Key's ID if it exists, undefined otherwise
async getId() {
try {
this.logger('Attempting to describe key to get ID');
const keyDescription = await this.describeKey();
return keyDescription.KeyMetadata?.KeyId;
const keyId = keyDescription.KeyMetadata?.KeyId;
this.logger(`Key ID retrieved: ${keyId}`);
return keyId;
} catch (err: any) {
if (err.name === 'NotFoundException') {
this.logger('Key not found');
return undefined;
}
this.logger(`Error retrieving key ID: ${err}`);
throw err;
}
}

create() {
this.logger('Creating new key');
return this._create(false);
}

Expand All @@ -175,13 +195,15 @@ export class AgentAwsKey extends CloudAgentKey {
* @returns The address of the new key
*/
update() {
this.logger('Updating key (creating new key for rotation)');
return this._create(true);
}

/**
* Requires update to have been called on this key prior
*/
async rotate() {
this.logger('Rotating keys');
const canonicalAlias = this.identifier;
const newAlias = canonicalAlias + '-new';
const oldAlias = canonicalAlias + '-old';
Expand Down Expand Up @@ -226,15 +248,19 @@ export class AgentAwsKey extends CloudAgentKey {

// Address should have changed now
await this.fetch();
this.logger('Keys rotated successfully');
}

async getSigner(
provider?: ethers.providers.Provider,
): Promise<ethers.Signer> {
this.logger('Getting signer');
const keyId = await this.getId();
if (!keyId) {
this.logger('Key ID not defined, cannot get signer');
throw Error('Key ID not defined');
}
this.logger(`Creating KmsEthersSigner with key ID: ${keyId}`);
// @ts-ignore We're using a newer version of Provider than
// KmsEthersSigner. The return type for getFeeData for this newer
// type is a superset of the return type for getFeeData for the older type,
Expand All @@ -252,12 +278,15 @@ export class AgentAwsKey extends CloudAgentKey {

private requireFetched() {
if (!this.remoteKey.fetched) {
this.logger('Key has not been fetched yet');
throw new Error('Key not fetched');
}
this.logger('Key has been fetched');
}

// Creates a new key and returns its address
private async _create(rotate: boolean) {
this.logger(`Creating key with rotation: ${rotate}`);
const client = await this.getClient();
const alias = this.identifier;
if (!rotate) {
Expand All @@ -269,6 +298,7 @@ export class AgentAwsKey extends CloudAgentKey {
(_) => _.AliasName === alias,
);
if (match) {
this.logger(`Alias ${alias} already exists`);
throw new Error(
`Attempted to create new key but alias ${alias} already exists`,
);
Expand All @@ -288,6 +318,7 @@ export class AgentAwsKey extends CloudAgentKey {

const createResponse = await client.send(command);
if (!createResponse.KeyMetadata) {
this.logger('KeyMetadata was not returned when creating the key');
throw new Error('KeyMetadata was not returned when creating the key');
}
const keyId = createResponse.KeyMetadata?.KeyId;
Expand All @@ -298,10 +329,12 @@ export class AgentAwsKey extends CloudAgentKey {
);

const address = this.fetchAddressFromAws(keyId);
this.logger(`New key created with ID: ${keyId}`);
return address;
}

private async fetchAddressFromAws(keyId?: string) {
this.logger(`Fetching address from AWS for key ID: ${keyId}`);
const client = await this.getClient();

if (!keyId) {
Expand All @@ -312,10 +345,15 @@ export class AgentAwsKey extends CloudAgentKey {
new GetPublicKeyCommand({ KeyId: keyId }),
);

return getEthereumAddress(Buffer.from(publicKeyResponse.PublicKey!));
const address = getEthereumAddress(
Buffer.from(publicKeyResponse.PublicKey!),
);
this.logger(`Address fetched: ${address}`);
return address;
}

private async describeKey(): Promise<DescribeKeyCommandOutput> {
this.logger('Describing key');
const client = await this.getClient();
return client.send(
new DescribeKeyCommand({
Expand All @@ -325,6 +363,7 @@ export class AgentAwsKey extends CloudAgentKey {
}

private async getAliases(): Promise<AliasListEntry[]> {
this.logger('Getting aliases');
const client = await this.getClient();
let aliases: AliasListEntry[] = [];
let marker: string | undefined = undefined;
Expand All @@ -350,6 +389,7 @@ export class AgentAwsKey extends CloudAgentKey {
break;
}
}
this.logger(`Aliases retrieved: ${aliases.length}`);
return aliases;
}
}
Loading

0 comments on commit 166e9d3

Please sign in to comment.