From 0302a282bfee85674181b9da0f1740244b6b93dc Mon Sep 17 00:00:00 2001 From: Nam Chu Hoai Date: Thu, 9 Nov 2023 19:29:49 -0500 Subject: [PATCH] Enroll additional validators on v3 mainnet (#2888) Co-authored-by: Yorke Rhodes --- typescript/infra/fork.sh | 6 +- typescript/infra/scripts/check-deploy.ts | 40 +++---- typescript/infra/scripts/deploy.ts | 102 ++++------------- typescript/infra/scripts/helloworld/deploy.ts | 103 ------------------ typescript/infra/scripts/utils.ts | 72 ------------ typescript/infra/scripts/verify-validators.ts | 86 +++++++-------- typescript/infra/src/agents/aws/validator.ts | 40 ++----- .../infra/src/config/helloworld/config.ts | 19 ---- typescript/sdk/src/consts/multisigIsm.ts | 46 ++++---- .../sdk/src/metadata/ChainMetadataManager.ts | 2 - .../sdk/src/router/HyperlaneRouterChecker.ts | 12 +- typescript/sdk/src/router/types.ts | 2 - typescript/utils/index.ts | 2 +- typescript/utils/src/checkpoints.ts | 2 +- typescript/utils/src/domains.ts | 4 +- typescript/utils/src/types.ts | 2 +- typescript/utils/src/validator.ts | 87 ++++----------- 17 files changed, 138 insertions(+), 489 deletions(-) delete mode 100644 typescript/infra/scripts/helloworld/deploy.ts delete mode 100644 typescript/infra/src/config/helloworld/config.ts diff --git a/typescript/infra/fork.sh b/typescript/infra/fork.sh index 2848e36f8c..cb909d043b 100755 --- a/typescript/infra/fork.sh +++ b/typescript/infra/fork.sh @@ -18,12 +18,12 @@ else fi # kill all child processes on exit -trap 'kill $(jobs -p)' EXIT +trap 'jobs -p | xargs -r kill' EXIT # exit 1 on any subsequent failures set -e -anvil --fork-url $RPC_URL --block-time 1 --silent > /dev/null & +anvil --fork-url $RPC_URL --silent > /dev/null & ANVIL_PID=$! while ! cast bn &> /dev/null; do @@ -44,5 +44,3 @@ yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN --govern - echo "=== Run $MODULE checker against forked $ENVIRONMENT after governance ===" yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN -m $MODULE - -kill $ANVIL_PID diff --git a/typescript/infra/scripts/check-deploy.ts b/typescript/infra/scripts/check-deploy.ts index 2b570c4779..c09889cf25 100644 --- a/typescript/infra/scripts/check-deploy.ts +++ b/typescript/infra/scripts/check-deploy.ts @@ -9,13 +9,10 @@ import { InterchainAccountChecker, InterchainQuery, InterchainQueryChecker, - filterChainMapToProtocol, } from '@hyperlane-xyz/sdk'; -import { ProtocolType } from '@hyperlane-xyz/utils'; import { Contexts } from '../config/contexts'; import { deployEnvToSdkEnv } from '../src/config/environment'; -import { helloWorldRouterConfig } from '../src/config/helloworld/config'; import { HyperlaneAppGovernor } from '../src/govern/HyperlaneAppGovernor'; import { HyperlaneCoreGovernor } from '../src/govern/HyperlaneCoreGovernor'; import { HyperlaneIgpGovernor } from '../src/govern/HyperlaneIgpGovernor'; @@ -27,7 +24,6 @@ import { getHelloWorldApp } from './helloworld/utils'; import { Modules, getEnvironmentConfig, - getProxiedRouterConfig, getArgs as getRootArgs, withContext, withModuleAndFork, @@ -43,23 +39,30 @@ function getArgs() { async function check() { const { fork, govern, module, environment, context } = await getArgs(); const config = getEnvironmentConfig(environment); - const multiProvider = await config.getMultiProvider(); + let multiProvider = await config.getMultiProvider(); // must rotate to forked provider before building core contracts if (fork) { await useLocalProvider(multiProvider, fork); + if (govern) { + multiProvider = multiProvider.extendChainMetadata({ + [fork]: { blocks: { confirmations: 0 } }, + }); + const owner = config.core[fork].owner; const signer = await impersonateAccount(owner); multiProvider.setSigner(fork, signer); } } - let governor: HyperlaneAppGovernor; const env = deployEnvToSdkEnv[environment]; + const core = HyperlaneCore.fromEnvironment(env, multiProvider); + const ismFactory = HyperlaneIsmFactory.fromEnvironment(env, multiProvider); + const routerConfig = core.getRouterConfig(config.owners); + + let governor: HyperlaneAppGovernor; if (module === Modules.CORE) { - const core = HyperlaneCore.fromEnvironment(env, multiProvider); - const ismFactory = HyperlaneIsmFactory.fromEnvironment(env, multiProvider); const checker = new HyperlaneCoreChecker( multiProvider, core, @@ -72,11 +75,6 @@ async function check() { const checker = new HyperlaneIgpChecker(multiProvider, igp, config.igp); governor = new HyperlaneIgpGovernor(checker, config.owners); } else if (module === Modules.INTERCHAIN_ACCOUNTS) { - const routerConfig = filterChainMapToProtocol( - await getProxiedRouterConfig(environment, multiProvider), - ProtocolType.Ethereum, - multiProvider, - ); const ica = InterchainAccount.fromEnvironment(env, multiProvider); const checker = new InterchainAccountChecker( multiProvider, @@ -85,10 +83,6 @@ async function check() { ); governor = new ProxiedRouterGovernor(checker, config.owners); } else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) { - const routerConfig = await getProxiedRouterConfig( - environment, - multiProvider, - ); const iqs = InterchainQuery.fromEnvironment(env, multiProvider); const checker = new InterchainQueryChecker( multiProvider, @@ -103,19 +97,11 @@ async function check() { Role.Deployer, Contexts.Hyperlane, // Owner should always be from the hyperlane context ); - const hwConfig = await helloWorldRouterConfig( - environment, - context, - multiProvider, - ); - const ismFactory = HyperlaneIsmFactory.fromEnvironment( - deployEnvToSdkEnv[environment], - multiProvider, - ); + const ismFactory = HyperlaneIsmFactory.fromEnvironment(env, multiProvider); const checker = new HelloWorldChecker( multiProvider, app, - hwConfig, + routerConfig, ismFactory, ); governor = new ProxiedRouterGovernor(checker, config.owners); diff --git a/typescript/infra/scripts/deploy.ts b/typescript/infra/scripts/deploy.ts index 3bb6903638..8e109110ff 100644 --- a/typescript/infra/scripts/deploy.ts +++ b/typescript/infra/scripts/deploy.ts @@ -4,9 +4,6 @@ import { prompt } from 'prompts'; import { HelloWorldDeployer } from '@hyperlane-xyz/helloworld'; import { ChainMap, - Chains, - HypERC20Config, - HypERC20Deployer, HyperlaneCore, HyperlaneCoreDeployer, HyperlaneDeployer, @@ -15,14 +12,8 @@ import { HyperlaneProxyFactoryDeployer, InterchainAccountDeployer, InterchainQueryDeployer, - IsmType, LiquidityLayerDeployer, - TokenType, } from '@hyperlane-xyz/sdk'; -import { - TokenConfig, - TokenDecimals, -} from '@hyperlane-xyz/sdk/dist/token/config'; import { objMap } from '@hyperlane-xyz/utils'; import { Contexts } from '../config/contexts'; @@ -40,8 +31,6 @@ import { getContractAddressesSdkFilepath, getEnvironmentConfig, getModuleDirectory, - getProxiedRouterConfig, - getRouterConfig, withContext, withModuleAndFork, } from './utils'; @@ -54,17 +43,22 @@ async function main() { environment, } = await withContext(withModuleAndFork(getArgs())).argv; const envConfig = getEnvironmentConfig(environment); - const multiProvider = await envConfig.getMultiProvider(); + const env = deployEnvToSdkEnv[environment]; + + let multiProvider = await envConfig.getMultiProvider(); + + // TODO: make this more generic + const deployerAddress = + environment === 'testnet4' + ? '0xfaD1C94469700833717Fa8a3017278BC1cA8031C' + : '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; if (fork) { + multiProvider = multiProvider.extendChainMetadata({ + [fork]: { blocks: { confirmations: 0 } }, + }); await useLocalProvider(multiProvider, fork); - // TODO: make this more generic - const deployerAddress = - environment === 'testnet4' - ? '0xfaD1C94469700833717Fa8a3017278BC1cA8031C' - : '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; - const signer = await impersonateAccount(deployerAddress); multiProvider.setSharedSigner(signer); } @@ -81,65 +75,20 @@ async function main() { multiProvider, ); deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory); - } else if (module === Modules.WARP) { - const owner = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; - const neutronRouter = - '0x9c504f7d878445228bef5684f9028cb388f63e58bf1077db75876c7651b9a71f'; - const ismFactory = HyperlaneIsmFactory.fromAddressesMap( - getAddresses(environment, Modules.PROXY_FACTORY), - multiProvider, - ); - const tokenConfig: TokenConfig & TokenDecimals = { - type: TokenType.synthetic, - name: 'TIA', - symbol: 'TIA.n', - decimals: 6, - totalSupply: 0, - }; - const core = HyperlaneCore.fromEnvironment( - deployEnvToSdkEnv[environment], - multiProvider, - ); - const routerConfig = core.getRouterConfig(owner); - const targetChains = [Chains.arbitrum]; - config = Object.fromEntries( - targetChains.map((chain) => { - const warpRouterConfig: HypERC20Config = { - ...routerConfig[chain], - ...tokenConfig, - interchainSecurityModule: { - type: IsmType.MESSAGE_ID_MULTISIG, - validators: [ - '0xa9b8c1f4998f781f958c63cfcd1708d02f004ff0', - '0xb65438a014fb05fbadcfe35bc6e25d372b6ba460', - '0xc79503a3e3011535a9c60f6d21f76f59823a38bd', - '0x42fa752defe92459370a052b6387a87f7de9b80c', - '0x54b2cca5091b098a1a993dec03c4d1ee9af65999', - '0x47aa126e05933b95c5eb90b26e6b668d84f4b25a', - ], - threshold: 4, - }, - // foreignDeployment: neutronRouter, - gas: 600_000, - }; - return [chain, warpRouterConfig]; - }), - ); - deployer = new HypERC20Deployer(multiProvider, ismFactory); } else if (module === Modules.INTERCHAIN_GAS_PAYMASTER) { config = envConfig.igp; deployer = new HyperlaneIgpDeployer(multiProvider); } else if (module === Modules.INTERCHAIN_ACCOUNTS) { - config = await getProxiedRouterConfig(environment, multiProvider); + const core = HyperlaneCore.fromEnvironment(env, multiProvider); + config = core.getRouterConfig(envConfig.owners); deployer = new InterchainAccountDeployer(multiProvider); } else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) { - config = await getProxiedRouterConfig(environment, multiProvider); + const core = HyperlaneCore.fromEnvironment(env, multiProvider); + config = core.getRouterConfig(envConfig.owners); deployer = new InterchainQueryDeployer(multiProvider); } else if (module === Modules.LIQUIDITY_LAYER) { - const routerConfig = await getProxiedRouterConfig( - environment, - multiProvider, - ); + const core = HyperlaneCore.fromEnvironment(env, multiProvider); + const routerConfig = core.getRouterConfig(envConfig.owners); if (!envConfig.liquidityLayerConfig) { throw new Error(`No liquidity layer config for ${environment}`); } @@ -165,16 +114,9 @@ async function main() { })); deployer = new TestQuerySenderDeployer(multiProvider); } else if (module === Modules.HELLO_WORLD) { - config = await getRouterConfig( - environment, - multiProvider, - true, // use deployer as owner - ); - const ismFactory = HyperlaneIsmFactory.fromAddressesMap( - getAddresses(environment, Modules.PROXY_FACTORY), - multiProvider, - ); - deployer = new HelloWorldDeployer(multiProvider, ismFactory); + const core = HyperlaneCore.fromEnvironment(env, multiProvider); + config = core.getRouterConfig(deployerAddress); + deployer = new HelloWorldDeployer(multiProvider); } else { console.log(`Skipping ${module}, deployer unimplemented`); return; @@ -212,7 +154,7 @@ async function main() { : undefined; // prompt for confirmation - if (environment === 'mainnet3' || environment === 'testnet4') { + if ((environment === 'mainnet3' || environment === 'testnet4') && !fork) { console.log(JSON.stringify(config, null, 2)); const { value: confirmed } = await prompt({ type: 'confirm', diff --git a/typescript/infra/scripts/helloworld/deploy.ts b/typescript/infra/scripts/helloworld/deploy.ts deleted file mode 100644 index 9d4fc317e0..0000000000 --- a/typescript/infra/scripts/helloworld/deploy.ts +++ /dev/null @@ -1,103 +0,0 @@ -import path from 'path'; - -import { HelloWorldDeployer } from '@hyperlane-xyz/helloworld'; -import { - HyperlaneIsmFactory, - filterChainMapExcludeProtocol, - serializeContractsMap, -} from '@hyperlane-xyz/sdk'; -import { - ProtocolType, - hexOrBase58ToHex, - objMap, - objMerge, -} from '@hyperlane-xyz/utils'; - -import { Contexts } from '../../config/contexts'; -import { deployEnvToSdkEnv } from '../../src/config/environment'; -import { helloWorldRouterConfig } from '../../src/config/helloworld/config'; -import { Role } from '../../src/roles'; -import { readJSON, writeJSON } from '../../src/utils/utils'; -import { - getEnvironmentConfig, - getEnvironmentDirectory, - getArgs as getRootArgs, - withContext, -} from '../utils'; - -function getArgs() { - return withContext(getRootArgs()) - .boolean('govern') - .default('govern', false) - .alias('g', 'govern').argv; -} - -async function main() { - const { environment, context } = await getArgs(); - const coreConfig = getEnvironmentConfig(environment); - // Always deploy from the hyperlane deployer - const multiProvider = await coreConfig.getMultiProvider( - Contexts.Hyperlane, - Role.Deployer, - ); - const configMap = await helloWorldRouterConfig( - environment, - context, - multiProvider, - ); - const ismFactory = HyperlaneIsmFactory.fromEnvironment( - deployEnvToSdkEnv[environment], - multiProvider, - ); - const deployer = new HelloWorldDeployer(multiProvider, ismFactory); - const dir = path.join( - getEnvironmentDirectory(environment), - 'helloworld', - context, - ); - - let existingVerificationInputs = {}; - try { - const addresses = readJSON(dir, 'addresses.json'); - deployer.cacheAddressesMap(addresses); - existingVerificationInputs = readJSON(dir, 'verification.json'); - } catch (e) { - console.info(`Could not load previous deployment, file may not exist`); - } - - const configMapWithForeignDeployments = objMerge( - configMap, - objMap( - filterChainMapExcludeProtocol( - deployer.cachedAddresses, - ProtocolType.Ethereum, - multiProvider, - ), - (_chain, addresses) => ({ - foreignDeployment: hexOrBase58ToHex(addresses.router), - }), - ), - ); - - try { - await deployer.deploy(configMapWithForeignDeployments); - } catch (e) { - console.error(`Encountered error during deploy`); - console.error(e); - } - - writeJSON(dir, 'addresses.json', { - // To include foreign deployments that may be present in the addresses.json already - ...deployer.cachedAddresses, - ...serializeContractsMap(deployer.deployedContracts), - }); - writeJSON( - dir, - 'verification.json', - deployer.mergeWithExistingVerificationInputs(existingVerificationInputs), - ); -} - -main() - .then(() => console.info('Deployment complete')) - .catch(console.error); diff --git a/typescript/infra/scripts/utils.ts b/typescript/infra/scripts/utils.ts index dca0b5a42a..825193ece0 100644 --- a/typescript/infra/scripts/utils.ts +++ b/typescript/infra/scripts/utils.ts @@ -8,11 +8,7 @@ import { ChainName, Chains, CoreConfig, - HyperlaneCore, - HyperlaneIgp, MultiProvider, - ProxiedRouterConfig, - RouterConfig, RpcConsensusType, collectValidators, } from '@hyperlane-xyz/sdk'; @@ -287,74 +283,6 @@ export async function assertCorrectKubeContext(coreConfig: EnvironmentConfig) { } } -export async function getRouterConfig( - environment: DeployEnvironment, - multiProvider: MultiProvider, - useMultiProviderOwners = false, -): Promise> { - const core = HyperlaneCore.fromEnvironment( - deployEnvToSdkEnv[environment], - multiProvider, - ); - // TODO: replace this with core.getRouterConfig - const igp = HyperlaneIgp.fromEnvironment( - deployEnvToSdkEnv[environment], - multiProvider, - ); - - const owners = getEnvironmentConfig(environment).owners; - const config: ChainMap = {}; - const knownChains = multiProvider.intersect( - core.chains().concat(igp.chains()), - ).intersection; - - for (const chain of knownChains) { - // CI will not have signers for all known chains. To avoid failing, we - // default to the owner configured in the environment if we cannot get a - // signer address. - const getSignerAddress = (chain: ChainName) => { - const signer = multiProvider.tryGetSigner(chain); - if (!signer) { - const owner = owners[chain]; - console.warn( - `Unable to get signer for chain, ${chain}, defaulting to configured owner ${owner}`, - ); - return owner; - } - return signer.getAddress(); - }; - - // MultiProvider signers are only used for Ethereum chains. - const owner = - useMultiProviderOwners && - multiProvider.getChainMetadata(chain).protocol === ProtocolType.Ethereum - ? await getSignerAddress(chain) - : owners[chain]; - config[chain] = { - owner: owner, - mailbox: core.getContracts(chain).mailbox.address, - // hook: igp.getContracts(chain).interchainGasPaymaster.address, - }; - } - return config; -} - -export async function getProxiedRouterConfig( - environment: DeployEnvironment, - multiProvider: MultiProvider, - useMultiProviderOwners = false, -): Promise> { - const config = await getRouterConfig( - environment, - multiProvider, - useMultiProviderOwners, - ); - return objMap(config, (chain, routerConfig) => ({ - timelock: environments[environment].core[chain].upgrade?.timelock, - ...routerConfig, - })); -} - export function getValidatorsByChain( config: ChainMap, ): ChainMap> { diff --git a/typescript/infra/scripts/verify-validators.ts b/typescript/infra/scripts/verify-validators.ts index 57404712a4..2433a6b4f3 100644 --- a/typescript/infra/scripts/verify-validators.ts +++ b/typescript/infra/scripts/verify-validators.ts @@ -1,15 +1,13 @@ import { HyperlaneCore } from '@hyperlane-xyz/sdk'; -import { objMap } from '@hyperlane-xyz/utils'; +import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; -import { CheckpointStatus, S3Validator } from '../src/agents/aws/validator'; +import { S3Validator } from '../src/agents/aws/validator'; import { deployEnvToSdkEnv } from '../src/config/environment'; import { getArgs, getEnvironmentConfig, getValidatorsByChain } from './utils'; async function main() { - const { environment, withMessageId } = await getArgs() - .boolean('with-message-id') - .default('with-message-id', true).argv; + const { environment } = await getArgs().argv; const config = getEnvironmentConfig(environment); const multiProvider = await config.getMultiProvider(); const core = HyperlaneCore.fromEnvironment( @@ -17,49 +15,41 @@ async function main() { multiProvider, ); - objMap(getValidatorsByChain(config.core), async (chain, set) => { - const validatorAnnounce = core.getContracts(chain).validatorAnnounce; - const storageLocations = - await validatorAnnounce.getAnnouncedStorageLocations([...set]); - const validators = await Promise.all( - [...set].map((validator, i) => { - // Only use the latest announcement for now - if (storageLocations[i].length != 1) { - throw new Error('Only support single announcement'); - } - return S3Validator.fromStorageLocation(storageLocations[i][0]); - }), - ); - const controlValidator = validators[0]; - for (let i = 1; i < validators.length; i++) { - const prospectiveValidator = validators[i]; - const address = prospectiveValidator.address; - const bucket = prospectiveValidator.s3Bucket.bucket; - try { - const metrics = await prospectiveValidator.compare( - controlValidator, - withMessageId, - ); - const valid = - metrics.filter((metric) => metric.status !== CheckpointStatus.VALID) - .length === 0; - if (!valid) { - console.log( - `${address}@${bucket} has >=1 non-valid checkpoints for ${chain}`, - ); - console.log(JSON.stringify(metrics, null, 2)); - } else { - console.log( - `${address}@${bucket} has valid checkpoints for ${chain}`, - ); - } - } catch (error) { - console.error(`Comparing validator ${address}@${bucket} failed:`); - console.error(error); - throw error; - } - } - }); + await promiseObjAll( + objMap(getValidatorsByChain(config.core), async (chain, set) => { + const validatorAnnounce = core.getContracts(chain).validatorAnnounce; + const storageLocations = + await validatorAnnounce.getAnnouncedStorageLocations([...set]); + const validators = await Promise.all( + [...set].map((_validator, i) => { + // Only use the latest announcement for now + if (storageLocations[i].length != 1) { + throw new Error('Only support single announcement'); + } + return S3Validator.fromStorageLocation(storageLocations[i][0]); + }), + ); + const controlValidator = validators[0]; + await Promise.all( + validators.slice(1).map(async (prospectiveValidator) => { + const address = prospectiveValidator.address; + const bucket = prospectiveValidator.s3Bucket.bucket; + try { + const metrics = await prospectiveValidator.compare( + controlValidator, + ); + console.log( + `${chain} ${bucket} validators against control ${controlValidator.s3Bucket.bucket}`, + ); + console.table(metrics); + } catch (error) { + console.error(`Comparing validator ${address}@${bucket} failed:`); + throw error; + } + }), + ); + }), + ); } main().catch(console.error); diff --git a/typescript/infra/src/agents/aws/validator.ts b/typescript/infra/src/agents/aws/validator.ts index d6582ccd7e..fd8ffa4b6f 100644 --- a/typescript/infra/src/agents/aws/validator.ts +++ b/typescript/infra/src/agents/aws/validator.ts @@ -5,7 +5,6 @@ import { S3Checkpoint, S3CheckpointWithId, SignatureLike, - isS3Checkpoint, isS3CheckpointWithId, } from '@hyperlane-xyz/utils'; @@ -27,14 +26,12 @@ interface CheckpointMetric { interface SignedCheckpoint { checkpoint: Checkpoint; - messageId?: HexString; + messageId: HexString; signature: SignatureLike; } type S3CheckpointReceipt = S3Receipt; -const checkpointKey = (checkpointIndex: number) => - `checkpoint_${checkpointIndex}.json`; const checkpointWithMessageIdKey = (checkpointIndex: number) => `checkpoint_${checkpointIndex}_with_id.json`; const LATEST_KEY = 'checkpoint_latest_index.json'; @@ -70,6 +67,7 @@ export class S3Validator extends BaseValidator { 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, @@ -99,11 +97,7 @@ export class S3Validator extends BaseValidator { return latestCheckpointIndex.data; } - async compare( - other: S3Validator, - withId = false, - count = 5, - ): Promise { + async compare(other: S3Validator, count = 5): Promise { const latestCheckpointIndex = await this.s3Bucket.getS3Obj( LATEST_KEY, ); @@ -140,11 +134,8 @@ export class S3Validator extends BaseValidator { const stop = Math.max(maxIndex - count, 0); for (; checkpointIndex > stop; checkpointIndex--) { - const expected = await other.getCheckpointReceipt( - checkpointIndex, - withId, - ); - const actual = await this.getCheckpointReceipt(checkpointIndex, withId); + const expected = await other.getCheckpointReceipt(checkpointIndex); + const actual = await this.getCheckpointReceipt(checkpointIndex); const metric: CheckpointMetric = { status: CheckpointStatus.MISSING, @@ -178,10 +169,10 @@ export class S3Validator extends BaseValidator { ) { metric.violation = `index mismatch: expected ${expected.data.checkpoint.index}, received ${actual.data.checkpoint.index}`; } else if ( - expected.data.checkpoint.mailbox_address !== - actual.data.checkpoint.mailbox_address + expected.data.checkpoint.merkle_tree_hook_address !== + actual.data.checkpoint.merkle_tree_hook_address ) { - metric.violation = `mailbox address mismatch: expected ${expected.data.checkpoint.mailbox_address}, received ${actual.data.checkpoint.mailbox_address}`; + metric.violation = `mailbox address mismatch: expected ${expected.data.checkpoint.merkle_tree_hook_address}, received ${actual.data.checkpoint.merkle_tree_hook_address}`; } else if ( expected.data.checkpoint.mailbox_domain !== actual.data.checkpoint.mailbox_domain @@ -209,26 +200,15 @@ export class S3Validator extends BaseValidator { private async getCheckpointReceipt( index: number, - withId = false, ): Promise { - const key = withId - ? checkpointWithMessageIdKey(index) - : checkpointKey(index); + const key = checkpointWithMessageIdKey(index); const s3Object = await this.s3Bucket.getS3Obj< S3Checkpoint | S3CheckpointWithId >(key); if (!s3Object) { return; } - if (isS3Checkpoint(s3Object.data)) { - return { - data: { - checkpoint: s3Object.data.value, - signature: s3Object.data.signature, - }, - modified: s3Object.modified, - }; - } else if (isS3CheckpointWithId(s3Object.data)) { + if (isS3CheckpointWithId(s3Object.data)) { return { data: { checkpoint: s3Object.data.value.checkpoint, diff --git a/typescript/infra/src/config/helloworld/config.ts b/typescript/infra/src/config/helloworld/config.ts deleted file mode 100644 index 9d384d7d7a..0000000000 --- a/typescript/infra/src/config/helloworld/config.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ChainMap, MultiProvider, RouterConfig } from '@hyperlane-xyz/sdk'; -import { objMap } from '@hyperlane-xyz/utils'; - -import { Contexts } from '../../../config/contexts'; -import { routingIsm } from '../../../config/routingIsm'; -import { getRouterConfig } from '../../../scripts/utils'; -import { DeployEnvironment } from '../environment'; - -export async function helloWorldRouterConfig( - environment: DeployEnvironment, - context: Contexts, - multiProvider: MultiProvider, -): Promise> { - const routerConfig = await getRouterConfig(environment, multiProvider, true); - return objMap(routerConfig, (chain, config) => ({ - ...config, - interchainSecurityModule: routingIsm(environment, chain, context), - })); -} diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index e6c9608e24..53ba756272 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -7,24 +7,25 @@ export const defaultMultisigIsmConfigs: ChainMap = { threshold: 2, validators: [ '0x63478422679303c3e4fc611b771fa4a707ef7f4a', - '0x2f4e808744df049d8acc050628f7bdd8265807f9', - '0x7bf30afcb6a7d92146d5a910ea4c154fba38d25e', + '0x622e43baf06ad808ca8399360d9a2d9a1a12688b', // dsrv + '0xf2c1e3888eb618f1f1a071ef3618f134715a9a49', // everstake ], }, ethereum: { - threshold: 2, + threshold: 3, validators: [ '0x03c842db86a6a3e524d4a6615390c1ea8e2b9541', - '0x4346776b10f5e0d9995d884b7a1dbaee4e24c016', - '0x749d6e7ad949e522c92181dc77f7bbc1c5d71506', + '0x94438a7de38d4548ae54df5c6010c4ebc5239eae', // dsrv + '0x5450447aee7b544c462c9352bef7cad049b0c2dc', // zeeprime + '0xce327111035dd38698c92c3778884dbbb0ca8103', // everstake ], }, avalanche: { threshold: 2, validators: [ '0x3fb8263859843bffb02950c492d492cae169f4cf', - '0xe58c63ad669b946e7c8211299f22679deecc9c83', - '0x6c754f1e9cd8287088b46a7c807303d55d728b49', + '0x402e0f8c6e4210d408b6ac00d197d4a099fcd25a', // dsrv + '0x716a1d4d3166c6151b05ce0450e0d77d94588eac', // everstake ], }, polygon: { @@ -32,47 +33,48 @@ export const defaultMultisigIsmConfigs: ChainMap = { validators: [ '0x12ecb319c7f4e8ac5eb5226662aeb8528c5cefac', '0x8dd8f8d34b5ecaa5f66de24b01acd7b8461c3916', - '0xdbf3666de031bea43ec35822e8c33b9a9c610322', + '0x722aa4d45387009684582bca8281440d16b8b40f', // everstake ], }, bsc: { threshold: 2, validators: [ '0x570af9b7b36568c8877eebba6c6727aa9dab7268', - '0x7bf928d5d262365d31d64eaa24755d48c3cae313', - '0x03047213365800f065356b4a2fe97c3c3a52296a', + '0x8292b1a53907ece0f76af8a50724e9492bcdc8a3', // bsc + '0xeaf5cf9100f36a4baeea779f8745dda86159103c', // everstake ], }, arbitrum: { - threshold: 2, + threshold: 3, validators: [ '0x4d966438fe9e2b1e7124c87bbb90cb4f0f6c59a1', - '0x6333e110b8a261cab28acb43030bcde59f26978a', - '0x3369e12edd52570806f126eb50be269ba5e65843', + '0xec68258a7c882ac2fc46b81ce80380054ffb4ef2', // dsrv + '0x5450447aee7b544c462c9352bef7cad049b0c2dc', // zeeprime + '0x092e1c19da58e87ea65198785ee83867fe4bb418', // everstake ], }, optimism: { threshold: 2, validators: [ '0x20349eadc6c72e94ce38268b96692b1a5c20de4f', - '0x04d040cee072272789e2d1f29aef73b3ad098db5', - '0x779a17e035018396724a6dec8a59bda1b5adf738', + '0x5b7d47b76c69740462432f6a5a0ca5005e014157', // dsrv + '0x22b1ad4322cdb5f2c76ebf4e5a93803d480fcf0d', // everstake ], }, moonbeam: { threshold: 2, validators: [ '0x2225e2f4e9221049456da93b71d2de41f3b6b2a8', - '0x4fe067bb455358e295bfcfb92519a6f9de94b98e', - '0xcc4a78aa162482bea43313cd836ba7b560b44fc4', + '0x645428d198d2e76cbd9c1647f5c80740bb750b97', // dsrv + '0xaed886392df07897743d8e272d438f00c4c9a2ae', // everstake ], }, gnosis: { threshold: 2, validators: [ '0xd4df66a859585678f2ea8357161d896be19cc1ca', - '0x06a833508579f8b59d756b3a1e72451fc70840c3', - '0xb93a72cee19402553c9dd7fed2461aebd04e2454', + '0x19fb7e04a1be6b39b6966a0b0c60b929a93ed672', // dsrv + '0xdb96116d13a2fadde9742d7cc88474a5ed39a03a', // everstake ], }, // solana: { @@ -85,7 +87,7 @@ export const defaultMultisigIsmConfigs: ChainMap = { validators: [ '0xb9453d675e0fa3c178a17b4ce1ad5b1a279b3af9', '0x4512985a574cb127b2af2d4bb676876ce804e3f8', - '0xb144bb2f599a5af095bc30367856f27ea8a8adc7', + '0x41188cb5a5493a961c467ba38a3f8b1f1d35ee63', // everstake ], }, scroll: { @@ -93,7 +95,7 @@ export const defaultMultisigIsmConfigs: ChainMap = { validators: [ '0xad557170a9f2f21c35e03de07cb30dcbcc3dff63', '0xb37fe43a9f47b7024c2d5ae22526cc66b5261533', - '0x7210fa0a6be39a75cb14d682ebfb37e2b53ecbe5', + '0x276de8e2b88e659c4e5ad30d62d9de42c3da3403', // everstake ], }, polygonzkevm: { @@ -101,7 +103,7 @@ export const defaultMultisigIsmConfigs: ChainMap = { validators: [ '0x86f2a44592bb98da766e880cfd70d3bbb295e61a', '0xc84076030bdabaabb9e61161d833dd84b700afda', - '0x6a1da2e0b7ae26aaece1377c0a4dbe25b85fa3ca', + '0x57231619fea13d85270ca6943298046c75a6dd01', // everstake ], }, // ----------------- Testnets ----------------- diff --git a/typescript/sdk/src/metadata/ChainMetadataManager.ts b/typescript/sdk/src/metadata/ChainMetadataManager.ts index 237cdccb24..d00f4e4598 100644 --- a/typescript/sdk/src/metadata/ChainMetadataManager.ts +++ b/typescript/sdk/src/metadata/ChainMetadataManager.ts @@ -329,8 +329,6 @@ export class ChainMetadataManager { ): ChainMetadataManager { const newMetadata: ChainMap> = {}; for (const [name, meta] of Object.entries(this.metadata)) { - if (!additionalMetadata[name]) - throw new Error(`No additional data provided for chain ${name}`); newMetadata[name] = { ...meta, ...additionalMetadata[name] }; } return new ChainMetadataManager(newMetadata); diff --git a/typescript/sdk/src/router/HyperlaneRouterChecker.ts b/typescript/sdk/src/router/HyperlaneRouterChecker.ts index 5c02684d81..7d3ded9a17 100644 --- a/typescript/sdk/src/router/HyperlaneRouterChecker.ts +++ b/typescript/sdk/src/router/HyperlaneRouterChecker.ts @@ -73,19 +73,13 @@ export class HyperlaneRouterChecker< ); if (!matches) { - this.app.logger( - `Deploying ISM; ISM config of actual ${actual} does not match expected config ${JSON.stringify( - value, - )}`, - ); - const deployedIsm = await this.ismFactory.deploy(chain, value); const violation: ConnectionClientViolation = { chain, type: violationType, contract: router, actual, - expected: deployedIsm.address, - description: `ISM config does not match deployed ISM at ${deployedIsm.address}`, + expected: value, + description: `ISM config does not match deployed ISM`, }; this.addViolation(violation); } @@ -136,7 +130,7 @@ export class HyperlaneRouterChecker< type: ClientViolationType.InterchainSecurityModule, contract: router, actual: ism, - expected: defaultIsm, + expected: zeroAddress, }); } } else { diff --git a/typescript/sdk/src/router/types.ts b/typescript/sdk/src/router/types.ts index a990503c70..a6de581d2d 100644 --- a/typescript/sdk/src/router/types.ts +++ b/typescript/sdk/src/router/types.ts @@ -63,8 +63,6 @@ export enum ClientViolationType { export interface ClientViolation extends CheckerViolation { type: ClientViolationType; contract: MailboxClient; - actual: string; - expected: string; description?: string; } diff --git a/typescript/utils/index.ts b/typescript/utils/index.ts index e2351b6f6c..35e81292dd 100644 --- a/typescript/utils/index.ts +++ b/typescript/utils/index.ts @@ -125,4 +125,4 @@ export { TokenCaip19Id, } from './src/types'; export { assert } from './src/validation'; -export { BaseValidator, Validator } from './src/validator'; +export { BaseValidator } from './src/validator'; diff --git a/typescript/utils/src/checkpoints.ts b/typescript/utils/src/checkpoints.ts index 010b7d0cce..e313187688 100644 --- a/typescript/utils/src/checkpoints.ts +++ b/typescript/utils/src/checkpoints.ts @@ -30,7 +30,7 @@ export function isS3CheckpointWithId(obj: any): obj is S3CheckpointWithId { export function isCheckpoint(obj: any): obj is Checkpoint { const isValidRoot = utils.isHexString(obj.root); const isValidIndex = Number.isSafeInteger(obj.index); - const isValidMailbox = utils.isHexString(obj.mailbox_address); + const isValidMailbox = utils.isHexString(obj.merkle_tree_hook_address); const isValidDomain = Number.isSafeInteger(obj.mailbox_domain); return isValidIndex && isValidRoot && isValidMailbox && isValidDomain; } diff --git a/typescript/utils/src/domains.ts b/typescript/utils/src/domains.ts index 2a2ba3efdc..21327d9515 100644 --- a/typescript/utils/src/domains.ts +++ b/typescript/utils/src/domains.ts @@ -2,9 +2,9 @@ import { utils } from 'ethers'; import { addressToBytes32 } from './addresses'; -export function domainHash(domain: number, mailbox: string): string { +export function domainHash(domain: number, merkle_tree_hook: string): string { return utils.solidityKeccak256( ['uint32', 'bytes32', 'string'], - [domain, addressToBytes32(mailbox), 'HYPERLANE'], + [domain, addressToBytes32(merkle_tree_hook), 'HYPERLANE'], ); } diff --git a/typescript/utils/src/types.ts b/typescript/utils/src/types.ts index 81799c6b4e..db36a54953 100644 --- a/typescript/utils/src/types.ts +++ b/typescript/utils/src/types.ts @@ -45,7 +45,7 @@ export type Checkpoint = { root: string; index: number; // safe because 2 ** 32 leaves < Number.MAX_VALUE mailbox_domain: Domain; - mailbox_address: Address; + merkle_tree_hook_address: Address; }; /** diff --git a/typescript/utils/src/validator.ts b/typescript/utils/src/validator.ts index 6c6fca7b63..f30b6d5eed 100644 --- a/typescript/utils/src/validator.ts +++ b/typescript/utils/src/validator.ts @@ -1,44 +1,38 @@ import { ethers } from 'ethers'; import { domainHash } from './domains'; -import { - Address, - Checkpoint, - Domain, - HexString, - S3Checkpoint, - SignatureLike, -} from './types'; +import { Address, Checkpoint, Domain, HexString, SignatureLike } from './types'; /** * Utilities for validators to construct and verify checkpoints. */ export class BaseValidator { - localDomain: Domain; - address: Address; - mailbox: Address; + constructor( + public readonly address: Address, + public readonly localDomain: Domain, + public readonly mailbox_address: Address, + ) {} - constructor(address: Address, localDomain: Domain, mailbox: Address) { - this.localDomain = localDomain; - this.address = address; - this.mailbox = mailbox; + announceDomainHash() { + return domainHash(this.localDomain, this.mailbox_address); } - domainHash() { - return domainHash(this.localDomain, this.mailbox); + checkpointDomainHash(merkle_tree_address: Address) { + return domainHash(this.localDomain, merkle_tree_address); } - message(checkpoint: Checkpoint, messageId?: HexString) { - const types = ['bytes32', 'bytes32', 'uint32']; - const values = [this.domainHash(), checkpoint.root, checkpoint.index]; - if (messageId) { - types.push('bytes32'); - values.push(messageId); - } + message(checkpoint: Checkpoint, messageId: HexString) { + const types = ['bytes32', 'bytes32', 'uint32', 'bytes32']; + const values = [ + this.checkpointDomainHash(checkpoint.merkle_tree_hook_address), + checkpoint.root, + checkpoint.index, + messageId, + ]; return ethers.utils.solidityPack(types, values); } - messageHash(checkpoint: Checkpoint, messageId?: HexString) { + messageHash(checkpoint: Checkpoint, messageId: HexString) { const message = this.message(checkpoint, messageId); return ethers.utils.arrayify(ethers.utils.keccak256(message)); } @@ -46,7 +40,7 @@ export class BaseValidator { recoverAddressFromCheckpoint( checkpoint: Checkpoint, signature: SignatureLike, - messageId?: HexString, + messageId: HexString, ): Address { const msgHash = this.messageHash(checkpoint, messageId); return ethers.utils.verifyMessage(msgHash, signature); @@ -55,7 +49,7 @@ export class BaseValidator { matchesSigner( checkpoint: Checkpoint, signature: SignatureLike, - messageId?: HexString, + messageId: HexString, ) { return ( this.recoverAddressFromCheckpoint( @@ -66,42 +60,3 @@ export class BaseValidator { ); } } - -export class Validator extends BaseValidator { - constructor( - protected signer: ethers.Signer, - address: Address, - localDomain: Domain, - mailbox: Address, - ) { - super(address, localDomain, mailbox); - } - - static async fromSigner( - signer: ethers.Signer, - localDomain: Domain, - mailbox: Address, - ) { - return new Validator( - signer, - await signer.getAddress(), - localDomain, - mailbox, - ); - } - - async signCheckpoint(root: HexString, index: number): Promise { - const checkpoint = { - root, - index, - mailbox_address: this.mailbox, - mailbox_domain: this.localDomain, - }; - const msgHash = this.messageHash(checkpoint); - const signature = await this.signer.signMessage(msgHash); - return { - value: checkpoint, - signature, - }; - } -}