@hyperlane-xyz/core
Version:
Core solidity contracts for Hyperlane
287 lines (268 loc) • 10.2 kB
text/typescript
'use strict'
import { ethers } from 'ethers'
import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes'
import { hexStringEquals } from '@eth-optimism/core-utils'
import { getContractFactory, getContractDefinition } from '../src/contract-defs'
import { names } from '../src/address-names'
import {
getInput,
color as c,
getArtifactFromManagedName,
getEtherscanUrl,
printSectionHead,
printComparison,
} from '../src/validation-utils'
task('validate:address-dictator')
.addParam(
'dictator',
'Address of the AddressDictator to validate.',
undefined,
types.string
)
.addParam(
'manager',
'Address of the Address Manager contract which would be updated by the Dictator.',
undefined,
types.string
)
.addParam(
'multisig',
'Address of the multisig contract which should be the final owner',
undefined,
types.string
)
.addOptionalParam(
'contractsRpcUrl',
'RPC Endpoint to query for data',
process.env.CONTRACTS_RPC_URL,
types.string
)
.setAction(async (args) => {
if (!args.contractsRpcUrl) {
throw new Error(
c.red('RPC URL must be set in your env, or passed as an argument.')
)
}
const provider = new ethers.providers.JsonRpcProvider(args.contractsRpcUrl)
const network = await provider.getNetwork()
console.log()
printSectionHead("First make sure you're on the right chain:")
console.log(
`Reading from the ${c.red(network.name)} network (Chain ID: ${c.red(
'' + network.chainId
)})`
)
await getInput(c.yellow('OK? Hit enter to continue.'))
const dictatorArtifact = getContractDefinition('AddressDictator')
const dictatorCode = await provider.getCode(args.dictator)
printSectionHead(`
Validate the Address Dictator deployment at\n${getEtherscanUrl(
network,
args.dictator
)}`)
printComparison(
'Comparing deployed AddressDictator bytecode against local build artifacts',
'Deployed AddressDictator code',
{ name: 'Compiled bytecode', value: dictatorArtifact.deployedBytecode },
{ name: 'Deployed bytecode', value: dictatorCode }
)
// Connect to the deployed AddressDictator.
const dictatorContract = getContractFactory('AddressDictator')
.attach(args.dictator)
.connect(provider)
const finalOwner = await dictatorContract.finalOwner()
printComparison(
'Comparing the finalOwner address in the AddressDictator to the multisig address',
'finalOwner',
{ name: 'multisig address', value: args.multisig },
{ name: 'finalOwner ', value: finalOwner }
)
const deployedManager = await dictatorContract.manager()
printComparison(
'Validating the AddressManager address in the AddressDictator',
'addressManager',
{ name: 'manager ', value: args.manager },
{ name: 'Address Manager', value: deployedManager }
)
await getInput(c.yellow('OK? Hit enter to continue.'))
// Get names and addresses from the Dictator.
const namedAddresses = await dictatorContract.getNamedAddresses()
// In order to reduce noise for the user, we query the AddressManager identify addresses that
// will not be changed, and skip over them in this block.
const managerContract = getContractFactory('Lib_AddressManager')
.attach(args.manager)
.connect(provider)
// Now we loop over those and compare the addresses/deployedBytecode to deployment artifacts.
for (const pair of namedAddresses) {
if (pair.name === 'L2CrossDomainMessenger') {
console.log('L2CrossDomainMessenger is set to:', pair.addr)
await getInput(c.yellow('OK? Hit enter to continue.'))
// This is an L2 predeploy, so we skip bytecode and config validation.
continue
}
const currentAddress = await managerContract.getAddress(pair.name)
const artifact = getArtifactFromManagedName(pair.name)
const addressChanged = !hexStringEquals(currentAddress, pair.addr)
if (addressChanged) {
printSectionHead(
`Validate the ${pair.name} deployment.
Current address: ${getEtherscanUrl(network, currentAddress)}
Upgraded address ${getEtherscanUrl(network, pair.addr)}`
)
const code = await provider.getCode(pair.addr)
printComparison(
`Verifying ${pair.name} source code against local deployment artifacts`,
`Deployed ${pair.name} code`,
{
name: 'artifact.deployedBytecode',
value: artifact.deployedBytecode,
},
{ name: 'Deployed bytecode ', value: code }
)
// Identify contracts which inherit from Lib_AddressResolver, and check that they
// have the right manager address.
if (Object.keys(artifact)) {
if (artifact.abi.some((el) => el.name === 'libAddressManager')) {
const libAddressManager = await getContractFactory(
'Lib_AddressResolver'
)
.attach(pair.addr)
.connect(provider)
.libAddressManager()
printComparison(
`Verifying ${pair.name} has the correct AddressManager address`,
`The AddressManager address in ${pair.name}`,
{ name: 'Deployed value', value: libAddressManager },
{ name: 'Expected value', value: deployedManager }
)
await getInput(c.yellow('OK? Hit enter to continue.'))
}
}
}
await validateDeployedConfig(provider, network, args.manager, pair)
}
console.log(c.green('\nAddressManager Validation complete!'))
})
/**
* Validates that the deployed contracts have the expected storage variables.
*
* @param {*} provider
* @param {{ name: string; addr: string }} pair The contract name and address
*/
const validateDeployedConfig = async (
provider,
network,
manager,
pair: { name: string; addr: string }
) => {
printSectionHead(`
Ensure that the ${pair.name} at\n${getEtherscanUrl(
network,
pair.addr
)} is configured correctly`)
if (pair.name === names.managed.contracts.StateCommitmentChain) {
const scc = getContractFactory(pair.name)
.attach(pair.addr)
.connect(provider)
// --scc-fraud-proof-window 604800 \
const fraudProofWindow = await scc.FRAUD_PROOF_WINDOW()
printComparison(
'Checking the fraudProofWindow of the StateCommitmentChain',
'StateCommitmentChain.fraudProofWindow',
{
name: 'Configured fraudProofWindow',
value: ethers.BigNumber.from(604_800).toHexString(),
},
{
name: 'Deployed fraudProofWindow ',
value: ethers.BigNumber.from(fraudProofWindow).toHexString(),
}
)
await getInput(c.yellow('OK? Hit enter to continue.'))
// --scc-sequencer-publish-window 12592000 \
const sequencerPublishWindow = await scc.SEQUENCER_PUBLISH_WINDOW()
printComparison(
'Checking the sequencerPublishWindow of the StateCommitmentChain',
'StateCommitmentChain.sequencerPublishWindow',
{
name: 'Configured sequencerPublishWindow ',
value: ethers.BigNumber.from(12592000).toHexString(),
},
{
name: 'Deployed sequencerPublishWindow',
value: ethers.BigNumber.from(sequencerPublishWindow).toHexString(),
}
)
await getInput(c.yellow('OK? Hit enter to continue.'))
} else if (pair.name === names.managed.contracts.CanonicalTransactionChain) {
const ctc = getContractFactory(pair.name)
.attach(pair.addr)
.connect(provider)
// --ctc-max-transaction-gas-limit 15000000 \
const maxTransactionGasLimit = await ctc.maxTransactionGasLimit()
printComparison(
'Checking the maxTransactionGasLimit of the CanonicalTransactionChain',
'CanonicalTransactionChain.maxTransactionGasLimit',
{
name: 'Configured maxTransactionGasLimit',
value: ethers.BigNumber.from(15_000_000).toHexString(),
},
{
name: 'Deployed maxTransactionGasLimit ',
value: ethers.BigNumber.from(maxTransactionGasLimit).toHexString(),
}
)
await getInput(c.yellow('OK? Hit enter to continue.'))
// --ctc-l2-gas-discount-divisor 32 \
const l2GasDiscountDivisor = await ctc.l2GasDiscountDivisor()
printComparison(
'Checking the l2GasDiscountDivisor of the CanonicalTransactionChain',
'CanonicalTransactionChain.l2GasDiscountDivisor',
{
name: 'Configured l2GasDiscountDivisor',
value: ethers.BigNumber.from(32).toHexString(),
},
{
name: 'Deployed l2GasDiscountDivisor ',
value: ethers.BigNumber.from(l2GasDiscountDivisor).toHexString(),
}
)
await getInput(c.yellow('OK? Hit enter to continue.'))
// --ctc-enqueue-gas-cost 60000 \
const enqueueGasCost = await ctc.enqueueGasCost()
printComparison(
'Checking the enqueueGasCost of the CanonicalTransactionChain',
'CanonicalTransactionChain.enqueueGasCost',
{
name: 'Configured enqueueGasCost',
value: ethers.BigNumber.from(60000).toHexString(),
},
{
name: 'Deployed enqueueGasCost ',
value: ethers.BigNumber.from(enqueueGasCost).toHexString(),
}
)
await getInput(c.yellow('OK? Hit enter to continue.'))
} else if (pair.name === names.managed.contracts.OVM_L1CrossDomainMessenger) {
const messengerManager = await getContractFactory('L1CrossDomainMessenger')
.attach(pair.addr)
.connect(provider)
.libAddressManager()
printComparison(
'Ensure that the L1CrossDomainMessenger (implementation) is initialized with a non-zero Address Manager variable',
"L1CrossDomainMessenger's Lib_AddressManager",
{
name: 'Configured Lib_AddressManager',
value: messengerManager,
},
{
name: 'Deployed Lib_AddressManager ',
value: manager,
}
)
} else {
console.log(c.green(`${pair.name} has no config to check`))
await getInput(c.yellow('OK? Hit enter to continue.'))
}
}