Commit 4e13b20f authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

op-deployer: Reduce test code duplication, put test funcs first (#12751)

parent 451224fe
...@@ -14,10 +14,9 @@ import ( ...@@ -14,10 +14,9 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-chain-ops/script" "github.com/ethereum-optimism/optimism/op-chain-ops/script"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-service/testutils/anvil" "github.com/ethereum-optimism/optimism/op-service/testutils/anvil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -156,336 +155,238 @@ func TestEndToEndApply(t *testing.T) { ...@@ -156,336 +155,238 @@ func TestEndToEndApply(t *testing.T) {
}) })
} }
func localArtifactsLocator(t *testing.T) *opcm.ArtifactsLocator { func TestApplyExistingOPCM(t *testing.T) {
_, testFilename, _, ok := runtime.Caller(0) anvil.Test(t)
require.Truef(t, ok, "failed to get test filename")
monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..")
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")
artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
require.NoError(t, err)
loc := &opcm.ArtifactsLocator{
URL: artifactsURL,
}
return loc
}
func createEnv( forkRPCUrl := os.Getenv("SEPOLIA_RPC_URL")
t *testing.T, if forkRPCUrl == "" {
ctx context.Context, t.Skip("no fork RPC URL provided")
lgr log.Logger,
l1Client *ethclient.Client,
bcaster broadcaster.Broadcaster,
deployerAddr common.Address,
) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) {
_, testFilename, _, ok := runtime.Caller(0)
require.Truef(t, ok, "failed to get test filename")
monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..")
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")
artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
require.NoError(t, err)
artifactsLocator := &opcm.ArtifactsLocator{
URL: artifactsURL,
} }
artifactsFS, cleanupArtifacts, err := pipeline.DownloadArtifacts(ctx, artifactsLocator, pipeline.NoopDownloadProgressor) lgr := testlog.Logger(t, slog.LevelDebug)
require.NoError(t, err)
defer func() {
require.NoError(t, cleanupArtifacts())
}()
host, err := pipeline.DefaultScriptHost( ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
bcaster, defer cancel()
runner, err := anvil.New(
forkRPCUrl,
lgr, lgr,
deployerAddr,
artifactsFS,
0,
) )
require.NoError(t, err) require.NoError(t, err)
env := &pipeline.Env{ require.NoError(t, runner.Start(ctx))
StateWriter: pipeline.NoopStateWriter(), t.Cleanup(func() {
L1ScriptHost: host, require.NoError(t, runner.Stop())
L1Client: l1Client, })
Broadcaster: bcaster,
Deployer: deployerAddr,
Logger: lgr,
}
bundle := pipeline.ArtifactsBundle{ l1Client, err := ethclient.Dial(runner.RPCUrl())
L1: artifactsFS, require.NoError(t, err)
L2: artifactsFS,
}
return env, bundle, host l1ChainID := big.NewInt(11155111)
} dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
// index 0 from Anvil's test set
priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
require.NoError(t, err)
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(priv, l1ChainID))
deployerAddr := common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
func addrFor(t *testing.T, dk *devkeys.MnemonicDevKeys, key devkeys.Key) common.Address { l2ChainID := uint256.NewInt(1)
addr, err := dk.Address(key)
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
ChainID: l1ChainID,
Client: l1Client,
Signer: signer,
From: deployerAddr,
})
require.NoError(t, err) require.NoError(t, err)
return addr
}
func newIntent( env, bundle, _ := createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr)
t *testing.T,
l1ChainID *big.Int,
dk *devkeys.MnemonicDevKeys,
l2ChainID *uint256.Int,
l1Loc *opcm.ArtifactsLocator,
l2Loc *opcm.ArtifactsLocator,
) (*state.Intent, *state.State) {
intent := &state.Intent{
DeploymentStrategy: state.DeploymentStrategyLive,
L1ChainID: l1ChainID.Uint64(),
SuperchainRoles: &state.SuperchainRoles{
ProxyAdminOwner: addrFor(t, dk, devkeys.L1ProxyAdminOwnerRole.Key(l1ChainID)),
ProtocolVersionsOwner: addrFor(t, dk, devkeys.SuperchainDeployerKey.Key(l1ChainID)),
Guardian: addrFor(t, dk, devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)),
},
FundDevAccounts: true,
L1ContractsLocator: l1Loc,
L2ContractsLocator: l2Loc,
Chains: []*state.ChainIntent{
newChainIntent(t, dk, l1ChainID, l2ChainID),
},
}
st := &state.State{
Version: 1,
}
return intent, st
}
func newChainIntent(t *testing.T, dk *devkeys.MnemonicDevKeys, l1ChainID *big.Int, l2ChainID *uint256.Int) *state.ChainIntent { intent, st := newIntent(
return &state.ChainIntent{ t,
ID: l2ChainID.Bytes32(), l1ChainID,
BaseFeeVaultRecipient: addrFor(t, dk, devkeys.BaseFeeVaultRecipientRole.Key(l1ChainID)), dk,
L1FeeVaultRecipient: addrFor(t, dk, devkeys.L1FeeVaultRecipientRole.Key(l1ChainID)), l2ChainID,
SequencerFeeVaultRecipient: addrFor(t, dk, devkeys.SequencerFeeVaultRecipientRole.Key(l1ChainID)), opcm.DefaultL1ContractsLocator,
Eip1559Denominator: 50, opcm.DefaultL2ContractsLocator,
Eip1559Elasticity: 6, )
Roles: state.ChainRoles{
L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)),
L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)),
SystemConfigOwner: addrFor(t, dk, devkeys.SystemConfigOwner.Key(l1ChainID)),
UnsafeBlockSigner: addrFor(t, dk, devkeys.SequencerP2PRole.Key(l1ChainID)),
Batcher: addrFor(t, dk, devkeys.BatcherRole.Key(l1ChainID)),
Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)),
Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)),
},
}
}
type codeGetter func(t *testing.T, addr common.Address) []byte require.NoError(t, deployer.ApplyPipeline(
ctx,
env,
bundle,
intent,
st,
))
func ethClientCodeGetter(ctx context.Context, client *ethclient.Client) codeGetter { validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent)
return func(t *testing.T, addr common.Address) []byte {
code, err := client.CodeAt(ctx, addr, nil)
require.NoError(t, err)
return code
}
} }
func stateDumpCodeGetter(st *state.State) codeGetter { func TestL2BlockTimeOverride(t *testing.T) {
return func(t *testing.T, addr common.Address) []byte { op_e2e.InitParallel(t)
acc, ok := st.L1StateDump.Data.Accounts[addr] kurtosisutil.Test(t)
require.True(t, ok, "no account found for address %s", addr)
return acc.Code ctx, cancel := context.WithCancel(context.Background())
defer cancel()
env, bundle, intent, st := setupGenesisChain(t)
intent.GlobalDeployOverrides = map[string]interface{}{
"l2BlockTime": float64(3),
} }
require.NoError(t, deployer.ApplyPipeline(
ctx,
env,
bundle,
intent,
st,
))
cfg, err := state.CombineDeployConfig(intent, intent.Chains[0], st, st.Chains[0])
require.NoError(t, err)
require.Equal(t, uint64(3), cfg.L2InitializationConfig.L2CoreDeployConfig.L2BlockTime, "L2 block time should be 3 seconds")
} }
func validateSuperchainDeployment(t *testing.T, st *state.State, cg codeGetter) { func TestApplyGenesisStrategy(t *testing.T) {
addrs := []struct { op_e2e.InitParallel(t)
name string
addr common.Address ctx, cancel := context.WithCancel(context.Background())
}{ defer cancel()
{"SuperchainProxyAdmin", st.SuperchainDeployment.ProxyAdminAddress},
{"SuperchainConfigProxy", st.SuperchainDeployment.SuperchainConfigProxyAddress}, env, bundle, intent, st := setupGenesisChain(t)
{"SuperchainConfigImpl", st.SuperchainDeployment.SuperchainConfigImplAddress}, intent.DeploymentStrategy = state.DeploymentStrategyGenesis
{"ProtocolVersionsProxy", st.SuperchainDeployment.ProtocolVersionsProxyAddress},
{"ProtocolVersionsImpl", st.SuperchainDeployment.ProtocolVersionsImplAddress}, require.NoError(t, deployer.ApplyPipeline(
{"OpcmProxy", st.ImplementationsDeployment.OpcmProxyAddress}, ctx,
{"PreimageOracleSingleton", st.ImplementationsDeployment.PreimageOracleSingletonAddress}, env,
{"MipsSingleton", st.ImplementationsDeployment.MipsSingletonAddress}, bundle,
} intent,
for _, addr := range addrs { st,
t.Run(addr.name, func(t *testing.T) { ))
code := cg(t, addr.addr)
require.NotEmpty(t, code, "contract %s at %s has no code", addr.name, addr.addr) cg := stateDumpCodeGetter(st)
validateSuperchainDeployment(t, st, cg)
for i := range intent.Chains {
t.Run(fmt.Sprintf("chain-%d", i), func(t *testing.T) {
validateOPChainDeployment(t, cg, st, intent)
}) })
} }
} }
func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, intent *state.Intent) { func TestProofParamOverrides(t *testing.T) {
// Validate that the implementation addresses are always set, even in subsequent deployments op_e2e.InitParallel(t)
// that pull from an existing OPCM deployment.
implAddrs := []struct {
name string
addr common.Address
}{
{"DelayedWETHImplAddress", st.ImplementationsDeployment.DelayedWETHImplAddress},
{"OptimismPortalImplAddress", st.ImplementationsDeployment.OptimismPortalImplAddress},
{"SystemConfigImplAddress", st.ImplementationsDeployment.SystemConfigImplAddress},
{"L1CrossDomainMessengerImplAddress", st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress},
{"L1ERC721BridgeImplAddress", st.ImplementationsDeployment.L1ERC721BridgeImplAddress},
{"L1StandardBridgeImplAddress", st.ImplementationsDeployment.L1StandardBridgeImplAddress},
{"OptimismMintableERC20FactoryImplAddress", st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress},
{"DisputeGameFactoryImplAddress", st.ImplementationsDeployment.DisputeGameFactoryImplAddress},
{"MipsSingletonAddress", st.ImplementationsDeployment.MipsSingletonAddress},
{"PreimageOracleSingletonAddress", st.ImplementationsDeployment.PreimageOracleSingletonAddress},
}
for _, addr := range implAddrs {
require.NotEmpty(t, addr.addr, "%s should be set", addr.name)
code := cg(t, addr.addr)
require.NotEmpty(t, code, "contract %s at %s has no code", addr.name, addr.addr)
}
for i, chainState := range st.Chains { ctx, cancel := context.WithCancel(context.Background())
chainAddrs := []struct { defer cancel()
name string
addr common.Address
}{
{"ProxyAdminAddress", chainState.ProxyAdminAddress},
{"AddressManagerAddress", chainState.AddressManagerAddress},
{"L1ERC721BridgeProxyAddress", chainState.L1ERC721BridgeProxyAddress},
{"SystemConfigProxyAddress", chainState.SystemConfigProxyAddress},
{"OptimismMintableERC20FactoryProxyAddress", chainState.OptimismMintableERC20FactoryProxyAddress},
{"L1StandardBridgeProxyAddress", chainState.L1StandardBridgeProxyAddress},
{"L1CrossDomainMessengerProxyAddress", chainState.L1CrossDomainMessengerProxyAddress},
{"OptimismPortalProxyAddress", chainState.OptimismPortalProxyAddress},
{"DisputeGameFactoryProxyAddress", chainState.DisputeGameFactoryProxyAddress},
{"AnchorStateRegistryProxyAddress", chainState.AnchorStateRegistryProxyAddress},
{"FaultDisputeGameAddress", chainState.FaultDisputeGameAddress},
{"PermissionedDisputeGameAddress", chainState.PermissionedDisputeGameAddress},
{"DelayedWETHPermissionedGameProxyAddress", chainState.DelayedWETHPermissionedGameProxyAddress},
// {"DelayedWETHPermissionlessGameProxyAddress", chainState.DelayedWETHPermissionlessGameProxyAddress},
}
for _, addr := range chainAddrs {
// TODO Delete this `if`` block once FaultDisputeGameAddress is deployed.
if addr.name == "FaultDisputeGameAddress" {
continue
}
code := cg(t, addr.addr)
require.NotEmpty(t, code, "contract %s at %s for chain %s has no code", addr.name, addr.addr, chainState.ID)
}
alloc := chainState.Allocs.Data.Accounts
chainIntent := intent.Chains[i]
checkImmutableBehindProxy(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient)
checkImmutableBehindProxy(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient)
checkImmutableBehindProxy(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient)
checkImmutableBehindProxy(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID)))
// ownership slots
var addrAsSlot common.Hash
addrAsSlot.SetBytes(chainIntent.Roles.L1ProxyAdminOwner.Bytes())
// slot 0
ownerSlot := common.Hash{}
checkStorageSlot(t, alloc, predeploys.ProxyAdminAddr, ownerSlot, addrAsSlot)
var defaultGovOwner common.Hash
defaultGovOwner.SetBytes(common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAdDEad").Bytes())
checkStorageSlot(t, alloc, predeploys.GovernanceTokenAddr, common.Hash{31: 0x0a}, defaultGovOwner)
require.Equal(t, int(chainIntent.Eip1559Denominator), 50, "EIP1559Denominator should be set") env, bundle, intent, st := setupGenesisChain(t)
require.Equal(t, int(chainIntent.Eip1559Elasticity), 6, "EIP1559Elasticity should be set") intent.GlobalDeployOverrides = map[string]any{
"withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1,
"minProposalSizeBytes": standard.MinProposalSizeBytes + 1,
"challengePeriodSeconds": standard.ChallengePeriodSeconds + 1,
"proofMaturityDelaySeconds": standard.ProofMaturityDelaySeconds + 1,
"disputeGameFinalityDelaySeconds": standard.DisputeGameFinalityDelaySeconds + 1,
"mipsVersion": standard.MIPSVersion + 1,
"disputeGameType": standard.DisputeGameType, // This must be set to the permissioned game
"disputeAbsolutePrestate": common.Hash{'A', 'B', 'S', 'O', 'L', 'U', 'T', 'E'},
"disputeMaxGameDepth": standard.DisputeMaxGameDepth + 1,
"disputeSplitDepth": standard.DisputeSplitDepth + 1,
"disputeClockExtension": standard.DisputeClockExtension + 1,
"disputeMaxClockDuration": standard.DisputeMaxClockDuration + 1,
"dangerouslyAllowCustomDisputeParameters": true,
} }
}
func getEIP1967ImplementationAddress(t *testing.T, allocations types.GenesisAlloc, proxyAddress common.Address) common.Address {
storage := allocations[proxyAddress].Storage
storageValue := storage[genesis.ImplementationSlot]
require.NotEmpty(t, storageValue, "Implementation address for %s should be set", proxyAddress)
return common.HexToAddress(storageValue.Hex())
}
type bytesMarshaler interface {
Bytes() []byte
}
func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) { require.NoError(t, deployer.ApplyPipeline(
implementationAddress := getEIP1967ImplementationAddress(t, allocations, proxyContract) ctx,
checkImmutable(t, allocations, implementationAddress, thing) env,
} bundle,
intent,
st,
))
func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) { allocs := st.L1StateDump.Data.Accounts
account, ok := allocations[implementationAddress] chainState := st.Chains[0]
require.True(t, ok, "%s not found in allocations", implementationAddress)
require.NotEmpty(t, account.Code, "%s should have code", implementationAddress)
require.True(
t,
bytes.Contains(account.Code, thing.Bytes()),
"%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes()),
)
}
func checkStorageSlot(t *testing.T, allocs types.GenesisAlloc, address common.Address, slot common.Hash, expected common.Hash) { uint64Caster := func(t *testing.T, val any) common.Hash {
account, ok := allocs[address] return common.BigToHash(new(big.Int).SetUint64(val.(uint64)))
require.True(t, ok, "account not found for address %s", address)
value, ok := account.Storage[slot]
if expected == (common.Hash{}) {
require.False(t, ok, "slot %s for account %s should not be set", slot, address)
return
} }
require.True(t, ok, "slot %s not found for account %s", slot, address)
require.Equal(t, expected, value, "slot %s for account %s should be %s", slot, address, expected)
}
func TestApplyExistingOPCM(t *testing.T) {
anvil.Test(t)
forkRPCUrl := os.Getenv("SEPOLIA_RPC_URL") tests := []struct {
if forkRPCUrl == "" { name string
t.Skip("no fork RPC URL provided") caster func(t *testing.T, val any) common.Hash
address common.Address
}{
{
"withdrawalDelaySeconds",
uint64Caster,
st.ImplementationsDeployment.DelayedWETHImplAddress,
},
{
"minProposalSizeBytes",
uint64Caster,
st.ImplementationsDeployment.PreimageOracleSingletonAddress,
},
{
"challengePeriodSeconds",
uint64Caster,
st.ImplementationsDeployment.PreimageOracleSingletonAddress,
},
{
"proofMaturityDelaySeconds",
uint64Caster,
st.ImplementationsDeployment.OptimismPortalImplAddress,
},
{
"disputeGameFinalityDelaySeconds",
uint64Caster,
st.ImplementationsDeployment.OptimismPortalImplAddress,
},
{
"disputeAbsolutePrestate",
func(t *testing.T, val any) common.Hash {
return val.(common.Hash)
},
chainState.PermissionedDisputeGameAddress,
},
{
"disputeMaxGameDepth",
uint64Caster,
chainState.PermissionedDisputeGameAddress,
},
{
"disputeSplitDepth",
uint64Caster,
chainState.PermissionedDisputeGameAddress,
},
{
"disputeClockExtension",
uint64Caster,
chainState.PermissionedDisputeGameAddress,
},
{
"disputeMaxClockDuration",
uint64Caster,
chainState.PermissionedDisputeGameAddress,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name]))
})
} }
}
lgr := testlog.Logger(t, slog.LevelDebug) func TestInteropDeployment(t *testing.T) {
op_e2e.InitParallel(t)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
runner, err := anvil.New( env, bundle, intent, st := setupGenesisChain(t)
forkRPCUrl, intent.UseInterop = true
lgr,
)
require.NoError(t, err)
require.NoError(t, runner.Start(ctx))
t.Cleanup(func() {
require.NoError(t, runner.Stop())
})
l1Client, err := ethclient.Dial(runner.RPCUrl())
require.NoError(t, err)
l1ChainID := big.NewInt(11155111)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
// index 0 from Anvil's test set
priv, err := crypto.HexToECDSA("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
require.NoError(t, err)
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(priv, l1ChainID))
deployerAddr := common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
l2ChainID := uint256.NewInt(1)
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{
Logger: lgr,
ChainID: l1ChainID,
Client: l1Client,
Signer: signer,
From: deployerAddr,
})
require.NoError(t, err)
env, bundle, _ := createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr)
intent, st := newIntent(
t,
l1ChainID,
dk,
l2ChainID,
opcm.DefaultL1ContractsLocator,
opcm.DefaultL2ContractsLocator,
)
require.NoError(t, deployer.ApplyPipeline( require.NoError(t, deployer.ApplyPipeline(
ctx, ctx,
...@@ -495,253 +396,98 @@ func TestApplyExistingOPCM(t *testing.T) { ...@@ -495,253 +396,98 @@ func TestApplyExistingOPCM(t *testing.T) {
st, st,
)) ))
validateOPChainDeployment(t, ethClientCodeGetter(ctx, l1Client), st, intent) chainState := st.Chains[0]
depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c")
checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot)
proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.L1ProxyAdminOwner.Bytes())
checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash)
} }
func TestL2BlockTimeOverride(t *testing.T) { func TestInvalidL2Genesis(t *testing.T) {
op_e2e.InitParallel(t) op_e2e.InitParallel(t)
kurtosisutil.Test(t)
lgr := testlog.Logger(t, slog.LevelDebug) lgr := testlog.Logger(t, slog.LevelDebug)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
enclaveCtx := kurtosisutil.StartEnclave(t, ctx, lgr, "github.com/ethpandaops/ethereum-package", TestParams)
service, err := enclaveCtx.GetServiceContext("el-1-geth-lighthouse")
require.NoError(t, err)
ip := service.GetMaybePublicIPAddress()
ports := service.GetPublicPorts()
rpcURL := fmt.Sprintf("http://%s:%d", ip, ports["rpc"].GetNumber())
l1Client, err := ethclient.Dial(rpcURL)
require.NoError(t, err)
depKey := new(deployerKey) depKey := new(deployerKey)
l1ChainID := big.NewInt(77799777) l1ChainID := big.NewInt(77799777)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err) require.NoError(t, err)
pk, err := dk.Secret(depKey)
require.NoError(t, err)
signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(pk, l1ChainID))
l2ChainID := uint256.NewInt(1) l2ChainID1 := uint256.NewInt(1)
deployerAddr, err := dk.Address(depKey) deployerAddr, err := dk.Address(depKey)
require.NoError(t, err) require.NoError(t, err)
loc := localArtifactsLocator(t) loc := localArtifactsLocator(t)
bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ // these tests were generated by grepping all usages of the deploy
Logger: lgr, // config in L2Genesis.s.sol.
ChainID: l1ChainID,
Client: l1Client,
Signer: signer,
From: deployerAddr,
})
require.NoError(t, err)
env, bundle, _ := createEnv(t, ctx, lgr, l1Client, bcaster, deployerAddr)
intent, st := newIntent(
t,
l1ChainID,
dk,
l2ChainID,
loc,
loc,
)
intent.GlobalDeployOverrides = map[string]interface{}{
"l2BlockTime": float64(3),
}
require.NoError(t, deployer.ApplyPipeline(
ctx,
env,
bundle,
intent,
st,
))
chainIntent, err := intent.Chain(l2ChainID.Bytes32())
require.NoError(t, err)
chainState, err := st.Chain(l2ChainID.Bytes32())
require.NoError(t, err)
cfg, err := state.CombineDeployConfig(intent, chainIntent, st, chainState)
require.NoError(t, err)
require.Equal(t, uint64(3), cfg.L2InitializationConfig.L2CoreDeployConfig.L2BlockTime, "L2 block time should be 3 seconds")
}
func TestApplyGenesisStrategy(t *testing.T) {
op_e2e.InitParallel(t)
lgr := testlog.Logger(t, slog.LevelDebug)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
depKey := new(deployerKey)
l1ChainID := big.NewInt(77799777)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
l2ChainID1 := uint256.NewInt(1)
l2ChainID2 := uint256.NewInt(2)
deployerAddr, err := dk.Address(depKey)
require.NoError(t, err)
loc := localArtifactsLocator(t)
env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID2))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis
require.NoError(t, deployer.ApplyPipeline(
ctx,
env,
bundle,
intent,
st,
))
cg := stateDumpCodeGetter(st)
validateSuperchainDeployment(t, st, cg)
for i := range intent.Chains {
t.Run(fmt.Sprintf("chain-%d", i), func(t *testing.T) {
validateOPChainDeployment(t, cg, st, intent)
})
}
}
func TestProofParamOverrides(t *testing.T) {
op_e2e.InitParallel(t)
lgr := testlog.Logger(t, slog.LevelDebug)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
depKey := new(deployerKey)
l1ChainID := big.NewInt(77799777)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
l2ChainID1 := uint256.NewInt(1)
deployerAddr, err := dk.Address(depKey)
require.NoError(t, err)
loc := localArtifactsLocator(t)
env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis
intent.GlobalDeployOverrides = map[string]any{
"withdrawalDelaySeconds": standard.WithdrawalDelaySeconds + 1,
"minProposalSizeBytes": standard.MinProposalSizeBytes + 1,
"challengePeriodSeconds": standard.ChallengePeriodSeconds + 1,
"proofMaturityDelaySeconds": standard.ProofMaturityDelaySeconds + 1,
"disputeGameFinalityDelaySeconds": standard.DisputeGameFinalityDelaySeconds + 1,
"mipsVersion": standard.MIPSVersion + 1,
"disputeGameType": standard.DisputeGameType, // This must be set to the permissioned game
"disputeAbsolutePrestate": common.Hash{'A', 'B', 'S', 'O', 'L', 'U', 'T', 'E'},
"disputeMaxGameDepth": standard.DisputeMaxGameDepth + 1,
"disputeSplitDepth": standard.DisputeSplitDepth + 1,
"disputeClockExtension": standard.DisputeClockExtension + 1,
"disputeMaxClockDuration": standard.DisputeMaxClockDuration + 1,
"dangerouslyAllowCustomDisputeParameters": true,
}
require.NoError(t, deployer.ApplyPipeline(
ctx,
env,
bundle,
intent,
st,
))
allocs := st.L1StateDump.Data.Accounts
chainState := st.Chains[0]
uint64Caster := func(t *testing.T, val any) common.Hash {
return common.BigToHash(new(big.Int).SetUint64(val.(uint64)))
}
tests := []struct { tests := []struct {
name string name string
caster func(t *testing.T, val any) common.Hash overrides map[string]any
address common.Address
}{ }{
{ {
"withdrawalDelaySeconds", name: "L2 proxy admin owner not set",
uint64Caster, overrides: map[string]any{
st.ImplementationsDeployment.DelayedWETHImplAddress, "proxyAdminOwner": nil,
}, },
{
"minProposalSizeBytes",
uint64Caster,
st.ImplementationsDeployment.PreimageOracleSingletonAddress,
},
{
"challengePeriodSeconds",
uint64Caster,
st.ImplementationsDeployment.PreimageOracleSingletonAddress,
},
{
"proofMaturityDelaySeconds",
uint64Caster,
st.ImplementationsDeployment.OptimismPortalImplAddress,
},
{
"disputeGameFinalityDelaySeconds",
uint64Caster,
st.ImplementationsDeployment.OptimismPortalImplAddress,
}, },
{ {
"disputeAbsolutePrestate", name: "base fee vault recipient not set",
func(t *testing.T, val any) common.Hash { overrides: map[string]any{
return val.(common.Hash) "baseFeeVaultRecipient": nil,
}, },
chainState.PermissionedDisputeGameAddress,
}, },
{ {
"disputeMaxGameDepth", name: "l1 fee vault recipient not set",
uint64Caster, overrides: map[string]any{
chainState.PermissionedDisputeGameAddress, "l1FeeVaultRecipient": nil,
},
}, },
{ {
"disputeSplitDepth", name: "sequencer fee vault recipient not set",
uint64Caster, overrides: map[string]any{
chainState.PermissionedDisputeGameAddress, "sequencerFeeVaultRecipient": nil,
},
}, },
{ {
"disputeClockExtension", name: "l1 chain ID not set",
uint64Caster, overrides: map[string]any{
chainState.PermissionedDisputeGameAddress, "l1ChainID": nil,
},
}, },
{ {
"disputeMaxClockDuration", name: "l2 chain ID not set",
uint64Caster, overrides: map[string]any{
chainState.PermissionedDisputeGameAddress, "l2ChainID": nil,
},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
checkImmutable(t, allocs, tt.address, tt.caster(t, intent.GlobalDeployOverrides[tt.name])) env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis
intent.GlobalDeployOverrides = tt.overrides
err := deployer.ApplyPipeline(
ctx,
env,
bundle,
intent,
st,
)
require.Error(t, err)
require.ErrorContains(t, err, "failed to combine L2 init config")
}) })
} }
} }
func TestInteropDeployment(t *testing.T) { func setupGenesisChain(t *testing.T) (*pipeline.Env, pipeline.ArtifactsBundle, *state.Intent, *state.State) {
op_e2e.InitParallel(t)
lgr := testlog.Logger(t, slog.LevelDebug) lgr := testlog.Logger(t, slog.LevelDebug)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
...@@ -763,104 +509,278 @@ func TestInteropDeployment(t *testing.T) { ...@@ -763,104 +509,278 @@ func TestInteropDeployment(t *testing.T) {
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc) intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1)) intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis intent.DeploymentStrategy = state.DeploymentStrategyGenesis
intent.UseInterop = true return env, bundle, intent, st
require.NoError(t, deployer.ApplyPipeline(
ctx,
env,
bundle,
intent,
st,
))
chainState := st.Chains[0]
depManagerSlot := common.HexToHash("0x1708e077affb93e89be2665fb0fb72581be66f84dc00d25fed755ae911905b1c")
checkImmutable(t, st.L1StateDump.Data.Accounts, st.ImplementationsDeployment.SystemConfigImplAddress, depManagerSlot)
proxyAdminOwnerHash := common.BytesToHash(intent.Chains[0].Roles.L1ProxyAdminOwner.Bytes())
checkStorageSlot(t, st.L1StateDump.Data.Accounts, chainState.SystemConfigProxyAddress, depManagerSlot, proxyAdminOwnerHash)
} }
func TestInvalidL2Genesis(t *testing.T) { func localArtifactsLocator(t *testing.T) *opcm.ArtifactsLocator {
op_e2e.InitParallel(t) _, testFilename, _, ok := runtime.Caller(0)
require.Truef(t, ok, "failed to get test filename")
lgr := testlog.Logger(t, slog.LevelDebug) monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..")
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")
ctx, cancel := context.WithCancel(context.Background()) artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
defer cancel()
depKey := new(deployerKey)
l1ChainID := big.NewInt(77799777)
dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err) require.NoError(t, err)
loc := &opcm.ArtifactsLocator{
l2ChainID1 := uint256.NewInt(1) URL: artifactsURL,
}
deployerAddr, err := dk.Address(depKey) return loc
}
func createEnv(
t *testing.T,
ctx context.Context,
lgr log.Logger,
l1Client *ethclient.Client,
bcaster broadcaster.Broadcaster,
deployerAddr common.Address,
) (*pipeline.Env, pipeline.ArtifactsBundle, *script.Host) {
_, testFilename, _, ok := runtime.Caller(0)
require.Truef(t, ok, "failed to get test filename")
monorepoDir := path.Join(path.Dir(testFilename), "..", "..", "..", "..")
artifactsDir := path.Join(monorepoDir, "packages", "contracts-bedrock", "forge-artifacts")
artifactsURL, err := url.Parse(fmt.Sprintf("file://%s", artifactsDir))
require.NoError(t, err) require.NoError(t, err)
artifactsLocator := &opcm.ArtifactsLocator{
URL: artifactsURL,
}
loc := localArtifactsLocator(t) artifactsFS, cleanupArtifacts, err := pipeline.DownloadArtifacts(ctx, artifactsLocator, pipeline.NoopDownloadProgressor)
require.NoError(t, err)
defer func() {
require.NoError(t, cleanupArtifacts())
}()
// these tests were generated by grepping all usages of the deploy host, err := pipeline.DefaultScriptHost(
// config in L2Genesis.s.sol. bcaster,
tests := []struct { lgr,
name string deployerAddr,
overrides map[string]any artifactsFS,
}{ 0,
{ )
name: "L2 proxy admin owner not set", require.NoError(t, err)
overrides: map[string]any{
"proxyAdminOwner": nil, env := &pipeline.Env{
}, StateWriter: pipeline.NoopStateWriter(),
}, L1ScriptHost: host,
{ L1Client: l1Client,
name: "base fee vault recipient not set", Broadcaster: bcaster,
overrides: map[string]any{ Deployer: deployerAddr,
"baseFeeVaultRecipient": nil, Logger: lgr,
}, }
},
{ bundle := pipeline.ArtifactsBundle{
name: "l1 fee vault recipient not set", L1: artifactsFS,
overrides: map[string]any{ L2: artifactsFS,
"l1FeeVaultRecipient": nil, }
},
}, return env, bundle, host
{ }
name: "sequencer fee vault recipient not set",
overrides: map[string]any{ func addrFor(t *testing.T, dk *devkeys.MnemonicDevKeys, key devkeys.Key) common.Address {
"sequencerFeeVaultRecipient": nil, addr, err := dk.Address(key)
}, require.NoError(t, err)
return addr
}
func newIntent(
t *testing.T,
l1ChainID *big.Int,
dk *devkeys.MnemonicDevKeys,
l2ChainID *uint256.Int,
l1Loc *opcm.ArtifactsLocator,
l2Loc *opcm.ArtifactsLocator,
) (*state.Intent, *state.State) {
intent := &state.Intent{
DeploymentStrategy: state.DeploymentStrategyLive,
L1ChainID: l1ChainID.Uint64(),
SuperchainRoles: &state.SuperchainRoles{
ProxyAdminOwner: addrFor(t, dk, devkeys.L1ProxyAdminOwnerRole.Key(l1ChainID)),
ProtocolVersionsOwner: addrFor(t, dk, devkeys.SuperchainDeployerKey.Key(l1ChainID)),
Guardian: addrFor(t, dk, devkeys.SuperchainConfigGuardianKey.Key(l1ChainID)),
}, },
{ FundDevAccounts: true,
name: "l1 chain ID not set", L1ContractsLocator: l1Loc,
overrides: map[string]any{ L2ContractsLocator: l2Loc,
"l1ChainID": nil, Chains: []*state.ChainIntent{
}, newChainIntent(t, dk, l1ChainID, l2ChainID),
}, },
{ }
name: "l2 chain ID not set", st := &state.State{
overrides: map[string]any{ Version: 1,
"l2ChainID": nil, }
}, return intent, st
}
func newChainIntent(t *testing.T, dk *devkeys.MnemonicDevKeys, l1ChainID *big.Int, l2ChainID *uint256.Int) *state.ChainIntent {
return &state.ChainIntent{
ID: l2ChainID.Bytes32(),
BaseFeeVaultRecipient: addrFor(t, dk, devkeys.BaseFeeVaultRecipientRole.Key(l1ChainID)),
L1FeeVaultRecipient: addrFor(t, dk, devkeys.L1FeeVaultRecipientRole.Key(l1ChainID)),
SequencerFeeVaultRecipient: addrFor(t, dk, devkeys.SequencerFeeVaultRecipientRole.Key(l1ChainID)),
Eip1559Denominator: 50,
Eip1559Elasticity: 6,
Roles: state.ChainRoles{
L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)),
L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)),
SystemConfigOwner: addrFor(t, dk, devkeys.SystemConfigOwner.Key(l1ChainID)),
UnsafeBlockSigner: addrFor(t, dk, devkeys.SequencerP2PRole.Key(l1ChainID)),
Batcher: addrFor(t, dk, devkeys.BatcherRole.Key(l1ChainID)),
Proposer: addrFor(t, dk, devkeys.ProposerRole.Key(l1ChainID)),
Challenger: addrFor(t, dk, devkeys.ChallengerRole.Key(l1ChainID)),
}, },
} }
for _, tt := range tests { }
t.Run(tt.name, func(t *testing.T) {
env, bundle, _ := createEnv(t, ctx, lgr, nil, broadcaster.NoopBroadcaster(), deployerAddr)
intent, st := newIntent(t, l1ChainID, dk, l2ChainID1, loc, loc)
intent.Chains = append(intent.Chains, newChainIntent(t, dk, l1ChainID, l2ChainID1))
intent.DeploymentStrategy = state.DeploymentStrategyGenesis
intent.GlobalDeployOverrides = tt.overrides
err := deployer.ApplyPipeline( type codeGetter func(t *testing.T, addr common.Address) []byte
ctx,
env, func ethClientCodeGetter(ctx context.Context, client *ethclient.Client) codeGetter {
bundle, return func(t *testing.T, addr common.Address) []byte {
intent, code, err := client.CodeAt(ctx, addr, nil)
st, require.NoError(t, err)
) return code
require.Error(t, err) }
require.ErrorContains(t, err, "failed to combine L2 init config") }
func stateDumpCodeGetter(st *state.State) codeGetter {
return func(t *testing.T, addr common.Address) []byte {
acc, ok := st.L1StateDump.Data.Accounts[addr]
require.True(t, ok, "no account found for address %s", addr)
return acc.Code
}
}
func validateSuperchainDeployment(t *testing.T, st *state.State, cg codeGetter) {
addrs := []struct {
name string
addr common.Address
}{
{"SuperchainProxyAdmin", st.SuperchainDeployment.ProxyAdminAddress},
{"SuperchainConfigProxy", st.SuperchainDeployment.SuperchainConfigProxyAddress},
{"SuperchainConfigImpl", st.SuperchainDeployment.SuperchainConfigImplAddress},
{"ProtocolVersionsProxy", st.SuperchainDeployment.ProtocolVersionsProxyAddress},
{"ProtocolVersionsImpl", st.SuperchainDeployment.ProtocolVersionsImplAddress},
{"OpcmProxy", st.ImplementationsDeployment.OpcmProxyAddress},
{"PreimageOracleSingleton", st.ImplementationsDeployment.PreimageOracleSingletonAddress},
{"MipsSingleton", st.ImplementationsDeployment.MipsSingletonAddress},
}
for _, addr := range addrs {
t.Run(addr.name, func(t *testing.T) {
code := cg(t, addr.addr)
require.NotEmpty(t, code, "contract %s at %s has no code", addr.name, addr.addr)
}) })
} }
} }
func validateOPChainDeployment(t *testing.T, cg codeGetter, st *state.State, intent *state.Intent) {
// Validate that the implementation addresses are always set, even in subsequent deployments
// that pull from an existing OPCM deployment.
implAddrs := []struct {
name string
addr common.Address
}{
{"DelayedWETHImplAddress", st.ImplementationsDeployment.DelayedWETHImplAddress},
{"OptimismPortalImplAddress", st.ImplementationsDeployment.OptimismPortalImplAddress},
{"SystemConfigImplAddress", st.ImplementationsDeployment.SystemConfigImplAddress},
{"L1CrossDomainMessengerImplAddress", st.ImplementationsDeployment.L1CrossDomainMessengerImplAddress},
{"L1ERC721BridgeImplAddress", st.ImplementationsDeployment.L1ERC721BridgeImplAddress},
{"L1StandardBridgeImplAddress", st.ImplementationsDeployment.L1StandardBridgeImplAddress},
{"OptimismMintableERC20FactoryImplAddress", st.ImplementationsDeployment.OptimismMintableERC20FactoryImplAddress},
{"DisputeGameFactoryImplAddress", st.ImplementationsDeployment.DisputeGameFactoryImplAddress},
{"MipsSingletonAddress", st.ImplementationsDeployment.MipsSingletonAddress},
{"PreimageOracleSingletonAddress", st.ImplementationsDeployment.PreimageOracleSingletonAddress},
}
for _, addr := range implAddrs {
require.NotEmpty(t, addr.addr, "%s should be set", addr.name)
code := cg(t, addr.addr)
require.NotEmpty(t, code, "contract %s at %s has no code", addr.name, addr.addr)
}
for i, chainState := range st.Chains {
chainAddrs := []struct {
name string
addr common.Address
}{
{"ProxyAdminAddress", chainState.ProxyAdminAddress},
{"AddressManagerAddress", chainState.AddressManagerAddress},
{"L1ERC721BridgeProxyAddress", chainState.L1ERC721BridgeProxyAddress},
{"SystemConfigProxyAddress", chainState.SystemConfigProxyAddress},
{"OptimismMintableERC20FactoryProxyAddress", chainState.OptimismMintableERC20FactoryProxyAddress},
{"L1StandardBridgeProxyAddress", chainState.L1StandardBridgeProxyAddress},
{"L1CrossDomainMessengerProxyAddress", chainState.L1CrossDomainMessengerProxyAddress},
{"OptimismPortalProxyAddress", chainState.OptimismPortalProxyAddress},
{"DisputeGameFactoryProxyAddress", chainState.DisputeGameFactoryProxyAddress},
{"AnchorStateRegistryProxyAddress", chainState.AnchorStateRegistryProxyAddress},
{"FaultDisputeGameAddress", chainState.FaultDisputeGameAddress},
{"PermissionedDisputeGameAddress", chainState.PermissionedDisputeGameAddress},
{"DelayedWETHPermissionedGameProxyAddress", chainState.DelayedWETHPermissionedGameProxyAddress},
// {"DelayedWETHPermissionlessGameProxyAddress", chainState.DelayedWETHPermissionlessGameProxyAddress},
}
for _, addr := range chainAddrs {
// TODO Delete this `if`` block once FaultDisputeGameAddress is deployed.
if addr.name == "FaultDisputeGameAddress" {
continue
}
code := cg(t, addr.addr)
require.NotEmpty(t, code, "contract %s at %s for chain %s has no code", addr.name, addr.addr, chainState.ID)
}
alloc := chainState.Allocs.Data.Accounts
chainIntent := intent.Chains[i]
checkImmutableBehindProxy(t, alloc, predeploys.BaseFeeVaultAddr, chainIntent.BaseFeeVaultRecipient)
checkImmutableBehindProxy(t, alloc, predeploys.L1FeeVaultAddr, chainIntent.L1FeeVaultRecipient)
checkImmutableBehindProxy(t, alloc, predeploys.SequencerFeeVaultAddr, chainIntent.SequencerFeeVaultRecipient)
checkImmutableBehindProxy(t, alloc, predeploys.OptimismMintableERC721FactoryAddr, common.BigToHash(new(big.Int).SetUint64(intent.L1ChainID)))
// ownership slots
var addrAsSlot common.Hash
addrAsSlot.SetBytes(chainIntent.Roles.L1ProxyAdminOwner.Bytes())
// slot 0
ownerSlot := common.Hash{}
checkStorageSlot(t, alloc, predeploys.ProxyAdminAddr, ownerSlot, addrAsSlot)
var defaultGovOwner common.Hash
defaultGovOwner.SetBytes(common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAdDEad").Bytes())
checkStorageSlot(t, alloc, predeploys.GovernanceTokenAddr, common.Hash{31: 0x0a}, defaultGovOwner)
require.Equal(t, int(chainIntent.Eip1559Denominator), 50, "EIP1559Denominator should be set")
require.Equal(t, int(chainIntent.Eip1559Elasticity), 6, "EIP1559Elasticity should be set")
}
}
func getEIP1967ImplementationAddress(t *testing.T, allocations types.GenesisAlloc, proxyAddress common.Address) common.Address {
storage := allocations[proxyAddress].Storage
storageValue := storage[genesis.ImplementationSlot]
require.NotEmpty(t, storageValue, "Implementation address for %s should be set", proxyAddress)
return common.HexToAddress(storageValue.Hex())
}
type bytesMarshaler interface {
Bytes() []byte
}
func checkImmutableBehindProxy(t *testing.T, allocations types.GenesisAlloc, proxyContract common.Address, thing bytesMarshaler) {
implementationAddress := getEIP1967ImplementationAddress(t, allocations, proxyContract)
checkImmutable(t, allocations, implementationAddress, thing)
}
func checkImmutable(t *testing.T, allocations types.GenesisAlloc, implementationAddress common.Address, thing bytesMarshaler) {
account, ok := allocations[implementationAddress]
require.True(t, ok, "%s not found in allocations", implementationAddress)
require.NotEmpty(t, account.Code, "%s should have code", implementationAddress)
require.True(
t,
bytes.Contains(account.Code, thing.Bytes()),
"%s code should contain %s immutable", implementationAddress, hex.EncodeToString(thing.Bytes()),
)
}
func checkStorageSlot(t *testing.T, allocs types.GenesisAlloc, address common.Address, slot common.Hash, expected common.Hash) {
account, ok := allocs[address]
require.True(t, ok, "account not found for address %s", address)
value, ok := account.Storage[slot]
if expected == (common.Hash{}) {
require.False(t, ok, "slot %s for account %s should not be set", slot, address)
return
}
require.True(t, ok, "slot %s not found for account %s", slot, address)
require.Equal(t, expected, value, "slot %s for account %s should be %s", slot, address, expected)
}
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