diff --git a/go.mod b/go.mod index 02a428b0adad3..01cea5e00bab8 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/docker/docker v20.10.21+incompatible github.com/docker/go-connections v0.4.0 github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 - github.com/ethereum/go-ethereum v1.11.4 + github.com/ethereum/go-ethereum v1.11.5 github.com/fsnotify/fsnotify v1.6.0 github.com/golang/snappy v0.0.4 github.com/google/go-cmp v0.5.9 @@ -189,6 +189,6 @@ require ( nhooyr.io/websocket v1.8.7 // indirect ) -replace github.com/ethereum/go-ethereum v1.11.4 => github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230321002540-11f0554a4313 +replace github.com/ethereum/go-ethereum v1.11.5 => github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230324105532-555b76f39878 -//replace github.com/ethereum/go-ethereum v1.11.4 => ../go-ethereum +//replace github.com/ethereum/go-ethereum v1.11.5 => ../go-ethereum diff --git a/go.sum b/go.sum index b01f9061f0a0c..8b4ea79a580f9 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= -github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230321002540-11f0554a4313 h1:dBPc4CEzqmHUeU/Awk7Lw2mAaTc59T5W8CvAr+4YuzU= -github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230321002540-11f0554a4313/go.mod h1:SGLXBOtu2JlKrNoUG76EatI2uJX/WZRY4nmEyvE9Q38= +github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230324105532-555b76f39878 h1:pk3lFrP6zay7+jT+yoFAWxvGbP1Z/5lsorimXGrQoxE= +github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230324105532-555b76f39878/go.mod h1:SGLXBOtu2JlKrNoUG76EatI2uJX/WZRY4nmEyvE9Q38= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ= diff --git a/op-chain-ops/genesis/check.go b/op-chain-ops/genesis/check.go index ca8105052a155..75eab7f691878 100644 --- a/op-chain-ops/genesis/check.go +++ b/op-chain-ops/genesis/check.go @@ -16,7 +16,6 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -320,7 +319,7 @@ func PostCheckPredeploys(prevDB, currDB *state.StateDB) error { // PostCheckPredeployStorage will ensure that the predeploys had their storage // wiped correctly. -func PostCheckPredeployStorage(db vm.StateDB, finalSystemOwner common.Address, proxyAdminOwner common.Address) error { +func PostCheckPredeployStorage(db *state.StateDB, finalSystemOwner common.Address, proxyAdminOwner common.Address) error { for name, addr := range predeploys.Predeploys { if addr == nil { return fmt.Errorf("nil address in predeploys mapping for %s", name) @@ -468,7 +467,7 @@ func PostCheckLegacyETH(prevDB, migratedDB *state.StateDB, migrationData crossdo } // PostCheckL1Block checks that the L1Block contract was properly set to the L1 origin. -func PostCheckL1Block(db vm.StateDB, info *derive.L1BlockInfo) error { +func PostCheckL1Block(db *state.StateDB, info *derive.L1BlockInfo) error { // Slot 0 is the concatenation of the block number and timestamp data := db.GetState(predeploys.L1BlockAddr, common.Hash{}).Bytes() blockNumber := binary.BigEndian.Uint64(data[24:]) @@ -558,7 +557,7 @@ func PostCheckL1Block(db vm.StateDB, info *derive.L1BlockInfo) error { return nil } -func CheckWithdrawalsAfter(db vm.StateDB, data crossdomain.MigrationData, l1CrossDomainMessenger *common.Address) error { +func CheckWithdrawalsAfter(db *state.StateDB, data crossdomain.MigrationData, l1CrossDomainMessenger *common.Address) error { wds, invalidMessages, err := data.ToWithdrawals() if err != nil { return err diff --git a/op-e2e/actions/l1_replica.go b/op-e2e/actions/l1_replica.go index 81c21768841e5..d3d18dec54551 100644 --- a/op-e2e/actions/l1_replica.go +++ b/op-e2e/actions/l1_replica.go @@ -72,6 +72,7 @@ func NewL1Replica(t Testing, log log.Logger, genesis *core.Genesis) *L1Replica { backend, err := eth.New(n, ethCfg) require.NoError(t, err) + backend.Merger().FinalizePoS() n.RegisterAPIs(tracers.APIs(backend.APIBackend)) diff --git a/op-e2e/geth.go b/op-e2e/geth.go index 966abafb1f81f..a01b215f2e752 100644 --- a/op-e2e/geth.go +++ b/op-e2e/geth.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -21,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" ) @@ -112,7 +114,6 @@ func initL1Geth(cfg *SystemConfig, genesis *core.Genesis, opts ...GethOption) (* ethConfig := ðconfig.Config{ NetworkId: cfg.DeployConfig.L1ChainID, Genesis: genesis, - Miner: miner.Config{Etherbase: cfg.DeployConfig.CliqueSignerAddress}, } nodeConfig := &node.Config{ Name: "l1-geth", @@ -128,44 +129,107 @@ func initL1Geth(cfg *SystemConfig, genesis *core.Genesis, opts ...GethOption) (* if err != nil { return nil, nil, err } + // Activate merge + l1Eth.Merger().FinalizePoS() - // Clique does not have safe/finalized block info. But we do want to test the usage of that, - // since post-merge L1 has it (incl. Goerli testnet which is already upgraded). So we mock it on top of clique. - l1Node.RegisterLifecycle(&fakeSafeFinalizedL1{ - eth: l1Eth, + // Instead of running a whole beacon node, we run this fake-proof-of-stake sidecar that sequences L1 blocks using the Engine API. + l1Node.RegisterLifecycle(&fakePoS{ + eth: l1Eth, + log: log.Root(), // geth logger is global anyway. Would be nice to replace with a local logger though. + blockTime: cfg.DeployConfig.L1BlockTime, // for testing purposes we make it really fast, otherwise we don't see it finalize in short tests finalizedDistance: 8, safeDistance: 4, + engineAPI: catalyst.NewConsensusAPI(l1Eth), }) return l1Node, l1Eth, nil } -type fakeSafeFinalizedL1 struct { - eth *eth.Ethereum +// fakePoS is a testing-only utility to attach to Geth, +// to build a fake proof-of-stake L1 chain with fixed block time and basic lagging safe/finalized blocks. +type fakePoS struct { + eth *eth.Ethereum + log log.Logger + blockTime uint64 + finalizedDistance uint64 safeDistance uint64 - sub ethereum.Subscription -} -var _ node.Lifecycle = (*fakeSafeFinalizedL1)(nil) + engineAPI *catalyst.ConsensusAPI + sub ethereum.Subscription +} -func (f *fakeSafeFinalizedL1) Start() error { - headChanges := make(chan core.ChainHeadEvent, 10) - headsSub := f.eth.BlockChain().SubscribeChainHeadEvent(headChanges) +func (f *fakePoS) Start() error { f.sub = event.NewSubscription(func(quit <-chan struct{}) error { - defer headsSub.Unsubscribe() + // poll every half a second: enough to catch up with any block time when ticks are missed + t := time.NewTicker(time.Second / 2) for { select { - case head := <-headChanges: - num := head.Block.NumberU64() - if num > f.finalizedDistance { - toFinalize := f.eth.BlockChain().GetHeaderByNumber(num - f.finalizedDistance) - f.eth.BlockChain().SetFinalized(toFinalize) + case now := <-t.C: + chain := f.eth.BlockChain() + head := chain.CurrentBlock() + finalized := chain.CurrentFinalBlock() + if finalized == nil { // fallback to genesis if nothing is finalized + finalized = chain.Genesis().Header() + } + safe := chain.CurrentSafeBlock() + if safe == nil { // fallback to finalized if nothing is safe + safe = finalized + } + if head.Number.Uint64() > f.finalizedDistance { // progress finalized block, if we can + finalized = f.eth.BlockChain().GetHeaderByNumber(head.Number.Uint64() - f.finalizedDistance) + } + if head.Number.Uint64() > f.safeDistance { // progress safe block, if we can + safe = f.eth.BlockChain().GetHeaderByNumber(head.Number.Uint64() - f.safeDistance) + } + // start building the block as soon as we are past the current head time + if head.Time >= uint64(now.Unix()) { + continue + } + res, err := f.engineAPI.ForkchoiceUpdatedV1(engine.ForkchoiceStateV1{ + HeadBlockHash: head.Hash(), + SafeBlockHash: safe.Hash(), + FinalizedBlockHash: finalized.Hash(), + }, &engine.PayloadAttributes{ + Timestamp: head.Time + f.blockTime, + Random: common.Hash{}, + SuggestedFeeRecipient: head.Coinbase, + }) + if err != nil { + f.log.Error("failed to start building L1 block", "err", err) + continue + } + if res.PayloadID == nil { + f.log.Error("failed to start block building", "res", res) + continue + } + // wait with sealing, if we are not behind already + delay := time.Until(time.Unix(int64(head.Time+f.blockTime), 0)) + tim := time.NewTimer(delay) + select { + case <-tim.C: + // no-op + case <-quit: + tim.Stop() + return nil + } + payload, err := f.engineAPI.GetPayloadV1(*res.PayloadID) + if err != nil { + f.log.Error("failed to finish building L1 block", "err", err) + continue + } + if _, err := f.engineAPI.NewPayloadV1(*payload); err != nil { + f.log.Error("failed to insert built L1 block", "err", err) + continue } - if num > f.safeDistance { - toSafe := f.eth.BlockChain().GetHeaderByNumber(num - f.safeDistance) - f.eth.BlockChain().SetSafe(toSafe) + if _, err := f.engineAPI.ForkchoiceUpdatedV1(engine.ForkchoiceStateV1{ + HeadBlockHash: payload.BlockHash, + SafeBlockHash: safe.Hash(), + FinalizedBlockHash: finalized.Hash(), + }, nil); err != nil { + f.log.Error("failed to make built L1 block canonical", "err", err) + continue } case <-quit: return nil @@ -175,7 +239,7 @@ func (f *fakeSafeFinalizedL1) Start() error { return nil } -func (f *fakeSafeFinalizedL1) Stop() error { +func (f *fakePoS) Stop() error { f.sub.Unsubscribe() return nil } diff --git a/op-e2e/setup.go b/op-e2e/setup.go index 015a3e373024f..db7429e205e3f 100644 --- a/op-e2e/setup.go +++ b/op-e2e/setup.go @@ -74,7 +74,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig { L1BlockTime: 2, L1GenesisBlockNonce: 4660, - CliqueSignerAddress: addresses.CliqueSigner, + CliqueSignerAddress: common.Address{}, // op-e2e used to run Clique, but now uses fake Proof of Stake. L1GenesisBlockTimestamp: hexutil.Uint64(time.Now().Unix()), L1GenesisBlockGasLimit: 30_000_000, L1GenesisBlockDifficulty: uint642big(1), @@ -384,11 +384,6 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) { didErrAfterStart = true return nil, err } - err = l1Backend.StartMining(1) - if err != nil { - didErrAfterStart = true - return nil, err - } for name, node := range sys.Nodes { if name == "l1" { continue