Commit d667bbb8 authored by Maurelian's avatar Maurelian Committed by GitHub

Merge pull request #1753 from ethereum-optimism/maurelian/check-moar-things

parents 5bacd4c0 bbd42e03
---
'@eth-optimism/contracts': patch
---
Add config checks to validation script
...@@ -30,9 +30,9 @@ export const color = Object.fromEntries( ...@@ -30,9 +30,9 @@ export const color = Object.fromEntries(
]) ])
) )
export const getArtifact = (name: string) => { // helper for finding the right artifact from the deployed name
// Paths to artifacts relative to artifacts/contracts const locateArtifact = (name: string) => {
const locations = { return {
'ChainStorageContainer-CTC-batches': 'ChainStorageContainer-CTC-batches':
'L1/rollup/ChainStorageContainer.sol/ChainStorageContainer.json', 'L1/rollup/ChainStorageContainer.sol/ChainStorageContainer.json',
'ChainStorageContainer-SCC-batches': 'ChainStorageContainer-SCC-batches':
...@@ -48,9 +48,12 @@ export const getArtifact = (name: string) => { ...@@ -48,9 +48,12 @@ export const getArtifact = (name: string) => {
'libraries/resolver/Lib_ResolvedDelegateProxy.sol/Lib_ResolvedDelegateProxy.json', 'libraries/resolver/Lib_ResolvedDelegateProxy.sol/Lib_ResolvedDelegateProxy.json',
Proxy__OVM_L1StandardBridge: Proxy__OVM_L1StandardBridge:
'chugsplash/L1ChugSplashProxy.sol/L1ChugSplashProxy.json', 'chugsplash/L1ChugSplashProxy.sol/L1ChugSplashProxy.json',
} }[name]
}
export const getArtifactFromManagedName = (name: string) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
return require(`../artifacts/contracts/${locations[name]}`) return require(`../artifacts/contracts/${locateArtifact(name)}`)
} }
export const getEtherscanUrl = (network, address: string) => { export const getEtherscanUrl = (network, address: string) => {
...@@ -63,26 +66,32 @@ const truncateLongString = (value: string): string => { ...@@ -63,26 +66,32 @@ const truncateLongString = (value: string): string => {
return value.length > 66 ? `${value.slice(0, 66)}...` : value return value.length > 66 ? `${value.slice(0, 66)}...` : value
} }
export const printSectionHead = (msg: string) => {
console.log(color.cyan(msg))
console.log(
color.cyan('='.repeat(Math.max(...msg.split('\n').map((s) => s.length))))
)
}
export const printComparison = ( export const printComparison = (
action: string, action: string,
description: string, description: string,
expected: { name: string; value: string }, expected: { name: string; value: any },
deployed: { name: string; value: string } deployed: { name: string; value: any }
) => { ) => {
console.log(action + ':') console.log(`\n${action}:`)
if (hexStringEquals(expected.value, deployed.value)) { if (hexStringEquals(expected.value, deployed.value)) {
console.log( console.log(
color.green( color.green(`${expected.name}: ${truncateLongString(expected.value)}`)
`${expected.name}: ${truncateLongString(expected.value)} )
matches console.log('matches')
${deployed.name}: ${truncateLongString(deployed.value)}` console.log(
) color.green(`${deployed.name}: ${truncateLongString(deployed.value)}`)
) )
console.log(color.green(`${description} looks good! 😎`)) console.log(color.green(`${description} looks good! 😎`))
} else { } else {
throw new Error(`${description} looks wrong. throw new Error(
${expected.value}\ndoes not match\n${deployed.value}. `${description} looks wrong. ${expected.value}\ndoes not match\n${deployed.value}.`
`) )
} }
console.log() // Add some whitespace
} }
...@@ -4,13 +4,15 @@ import { ethers } from 'ethers' ...@@ -4,13 +4,15 @@ import { ethers } from 'ethers'
import { task } from 'hardhat/config' import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes' import * as types from 'hardhat/internal/core/params/argumentTypes'
import { hexStringEquals } from '@eth-optimism/core-utils' import { hexStringEquals } from '@eth-optimism/core-utils'
import { getContractFactory } from '../src/contract-defs' import { getContractFactory, getContractDefinition } from '../src/contract-defs'
import { names } from '../src/address-names'
import { import {
getInput, getInput,
color as c, color as c,
getArtifact, getArtifactFromManagedName,
getEtherscanUrl, getEtherscanUrl,
printSectionHead,
printComparison, printComparison,
} from '../src/validation-utils' } from '../src/validation-utils'
...@@ -49,7 +51,7 @@ task('validate:address-dictator') ...@@ -49,7 +51,7 @@ task('validate:address-dictator')
const network = await provider.getNetwork() const network = await provider.getNetwork()
console.log() console.log()
console.log(c.cyan("First make sure you're on the right chain:")) printSectionHead("First make sure you're on the right chain:")
console.log( console.log(
`Reading from the ${c.red(network.name)} network (Chain ID: ${c.red( `Reading from the ${c.red(network.name)} network (Chain ID: ${c.red(
'' + network.chainId '' + network.chainId
...@@ -57,16 +59,14 @@ task('validate:address-dictator') ...@@ -57,16 +59,14 @@ task('validate:address-dictator')
) )
await getInput(c.yellow('OK? Hit enter to continue.')) await getInput(c.yellow('OK? Hit enter to continue.'))
// eslint-disable-next-line @typescript-eslint/no-var-requires const dictatorArtifact = getContractDefinition('AddressDictator')
const dictatorArtifact = require('../artifacts/contracts/L1/deployment/AddressDictator.sol/AddressDictator.json')
const dictatorCode = await provider.getCode(args.dictator) const dictatorCode = await provider.getCode(args.dictator)
console.log( printSectionHead(`
c.cyan(` Validate the Address Dictator deployment at\n${getEtherscanUrl(
Now validating the Address Dictator deployment at\n${getEtherscanUrl( network,
network, args.dictator
args.dictator )}`)
)}`)
)
printComparison( printComparison(
'Comparing deployed AddressDictator bytecode against local build artifacts', 'Comparing deployed AddressDictator bytecode against local build artifacts',
'Deployed AddressDictator code', 'Deployed AddressDictator code',
...@@ -87,12 +87,12 @@ Now validating the Address Dictator deployment at\n${getEtherscanUrl( ...@@ -87,12 +87,12 @@ Now validating the Address Dictator deployment at\n${getEtherscanUrl(
{ name: 'finalOwner ', value: finalOwner } { name: 'finalOwner ', value: finalOwner }
) )
const manager = await dictatorContract.manager() const deployedManager = await dictatorContract.manager()
printComparison( printComparison(
'Validating the AddressManager address in the AddressDictator', 'Validating the AddressManager address in the AddressDictator',
'addressManager', 'addressManager',
{ name: 'manager', value: args.manager }, { name: 'manager ', value: args.manager },
{ name: 'Address Manager', value: manager } { name: 'Address Manager', value: deployedManager }
) )
await getInput(c.yellow('OK? Hit enter to continue.')) await getInput(c.yellow('OK? Hit enter to continue.'))
...@@ -107,15 +107,20 @@ Now validating the Address Dictator deployment at\n${getEtherscanUrl( ...@@ -107,15 +107,20 @@ Now validating the Address Dictator deployment at\n${getEtherscanUrl(
// Now we loop over those and compare the addresses/deployedBytecode to deployment artifacts. // Now we loop over those and compare the addresses/deployedBytecode to deployment artifacts.
for (const pair of namedAddresses) { 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 currentAddress = await managerContract.getAddress(pair.name)
const artifact = getArtifact(pair.name) const artifact = getArtifactFromManagedName(pair.name)
const addressChanged = !hexStringEquals(currentAddress, pair.addr) const addressChanged = !hexStringEquals(currentAddress, pair.addr)
if (addressChanged) { if (addressChanged) {
console.log( printSectionHead(
c.cyan(` `Validate the ${pair.name} deployment.
Now validating the ${pair.name} deployment.
Current address: ${getEtherscanUrl(network, currentAddress)} Current address: ${getEtherscanUrl(network, currentAddress)}
Upgraded address ${getEtherscanUrl(network, pair.addr)}`) Upgraded address ${getEtherscanUrl(network, pair.addr)}`
) )
const code = await provider.getCode(pair.addr) const code = await provider.getCode(pair.addr)
...@@ -144,12 +149,138 @@ Upgraded address ${getEtherscanUrl(network, pair.addr)}`) ...@@ -144,12 +149,138 @@ Upgraded address ${getEtherscanUrl(network, pair.addr)}`)
`Verifying ${pair.name} has the correct AddressManager address`, `Verifying ${pair.name} has the correct AddressManager address`,
`The AddressManager address in ${pair.name}`, `The AddressManager address in ${pair.name}`,
{ name: 'Deployed value', value: libAddressManager }, { name: 'Deployed value', value: libAddressManager },
{ name: 'Expected value', value: manager } { name: 'Expected value', value: deployedManager }
) )
await getInput(c.yellow('OK? Hit enter to continue.'))
} }
} }
} }
await getInput(c.yellow('OK? Hit enter to continue.')) await validateDeployedConfig(provider, network, args.manager, pair)
} }
console.log(c.green('AddressManager Validation complete!')) 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.'))
}
}
...@@ -3,13 +3,14 @@ ...@@ -3,13 +3,14 @@
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { task } from 'hardhat/config' import { task } from 'hardhat/config'
import * as types from 'hardhat/internal/core/params/argumentTypes' import * as types from 'hardhat/internal/core/params/argumentTypes'
import { getContractFactory } from '../src/contract-defs' import { getContractFactory, getContractDefinition } from '../src/contract-defs'
import { import {
getInput, getInput,
color as c, color as c,
getEtherscanUrl, getEtherscanUrl,
printComparison, printComparison,
printSectionHead,
} from '../src/validation-utils' } from '../src/validation-utils'
task('validate:chugsplash-dictator') task('validate:chugsplash-dictator')
...@@ -54,24 +55,24 @@ task('validate:chugsplash-dictator') ...@@ -54,24 +55,24 @@ task('validate:chugsplash-dictator')
)})` )})`
) )
await getInput(c.yellow('OK? Hit enter to continue.')) await getInput(c.yellow('OK? Hit enter to continue.'))
console.log()
// eslint-disable-next-line @typescript-eslint/no-var-requires const dictatorArtifact = getContractDefinition('ChugSplashDictator')
const dictatorArtifact = require('../artifacts/contracts/L1/deployment/ChugSplashDictator.sol/ChugSplashDictator.json')
const dictatorCode = await provider.getCode(args.dictator) const dictatorCode = await provider.getCode(args.dictator)
console.log( printSectionHead(
c.cyan(` `Validate the Chugsplash Dictator deployment at\n${getEtherscanUrl(
Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl(
network, network,
args.dictator args.dictator
)}`) )}`
) )
printComparison( printComparison(
'Comparing deployed ChugSplashDictator bytecode against local build artifacts', 'Compare the deployed ChugSplashDictator bytecode against local build artifacts',
'Deployed ChugSplashDictator code', 'Deployed ChugSplashDictator code',
{ name: 'Compiled bytecode', value: dictatorArtifact.deployedBytecode }, { name: 'Compiled bytecode', value: dictatorArtifact.deployedBytecode },
{ name: 'Deployed bytecode', value: dictatorCode } { name: 'Deployed bytecode', value: dictatorCode }
) )
await getInput(c.yellow('OK? Hit enter to continue.')) await getInput(c.yellow('OK? Hit enter to continue.'))
console.log()
console.log( console.log(
c.cyan("The next 4 checks will validate the ChugSplashDictator's config") c.cyan("The next 4 checks will validate the ChugSplashDictator's config")
...@@ -82,12 +83,13 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl( ...@@ -82,12 +83,13 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl(
.connect(provider) .connect(provider)
const finalOwner = await dictatorContract.finalOwner() const finalOwner = await dictatorContract.finalOwner()
printComparison( printComparison(
'1. Comparing the finalOwner address in the ChugSplashDictator to the multisig address', 'Compare the finalOwner address in the ChugSplashDictator to the multisig address',
'finalOwner', 'finalOwner',
{ name: 'multisig address', value: args.multisig }, { name: 'multisig address', value: args.multisig },
{ name: 'finalOwner ', value: finalOwner } { name: 'finalOwner ', value: finalOwner }
) )
await getInput(c.yellow('OK? Hit enter to continue.')) await getInput(c.yellow('OK? Hit enter to continue.'))
console.log()
const dictatorMessengerSlotKey = await dictatorContract.messengerSlotKey() const dictatorMessengerSlotKey = await dictatorContract.messengerSlotKey()
const dictatorMessengerSlotVal = await dictatorContract.messengerSlotVal() const dictatorMessengerSlotVal = await dictatorContract.messengerSlotVal()
...@@ -96,7 +98,7 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl( ...@@ -96,7 +98,7 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl(
dictatorMessengerSlotKey dictatorMessengerSlotKey
) )
printComparison( printComparison(
'2. Comparing the messenger slot key/value to be set, with the current values in the proxy', 'Compare the Messenger slot key/value to be set, with the current values in the proxy',
`Storage slot key ${dictatorMessengerSlotKey}`, `Storage slot key ${dictatorMessengerSlotKey}`,
{ {
name: `Value in the proxy at slot key\n${dictatorMessengerSlotKey}`, name: `Value in the proxy at slot key\n${dictatorMessengerSlotKey}`,
...@@ -108,6 +110,7 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl( ...@@ -108,6 +110,7 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl(
} }
) )
await getInput(c.yellow('OK? Hit enter to continue.')) await getInput(c.yellow('OK? Hit enter to continue.'))
console.log()
const dictatorBridgeSlotKey = await dictatorContract.bridgeSlotKey() const dictatorBridgeSlotKey = await dictatorContract.bridgeSlotKey()
const dictatorBridgeSlotVal = await dictatorContract.bridgeSlotVal() const dictatorBridgeSlotVal = await dictatorContract.bridgeSlotVal()
...@@ -116,7 +119,7 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl( ...@@ -116,7 +119,7 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl(
dictatorBridgeSlotKey dictatorBridgeSlotKey
) )
printComparison( printComparison(
'3. Comparing the _Bridge_ slot key/value to be set, with the current values in the proxy', 'Compare the Bridge slot key/value to be set, with the current values in the proxy',
`Storage slot key ${dictatorBridgeSlotKey}`, `Storage slot key ${dictatorBridgeSlotKey}`,
{ {
name: `Value currently in the proxy at slot key\n${dictatorBridgeSlotKey}`, name: `Value currently in the proxy at slot key\n${dictatorBridgeSlotKey}`,
...@@ -128,15 +131,15 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl( ...@@ -128,15 +131,15 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl(
} }
) )
await getInput(c.yellow('OK? Hit enter to continue.')) await getInput(c.yellow('OK? Hit enter to continue.'))
console.log()
// eslint-disable-next-line @typescript-eslint/no-var-requires const bridgeArtifact = getContractDefinition('L1StandardBridge')
const bridgeArtifact = require('../artifacts/contracts/L1/messaging/L1StandardBridge.sol/L1StandardBridge.json')
const expectedCodeHash = ethers.utils.keccak256( const expectedCodeHash = ethers.utils.keccak256(
bridgeArtifact.deployedBytecode bridgeArtifact.deployedBytecode
) )
const actualCodeHash = await dictatorContract.codeHash() const actualCodeHash = await dictatorContract.codeHash()
printComparison( printComparison(
"4. Comparing the Dictator's codeHash against hash of the local L1StandardBridge build artifacts", "Compare the Dictator's codeHash against hash of the local L1StandardBridge build artifacts",
"Dictator's codeHash", "Dictator's codeHash",
{ {
name: 'Expected codeHash', name: 'Expected codeHash',
...@@ -148,5 +151,6 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl( ...@@ -148,5 +151,6 @@ Now validating the Chugsplash Dictator deployment at\n${getEtherscanUrl(
} }
) )
await getInput(c.yellow('OK? Hit enter to continue.')) await getInput(c.yellow('OK? Hit enter to continue.'))
console.log()
console.log(c.green('Chugsplash Dictator Validation complete!')) console.log(c.green('Chugsplash Dictator Validation complete!'))
}) })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment