Commit c9567718 authored by protolambda's avatar protolambda Committed by GitHub

L2 genesis - refactor to use solidity script (#10106)

* L2 genesis solidity updates by Wyatt and Mark

Commits:
- Tmp change for Graphite
- Add Missing Predeploys to L2 Genesis Script
- contracts-bedrock: refactor L2 genesis generation
- wip
- temp
- l2 genesis generation wip
- updates
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>
Co-authored-by: default avatarWyatt Barnes <me@wyatt.email>

* L2 genesis refactor fixes

Squashed commits:

todo

add ProxyAdmin predeploy, clean up test assertions

order predeploy setters

split Predeploys and Preinstalls

L2 genesis script: bytes32(0) style suggestion

move predeploy utils to predeploy library

preinstalls bytecode

fix typos

permit2 bytecode immutable patching

activate ecotone

cleanup tests, fixes, ecotone

work in progress GenesisL2 addition to Setup()

fixes

devnet allocs

fix go lint

more fixes

fix predeploys proxy impl checking test

fix solady

continue Go integration

l2 genesis Go integration stuff

fix lint

fix go lint

lint fixes

fixes for some PR review comments

Predeploys test clean up

go test superseded by solidity testing

fix lint

cleanup and review fixes

minor fixes

test fixes

op-e2e l2 allocs filepath fix

more devnet test funds

improve logging

fix devnet allocs-l2 path naming and output file moving to .devnet

devnet allocs CI fixes

circle ci workspace allocs-l2 fixes

op-e2e: fix alloc npe

fix

enforce genesis allocs copy

op-e2e fix

go test fixes, 4788 nonce edge case, dev accounts fix, misc fixes

fix test, fix flake

fix tests

Proxy artifact workaround

update gas snapshot

undo workaround, apply config change to fix

undo failed workarounds

* contracts-bedrock: delete dead L2 genesis testing code

* state-diff: update

* ctb: L2 genesis delete dead comment

* contracts-bedrock: small cleanups

* ctb: cleanup L2 genesis comments

---------
Co-authored-by: default avatarWyatt Barnes <me@wyatt.email>
Co-authored-by: default avatarMark Tyneway <mark.tyneway@gmail.com>
parent 53afefdd
...@@ -229,11 +229,17 @@ jobs: ...@@ -229,11 +229,17 @@ jobs:
- "packages/contracts-bedrock/tsconfig.tsbuildinfo" - "packages/contracts-bedrock/tsconfig.tsbuildinfo"
- "packages/contracts-bedrock/tsconfig.build.tsbuildinfo" - "packages/contracts-bedrock/tsconfig.build.tsbuildinfo"
- ".devnet/allocs-l1.json" - ".devnet/allocs-l1.json"
- ".devnet/allocs-l2.json"
- ".devnet/allocs-l2-delta.json"
- ".devnet/addresses.json" - ".devnet/addresses.json"
- ".devnet-fault-proofs/allocs-l1.json" - ".devnet-fault-proofs/allocs-l1.json"
- ".devnet-fault-proofs/addresses.json" - ".devnet-fault-proofs/addresses.json"
- ".devnet-fault-proofs/allocs-l2.json"
- ".devnet-fault-proofs/allocs-l2-delta.json"
- ".devnet-plasma/allocs-l1.json" - ".devnet-plasma/allocs-l1.json"
- ".devnet-plasma/addresses.json" - ".devnet-plasma/addresses.json"
- ".devnet-plasma/allocs-l2.json"
- ".devnet-plasma/allocs-l2-delta.json"
- "packages/contracts-bedrock/deploy-config/devnetL1.json" - "packages/contracts-bedrock/deploy-config/devnetL1.json"
- "packages/contracts-bedrock/deployments/devnetL1" - "packages/contracts-bedrock/deployments/devnetL1"
...@@ -963,6 +969,8 @@ jobs: ...@@ -963,6 +969,8 @@ jobs:
name: Load devnet-allocs name: Load devnet-allocs
command: | command: |
mkdir -p .devnet mkdir -p .devnet
cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l2.json .devnet/allocs-l2.json
cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l2-delta.json .devnet/allocs-l2-delta.json
cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l1.json .devnet/allocs-l1.json cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l1.json .devnet/allocs-l1.json
cp /tmp/workspace/.devnet<<parameters.fpac>>/addresses.json .devnet/addresses.json cp /tmp/workspace/.devnet<<parameters.fpac>>/addresses.json .devnet/addresses.json
cp /tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json cp /tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json
...@@ -1155,6 +1163,8 @@ jobs: ...@@ -1155,6 +1163,8 @@ jobs:
- persist_to_workspace: - persist_to_workspace:
root: . root: .
paths: paths:
- ".devnet/allocs-l2.json"
- ".devnet/allocs-l2-delta.json"
- ".devnet/allocs-l1.json" - ".devnet/allocs-l1.json"
- ".devnet/addresses.json" - ".devnet/addresses.json"
- "packages/contracts-bedrock/deploy-config/devnetL1.json" - "packages/contracts-bedrock/deploy-config/devnetL1.json"
......
...@@ -63,7 +63,7 @@ def main(): ...@@ -63,7 +63,7 @@ def main():
devnet_dir = pjoin(monorepo_dir, '.devnet') devnet_dir = pjoin(monorepo_dir, '.devnet')
contracts_bedrock_dir = pjoin(monorepo_dir, 'packages', 'contracts-bedrock') contracts_bedrock_dir = pjoin(monorepo_dir, 'packages', 'contracts-bedrock')
deployment_dir = pjoin(contracts_bedrock_dir, 'deployments', 'devnetL1') deployment_dir = pjoin(contracts_bedrock_dir, 'deployments', 'devnetL1')
forge_dump_path = pjoin(contracts_bedrock_dir, 'Deploy-900.json') forge_l1_dump_path = pjoin(contracts_bedrock_dir, 'state-dump-900.json')
op_node_dir = pjoin(args.monorepo_dir, 'op-node') op_node_dir = pjoin(args.monorepo_dir, 'op-node')
ops_bedrock_dir = pjoin(monorepo_dir, 'ops-bedrock') ops_bedrock_dir = pjoin(monorepo_dir, 'ops-bedrock')
deploy_config_dir = pjoin(contracts_bedrock_dir, 'deploy-config') deploy_config_dir = pjoin(contracts_bedrock_dir, 'deploy-config')
...@@ -77,7 +77,7 @@ def main(): ...@@ -77,7 +77,7 @@ def main():
devnet_dir=devnet_dir, devnet_dir=devnet_dir,
contracts_bedrock_dir=contracts_bedrock_dir, contracts_bedrock_dir=contracts_bedrock_dir,
deployment_dir=deployment_dir, deployment_dir=deployment_dir,
forge_dump_path=forge_dump_path, forge_l1_dump_path=forge_l1_dump_path,
l1_deployments_path=pjoin(deployment_dir, '.deploy'), l1_deployments_path=pjoin(deployment_dir, '.deploy'),
deploy_config_dir=deploy_config_dir, deploy_config_dir=deploy_config_dir,
devnet_config_path=devnet_config_path, devnet_config_path=devnet_config_path,
...@@ -88,7 +88,7 @@ def main(): ...@@ -88,7 +88,7 @@ def main():
sdk_dir=sdk_dir, sdk_dir=sdk_dir,
genesis_l1_path=pjoin(devnet_dir, 'genesis-l1.json'), genesis_l1_path=pjoin(devnet_dir, 'genesis-l1.json'),
genesis_l2_path=pjoin(devnet_dir, 'genesis-l2.json'), genesis_l2_path=pjoin(devnet_dir, 'genesis-l2.json'),
allocs_path=pjoin(devnet_dir, 'allocs-l1.json'), allocs_l1_path=pjoin(devnet_dir, 'allocs-l1.json'),
addresses_json_path=pjoin(devnet_dir, 'addresses.json'), addresses_json_path=pjoin(devnet_dir, 'addresses.json'),
sdk_addresses_json_path=pjoin(devnet_dir, 'sdk-addresses.json'), sdk_addresses_json_path=pjoin(devnet_dir, 'sdk-addresses.json'),
rollup_config_path=pjoin(devnet_dir, 'rollup.json') rollup_config_path=pjoin(devnet_dir, 'rollup.json')
...@@ -102,7 +102,8 @@ def main(): ...@@ -102,7 +102,8 @@ def main():
os.makedirs(devnet_dir, exist_ok=True) os.makedirs(devnet_dir, exist_ok=True)
if args.allocs: if args.allocs:
devnet_l1_genesis(paths) devnet_l1_allocs(paths)
devnet_l2_allocs(paths)
return return
git_commit = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True, text=True).stdout.strip() git_commit = subprocess.run(['git', 'rev-parse', 'HEAD'], capture_output=True, text=True).stdout.strip()
...@@ -136,8 +137,8 @@ def init_devnet_l1_deploy_config(paths, update_timestamp=False): ...@@ -136,8 +137,8 @@ def init_devnet_l1_deploy_config(paths, update_timestamp=False):
deploy_config['usePlasma'] = True deploy_config['usePlasma'] = True
write_json(paths.devnet_config_path, deploy_config) write_json(paths.devnet_config_path, deploy_config)
def devnet_l1_genesis(paths): def devnet_l1_allocs(paths):
log.info('Generating L1 genesis state') log.info('Generating L1 genesis allocs')
init_devnet_l1_deploy_config(paths) init_devnet_l1_deploy_config(paths)
fqn = 'scripts/Deploy.s.sol:Deploy' fqn = 'scripts/Deploy.s.sol:Deploy'
...@@ -146,27 +147,52 @@ def devnet_l1_genesis(paths): ...@@ -146,27 +147,52 @@ def devnet_l1_genesis(paths):
'forge', 'script', '--chain-id', '900', fqn, "--sig", "runWithStateDump()", "--private-key", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" 'forge', 'script', '--chain-id', '900', fqn, "--sig", "runWithStateDump()", "--private-key", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
], env={}, cwd=paths.contracts_bedrock_dir) ], env={}, cwd=paths.contracts_bedrock_dir)
forge_dump = read_json(paths.forge_dump_path) forge_dump = read_json(paths.forge_l1_dump_path)
write_json(paths.allocs_path, { "accounts": forge_dump }) write_json(paths.allocs_l1_path, { "accounts": forge_dump })
os.remove(paths.forge_dump_path) os.remove(paths.forge_l1_dump_path)
shutil.copy(paths.l1_deployments_path, paths.addresses_json_path) shutil.copy(paths.l1_deployments_path, paths.addresses_json_path)
def devnet_l2_allocs(paths):
log.info('Generating L2 genesis allocs, with L1 addresses: '+paths.l1_deployments_path)
fqn = 'scripts/L2Genesis.s.sol:L2Genesis'
# Use foundry pre-funded account #1 for the deployer
run_command([
'forge', 'script', '--chain-id', '901', fqn, "--sig", "runWithAllUpgrades()", "--private-key", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
], env={
'CONTRACT_ADDRESSES_PATH': paths.l1_deployments_path,
}, cwd=paths.contracts_bedrock_dir)
# For the previous forks, and the latest fork (default, thus empty prefix),
# move the forge-dumps into place as .devnet allocs.
for suffix in ["-delta", ""]:
input_path = pjoin(paths.contracts_bedrock_dir, f"state-dump-901{suffix}.json")
forge_dump = read_json(input_path)
output_path = pjoin(paths.devnet_dir, f'allocs-l2{suffix}.json')
write_json(output_path, { "accounts": forge_dump })
os.remove(input_path)
log.info("Generated L2 allocs: "+output_path)
# Bring up the devnet where the contracts are deployed to L1 # Bring up the devnet where the contracts are deployed to L1
def devnet_deploy(paths): def devnet_deploy(paths):
if os.path.exists(paths.genesis_l1_path): if os.path.exists(paths.genesis_l1_path):
log.info('L1 genesis already generated.') log.info('L1 genesis already generated.')
else: else:
log.info('Generating L1 genesis.') log.info('Generating L1 genesis.')
if not os.path.exists(paths.allocs_path) or DEVNET_FPAC: if not os.path.exists(paths.allocs_l1_path) or DEVNET_FPAC or DEVNET_PLASMA:
# If this is the FPAC devnet then we need to generate the allocs # If this is the FPAC devnet then we need to generate the allocs
# file here always. This is because CI will run devnet-allocs # file here always. This is because CI will run devnet-allocs
# without DEVNET_FPAC=true which means the allocs will be wrong. # without DEVNET_FPAC=true which means the allocs will be wrong.
# Re-running this step means the allocs will be correct. # Re-running this step means the allocs will be correct.
devnet_l1_genesis(paths) devnet_l1_allocs(paths)
else:
log.info('Re-using existing L1 allocs.')
# It's odd that we want to regenerate the devnetL1.json file with # It's odd that we want to regenerate the devnetL1.json file with
# an updated timestamp different than the one used in the devnet_l1_genesis # an updated timestamp different than the one used in the devnet_l1_allocs
# function. But, without it, CI flakes on this test rather consistently. # function. But, without it, CI flakes on this test rather consistently.
# If someone reads this comment and understands why this is being done, please # If someone reads this comment and understands why this is being done, please
# update this comment to explain. # update this comment to explain.
...@@ -174,7 +200,7 @@ def devnet_deploy(paths): ...@@ -174,7 +200,7 @@ def devnet_deploy(paths):
run_command([ run_command([
'go', 'run', 'cmd/main.go', 'genesis', 'l1', 'go', 'run', 'cmd/main.go', 'genesis', 'l1',
'--deploy-config', paths.devnet_config_path, '--deploy-config', paths.devnet_config_path,
'--l1-allocs', paths.allocs_path, '--l1-allocs', paths.allocs_l1_path,
'--l1-deployments', paths.addresses_json_path, '--l1-deployments', paths.addresses_json_path,
'--outfile.l1', paths.genesis_l1_path, '--outfile.l1', paths.genesis_l1_path,
], cwd=paths.op_node_dir) ], cwd=paths.op_node_dir)
...@@ -190,10 +216,19 @@ def devnet_deploy(paths): ...@@ -190,10 +216,19 @@ def devnet_deploy(paths):
log.info('L2 genesis and rollup configs already generated.') log.info('L2 genesis and rollup configs already generated.')
else: else:
log.info('Generating L2 genesis and rollup configs.') log.info('Generating L2 genesis and rollup configs.')
l2_allocs_path = pjoin(paths.devnet_dir, 'allocs-l2.json')
if os.path.exists(l2_allocs_path) == False or DEVNET_FPAC == True:
# Also regenerate if FPAC.
# The FPAC flag may affect the L1 deployments addresses, which may affect the L2 genesis.
devnet_l2_allocs(paths)
else:
log.info('Re-using existing L2 allocs.')
run_command([ run_command([
'go', 'run', 'cmd/main.go', 'genesis', 'l2', 'go', 'run', 'cmd/main.go', 'genesis', 'l2',
'--l1-rpc', 'http://localhost:8545', '--l1-rpc', 'http://localhost:8545',
'--deploy-config', paths.devnet_config_path, '--deploy-config', paths.devnet_config_path,
'--l2-allocs', l2_allocs_path,
'--l1-deployments', paths.addresses_json_path, '--l1-deployments', paths.addresses_json_path,
'--outfile.l2', paths.genesis_l2_path, '--outfile.l2', paths.genesis_l2_path,
'--outfile.rollup', paths.rollup_config_path '--outfile.rollup', paths.rollup_config_path
......
...@@ -10,9 +10,12 @@ import ( ...@@ -10,9 +10,12 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"golang.org/x/exp/maps"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core"
gstate "github.com/ethereum/go-ethereum/core/state" gstate "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -803,6 +806,7 @@ func (d *ForgeDump) UnmarshalJSON(b []byte) error { ...@@ -803,6 +806,7 @@ func (d *ForgeDump) UnmarshalJSON(b []byte) error {
d.Root = dump.Root d.Root = dump.Root
d.Accounts = make(map[string]gstate.DumpAccount) d.Accounts = make(map[string]gstate.DumpAccount)
for addr, acc := range dump.Accounts { for addr, acc := range dump.Accounts {
acc := acc
d.Accounts[addr.String()] = gstate.DumpAccount{ d.Accounts[addr.String()] = gstate.DumpAccount{
Balance: acc.Balance, Balance: acc.Balance,
Nonce: (uint64)(acc.Nonce), Nonce: (uint64)(acc.Nonce),
...@@ -817,6 +821,45 @@ func (d *ForgeDump) UnmarshalJSON(b []byte) error { ...@@ -817,6 +821,45 @@ func (d *ForgeDump) UnmarshalJSON(b []byte) error {
return nil return nil
} }
type ForgeAllocs struct {
Accounts core.GenesisAlloc `json:"accounts"`
}
func (d *ForgeAllocs) Copy() *ForgeAllocs {
out := make(core.GenesisAlloc, len(d.Accounts))
maps.Copy(out, d.Accounts)
return &ForgeAllocs{Accounts: out}
}
func (d *ForgeAllocs) UnmarshalJSON(b []byte) error {
// forge, since integrating Alloy, likes to hex-encode everything.
type forgeAllocAccount struct {
Balance hexutil.Big `json:"balance"`
Nonce hexutil.Uint64 `json:"nonce"`
Code hexutil.Bytes `json:"code,omitempty"`
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
}
type forgeAllocs struct {
Accounts map[common.Address]forgeAllocAccount `json:"accounts"`
}
var allocs forgeAllocs
if err := json.Unmarshal(b, &allocs); err != nil {
return err
}
d.Accounts = make(core.GenesisAlloc, len(allocs.Accounts))
for addr, acc := range allocs.Accounts {
acc := acc
d.Accounts[addr] = core.GenesisAccount{
Code: acc.Code,
Storage: acc.Storage,
Balance: acc.Balance.ToInt(),
Nonce: (uint64)(acc.Nonce),
PrivateKey: nil,
}
}
return nil
}
// NewL2ImmutableConfig will create an ImmutableConfig given an instance of a // NewL2ImmutableConfig will create an ImmutableConfig given an instance of a
// DeployConfig and a block. // DeployConfig and a block.
func NewL2ImmutableConfig(config *DeployConfig, block *types.Block) (*immutables.PredeploysImmutableConfig, error) { func NewL2ImmutableConfig(config *DeployConfig, block *types.Block) (*immutables.PredeploysImmutableConfig, error) {
......
...@@ -51,7 +51,7 @@ var DevAccounts = []common.Address{ ...@@ -51,7 +51,7 @@ var DevAccounts = []common.Address{
common.HexToAddress("0xcd3B766CCDd6AE721141F452C550Ca635964ce71"), common.HexToAddress("0xcd3B766CCDd6AE721141F452C550Ca635964ce71"),
common.HexToAddress("0xdD2FD4581271e230360230F9337D5c0430Bf44C0"), common.HexToAddress("0xdD2FD4581271e230360230F9337D5c0430Bf44C0"),
common.HexToAddress("0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097"), common.HexToAddress("0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097"),
common.HexToAddress("0xde3829a23df1479438622a08a116e8eb3f620bb5"), common.HexToAddress("0xDe3829A23DF1479438622a08a116E8Eb3f620BB5"),
common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"),
// Test account used by geth tests // Test account used by geth tests
common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"), common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"),
......
package genesis package genesis
import ( import (
"encoding/json"
"fmt" "fmt"
"math/big" "os"
"path/filepath"
"github.com/ethereum/go-ethereum/common" hdwallet "github.com/ethereum-optimism/go-ethereum-hdwallet"
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/immutables"
"github.com/ethereum-optimism/optimism/op-chain-ops/squash"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-service/eth"
) )
type L2AllocsMode string
const (
L2AllocsDelta L2AllocsMode = "delta"
L2AllocsEcotone L2AllocsMode = "" // the default in solidity scripting / testing
)
type AllocsLoader func(mode L2AllocsMode) *ForgeAllocs
// BuildL2Genesis will build the L2 genesis block. // BuildL2Genesis will build the L2 genesis block.
func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Genesis, error) { func BuildL2Genesis(config *DeployConfig, dump *ForgeAllocs, l1StartBlock *types.Block) (*core.Genesis, error) {
genspec, err := NewL2Genesis(config, l1StartBlock) genspec, err := NewL2Genesis(config, l1StartBlock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
genspec.Alloc = dump.Accounts
db := state.NewMemoryStateDB(genspec) // ensure the dev accounts are not funded unintentionally
if config.FundDevAccounts { if hasDevAccounts, err := HasAnyDevAccounts(dump.Accounts); err != nil {
log.Info("Funding developer accounts in L2 genesis") return nil, fmt.Errorf("failed to check dev accounts: %w", err)
FundDevAccounts(db) } else if hasDevAccounts != config.FundDevAccounts {
return nil, fmt.Errorf("deploy config mismatch with allocs. Deploy config fundDevAccounts: %v, actual allocs: %v", config.FundDevAccounts, hasDevAccounts)
} }
// sanity check the permit2 immutable, to verify we using the allocs for the right chain.
SetPrecompileBalances(db) chainID := [32]byte(genspec.Alloc[predeploys.Permit2Addr].Code[6945 : 6945+32])
expected := uint256.MustFromBig(genspec.Config.ChainID).Bytes32()
storage, err := NewL2StorageConfig(config, l1StartBlock) if chainID != expected {
if err != nil { return nil, fmt.Errorf("allocs were generated for chain ID %x, but expected chain %x (%d)", chainID, expected, genspec.Config.ChainID)
return nil, err
} }
return genspec, nil
}
immutableConfig, err := NewL2ImmutableConfig(config, l1StartBlock) var testMnemonic = "test test test test test test test test test test test junk"
if err != nil {
return nil, err
}
// Set up the proxies func HasAnyDevAccounts(allocs core.GenesisAlloc) (bool, error) {
err = setProxies(db, predeploys.ProxyAdminAddr, BigL2PredeployNamespace, 2048) wallet, err := hdwallet.NewFromMnemonic(testMnemonic)
if err != nil { if err != nil {
return nil, err return false, fmt.Errorf("failed to create wallet: %w", err)
} }
account := func(path string) accounts.Account {
// Set up the implementations that contain immutables return accounts.Account{URL: accounts.URL{Path: path}}
deployResults, err := immutables.Deploy(immutableConfig)
if err != nil {
return nil, fmt.Errorf("immutables.Deploy failed: %w", err)
} }
for name, predeploy := range predeploys.Predeploys { for i := 0; i < 30; i++ {
if predeploy.Enabled != nil && !predeploy.Enabled(config) { key, err := wallet.PrivateKey(account(fmt.Sprintf("m/44'/60'/0'/0/%d", i)))
log.Warn("Skipping disabled predeploy.", "name", name, "address", predeploy.Address) if err != nil {
continue return false, err
} }
addr := crypto.PubkeyToAddress(key.PublicKey)
codeAddr := predeploy.Address if _, ok := allocs[addr]; ok {
switch name { return true, nil
case "Permit2":
deployerAddressBytes, err := bindings.GetDeployerAddress(name)
if err != nil {
return nil, err
}
deployerAddress := common.BytesToAddress(deployerAddressBytes)
predeploys := map[string]*common.Address{
"DeterministicDeploymentProxy": &deployerAddress,
}
backend, err := deployer.NewBackendWithChainIDAndPredeploys(
new(big.Int).SetUint64(config.L2ChainID),
predeploys,
)
if err != nil {
return nil, fmt.Errorf("NewBackendWithChainIDAndPredeploys failed: %w", err)
}
deployedBin, err := deployer.DeployWithDeterministicDeployer(backend, name)
if err != nil {
backend.Close()
return nil, fmt.Errorf("DeployWithDeterministicDeployer failed: %w", err)
}
backend.Close()
deployResults[name] = deployedBin
fallthrough
case "MultiCall3", "Create2Deployer", "Safe_v130",
"SafeL2_v130", "MultiSendCallOnly_v130", "SafeSingletonFactory",
"DeterministicDeploymentProxy", "MultiSend_v130", "SenderCreator", "EntryPoint":
db.CreateAccount(codeAddr)
default:
if !predeploy.ProxyDisabled {
codeAddr, err = AddressToCodeNamespace(predeploy.Address)
if err != nil {
return nil, fmt.Errorf("error converting to code namespace: %w", err)
}
db.CreateAccount(codeAddr)
db.SetState(predeploy.Address, ImplementationSlot, eth.AddressAsLeftPaddedHash(codeAddr))
log.Info("Set proxy", "name", name, "address", predeploy.Address, "implementation", codeAddr)
}
} }
if predeploy.ProxyDisabled && db.Exist(predeploy.Address) {
db.DeleteState(predeploy.Address, AdminSlot)
}
if err := setupPredeploy(db, deployResults, storage, name, predeploy.Address, codeAddr); err != nil {
return nil, fmt.Errorf("setupPredeploy failed: %w", err)
}
code := db.GetCode(codeAddr)
if len(code) == 0 {
return nil, fmt.Errorf("code not set for %s", name)
}
}
if err := PerformUpgradeTxs(db); err != nil {
return nil, fmt.Errorf("failed to perform upgrade txs: %w", err)
} }
return false, nil
return db.Genesis(), nil
} }
func PerformUpgradeTxs(db *state.MemoryStateDB) error { func LoadForgeAllocs(allocsPath string) (*ForgeAllocs, error) {
// Only the Ecotone upgrade is performed with upgrade-txs. path := filepath.Join(allocsPath)
if !db.Genesis().Config.IsEcotone(db.Genesis().Timestamp) { f, err := os.OpenFile(path, os.O_RDONLY, 0644)
return nil
}
sim := squash.NewSimulator(db)
ecotone, err := derive.EcotoneNetworkUpgradeTransactions()
if err != nil { if err != nil {
return fmt.Errorf("failed to build ecotone upgrade txs: %w", err) return nil, fmt.Errorf("failed to open forge allocs %q: %w", path, err)
} }
if err := sim.AddUpgradeTxs(ecotone); err != nil { defer f.Close()
return fmt.Errorf("failed to apply ecotone upgrade txs: %w", err) var out ForgeAllocs
if err := json.NewDecoder(f).Decode(&out); err != nil {
return nil, fmt.Errorf("failed to json-decode forge allocs %q: %w", path, err)
} }
return nil return &out, nil
} }
package genesis_test
import (
"context"
"encoding/json"
"flag"
"math/big"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum/go-ethereum/eth/ethconfig"
)
var writeFile bool
func init() {
flag.BoolVar(&writeFile, "write-file", false, "write the genesis file to disk")
}
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// Tests the BuildL2MainnetGenesis factory with the provided config.
func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesis {
backend := backends.NewSimulatedBackend( // nolint:staticcheck
core.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
},
15000000,
)
block, err := backend.BlockByNumber(context.Background(), common.Big0)
require.NoError(t, err)
gen, err := genesis.BuildL2Genesis(config, block)
require.Nil(t, err)
require.NotNil(t, gen)
proxyBytecode, err := bindings.GetDeployedBytecode("Proxy")
require.NoError(t, err)
// for simulation we need a regular EVM, not with system-deposit information.
chainConfig := params.ChainConfig{
ChainID: big.NewInt(1337),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
// Activated proof of stake. We manually build/commit blocks in the simulator anyway,
// and the timestamp verification of PoS is not against the wallclock,
// preventing blocks from getting stuck temporarily in the future-blocks queue, decreasing setup time a lot.
MergeNetsplitBlock: big.NewInt(0),
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
ShanghaiTime: new(uint64),
}
// Apply the genesis to the backend
cfg := ethconfig.Defaults
cfg.Preimages = true
cfg.Genesis = &core.Genesis{
Config: &chainConfig,
Timestamp: 1234567,
Difficulty: big.NewInt(0),
Alloc: gen.Alloc,
GasLimit: 30_000_000,
}
backend = backends.NewSimulatedBackendFromConfig(cfg)
for name, predeploy := range predeploys.Predeploys {
addr := predeploy.Address
if addr == predeploys.L1BlockAddr {
testL1Block(t, backend, config, block)
}
account, ok := gen.Alloc[addr]
require.Equal(t, true, ok, name)
require.Greater(t, len(account.Code), 0)
adminSlot, ok := account.Storage[genesis.AdminSlot]
isProxy := !predeploy.ProxyDisabled ||
(!config.EnableGovernance && addr == predeploys.GovernanceTokenAddr)
if isProxy {
require.Equal(t, true, ok, name)
require.Equal(t, eth.AddressAsLeftPaddedHash(predeploys.ProxyAdminAddr), adminSlot)
require.Equal(t, proxyBytecode, account.Code)
} else {
require.Equal(t, false, ok, name)
require.NotEqual(t, proxyBytecode, account.Code, name)
}
}
// All of the precompile addresses should be funded with a single wei
for i := 0; i < genesis.PrecompileCount; i++ {
addr := common.BytesToAddress([]byte{byte(i)})
require.Equal(t, common.Big1, gen.Alloc[addr].Balance)
}
create2Deployer := gen.Alloc[predeploys.Create2DeployerAddr]
codeHash := crypto.Keccak256Hash(create2Deployer.Code)
require.Equal(t, codeHash, bindings.Create2DeployerCodeHash)
if writeFile {
file, _ := json.MarshalIndent(gen, "", " ")
_ = os.WriteFile("genesis.json", file, 0644)
}
return gen
}
// testL1Block tests that the state is set correctly in the L1Block predeploy
func testL1Block(t *testing.T, caller bind.ContractCaller, config *genesis.DeployConfig, block *types.Block) {
contract, err := bindings.NewL1BlockCaller(predeploys.L1BlockAddr, caller)
require.NoError(t, err)
number, err := contract.Number(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, block.Number().Uint64(), number)
timestamp, err := contract.Timestamp(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, block.Time(), timestamp)
basefee, err := contract.Basefee(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, block.BaseFee(), basefee)
hash, err := contract.Hash(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, block.Hash(), common.Hash(hash))
sequenceNumber, err := contract.SequenceNumber(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, uint64(0), sequenceNumber)
blobBaseFeeScalar, err := contract.BlobBaseFeeScalar(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, config.GasPriceOracleBlobBaseFeeScalar, blobBaseFeeScalar)
baseFeeScalar, err := contract.BaseFeeScalar(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, config.GasPriceOracleBaseFeeScalar, baseFeeScalar)
batcherHeader, err := contract.BatcherHash(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, eth.AddressAsLeftPaddedHash(config.BatchSenderAddress), common.Hash(batcherHeader))
l1FeeOverhead, err := contract.L1FeeOverhead(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, config.GasPriceOracleOverhead, l1FeeOverhead.Uint64())
l1FeeScalar, err := contract.L1FeeScalar(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, config.GasPriceOracleScalar, l1FeeScalar.Uint64())
blobBaseFee, err := contract.BlobBaseFee(&bind.CallOpts{})
require.NoError(t, err)
if excessBlobGas := block.ExcessBlobGas(); excessBlobGas != nil {
require.Equal(t, uint64(0), *excessBlobGas)
}
require.Equal(t, big.NewInt(1), blobBaseFee)
}
func TestBuildL2MainnetGenesis(t *testing.T) {
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err)
config.EnableGovernance = true
config.FundDevAccounts = false
gen := testBuildL2Genesis(t, config)
require.Equal(t, 2333, len(gen.Alloc))
}
func TestBuildL2MainnetNoGovernanceGenesis(t *testing.T) {
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err)
config.EnableGovernance = false
config.FundDevAccounts = false
gen := testBuildL2Genesis(t, config)
require.Equal(t, 2333, len(gen.Alloc))
}
package genesis package genesis
import ( import (
"errors"
"fmt"
"math/big"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/immutables"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum-optimism/optimism/op-service/eth"
) )
// PrecompileCount represents the number of precompile addresses // PrecompileCount represents the number of precompile addresses
...@@ -32,31 +22,6 @@ func FundDevAccounts(db vm.StateDB) { ...@@ -32,31 +22,6 @@ func FundDevAccounts(db vm.StateDB) {
} }
} }
func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int, count uint64) error {
depBytecode, err := bindings.GetDeployedBytecode("Proxy")
if err != nil {
return err
}
if len(depBytecode) == 0 {
return errors.New("Proxy has empty bytecode")
}
for i := uint64(0); i <= count; i++ {
bigAddr := new(big.Int).Or(namespace, new(big.Int).SetUint64(i))
addr := common.BigToAddress(bigAddr)
if !db.Exist(addr) {
db.CreateAccount(addr)
}
db.SetCode(addr, depBytecode)
db.SetState(addr, AdminSlot, eth.AddressAsLeftPaddedHash(proxyAdminAddr))
log.Trace("Set proxy", "address", addr, "admin", proxyAdminAddr)
}
return nil
}
// SetPrecompileBalances will set a single wei at each precompile address. // SetPrecompileBalances will set a single wei at each precompile address.
// This is an optimization to make calling them cheaper. // This is an optimization to make calling them cheaper.
func SetPrecompileBalances(db vm.StateDB) { func SetPrecompileBalances(db vm.StateDB) {
...@@ -66,29 +31,3 @@ func SetPrecompileBalances(db vm.StateDB) { ...@@ -66,29 +31,3 @@ func SetPrecompileBalances(db vm.StateDB) {
db.AddBalance(addr, uint256.NewInt(1)) db.AddBalance(addr, uint256.NewInt(1))
} }
} }
func setupPredeploy(db vm.StateDB, deployResults immutables.DeploymentResults, storage state.StorageConfig, name string, proxyAddr common.Address, implAddr common.Address) error {
// Use the generated bytecode when there are immutables
// otherwise use the artifact deployed bytecode
if bytecode, ok := deployResults[name]; ok {
log.Info("Setting deployed bytecode with immutables", "name", name, "address", implAddr)
db.SetCode(implAddr, bytecode)
} else {
depBytecode, err := bindings.GetDeployedBytecode(name)
if err != nil {
return fmt.Errorf("GetDeployedBytecode failed: %w", err)
}
log.Info("Setting deployed bytecode from solc compiler output", "name", name, "address", implAddr)
db.SetCode(implAddr, depBytecode)
}
// Set the storage values
if storageConfig, ok := storage[name]; ok {
log.Info("Setting storage", "name", name, "address", proxyAddr)
if err := state.SetStorage(name, proxyAddr, storageConfig, db); err != nil {
return err
}
}
return nil
}
{
"l1StartingBlockTag": "earliest",
"l1ChainID": 900,
"l2ChainID": 901,
"l2BlockTime": 2,
"maxSequencerDrift": 100,
"sequencerWindowSize": 4,
"channelTimeout": 40,
"p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"batchInboxAddress": "0xff00000000000000000000000000000000000000",
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"l2OutputOracleSubmissionInterval": 20,
"l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l1BlockTime": 15,
"cliqueSignerAddress": "0xca062b0fd91172d89bcd4bb084ac4e21972cc467",
"baseFeeVaultRecipient": "0xBcd4042DE499D14e55001CcbB24a551F3b954096",
"l1FeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788",
"sequencerFeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788",
"baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
"l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
"sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
"baseFeeVaultWithdrawalNetwork": "local",
"l1FeeVaultWithdrawalNetwork": "local",
"sequencerFeeVaultWithdrawalNetwork": "local",
"l1ERC721BridgeProxy": "0xff000000000000000000000000000000000000ff",
"l1StandardBridgeProxy": "0xff000000000000000000000000000000000000fd",
"l1CrossDomainMessengerProxy": "0xff000000000000000000000000000000000000dd",
"deploymentWaitConfirmations": 1,
"fundDevAccounts": true,
"enableGovernance": true,
"governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism",
"governanceTokenOwner": "0x0000000000000000000000000000000000000333",
"l2GenesisRegolithTimeOffset": "0x0",
"l2GenesisCanyonTimeOffset": "0x0"
}
...@@ -43,7 +43,7 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) { ...@@ -43,7 +43,7 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
genesisBlock := hexutil.Uint64(0) genesisBlock := hexutil.Uint64(0)
ecotoneOffset := hexutil.Uint64(2) ecotoneOffset := hexutil.Uint64(4)
dp.DeployConfig.L1CancunTimeOffset = &genesisBlock // can be removed once Cancun on L1 is the default dp.DeployConfig.L1CancunTimeOffset = &genesisBlock // can be removed once Cancun on L1 is the default
...@@ -59,6 +59,10 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) { ...@@ -59,6 +59,10 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) {
_, _, miner, sequencer, engine, verifier, _, _ := setupReorgTestActors(t, dp, sd, log) _, _, miner, sequencer, engine, verifier, _, _ := setupReorgTestActors(t, dp, sd, log)
ethCl := engine.EthClient() ethCl := engine.EthClient()
// build a single block to move away from the genesis with 0-values in L1Block contract
sequencer.ActL2StartBlock(t)
sequencer.ActL2EndBlock(t)
// start op-nodes // start op-nodes
sequencer.ActL2PipelineFull(t) sequencer.ActL2PipelineFull(t)
verifier.ActL2PipelineFull(t) verifier.ActL2PipelineFull(t)
...@@ -101,7 +105,7 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) { ...@@ -101,7 +105,7 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) {
txn := transactions[i] txn := transactions[i]
receipt, err := ethCl.TransactionReceipt(context.Background(), txn.Hash()) receipt, err := ethCl.TransactionReceipt(context.Background(), txn.Hash())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "tx %d must pass", i)
require.NotEmpty(t, txn.Data(), "upgrade tx must provide input data") require.NotEmpty(t, txn.Data(), "upgrade tx must provide input data")
} }
...@@ -162,11 +166,14 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) { ...@@ -162,11 +166,14 @@ func TestEcotoneNetworkUpgradeTransactions(gt *testing.T) {
require.NotNil(t, latestBlock.BeaconRoot()) require.NotNil(t, latestBlock.BeaconRoot())
require.Equal(t, *latestBlock.BeaconRoot(), common.Hash{}, require.Equal(t, *latestBlock.BeaconRoot(), common.Hash{},
"L1 genesis block has zeroed parent-beacon-block-root, since it has no parent block, and that propagates into L2") "L1 genesis block has zeroed parent-beacon-block-root, since it has no parent block, and that propagates into L2")
// The first block is an exception in upgrade-networks, // Legacy check:
// since the beacon-block root contract isn't there at Ecotone activation, // > The first block is an exception in upgrade-networks,
// and the beacon-block-root insertion is processed at the start of the block before deposit txs. // > since the beacon-block root contract isn't there at Ecotone activation,
// If the contract was permissionlessly deployed before, the contract storage will be updated however. // > and the beacon-block-root insertion is processed at the start of the block before deposit txs.
checkBeaconBlockRoot(latestBlock.Time(), common.Hash{}, 0, "ecotone activation block has no data yet (since contract wasn't there)") // > If the contract was permissionlessly deployed before, the contract storage will be updated however.
// > checkBeaconBlockRoot(latestBlock.Time(), common.Hash{}, 0, "ecotone activation block has no data yet (since contract wasn't there)")
// Note: 4788 is now installed as preinstall, and thus always there.
checkBeaconBlockRoot(latestBlock.Time(), common.Hash{}, latestBlock.Time(), "4788 lookup of first cancun block is 0 hash")
// Build empty L2 block, to pass ecotone activation // Build empty L2 block, to pass ecotone activation
sequencer.ActL2StartBlock(t) sequencer.ActL2StartBlock(t)
......
package actions package actions
import ( import (
"context"
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand" crand "crypto/rand"
"fmt" "fmt"
...@@ -501,6 +502,10 @@ func TestSpanBatchLowThroughputChain(gt *testing.T) { ...@@ -501,6 +502,10 @@ func TestSpanBatchLowThroughputChain(gt *testing.T) {
addr := crypto.PubkeyToAddress(privateKey.PublicKey) addr := crypto.PubkeyToAddress(privateKey.PublicKey)
require.NoError(t, err) require.NoError(t, err)
addrs[i] = addr addrs[i] = addr
bal, err := cl.BalanceAt(context.Background(), addr, nil)
require.NoError(gt, err)
require.Equal(gt, 1, bal.Cmp(common.Big0), "account %d must have non-zero balance, address: %s, balance: %d", i, addr, bal)
} }
sequencer.ActL2PipelineFull(t) sequencer.ActL2PipelineFull(t)
......
...@@ -44,6 +44,8 @@ var ( ...@@ -44,6 +44,8 @@ var (
// L1Deployments maps contract names to accounts in the L1 // L1Deployments maps contract names to accounts in the L1
// genesis block state. // genesis block state.
L1Deployments *genesis.L1Deployments L1Deployments *genesis.L1Deployments
// l2Allocs represents the L2 allocs, by hardfork/mode (e.g. delta, ecotone, interop, other)
l2Allocs map[genesis.L2AllocsMode]*genesis.ForgeAllocs
// DeployConfig represents the deploy config used by the system. // DeployConfig represents the deploy config used by the system.
DeployConfig *genesis.DeployConfig DeployConfig *genesis.DeployConfig
// ExternalL2Shim is the shim to use if external ethereum client testing is // ExternalL2Shim is the shim to use if external ethereum client testing is
...@@ -57,7 +59,7 @@ var ( ...@@ -57,7 +59,7 @@ var (
) )
func init() { func init() {
var l1AllocsPath, l1DeploymentsPath, deployConfigPath, externalL2 string var l1AllocsPath, l2AllocsDir, l1DeploymentsPath, deployConfigPath, externalL2 string
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
...@@ -69,10 +71,12 @@ func init() { ...@@ -69,10 +71,12 @@ func init() {
} }
defaultL1AllocsPath := filepath.Join(root, ".devnet", "allocs-l1.json") defaultL1AllocsPath := filepath.Join(root, ".devnet", "allocs-l1.json")
defaultL2AllocsDir := filepath.Join(root, ".devnet")
defaultL1DeploymentsPath := filepath.Join(root, ".devnet", "addresses.json") defaultL1DeploymentsPath := filepath.Join(root, ".devnet", "addresses.json")
defaultDeployConfigPath := filepath.Join(root, "packages", "contracts-bedrock", "deploy-config", "devnetL1.json") defaultDeployConfigPath := filepath.Join(root, "packages", "contracts-bedrock", "deploy-config", "devnetL1.json")
flag.StringVar(&l1AllocsPath, "l1-allocs", defaultL1AllocsPath, "") flag.StringVar(&l1AllocsPath, "l1-allocs", defaultL1AllocsPath, "")
flag.StringVar(&l2AllocsDir, "l2-allocs-dir", defaultL2AllocsDir, "")
flag.StringVar(&l1DeploymentsPath, "l1-deployments", defaultL1DeploymentsPath, "") flag.StringVar(&l1DeploymentsPath, "l1-deployments", defaultL1DeploymentsPath, "")
flag.StringVar(&deployConfigPath, "deploy-config", defaultDeployConfigPath, "") flag.StringVar(&deployConfigPath, "deploy-config", defaultDeployConfigPath, "")
flag.StringVar(&externalL2, "externalL2", "", "Enable tests with external L2") flag.StringVar(&externalL2, "externalL2", "", "Enable tests with external L2")
...@@ -108,6 +112,20 @@ func init() { ...@@ -108,6 +112,20 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
l2Allocs = make(map[genesis.L2AllocsMode]*genesis.ForgeAllocs)
mustL2Allocs := func(mode genesis.L2AllocsMode) {
name := "allocs-l2"
if mode != "" {
name += "-" + string(mode)
}
allocs, err := genesis.LoadForgeAllocs(filepath.Join(l2AllocsDir, name+".json"))
if err != nil {
panic(err)
}
l2Allocs[mode] = allocs
}
mustL2Allocs(genesis.L2AllocsEcotone)
mustL2Allocs(genesis.L2AllocsDelta)
L1Deployments, err = genesis.NewL1Deployments(l1DeploymentsPath) L1Deployments, err = genesis.NewL1Deployments(l1DeploymentsPath)
if err != nil { if err != nil {
panic(err) panic(err)
...@@ -138,6 +156,14 @@ func init() { ...@@ -138,6 +156,14 @@ func init() {
} }
} }
func L2Allocs(mode genesis.L2AllocsMode) *genesis.ForgeAllocs {
allocs, ok := l2Allocs[mode]
if !ok {
panic(fmt.Errorf("unknown L2 allocs mode: %q", mode))
}
return allocs.Copy()
}
func initExternalL2(externalL2 string) error { func initExternalL2(externalL2 string) error {
var err error var err error
ExternalL2Shim, err = filepath.Abs(filepath.Join(externalL2, "shim")) ExternalL2Shim, err = filepath.Abs(filepath.Join(externalL2, "shim"))
......
...@@ -122,7 +122,13 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * ...@@ -122,7 +122,13 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
l1Block := l1Genesis.ToBlock() l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2Genesis(deployConf, l1Block) var allocsMode genesis.L2AllocsMode
allocsMode = genesis.L2AllocsDelta
if ecotoneTime := deployConf.EcotoneTime(l1Block.Time()); ecotoneTime != nil && *ecotoneTime <= 0 {
allocsMode = genesis.L2AllocsEcotone
}
l2Allocs := config.L2Allocs(allocsMode)
l2Genesis, err := genesis.BuildL2Genesis(deployConf, l2Allocs, l1Block)
require.NoError(t, err, "failed to create l2 genesis") require.NoError(t, err, "failed to create l2 genesis")
if alloc.PrefundTestUsers { if alloc.PrefundTestUsers {
for _, addr := range deployParams.Addresses.All() { for _, addr := range deployParams.Addresses.All() {
......
...@@ -58,7 +58,13 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e ...@@ -58,7 +58,13 @@ func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, e
require.Nil(t, err) require.Nil(t, err)
l1Block := l1Genesis.ToBlock() l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2Genesis(cfg.DeployConfig, l1Block) var allocsMode genesis.L2AllocsMode
allocsMode = genesis.L2AllocsDelta
if ecotoneTime := cfg.DeployConfig.EcotoneTime(l1Block.Time()); ecotoneTime != nil && *ecotoneTime <= 0 {
allocsMode = genesis.L2AllocsEcotone
}
l2Allocs := config.L2Allocs(allocsMode)
l2Genesis, err := genesis.BuildL2Genesis(cfg.DeployConfig, l2Allocs, l1Block)
require.Nil(t, err) require.Nil(t, err)
l2GenesisBlock := l2Genesis.ToBlock() l2GenesisBlock := l2Genesis.ToBlock()
......
...@@ -26,7 +26,6 @@ import ( ...@@ -26,7 +26,6 @@ import (
func TestMissingGasLimit(t *testing.T) { func TestMissingGasLimit(t *testing.T) {
InitParallel(t) InitParallel(t)
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FundDevAccounts = false
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg) opGeth, err := NewOpGeth(t, ctx, &cfg)
...@@ -72,17 +71,19 @@ func TestTxGasSameAsBlockGasLimit(t *testing.T) { ...@@ -72,17 +71,19 @@ func TestTxGasSameAsBlockGasLimit(t *testing.T) {
func TestInvalidDepositInFCU(t *testing.T) { func TestInvalidDepositInFCU(t *testing.T) {
InitParallel(t) InitParallel(t)
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FundDevAccounts = false
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel() defer cancel()
opGeth, err := NewOpGeth(t, ctx, &cfg) opGeth, err := NewOpGeth(t, ctx, &cfg)
require.NoError(t, err) require.NoError(t, err)
defer opGeth.Close() defer opGeth.Close()
// Create a deposit from alice that will always fail (not enough funds) // Create a deposit from a new account that will always fail (not enough funds)
fromAddr := cfg.Secrets.Addresses().Alice fromKey, err := crypto.GenerateKey()
require.NoError(t, err)
fromAddr := crypto.PubkeyToAddress(fromKey.PublicKey)
balance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil) balance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
require.Nil(t, err) require.Nil(t, err)
t.Logf("alice balance: %d, %s", balance, fromAddr)
require.Equal(t, 0, balance.Cmp(common.Big0)) require.Equal(t, 0, balance.Cmp(common.Big0))
badDepositTx := types.NewTx(&types.DepositTx{ badDepositTx := types.NewTx(&types.DepositTx{
...@@ -98,7 +99,7 @@ func TestInvalidDepositInFCU(t *testing.T) { ...@@ -98,7 +99,7 @@ func TestInvalidDepositInFCU(t *testing.T) {
_, err = opGeth.AddL2Block(ctx, badDepositTx) _, err = opGeth.AddL2Block(ctx, badDepositTx)
require.NoError(t, err) require.NoError(t, err)
// Deposit tx was included, but Alice still shouldn't have any ETH // Deposit tx was included, but our account still shouldn't have any ETH
balance, err = opGeth.L2Client.BalanceAt(ctx, fromAddr, nil) balance, err = opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 0, balance.Cmp(common.Big0)) require.Equal(t, 0, balance.Cmp(common.Big0))
......
...@@ -103,7 +103,7 @@ func setupSequencerFailoverTest(t *testing.T) (*System, map[string]*conductor) { ...@@ -103,7 +103,7 @@ func setupSequencerFailoverTest(t *testing.T) (*System, map[string]*conductor) {
return healthy(t, ctx, c1) && return healthy(t, ctx, c1) &&
healthy(t, ctx, c2) && healthy(t, ctx, c2) &&
healthy(t, ctx, c3) healthy(t, ctx, c3)
}, 30*time.Second, 500*time.Millisecond, "Expected sequencers to become healthy") }, 50*time.Second, 500*time.Millisecond, "Expected sequencers to become healthy")
// unpause all conductors // unpause all conductors
require.NoError(t, c1.client.Resume(ctx)) require.NoError(t, c1.client.Resume(ctx))
......
...@@ -479,7 +479,14 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -479,7 +479,14 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
} }
l1Block := l1Genesis.ToBlock() l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2Genesis(cfg.DeployConfig, l1Block) var allocsMode genesis.L2AllocsMode
allocsMode = genesis.L2AllocsDelta
if ecotoneTime := cfg.DeployConfig.EcotoneTime(l1Block.Time()); ecotoneTime != nil && *ecotoneTime <= 0 {
allocsMode = genesis.L2AllocsEcotone
}
t.Log("Generating L2 genesis", "l2_allocs_mode", string(allocsMode))
l2Allocs := config.L2Allocs(allocsMode)
l2Genesis, err := genesis.BuildL2Genesis(cfg.DeployConfig, l2Allocs, l1Block)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -1223,6 +1223,10 @@ func testFees(t *testing.T, cfg SystemConfig) { ...@@ -1223,6 +1223,10 @@ func testFees(t *testing.T, cfg SystemConfig) {
l2Verif := sys.Clients["verifier"] l2Verif := sys.Clients["verifier"]
l1 := sys.Clients["l1"] l1 := sys.Clients["l1"]
// Wait for first block after genesis. The genesis block has zero L1Block values and will throw off the GPO checks
_, err = geth.WaitForBlock(big.NewInt(1), l2Verif, time.Minute)
require.NoError(t, err)
config := sys.L2Genesis().Config config := sys.L2Genesis().Config
sga := &stateGetterAdapter{ sga := &stateGetterAdapter{
......
...@@ -57,6 +57,10 @@ var ( ...@@ -57,6 +57,10 @@ var (
Name: "outfile.l1", Name: "outfile.l1",
Usage: "Path to L1 genesis output file", Usage: "Path to L1 genesis output file",
} }
l2AllocsFlag = &cli.StringFlag{
Name: "l2-allocs",
Usage: "Path to L2 genesis state dump",
}
l1Flags = []cli.Flag{ l1Flags = []cli.Flag{
deployConfigFlag, deployConfigFlag,
...@@ -69,6 +73,7 @@ var ( ...@@ -69,6 +73,7 @@ var (
l1RPCFlag, l1RPCFlag,
l1StartingBlockFlag, l1StartingBlockFlag,
deployConfigFlag, deployConfigFlag,
l2AllocsFlag,
l1DeploymentsFlag, l1DeploymentsFlag,
outfileL2Flag, outfileL2Flag,
outfileRollupFlag, outfileRollupFlag,
...@@ -165,6 +170,16 @@ var Subcommands = cli.Commands{ ...@@ -165,6 +170,16 @@ var Subcommands = cli.Commands{
} }
} }
var l2Allocs *genesis.ForgeAllocs
if l2AllocsPath := ctx.String("l2-allocs"); l2AllocsPath != "" {
l2Allocs, err = genesis.LoadForgeAllocs(l2AllocsPath)
if err != nil {
return err
}
} else {
return errors.New("missing l2-allocs")
}
if l1RPC != "" { if l1RPC != "" {
client, err := ethclient.Dial(l1RPC) client, err := ethclient.Dial(l1RPC)
if err != nil { if err != nil {
...@@ -205,7 +220,7 @@ var Subcommands = cli.Commands{ ...@@ -205,7 +220,7 @@ var Subcommands = cli.Commands{
log.Info("Using L1 Start Block", "number", l1StartBlock.Number(), "hash", l1StartBlock.Hash().Hex()) log.Info("Using L1 Start Block", "number", l1StartBlock.Number(), "hash", l1StartBlock.Hash().Hex())
// Build the L2 genesis block // Build the L2 genesis block
l2Genesis, err := genesis.BuildL2Genesis(config, l1StartBlock) l2Genesis, err := genesis.BuildL2Genesis(config, l2Allocs, l1StartBlock)
if err != nil { if err != nil {
return fmt.Errorf("error creating l2 genesis: %w", err) return fmt.Errorf("error creating l2 genesis: %w", err)
} }
......
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 356411) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_0() (gas: 356410)
GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2954596) GasBenchMark_L1CrossDomainMessenger:test_sendMessage_benchmark_1() (gas: 2954595)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 549198) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 549197)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4061174) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4061173)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 450326) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 450326)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3496075) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3496075)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 59809) GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 59809)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92951) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92950)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68398) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68398)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 69062) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 69062)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155567) GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155567)
\ No newline at end of file
...@@ -52,9 +52,10 @@ library Config { ...@@ -52,9 +52,10 @@ library Config {
/// @notice Returns the path that the state dump file should be written to or read from /// @notice Returns the path that the state dump file should be written to or read from
/// on the local filesystem. /// on the local filesystem.
function stateDumpPath(string memory _name) internal view returns (string memory _env) { function stateDumpPath(string memory _suffix) internal view returns (string memory _env) {
_env = vm.envOr( _env = vm.envOr(
"STATE_DUMP_PATH", string.concat(vm.projectRoot(), "/", _name, "-", vm.toString(block.chainid), ".json") "STATE_DUMP_PATH",
string.concat(vm.projectRoot(), "/state-dump-", vm.toString(block.chainid), _suffix, ".json")
); );
} }
...@@ -94,7 +95,7 @@ library Config { ...@@ -94,7 +95,7 @@ library Config {
return "optimism-goerli"; return "optimism-goerli";
} else if (chainid == Chains.OPMainnet) { } else if (chainid == Chains.OPMainnet) {
return "optimism-mainnet"; return "optimism-mainnet";
} else if (chainid == Chains.LocalDevnet || chainid == Chains.GethDevnet) { } else if (chainid == Chains.LocalDevnet || chainid == Chains.GethDevnet || chainid == Chains.OPLocalDevnet) {
return "devnetL1"; return "devnetL1";
} else if (chainid == Chains.Hardhat) { } else if (chainid == Chains.Hardhat) {
return "hardhat"; return "hardhat";
......
...@@ -262,8 +262,7 @@ contract Deploy is Deployer { ...@@ -262,8 +262,7 @@ contract Deploy is Deployer {
function runWithStateDump() public { function runWithStateDump() public {
_run(); _run();
vm.dumpState(Config.stateDumpPath(""));
vm.dumpState(Config.stateDumpPath(name()));
} }
/// @notice Deploy all L1 contracts and write the state diff to a file. /// @notice Deploy all L1 contracts and write the state diff to a file.
...@@ -273,12 +272,16 @@ contract Deploy is Deployer { ...@@ -273,12 +272,16 @@ contract Deploy is Deployer {
/// @notice Internal function containing the deploy logic. /// @notice Internal function containing the deploy logic.
function _run() internal { function _run() internal {
console.log("start of L1 Deploy!");
deploySafe(); deploySafe();
console.log("deployed Safe!");
setupSuperchain(); setupSuperchain();
console.log("set up superchain!");
if (cfg.usePlasma()) { if (cfg.usePlasma()) {
setupOpPlasma(); setupOpPlasma();
} }
setupOpChain(); setupOpChain();
console.log("set up op chain!");
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
......
...@@ -35,8 +35,10 @@ contract DeployConfig is Script { ...@@ -35,8 +35,10 @@ contract DeployConfig is Script {
address public proxyAdminOwner; address public proxyAdminOwner;
address public baseFeeVaultRecipient; address public baseFeeVaultRecipient;
uint256 public baseFeeVaultMinimumWithdrawalAmount; uint256 public baseFeeVaultMinimumWithdrawalAmount;
uint256 public baseFeeVaultWithdrawalNetwork;
address public l1FeeVaultRecipient; address public l1FeeVaultRecipient;
uint256 public l1FeeVaultMinimumWithdrawalAmount; uint256 public l1FeeVaultMinimumWithdrawalAmount;
uint256 public l1FeeVaultWithdrawalNetwork;
address public sequencerFeeVaultRecipient; address public sequencerFeeVaultRecipient;
uint256 public sequencerFeeVaultMinimumWithdrawalAmount; uint256 public sequencerFeeVaultMinimumWithdrawalAmount;
uint256 public sequencerFeeVaultWithdrawalNetwork; uint256 public sequencerFeeVaultWithdrawalNetwork;
...@@ -44,7 +46,6 @@ contract DeployConfig is Script { ...@@ -44,7 +46,6 @@ contract DeployConfig is Script {
string public governanceTokenSymbol; string public governanceTokenSymbol;
address public governanceTokenOwner; address public governanceTokenOwner;
uint256 public l2GenesisBlockGasLimit; uint256 public l2GenesisBlockGasLimit;
uint256 public l2GenesisBlockBaseFeePerGas;
uint256 public gasPriceOracleOverhead; uint256 public gasPriceOracleOverhead;
uint256 public gasPriceOracleScalar; uint256 public gasPriceOracleScalar;
bool public enableGovernance; bool public enableGovernance;
...@@ -98,12 +99,14 @@ contract DeployConfig is Script { ...@@ -98,12 +99,14 @@ contract DeployConfig is Script {
l2OutputOracleProposer = stdJson.readAddress(_json, "$.l2OutputOracleProposer"); l2OutputOracleProposer = stdJson.readAddress(_json, "$.l2OutputOracleProposer");
l2OutputOracleChallenger = stdJson.readAddress(_json, "$.l2OutputOracleChallenger"); l2OutputOracleChallenger = stdJson.readAddress(_json, "$.l2OutputOracleChallenger");
finalizationPeriodSeconds = stdJson.readUint(_json, "$.finalizationPeriodSeconds"); finalizationPeriodSeconds = stdJson.readUint(_json, "$.finalizationPeriodSeconds");
fundDevAccounts = stdJson.readBool(_json, "$.fundDevAccounts"); fundDevAccounts = _readOr(_json, "$.fundDevAccounts", false);
proxyAdminOwner = stdJson.readAddress(_json, "$.proxyAdminOwner"); proxyAdminOwner = stdJson.readAddress(_json, "$.proxyAdminOwner");
baseFeeVaultRecipient = stdJson.readAddress(_json, "$.baseFeeVaultRecipient"); baseFeeVaultRecipient = stdJson.readAddress(_json, "$.baseFeeVaultRecipient");
baseFeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.baseFeeVaultMinimumWithdrawalAmount"); baseFeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.baseFeeVaultMinimumWithdrawalAmount");
baseFeeVaultWithdrawalNetwork = stdJson.readUint(_json, "$.baseFeeVaultWithdrawalNetwork");
l1FeeVaultRecipient = stdJson.readAddress(_json, "$.l1FeeVaultRecipient"); l1FeeVaultRecipient = stdJson.readAddress(_json, "$.l1FeeVaultRecipient");
l1FeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.l1FeeVaultMinimumWithdrawalAmount"); l1FeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.l1FeeVaultMinimumWithdrawalAmount");
l1FeeVaultWithdrawalNetwork = stdJson.readUint(_json, "$.l1FeeVaultWithdrawalNetwork");
sequencerFeeVaultRecipient = stdJson.readAddress(_json, "$.sequencerFeeVaultRecipient"); sequencerFeeVaultRecipient = stdJson.readAddress(_json, "$.sequencerFeeVaultRecipient");
sequencerFeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.sequencerFeeVaultMinimumWithdrawalAmount"); sequencerFeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.sequencerFeeVaultMinimumWithdrawalAmount");
sequencerFeeVaultWithdrawalNetwork = stdJson.readUint(_json, "$.sequencerFeeVaultWithdrawalNetwork"); sequencerFeeVaultWithdrawalNetwork = stdJson.readUint(_json, "$.sequencerFeeVaultWithdrawalNetwork");
...@@ -111,7 +114,6 @@ contract DeployConfig is Script { ...@@ -111,7 +114,6 @@ contract DeployConfig is Script {
governanceTokenSymbol = stdJson.readString(_json, "$.governanceTokenSymbol"); governanceTokenSymbol = stdJson.readString(_json, "$.governanceTokenSymbol");
governanceTokenOwner = stdJson.readAddress(_json, "$.governanceTokenOwner"); governanceTokenOwner = stdJson.readAddress(_json, "$.governanceTokenOwner");
l2GenesisBlockGasLimit = stdJson.readUint(_json, "$.l2GenesisBlockGasLimit"); l2GenesisBlockGasLimit = stdJson.readUint(_json, "$.l2GenesisBlockGasLimit");
l2GenesisBlockBaseFeePerGas = stdJson.readUint(_json, "$.l2GenesisBlockBaseFeePerGas");
gasPriceOracleOverhead = stdJson.readUint(_json, "$.gasPriceOracleOverhead"); gasPriceOracleOverhead = stdJson.readUint(_json, "$.gasPriceOracleOverhead");
gasPriceOracleScalar = stdJson.readUint(_json, "$.gasPriceOracleScalar"); gasPriceOracleScalar = stdJson.readUint(_json, "$.gasPriceOracleScalar");
enableGovernance = stdJson.readBool(_json, "$.enableGovernance"); enableGovernance = stdJson.readBool(_json, "$.enableGovernance");
...@@ -121,10 +123,10 @@ contract DeployConfig is Script { ...@@ -121,10 +123,10 @@ contract DeployConfig is Script {
requiredProtocolVersion = stdJson.readUint(_json, "$.requiredProtocolVersion"); requiredProtocolVersion = stdJson.readUint(_json, "$.requiredProtocolVersion");
recommendedProtocolVersion = stdJson.readUint(_json, "$.recommendedProtocolVersion"); recommendedProtocolVersion = stdJson.readUint(_json, "$.recommendedProtocolVersion");
useFaultProofs = stdJson.readBool(_json, "$.useFaultProofs"); useFaultProofs = _readOr(_json, "$.useFaultProofs", false);
proofMaturityDelaySeconds = stdJson.readUint(_json, "$.proofMaturityDelaySeconds"); proofMaturityDelaySeconds = _readOr(_json, "$.proofMaturityDelaySeconds", 0);
disputeGameFinalityDelaySeconds = stdJson.readUint(_json, "$.disputeGameFinalityDelaySeconds"); disputeGameFinalityDelaySeconds = _readOr(_json, "$.disputeGameFinalityDelaySeconds", 0);
respectedGameType = stdJson.readUint(_json, "$.respectedGameType"); respectedGameType = _readOr(_json, "$.respectedGameType", 0);
faultGameAbsolutePrestate = stdJson.readUint(_json, "$.faultGameAbsolutePrestate"); faultGameAbsolutePrestate = stdJson.readUint(_json, "$.faultGameAbsolutePrestate");
faultGameMaxDepth = stdJson.readUint(_json, "$.faultGameMaxDepth"); faultGameMaxDepth = stdJson.readUint(_json, "$.faultGameMaxDepth");
......
...@@ -143,6 +143,14 @@ library ForgeArtifacts { ...@@ -143,6 +143,14 @@ library ForgeArtifacts {
slot_ = abi.decode(rawSlot, (StorageSlot)); slot_ = abi.decode(rawSlot, (StorageSlot));
} }
/// @notice Returns whether or not a contract is initialized.
/// Needs the name to get the storage layout.
function isInitialized(string memory _name, address _address) internal returns (bool initialized_) {
StorageSlot memory slot = ForgeArtifacts.getInitializedSlot(_name);
bytes32 slotVal = vm.load(_address, bytes32(vm.parseUint(slot.slot)));
initialized_ = uint8((uint256(slotVal) >> (slot.offset * 8)) & 0xFF) != 0;
}
/// @notice Returns the function ABIs of all L1 contracts. /// @notice Returns the function ABIs of all L1 contracts.
function getContractFunctionAbis( function getContractFunctionAbis(
string memory path, string memory path,
...@@ -196,10 +204,7 @@ library ForgeArtifacts { ...@@ -196,10 +204,7 @@ library ForgeArtifacts {
/// @notice Accepts a filepath and then ensures that the directory /// @notice Accepts a filepath and then ensures that the directory
/// exists for the file to live in. /// exists for the file to live in.
function ensurePath(string memory _path) internal { function ensurePath(string memory _path) internal {
(, bytes memory returndata) = string[] memory outputs = vm.split(_path, "/");
address(vm).call(abi.encodeWithSignature("split(string,string)", _path, string("/")));
string[] memory outputs = abi.decode(returndata, (string[]));
string memory path = ""; string memory path = "";
for (uint256 i = 0; i < outputs.length - 1; i++) { for (uint256 i = 0; i < outputs.length - 1; i++) {
path = string.concat(path, outputs[i], "/"); path = string.concat(path, outputs[i], "/");
......
...@@ -1988,7 +1988,7 @@ ...@@ -1988,7 +1988,7 @@
"newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000003c" "slot": "0x000000000000000000000000000000000000000000000000000000000000003d"
} }
], ],
"value": 0 "value": 0
...@@ -2014,7 +2014,7 @@ ...@@ -2014,7 +2014,7 @@
"newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000003d" "slot": "0x000000000000000000000000000000000000000000000000000000000000003e"
} }
], ],
"value": 0 "value": 0
...@@ -2475,7 +2475,7 @@ ...@@ -2475,7 +2475,7 @@
"newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000003c" "slot": "0x000000000000000000000000000000000000000000000000000000000000003d"
} }
], ],
"value": 0 "value": 0
...@@ -2553,7 +2553,7 @@ ...@@ -2553,7 +2553,7 @@
"newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000003d" "slot": "0x000000000000000000000000000000000000000000000000000000000000003e"
} }
], ],
"value": 0 "value": 0
...@@ -6095,7 +6095,7 @@ ...@@ -6095,7 +6095,7 @@
"newValue": "0x0000000000000000000000000000000000000000000000000000000000000834", "newValue": "0x0000000000000000000000000000000000000000000000000000000000000834",
"previousValue": "0x0000000000000000000000000000000000000000000000000000000000000834", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000834",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000002c" "slot": "0x000000000000000000000000000000000000000000000000000000000000002d"
} }
], ],
"value": 0 "value": 0
...@@ -6121,7 +6121,7 @@ ...@@ -6121,7 +6121,7 @@
"newValue": "0x00000000000000000000000000000000000000000000000000000000000f4240", "newValue": "0x00000000000000000000000000000000000000000000000000000000000f4240",
"previousValue": "0x00000000000000000000000000000000000000000000000000000000000f4240", "previousValue": "0x00000000000000000000000000000000000000000000000000000000000f4240",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000002d" "slot": "0x000000000000000000000000000000000000000000000000000000000000002e"
} }
], ],
"value": 0 "value": 0
...@@ -6147,7 +6147,7 @@ ...@@ -6147,7 +6147,7 @@
"newValue": "0x00000000000000000000000000000000000000000000000000000000017d7840", "newValue": "0x00000000000000000000000000000000000000000000000000000000017d7840",
"previousValue": "0x00000000000000000000000000000000000000000000000000000000017d7840", "previousValue": "0x00000000000000000000000000000000000000000000000000000000017d7840",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000002a" "slot": "0x000000000000000000000000000000000000000000000000000000000000002c"
} }
], ],
"value": 0 "value": 0
...@@ -6856,7 +6856,7 @@ ...@@ -6856,7 +6856,7 @@
"newValue": "0x0000000000000000000000000000000000000000000000000000000000000834", "newValue": "0x0000000000000000000000000000000000000000000000000000000000000834",
"previousValue": "0x0000000000000000000000000000000000000000000000000000000000000834", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000834",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000002c" "slot": "0x000000000000000000000000000000000000000000000000000000000000002d"
} }
], ],
"value": 0 "value": 0
...@@ -6934,7 +6934,7 @@ ...@@ -6934,7 +6934,7 @@
"newValue": "0x00000000000000000000000000000000000000000000000000000000000f4240", "newValue": "0x00000000000000000000000000000000000000000000000000000000000f4240",
"previousValue": "0x00000000000000000000000000000000000000000000000000000000000f4240", "previousValue": "0x00000000000000000000000000000000000000000000000000000000000f4240",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000002d" "slot": "0x000000000000000000000000000000000000000000000000000000000000002e"
} }
], ],
"value": 0 "value": 0
...@@ -7090,7 +7090,7 @@ ...@@ -7090,7 +7090,7 @@
"newValue": "0x00000000000000000000000000000000000000000000000000000000017d7840", "newValue": "0x00000000000000000000000000000000000000000000000000000000017d7840",
"previousValue": "0x00000000000000000000000000000000000000000000000000000000017d7840", "previousValue": "0x00000000000000000000000000000000000000000000000000000000017d7840",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000002a" "slot": "0x000000000000000000000000000000000000000000000000000000000000002c"
} }
], ],
"value": 0 "value": 0
...@@ -7246,7 +7246,7 @@ ...@@ -7246,7 +7246,7 @@
"newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000",
"reverted": false, "reverted": false,
"slot": "0x000000000000000000000000000000000000000000000000000000000000003b" "slot": "0x000000000000000000000000000000000000000000000000000000000000003c"
} }
], ],
"value": 0 "value": 0
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -3,6 +3,7 @@ pragma solidity 0.8.15; ...@@ -3,6 +3,7 @@ pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { CommonTest } from "test/setup/CommonTest.sol"; import { CommonTest } from "test/setup/CommonTest.sol";
import { OutputMode } from "scripts/L2Genesis.s.sol";
// Libraries // Libraries
import { Encoding } from "src/libraries/Encoding.sol"; import { Encoding } from "src/libraries/Encoding.sol";
...@@ -37,7 +38,10 @@ contract GasPriceOracle_Test is CommonTest { ...@@ -37,7 +38,10 @@ contract GasPriceOracle_Test is CommonTest {
contract GasPriceOracleBedrock_Test is GasPriceOracle_Test { contract GasPriceOracleBedrock_Test is GasPriceOracle_Test {
/// @dev Sets up the test suite. /// @dev Sets up the test suite.
function setUp() public virtual override { function setUp() public virtual override {
// The gasPriceOracle tests rely on an L2 genesis that is not past Ecotone.
l2OutputMode = OutputMode.LOCAL_DELTA;
super.setUp(); super.setUp();
assertEq(gasPriceOracle.isEcotone(), false);
vm.prank(depositor); vm.prank(depositor);
l1Block.setL1BlockValues({ l1Block.setL1BlockValues({
...@@ -109,7 +113,9 @@ contract GasPriceOracleBedrock_Test is GasPriceOracle_Test { ...@@ -109,7 +113,9 @@ contract GasPriceOracleBedrock_Test is GasPriceOracle_Test {
contract GasPriceOracleEcotone_Test is GasPriceOracle_Test { contract GasPriceOracleEcotone_Test is GasPriceOracle_Test {
/// @dev Sets up the test suite. /// @dev Sets up the test suite.
function setUp() public virtual override { function setUp() public virtual override {
l2OutputMode = OutputMode.LOCAL_LATEST; // activate ecotone
super.setUp(); super.setUp();
assertEq(gasPriceOracle.isEcotone(), true);
bytes memory calldataPacked = Encoding.encodeSetL1BlockValuesEcotone( bytes memory calldataPacked = Encoding.encodeSetL1BlockValuesEcotone(
baseFeeScalar, blobBaseFeeScalar, sequenceNumber, timestamp, number, baseFee, blobBaseFee, hash, batcherHash baseFeeScalar, blobBaseFeeScalar, sequenceNumber, timestamp, number, baseFee, blobBaseFee, hash, batcherHash
...@@ -119,9 +125,6 @@ contract GasPriceOracleEcotone_Test is GasPriceOracle_Test { ...@@ -119,9 +125,6 @@ contract GasPriceOracleEcotone_Test is GasPriceOracle_Test {
vm.prank(depositor); vm.prank(depositor);
(bool success,) = address(l1Block).call(calldataPacked); (bool success,) = address(l1Block).call(calldataPacked);
require(success, "Function call failed"); require(success, "Function call failed");
vm.prank(depositor);
gasPriceOracle.setEcotone();
} }
/// @dev Tests that `setEcotone` is only callable by the depositor. /// @dev Tests that `setEcotone` is only callable by the depositor.
......
...@@ -28,10 +28,12 @@ contract L2StandardBridge_Test is Bridge_Initializer { ...@@ -28,10 +28,12 @@ contract L2StandardBridge_Test is Bridge_Initializer {
function test_constructor_succeeds() external view { function test_constructor_succeeds() external view {
L2StandardBridge impl = L2StandardBridge impl =
L2StandardBridge(payable(EIP1967Helper.getImplementation(deploy.mustGetAddress("L2StandardBridge")))); L2StandardBridge(payable(EIP1967Helper.getImplementation(deploy.mustGetAddress("L2StandardBridge"))));
assertEq(address(impl.MESSENGER()), address(0)); // The implementation contract is initialized with a 0 L1 bridge address,
assertEq(address(impl.messenger()), address(0)); // but the L2 cross-domain-messenger is always set to the predeploy address for both proxy and implementation.
assertEq(address(impl.OTHER_BRIDGE()), address(0)); assertEq(address(impl.MESSENGER()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor zero check MESSENGER");
assertEq(address(impl.otherBridge()), address(0)); assertEq(address(impl.messenger()), Predeploys.L2_CROSS_DOMAIN_MESSENGER, "constructor zero check messenger");
assertEq(address(impl.OTHER_BRIDGE()), address(0), "constructor zero check OTHER_BRIDGE");
assertEq(address(impl.otherBridge()), address(0), "constructor zero check otherBridge");
} }
/// @dev Tests that the bridge is initialized correctly. /// @dev Tests that the bridge is initialized correctly.
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { L2Genesis, OutputMode, L1Dependencies } from "scripts/L2Genesis.s.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { Constants } from "src/libraries/Constants.sol";
/// @title L2GenesisTest
/// @notice Test suite for L2Genesis script.
contract L2GenesisTest is Test {
L2Genesis genesis;
function setUp() public {
genesis = new L2Genesis();
// Note: to customize L1 addresses,
// simply pass in the L1 addresses argument for Genesis setup functions that depend on it.
// L1 addresses, or L1 artifacts, are not stored globally.
genesis.setUp();
}
/// @notice Creates a temp file and returns the path to it.
function tmpfile() internal returns (string memory) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = "mktemp";
bytes memory result = vm.ffi(commands);
return string(result);
}
/// @notice Deletes a file at a given filesystem path. Does not force delete
/// and does not recursively delete.
function deleteFile(string memory path) internal {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("rm ", path);
vm.ffi(commands);
}
/// @notice Returns the number of top level keys in a JSON object at a given
/// file path.
function getJSONKeyCount(string memory path) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq 'keys | length' < ", path, " | xargs cast abi-encode 'f(uint256)'");
return abi.decode(vm.ffi(commands), (uint256));
}
/// @notice Helper function to run a function with a temporary dump file.
function withTempDump(function (string memory) internal f) internal {
string memory path = tmpfile();
f(path);
deleteFile(path);
}
/// @notice Helper function for reading the number of storage keys for a given account.
function getStorageKeysCount(string memory _path, address _addr) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] =
string.concat("jq -r '.[\"", vm.toLowercase(vm.toString(_addr)), "\"].storage | length' < ", _path);
return vm.parseUint(string(vm.ffi(commands)));
}
/// @notice Returns the number of accounts that contain particular code at a given path to a genesis file.
function getCodeCount(string memory path, string memory name) internal returns (uint256) {
bytes memory code = vm.getDeployedCode(name);
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat(
"jq -r 'map_values(select(.code == \"",
vm.toString(code),
"\")) | length' < ",
path,
" | xargs cast abi-encode 'f(uint256)'"
);
return abi.decode(vm.ffi(commands), (uint256));
}
/// @notice Returns the number of accounts that have a particular slot set.
function getPredeployCountWithSlotSet(string memory path, bytes32 slot) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat(
"jq 'map_values(.storage | select(has(\"",
vm.toString(slot),
"\"))) | keys | length' < ",
path,
" | xargs cast abi-encode 'f(uint256)'"
);
return abi.decode(vm.ffi(commands), (uint256));
}
/// @notice Returns the number of accounts that have a particular slot set to a particular value.
function getPredeployCountWithSlotSetToValue(
string memory path,
bytes32 slot,
bytes32 value
)
internal
returns (uint256)
{
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat(
"jq 'map_values(.storage | select(.\"",
vm.toString(slot),
"\" == \"",
vm.toString(value),
"\")) | length' < ",
path,
" | xargs cast abi-encode 'f(uint256)'"
);
return abi.decode(vm.ffi(commands), (uint256));
}
/// @notice Tests the genesis predeploys setup using a temp file.
function test_genesis_predeploys() external {
withTempDump(_test_genesis_predeploys);
}
/// @notice Tests the genesis predeploys setup.
function _test_genesis_predeploys(string memory _path) internal {
// Set the predeploy proxies into state
genesis.setPredeployProxies();
genesis.writeGenesisAllocs(_path);
// 2 predeploys do not have proxies
assertEq(getCodeCount(_path, "Proxy.sol:Proxy"), Predeploys.PREDEPLOY_COUNT - 2);
// 17 proxies have the implementation set
assertEq(getPredeployCountWithSlotSet(_path, Constants.PROXY_IMPLEMENTATION_ADDRESS), 17);
// All proxies except 2 have the proxy 1967 admin slot set to the proxy admin
assertEq(
getPredeployCountWithSlotSetToValue(
_path, Constants.PROXY_OWNER_ADDRESS, bytes32(uint256(uint160(Predeploys.PROXY_ADMIN)))
),
Predeploys.PREDEPLOY_COUNT - 2
);
// Also see Predeploys.t.test_predeploysSet_succeeds which uses L1Genesis for the CommonTest prestate.
}
/// @notice Tests the number of accounts in the genesis setup
function test_allocs_size() external {
withTempDump(_test_allocs_size);
}
/// @notice Creates mock L1Dependencies for testing purposes.
function _dummyL1Deps() internal pure returns (L1Dependencies memory _deps) {
return L1Dependencies({
l1CrossDomainMessengerProxy: payable(address(0x100000)),
l1StandardBridgeProxy: payable(address(0x100001)),
l1ERC721BridgeProxy: payable(address(0x100002))
});
}
/// @notice Tests the number of accounts in the genesis setup
function _test_allocs_size(string memory _path) internal {
genesis.runWithOptions(OutputMode.LOCAL_LATEST, _dummyL1Deps());
genesis.writeGenesisAllocs(_path);
uint256 expected = 0;
expected += 2048 - 2; // predeploy proxies
expected += 19; // predeploy implementations (excl. legacy erc20-style eth and legacy message sender)
expected += 256; // precompiles
expected += 12; // preinstalls
expected += 1; // 4788 deployer account
// 16 prefunded dev accounts are excluded
assertEq(expected, getJSONKeyCount(_path), "key count check");
// 3 slots: implementation, owner, admin
assertEq(3, getStorageKeysCount(_path, Predeploys.PROXY_ADMIN), "proxy admin storage check");
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { CommonTest } from "test/setup/CommonTest.sol";
import { Preinstalls } from "src/libraries/Preinstalls.sol";
import { Bytes } from "src/libraries/Bytes.sol";
import { console2 as console } from "forge-std/console2.sol";
interface IEIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
/// @title PreinstallsTest
contract PreinstallsTest is CommonTest {
/// @dev The domain separator commits to the chainid of the chain
function test_preinstall_permit2_domain_separator() external view {
bytes32 domainSeparator = IEIP712(Preinstalls.Permit2).DOMAIN_SEPARATOR();
bytes32 typeHash =
keccak256(abi.encodePacked("EIP712Domain(string name,uint256 chainId,address verifyingContract)"));
bytes32 nameHash = keccak256(abi.encodePacked("Permit2"));
uint256 chainId = block.chainid;
bytes memory encoded = abi.encode(typeHash, nameHash, chainId, Preinstalls.Permit2);
bytes32 expectedDomainSeparator = keccak256(encoded);
assertEq(domainSeparator, expectedDomainSeparator, "Domain separator mismatch");
assertEq(chainId, uint256(31337));
assertEq(domainSeparator, bytes32(0x4d553c58ae79a6c4ba64f0e690a5d1cd2deff8c6b91cf38300e0f2b76f9ee346));
// Warning the Permit2 domain separator as cached in the DeployPermit2.sol bytecode is incorrect.
}
function test_permit2_templating() external pure {
bytes memory customCode = Preinstalls.getPermit2Code(1234);
assertNotEq(customCode.length, 0, "must have code");
assertEq(uint256(bytes32(Bytes.slice(customCode, 6945, 32))), uint256(1234), "expecting custom chain ID");
assertEq(
bytes32(Bytes.slice(customCode, 6983, 32)),
bytes32(0x6cda538cafce36292a6ef27740629597f85f6716f5694d26d5c59fc1d07cfd95),
"expecting custom domain separator"
);
bytes memory defaultCode = Preinstalls.getPermit2Code(1);
assertNotEq(defaultCode.length, 0, "must have code");
assertEq(uint256(bytes32(Bytes.slice(defaultCode, 6945, 32))), uint256(1), "expecting default chain ID");
assertEq(
bytes32(Bytes.slice(defaultCode, 6983, 32)),
bytes32(0x866a5aba21966af95d6c7ab78eb2b2fc913915c28be3b9aa07cc04ff903e3f28),
"expecting default domain separator"
);
assertEq(defaultCode, Preinstalls.Permit2TemplateCode, "template is using chain ID 1");
}
function assertPreinstall(address _addr, bytes memory _code) internal view {
assertNotEq(_code.length, 0, "must have code");
assertNotEq(_addr.code.length, 0, "deployed preinstall account must have code");
assertEq(_addr.code, _code, "equal code must be deployed");
assertEq(Preinstalls.getDeployedCode(_addr, block.chainid), _code, "deployed-code getter must match");
assertNotEq(Preinstalls.getName(_addr), "", "must have a name");
if (_addr != Preinstalls.DeterministicDeploymentProxy) {
assertEq(vm.getNonce(_addr), 1, "preinstall account must have 1 nonce");
}
}
function test_preinstall_multicall3_succeeds() external view {
assertPreinstall(Preinstalls.MultiCall3, Preinstalls.MultiCall3Code);
}
function test_preinstall_create2Deployer_succeeds() external view {
assertPreinstall(Preinstalls.Create2Deployer, Preinstalls.Create2DeployerCode);
}
function test_preinstall_safev130_succeeds() external view {
assertPreinstall(Preinstalls.Safe_v130, Preinstalls.Safe_v130Code);
}
function test_preinstall_safeL2v130_succeeds() external view {
assertPreinstall(Preinstalls.SafeL2_v130, Preinstalls.SafeL2_v130Code);
}
function test_preinstall_multisendCallOnlyv130_succeeds() external view {
assertPreinstall(Preinstalls.MultiSendCallOnly_v130, Preinstalls.MultiSendCallOnly_v130Code);
}
function test_preinstall_safeSingletonFactory_succeeds() external view {
assertPreinstall(Preinstalls.SafeSingletonFactory, Preinstalls.SafeSingletonFactoryCode);
}
function test_preinstall_deterministicDeploymentProxy_succeeds() external view {
assertPreinstall(Preinstalls.DeterministicDeploymentProxy, Preinstalls.DeterministicDeploymentProxyCode);
}
function test_preinstall_multisendv130_succeeds() external view {
assertPreinstall(Preinstalls.MultiSend_v130, Preinstalls.MultiSend_v130Code);
}
function test_preinstall_permit2_succeeds() external {
uint256 pre = block.chainid;
vm.chainId(901); // TODO legacy deployment does not use same chainID as tests run with
assertPreinstall(Preinstalls.Permit2, Preinstalls.getPermit2Code(block.chainid));
vm.chainId(pre);
}
function test_preinstall_senderCreator_succeeds() external view {
assertPreinstall(Preinstalls.SenderCreator, Preinstalls.SenderCreatorCode);
}
function test_preinstall_entrypoint_succeeds() external view {
assertPreinstall(Preinstalls.EntryPoint, Preinstalls.EntryPointCode);
}
function test_preinstall_beaconBlockRoots_succeeds() external view {
assertPreinstall(Preinstalls.BeaconBlockRoots, Preinstalls.BeaconBlockRootsCode);
assertEq(vm.getNonce(Preinstalls.BeaconBlockRootsSender), 1, "4788 sender must have nonce=1");
}
}
...@@ -8,20 +8,28 @@ import { Vm } from "forge-std/Vm.sol"; ...@@ -8,20 +8,28 @@ import { Vm } from "forge-std/Vm.sol";
library EIP1967Helper { library EIP1967Helper {
/// @notice The storage slot that holds the address of a proxy implementation. /// @notice The storage slot that holds the address of a proxy implementation.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)` /// @dev `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`
bytes32 internal constant PROXY_IMPLEMENTATION_ADDRESS = bytes32 internal constant PROXY_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/// @notice The storage slot that holds the address of the owner. /// @notice The storage slot that holds the address of the owner.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)` /// @dev `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`
bytes32 internal constant PROXY_OWNER_ADDRESS = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; bytes32 internal constant PROXY_ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function getAdmin(address _proxy) internal view returns (address) { function getAdmin(address _proxy) internal view returns (address) {
return address(uint160(uint256(vm.load(address(_proxy), PROXY_OWNER_ADDRESS)))); return address(uint160(uint256(vm.load(address(_proxy), PROXY_ADMIN_SLOT))));
}
function setAdmin(address _addr, address _admin) internal {
vm.store(_addr, PROXY_ADMIN_SLOT, bytes32(uint256(uint160(_admin))));
} }
function getImplementation(address _proxy) internal view returns (address) { function getImplementation(address _proxy) internal view returns (address) {
return address(uint160(uint256(vm.load(address(_proxy), PROXY_IMPLEMENTATION_ADDRESS)))); return address(uint160(uint256(vm.load(address(_proxy), PROXY_IMPLEMENTATION_SLOT))));
}
function setImplementation(address _addr, address _impl) internal {
vm.store(_addr, PROXY_IMPLEMENTATION_SLOT, bytes32(uint256(uint160(_impl))));
} }
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { console2 as console } from "forge-std/console2.sol";
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
import { Preinstalls } from "src/libraries/Preinstalls.sol";
import { L2CrossDomainMessenger } from "src/L2/L2CrossDomainMessenger.sol"; import { L2CrossDomainMessenger } from "src/L2/L2CrossDomainMessenger.sol";
import { L2StandardBridge } from "src/L2/L2StandardBridge.sol"; import { L2StandardBridge } from "src/L2/L2StandardBridge.sol";
import { L2ToL1MessagePasser } from "src/L2/L2ToL1MessagePasser.sol"; import { L2ToL1MessagePasser } from "src/L2/L2ToL1MessagePasser.sol";
...@@ -24,6 +26,7 @@ import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol"; ...@@ -24,6 +26,7 @@ import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol"; import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
import { DeployConfig } from "scripts/DeployConfig.s.sol"; import { DeployConfig } from "scripts/DeployConfig.s.sol";
import { Deploy } from "scripts/Deploy.s.sol"; import { Deploy } from "scripts/Deploy.s.sol";
import { L2Genesis, L1Dependencies, OutputMode } from "scripts/L2Genesis.s.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol"; import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { ProtocolVersions } from "src/L1/ProtocolVersions.sol"; import { ProtocolVersions } from "src/L1/ProtocolVersions.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol";
...@@ -51,6 +54,12 @@ contract Setup { ...@@ -51,6 +54,12 @@ contract Setup {
/// mutating any nonces. MUST not have constructor logic. /// mutating any nonces. MUST not have constructor logic.
Deploy internal constant deploy = Deploy(address(uint160(uint256(keccak256(abi.encode("optimism.deploy")))))); Deploy internal constant deploy = Deploy(address(uint160(uint256(keccak256(abi.encode("optimism.deploy"))))));
L2Genesis internal constant l2Genesis =
L2Genesis(address(uint160(uint256(keccak256(abi.encode("optimism.l2genesis"))))));
// @notice Allows users of Setup to override what L2 genesis is being created.
OutputMode l2OutputMode = OutputMode.LOCAL_LATEST;
OptimismPortal optimismPortal; OptimismPortal optimismPortal;
OptimismPortal2 optimismPortal2; OptimismPortal2 optimismPortal2;
DisputeGameFactory disputeGameFactory; DisputeGameFactory disputeGameFactory;
...@@ -89,13 +98,22 @@ contract Setup { ...@@ -89,13 +98,22 @@ contract Setup {
/// will also need to include the bytecode for the Deploy contract. /// will also need to include the bytecode for the Deploy contract.
/// This is a hack as we are pushing solidity to the edge. /// This is a hack as we are pushing solidity to the edge.
function setUp() public virtual { function setUp() public virtual {
console.log("L1 setup start!");
vm.etch(address(deploy), vm.getDeployedCode("Deploy.s.sol:Deploy")); vm.etch(address(deploy), vm.getDeployedCode("Deploy.s.sol:Deploy"));
vm.allowCheatcodes(address(deploy)); vm.allowCheatcodes(address(deploy));
deploy.setUp(); deploy.setUp();
console.log("L1 setup done!");
console.log("L2 setup start!");
vm.etch(address(l2Genesis), vm.getDeployedCode("L2Genesis.s.sol:L2Genesis"));
vm.allowCheatcodes(address(l2Genesis));
l2Genesis.setUp();
console.log("L2 setup done!");
} }
/// @dev Sets up the L1 contracts. /// @dev Sets up the L1 contracts.
function L1() public { function L1() public {
console.log("Setup: creating L1 deployments");
// Set the deterministic deployer in state to ensure that it is there // Set the deterministic deployer in state to ensure that it is there
vm.etch( vm.etch(
0x4e59b44847b379578588920cA78FbF26c0B4956C, 0x4e59b44847b379578588920cA78FbF26c0B4956C,
...@@ -103,6 +121,7 @@ contract Setup { ...@@ -103,6 +121,7 @@ contract Setup {
); );
deploy.run(); deploy.run();
console.log("Setup: completed L1 deployment, registering addresses now");
optimismPortal = OptimismPortal(deploy.mustGetAddress("OptimismPortalProxy")); optimismPortal = OptimismPortal(deploy.mustGetAddress("OptimismPortalProxy"));
optimismPortal2 = OptimismPortal2(deploy.mustGetAddress("OptimismPortalProxy")); optimismPortal2 = OptimismPortal2(deploy.mustGetAddress("OptimismPortalProxy"));
...@@ -151,61 +170,63 @@ contract Setup { ...@@ -151,61 +170,63 @@ contract Setup {
vm.label(address(dataAvailabilityChallenge), "DataAvailabilityChallengeProxy"); vm.label(address(dataAvailabilityChallenge), "DataAvailabilityChallengeProxy");
vm.label(deploy.mustGetAddress("DataAvailabilityChallenge"), "DataAvailabilityChallenge"); vm.label(deploy.mustGetAddress("DataAvailabilityChallenge"), "DataAvailabilityChallenge");
} }
console.log("Setup: registered L1 deployments");
} }
/// @dev Sets up the L2 contracts. Depends on `L1()` being called first. /// @dev Sets up the L2 contracts. Depends on `L1()` being called first.
function L2() public { function L2() public {
string memory allocsPath = string.concat(vm.projectRoot(), "/.testdata/genesis.json"); console.log("Setup: creating L2 genesis, with output mode %d", uint256(l2OutputMode));
if (vm.isFile(allocsPath) == false) { l2Genesis.runWithOptions(
string[] memory args = new string[](3); l2OutputMode,
args[0] = Executables.bash; L1Dependencies({
args[1] = "-c"; l1CrossDomainMessengerProxy: payable(address(l1CrossDomainMessenger)),
args[2] = string.concat(vm.projectRoot(), "/scripts/generate-l2-genesis.sh"); l1StandardBridgeProxy: payable(address(l1StandardBridge)),
Vm.FfiResult memory result = vm.tryFfi(args); l1ERC721BridgeProxy: payable(address(l1ERC721Bridge))
if (result.exitCode != 0) { })
revert FfiFailed( );
string.concat(
"FFI call to generate genesis.json failed with exit code: ",
string(abi.encodePacked(result.exitCode)),
".\nCommand: ",
Executables.bash,
" -c ",
vm.projectRoot(),
"/scripts/generate-l2-genesis.sh",
".\nOutput: ",
string(result.stdout),
"\nError: ",
string(result.stderr)
)
);
}
}
// Prevent race condition where the genesis.json file is not yet created
while (vm.isFile(allocsPath) == false) {
vm.sleep(1);
}
vm.loadAllocs(allocsPath);
// Set the governance token's owner to be the final system owner // Set the governance token's owner to be the final system owner
address finalSystemOwner = deploy.cfg().finalSystemOwner(); address finalSystemOwner = deploy.cfg().finalSystemOwner();
vm.prank(governanceToken.owner()); vm.prank(governanceToken.owner());
governanceToken.transferOwnership(finalSystemOwner); governanceToken.transferOwnership(finalSystemOwner);
vm.label(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY, "OptimismMintableERC20Factory"); // L2 predeploys
vm.label(Predeploys.L2_STANDARD_BRIDGE, "L2StandardBridge"); labelPredeploy(Predeploys.L2_STANDARD_BRIDGE);
vm.label(Predeploys.L2_CROSS_DOMAIN_MESSENGER, "L2CrossDomainMessenger"); labelPredeploy(Predeploys.L2_CROSS_DOMAIN_MESSENGER);
vm.label(Predeploys.L2_TO_L1_MESSAGE_PASSER, "L2ToL1MessagePasser"); labelPredeploy(Predeploys.L2_TO_L1_MESSAGE_PASSER);
vm.label(Predeploys.SEQUENCER_FEE_WALLET, "SequencerFeeVault"); labelPredeploy(Predeploys.SEQUENCER_FEE_WALLET);
vm.label(Predeploys.L2_ERC721_BRIDGE, "L2ERC721Bridge"); labelPredeploy(Predeploys.L2_ERC721_BRIDGE);
vm.label(Predeploys.BASE_FEE_VAULT, "BaseFeeVault"); labelPredeploy(Predeploys.BASE_FEE_VAULT);
vm.label(Predeploys.L1_FEE_VAULT, "L1FeeVault"); labelPredeploy(Predeploys.L1_FEE_VAULT);
vm.label(Predeploys.L1_BLOCK_ATTRIBUTES, "L1Block"); labelPredeploy(Predeploys.L1_BLOCK_ATTRIBUTES);
vm.label(Predeploys.GAS_PRICE_ORACLE, "GasPriceOracle"); labelPredeploy(Predeploys.GAS_PRICE_ORACLE);
vm.label(Predeploys.LEGACY_MESSAGE_PASSER, "LegacyMessagePasser"); labelPredeploy(Predeploys.LEGACY_MESSAGE_PASSER);
vm.label(Predeploys.GOVERNANCE_TOKEN, "GovernanceToken"); labelPredeploy(Predeploys.GOVERNANCE_TOKEN);
vm.label(Predeploys.EAS, "EAS"); labelPredeploy(Predeploys.EAS);
vm.label(Predeploys.SCHEMA_REGISTRY, "SchemaRegistry"); labelPredeploy(Predeploys.SCHEMA_REGISTRY);
// L2 Preinstalls
labelPreinstall(Preinstalls.MultiCall3);
labelPreinstall(Preinstalls.Create2Deployer);
labelPreinstall(Preinstalls.Safe_v130);
labelPreinstall(Preinstalls.SafeL2_v130);
labelPreinstall(Preinstalls.MultiSendCallOnly_v130);
labelPreinstall(Preinstalls.SafeSingletonFactory);
labelPreinstall(Preinstalls.DeterministicDeploymentProxy);
labelPreinstall(Preinstalls.MultiSend_v130);
labelPreinstall(Preinstalls.Permit2);
labelPreinstall(Preinstalls.SenderCreator);
labelPreinstall(Preinstalls.EntryPoint);
labelPreinstall(Preinstalls.BeaconBlockRoots);
console.log("Setup: completed L2 genesis");
}
function labelPredeploy(address _addr) internal {
vm.label(_addr, Predeploys.getName(_addr));
}
function labelPreinstall(address _addr) internal {
vm.label(_addr, Preinstalls.getName(_addr));
} }
} }
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