Enroll additional validators on v3 mainnet (#2888)

Co-authored-by: Yorke Rhodes <yorke@hyperlane.xyz>
typechain11
Nam Chu Hoai 1 year ago committed by GitHub
parent e4ed2c3f86
commit 0302a282bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      typescript/infra/fork.sh
  2. 40
      typescript/infra/scripts/check-deploy.ts
  3. 102
      typescript/infra/scripts/deploy.ts
  4. 103
      typescript/infra/scripts/helloworld/deploy.ts
  5. 72
      typescript/infra/scripts/utils.ts
  6. 86
      typescript/infra/scripts/verify-validators.ts
  7. 40
      typescript/infra/src/agents/aws/validator.ts
  8. 19
      typescript/infra/src/config/helloworld/config.ts
  9. 46
      typescript/sdk/src/consts/multisigIsm.ts
  10. 2
      typescript/sdk/src/metadata/ChainMetadataManager.ts
  11. 12
      typescript/sdk/src/router/HyperlaneRouterChecker.ts
  12. 2
      typescript/sdk/src/router/types.ts
  13. 2
      typescript/utils/index.ts
  14. 2
      typescript/utils/src/checkpoints.ts
  15. 4
      typescript/utils/src/domains.ts
  16. 2
      typescript/utils/src/types.ts
  17. 87
      typescript/utils/src/validator.ts

@ -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

@ -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<any, any>;
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<any, any>;
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);

@ -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',

@ -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);

@ -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<ChainMap<RouterConfig>> {
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<RouterConfig> = {};
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<ChainMap<ProxiedRouterConfig>> {
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<CoreConfig>,
): ChainMap<Set<string>> {

@ -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);

@ -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<SignedCheckpoint>;
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<CheckpointMetric[]> {
async compare(other: S3Validator, count = 5): Promise<CheckpointMetric[]> {
const latestCheckpointIndex = await this.s3Bucket.getS3Obj<number>(
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<S3CheckpointReceipt | undefined> {
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,

@ -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<ChainMap<RouterConfig>> {
const routerConfig = await getRouterConfig(environment, multiProvider, true);
return objMap(routerConfig, (chain, config) => ({
...config,
interchainSecurityModule: routingIsm(environment, chain, context),
}));
}

@ -7,24 +7,25 @@ export const defaultMultisigIsmConfigs: ChainMap<MultisigConfig> = {
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<MultisigConfig> = {
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<MultisigConfig> = {
validators: [
'0xb9453d675e0fa3c178a17b4ce1ad5b1a279b3af9',
'0x4512985a574cb127b2af2d4bb676876ce804e3f8',
'0xb144bb2f599a5af095bc30367856f27ea8a8adc7',
'0x41188cb5a5493a961c467ba38a3f8b1f1d35ee63', // everstake
],
},
scroll: {
@ -93,7 +95,7 @@ export const defaultMultisigIsmConfigs: ChainMap<MultisigConfig> = {
validators: [
'0xad557170a9f2f21c35e03de07cb30dcbcc3dff63',
'0xb37fe43a9f47b7024c2d5ae22526cc66b5261533',
'0x7210fa0a6be39a75cb14d682ebfb37e2b53ecbe5',
'0x276de8e2b88e659c4e5ad30d62d9de42c3da3403', // everstake
],
},
polygonzkevm: {
@ -101,7 +103,7 @@ export const defaultMultisigIsmConfigs: ChainMap<MultisigConfig> = {
validators: [
'0x86f2a44592bb98da766e880cfd70d3bbb295e61a',
'0xc84076030bdabaabb9e61161d833dd84b700afda',
'0x6a1da2e0b7ae26aaece1377c0a4dbe25b85fa3ca',
'0x57231619fea13d85270ca6943298046c75a6dd01', // everstake
],
},
// ----------------- Testnets -----------------

@ -329,8 +329,6 @@ export class ChainMetadataManager<MetaExt = {}> {
): ChainMetadataManager<MetaExt & NewExt> {
const newMetadata: ChainMap<ChainMetadata<MetaExt & NewExt>> = {};
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);

@ -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 {

@ -63,8 +63,6 @@ export enum ClientViolationType {
export interface ClientViolation extends CheckerViolation {
type: ClientViolationType;
contract: MailboxClient;
actual: string;
expected: string;
description?: string;
}

@ -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';

@ -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;
}

@ -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'],
);
}

@ -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;
};
/**

@ -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<S3Checkpoint> {
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,
};
}
}

Loading…
Cancel
Save