Modify Core deployment for chain expansion (#1040)

* Modify Core deployment for chain expansion

* PR review

* Fix test
pull/1045/head
Nam Chu Hoai 2 years ago committed by GitHub
parent f19110f4de
commit ef763dce18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 84
      typescript/infra/scripts/core.ts
  2. 10
      typescript/infra/src/config/agent.ts
  3. 62
      typescript/infra/src/core/deploy.ts
  4. 2
      typescript/infra/test/core.test.ts
  5. 8
      typescript/sdk/src/deploy/AbacusDeployer.ts
  6. 6
      typescript/sdk/src/deploy/core/AbacusCoreDeployer.ts
  7. 19
      typescript/sdk/src/providers/MultiProvider.ts

@ -1,4 +1,7 @@
import {
ChainMap,
Chains,
ContractVerificationInput,
buildContracts,
coreFactories,
serializeContracts,
@ -13,54 +16,73 @@ import {
getCoreRustDirectory,
getCoreVerificationDirectory,
getEnvironment,
getEnvironmentDirectory,
} from './utils';
function mergeVerificationInputs<ChainName extends Chains>(
existingInputsMap: ChainMap<ChainName, ContractVerificationInput[]>,
newInputsMap: ChainMap<ChainName, ContractVerificationInput[]>,
): ChainMap<ChainName, ContractVerificationInput[]> {
const allChains = new Set<ChainName>();
Object.keys(existingInputsMap).forEach((_) => allChains.add(_ as ChainName));
Object.keys(newInputsMap).forEach((_) => allChains.add(_ as ChainName));
// @ts-ignore
const ret: ChainMap<ChainName, ContractVerificationInput[]> = {};
for (const chain of allChains) {
const existingInputs = existingInputsMap[chain] || [];
const newInputs = newInputsMap[chain] || [];
ret[chain] = [...existingInputs, ...newInputs];
}
return ret;
}
async function main() {
const environment = await getEnvironment();
const config = getCoreEnvironmentConfig(environment) as any;
const multiProvider = await config.getMultiProvider();
const deployer = new AbacusCoreInfraDeployer(multiProvider, config.core);
let partial_contracts = {};
try {
let previousContracts = {};
previousAddressParsing: try {
if (environment === 'test') {
break previousAddressParsing;
}
const addresses = readJSON(
getEnvironmentDirectory(environment),
'partial_core_addresses.json',
getCoreContractsSdkFilepath(),
`${environment}.json`,
);
partial_contracts = buildContracts(addresses, coreFactories);
previousContracts = buildContracts(addresses, coreFactories);
} catch (e) {
console.info('Could not load partial core addresses, file may not exist');
}
try {
const contracts = await deployer.deploy(partial_contracts);
writeJSON(
getCoreContractsSdkFilepath(),
`${environment}.json`,
serializeContracts(contracts),
);
writeJSON(
getCoreVerificationDirectory(environment),
'verification.json',
deployer.verificationInputs,
);
deployer.writeRustConfigs(
environment,
getCoreRustDirectory(environment),
contracts,
);
await deployer.deploy(previousContracts);
} catch (e) {
console.error(`Encountered error during deploy`);
console.error(e);
// persist partial deployment
writeJSON(
getEnvironmentDirectory(environment),
'partial_core_addresses.json',
{
...serializeContracts(deployer.deployedContracts),
...serializeContracts(partial_contracts),
},
);
}
// Persist artifacts, irrespective of deploy success
writeJSON(
getCoreContractsSdkFilepath(),
`${environment}.json`,
serializeContracts(deployer.deployedContracts),
);
const existingVerificationInputs = readJSON(
getCoreVerificationDirectory(environment),
'verification.json',
);
writeJSON(
getCoreVerificationDirectory(environment),
'verification.json',
mergeVerificationInputs(
existingVerificationInputs,
deployer.verificationInputs,
),
);
deployer.writeRustConfigs(environment, getCoreRustDirectory(environment));
}
main().then(console.log).catch(console.error);

@ -244,7 +244,7 @@ export type RustContractBlock<T> = {
export type OutboxAddresses = {
outbox: types.Address;
interchainGasPaymaster?: types.Address;
interchainGasPaymaster: types.Address;
};
export type InboxAddresses = {
@ -256,8 +256,12 @@ export type RustConfig<Chain extends ChainName> = {
environment: DeployEnvironment;
index?: { from: string };
signers: Partial<ChainMap<Chain, RustSigner>>;
inboxes: RemoteChainMap<Chain, any, RustContractBlock<InboxAddresses>>;
outbox: RustContractBlock<OutboxAddresses>;
inboxes: RemoteChainMap<
Chain,
any,
RustContractBlock<Partial<InboxAddresses>>
>;
outbox: RustContractBlock<Partial<OutboxAddresses>>;
tracing: {
level: string;
fmt: 'json';

@ -12,13 +12,10 @@ import { writeJSON } from '../utils/utils';
export class AbacusCoreInfraDeployer<
Chain extends ChainName,
> extends AbacusCoreDeployer<Chain> {
writeRustConfigs(
environment: DeployEnvironment,
directory: string,
contractsMap: Awaited<ReturnType<AbacusCoreDeployer<Chain>['deploy']>>,
) {
writeRustConfigs(environment: DeployEnvironment, directory: string) {
const configChains = Object.keys(this.configMap);
objMap(this.configMap, (chain) => {
const contracts = contractsMap[chain];
const contracts = this.deployedContracts[chain];
const outboxMetadata = chainMetadata[chain];
@ -28,8 +25,8 @@ export class AbacusCoreInfraDeployer<
inboxes: {},
outbox: {
addresses: {
outbox: contracts.outbox.address,
interchainGasPaymaster: contracts.interchainGasPaymaster.address,
outbox: contracts?.outbox?.address,
interchainGasPaymaster: contracts?.interchainGasPaymaster?.address,
},
domain: outboxMetadata.id.toString(),
name: chain,
@ -53,31 +50,34 @@ export class AbacusCoreInfraDeployer<
rustConfig.index = { from: startingBlockNumber.toString() };
}
this.multiProvider.remoteChains(chain).forEach((remote) => {
// The agent configuration file should contain the `chain`'s inbox on
// all the remote chains
const remoteContracts = contractsMap[remote];
const inboxContracts =
remoteContracts.inboxes[chain as Exclude<Chain, Chain>];
this.multiProvider
.remoteChains(chain)
.filter((_) => configChains.includes(_))
.forEach((remote) => {
// The agent configuration file should contain the `chain`'s inbox on
// all the remote chains
const remoteContracts = this.deployedContracts[remote];
const inboxContracts =
remoteContracts?.inboxes?.[chain as Exclude<Chain, Chain>];
const metadata = chainMetadata[remote];
const inbox = {
domain: metadata.id.toString(),
name: remote,
rpcStyle: 'ethereum',
finalityBlocks: metadata.finalityBlocks.toString(),
connection: {
type: ConnectionType.Http,
url: '',
},
addresses: {
inbox: inboxContracts.inbox.address,
validatorManager: inboxContracts.inboxValidatorManager.address,
},
} as const;
const metadata = chainMetadata[remote];
const inbox = {
domain: metadata.id.toString(),
name: remote,
rpcStyle: 'ethereum',
finalityBlocks: metadata.finalityBlocks.toString(),
connection: {
type: ConnectionType.Http,
url: '',
},
addresses: {
inbox: inboxContracts?.inbox.address,
validatorManager: inboxContracts?.inboxValidatorManager.address,
},
} as const;
rustConfig.inboxes[remote] = inbox;
});
rustConfig.inboxes[remote] = inbox;
});
writeJSON(directory, `${chain}_config.json`, rustConfig);
});
}

@ -49,7 +49,7 @@ describe('core', async () => {
const base = './test/outputs/core';
writeJSON(base, 'contracts.json', serializeContracts(contracts));
writeJSON(base, 'verification.json', deployer.verificationInputs);
deployer.writeRustConfigs(environment, path.join(base, 'rust'), contracts);
deployer.writeRustConfigs(environment, path.join(base, 'rust'));
});
it('transfers ownership', async () => {

@ -67,9 +67,11 @@ export abstract class AbacusDeployer<
},
);
const configChains = Object.keys(this.configMap) as Chain[];
const targetChains = this.multiProvider
.chains()
.filter((chain) => configChains.includes(chain));
const targetChains = this.multiProvider.intersect(
configChains,
false,
).intersection;
this.logger(`Start deploy to ${targetChains}`);
for (const chain of targetChains) {
const chainConnection = this.multiProvider.getChainConnection(chain);

@ -156,7 +156,11 @@ export class AbacusCoreDeployer<Chain extends ChainName> extends AbacusDeployer<
}
});
const remotes = this.multiProvider.remoteChains(chain);
const configChains = Object.keys(this.configMap) as Chain[];
const remotes = this.multiProvider
.intersect(configChains, false)
.multiProvider.remoteChains(chain);
const inboxes: Partial<Record<Chain, InboxContracts>> =
this.deployedContracts[chain]?.inboxes ?? ({} as any);

@ -106,14 +106,27 @@ export class MultiProvider<
*/
intersect<IntersectionChain extends Chain>(
chains: ChainName[],
throwIfNotSubset = false,
): {
intersection: IntersectionChain[];
multiProvider: MultiProvider<IntersectionChain>;
} {
const ownChains = this.chains();
const intersection = ownChains.filter((c) =>
chains.includes(c),
) as IntersectionChain[];
const intersection = [] as IntersectionChain[];
for (const chain of chains) {
// @ts-ignore
if (ownChains.includes(chain)) {
// @ts-ignore
intersection.push(chain);
} else {
if (throwIfNotSubset) {
throw new Error(
`MultiProvider#intersect: chains specified ${chain}, but ownChains did not include it`,
);
}
}
}
if (!intersection.length) {
throw new Error(`No chains shared between MultiProvider and list`);

Loading…
Cancel
Save