diff --git a/go.mod b/go.mod index 76c4dcc81143..441e778fba91 100644 --- a/go.mod +++ b/go.mod @@ -253,7 +253,7 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/ethereum/go-ethereum v1.13.8 => github.com/welkin22/op-geth v1.101308.2-opbnb +replace github.com/ethereum/go-ethereum v1.13.8 => github.com/welkin22/op-geth v1.101308.2-opbnb2 replace github.com/cometbft/cometbft => github.com/bnb-chain/greenfield-cometbft v1.0.0 diff --git a/go.sum b/go.sum index 49dd8637d7ac..d61d374a9665 100644 --- a/go.sum +++ b/go.sum @@ -381,8 +381,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 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/superchain-registry/superchain v0.0.0-20240222155908-ab073f6aa74f h1:L2ub0d0iW2Nqwh1r9WxMqebgZf7rU+wHuVCv21uAGx8= -github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240222155908-ab073f6aa74f/go.mod h1:7xh2awFQqsiZxFrHKTgEd+InVfDRrkKVUIuK8SAFHp0= +github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240306093353-c557df8e6f41 h1:WKJvsRyW/YNgyT0P2x5U530ITOY8Dv9TrZnbliqSXd8= +github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240306093353-c557df8e6f41/go.mod h1:7xh2awFQqsiZxFrHKTgEd+InVfDRrkKVUIuK8SAFHp0= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.10.13/go.mod h1:W3yfrFyL9C1pHcwY5hmRHVDaorTiQxhYBkKyu5mEDHw= @@ -1593,8 +1593,8 @@ github.com/wealdtech/go-eth2-types/v2 v2.5.2/go.mod h1:8lkNUbgklSQ4LZ2oMSuxSdR7W github.com/wealdtech/go-eth2-util v1.6.3/go.mod h1:0hFMj/qtio288oZFHmAbCnPQ9OB3c4WFzs5NVPKTY4k= github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3/go.mod h1:qiIimacW5NhVRy8o+YxWo9YrecXqDAKKbL0+sOa0SJ4= github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2/go.mod h1:k6kmiKWSWBTd4OxFifTEkPaBLhZspnO2KFD5XJY9nqg= -github.com/welkin22/op-geth v1.101308.2-opbnb h1:/MLtVte6Is0gmtg2puAF249XSsG8xNsDjPDs3f+GUfI= -github.com/welkin22/op-geth v1.101308.2-opbnb/go.mod h1:OvmRX7Y/QVkkjs3elxFy+jmXfIOkhuMHmpEHljKI/IU= +github.com/welkin22/op-geth v1.101308.2-opbnb2 h1:VRRSXyqw0zszivEBAcLuMi3uFZqunZzmAY0lNL95128= +github.com/welkin22/op-geth v1.101308.2-opbnb2/go.mod h1:kwg6KPGU98pFj2ZZF7z45FsKJdhIrfaQ7SW1Kac1RvY= github.com/wercker/journalhook v0.0.0-20180428041537-5d0a5ae867b3/go.mod h1:XCsSkdKK4gwBMNrOCZWww0pX6AOt+2gYc5Z6jBRrNVg= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index 017b06cf6aef..c542740e1d2c 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -107,13 +107,13 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex // Calculate bsc block base fee var l1BaseFee *big.Int - if ba.cfg.IsSnow(l2Parent.Time + ba.cfg.BlockTime) { + if ba.rollupCfg.IsSnow(l2Parent.Time + ba.rollupCfg.BlockTime) { l1BaseFee, err = SnowL1GasPrice(ctx, ba, epoch) if err != nil { return nil, err } - } else if ba.cfg.IsFermat(big.NewInt(int64(l2Parent.Number + 1))) { - l1BaseFee = bsc.BaseFeeByNetworks(ba.cfg.L2ChainID) + } else if ba.rollupCfg.IsFermat(big.NewInt(int64(l2Parent.Number + 1))) { + l1BaseFee = bsc.BaseFeeByNetworks(ba.rollupCfg.L2ChainID) } else { _, transactions, err := ba.l1.InfoAndTxsByHash(ctx, epoch.Hash) if err != nil { diff --git a/op-node/rollup/derive/engine_queue.go b/op-node/rollup/derive/engine_queue.go index 01de65807084..40be46440b78 100644 --- a/op-node/rollup/derive/engine_queue.go +++ b/op-node/rollup/derive/engine_queue.go @@ -459,7 +459,7 @@ func (eq *EngineQueue) postProcessSafeL2() error { eq.log.Debug("updated finality-data", "last_l1", last.L1Block, "last_l2", last.L2Block) } } - eq.l1Fetcher.ClearReceiptsCacheBefore(eq.safeHead.L1Origin.Number) + eq.l1Fetcher.ClearReceiptsCacheBefore(eq.ec.SafeL2Head().L1Origin.Number) return nil } diff --git a/op-node/rollup/driver/state.go b/op-node/rollup/driver/state.go index bbc79360d452..8292c5d2c16c 100644 --- a/op-node/rollup/driver/state.go +++ b/op-node/rollup/driver/state.go @@ -269,7 +269,7 @@ func (s *Driver) eventLoop() { s.derivation.Reset() } else if err != nil { s.log.Error("Sequencer critical error", "err", err) - return + return err } planSequencerAction() // schedule the next sequencer action to keep the sequencing looping return nil @@ -352,7 +352,7 @@ func (s *Driver) eventLoop() { continue case respCh := <-s.stopSequencer: if s.driverConfig.SequencerStopped { - respCh <- hashAndError{err: errors.New("sequencer not running")} + respCh <- hashAndError{err: ErrSequencerAlreadyStopped} } else { if err := s.sequencerNotifs.SequencerStopped(); err != nil { respCh <- hashAndError{err: fmt.Errorf("sequencer start notification: %w", err)} @@ -360,7 +360,10 @@ func (s *Driver) eventLoop() { } s.log.Warn("Sequencer has been stopped") s.driverConfig.SequencerStopped = true - respCh <- hashAndError{hash: s.derivation.UnsafeL2Head().Hash} + // Cancel any inflight block building. If we don't cancel this, we can resume sequencing an old block + // even if we've received new unsafe heads in the interim, causing us to introduce a re-org. + s.sequencer.CancelBuildingBlock(s.driverCtx) + respCh <- hashAndError{hash: s.engineController.UnsafeL2Head().Hash} } continue case respCh := <-s.sequencerActive: diff --git a/op-proposer/proposer/driver.go b/op-proposer/proposer/driver.go index c254fbd1ee36..2364723243bd 100644 --- a/op-proposer/proposer/driver.go +++ b/op-proposer/proposer/driver.go @@ -304,7 +304,7 @@ func (l *L2OutputSubmitter) FetchOutput(ctx context.Context, block *big.Int) (*e // ProposeL2OutputTxData creates the transaction data for the ProposeL2Output function func (l *L2OutputSubmitter) ProposeL2OutputTxData(output *eth.OutputResponse) ([]byte, error) { - if l.allowNonFinalized { + if l.Cfg.AllowNonFinalized { return proposeL2OutputTxData(l.l2ooABI, output, true) } return proposeL2OutputTxData(l.l2ooABI, output, false) @@ -322,7 +322,7 @@ func proposeL2OutputTxData(abi *abi.ABI, output *eth.OutputResponse, withCurrent output.OutputRoot, new(big.Int).SetUint64(output.BlockRef.Number), currentL1Hash, - new(big.Int).SetUint64(output.Status.CurrentL1.Number) + new(big.Int).SetUint64(output.Status.CurrentL1.Number), ) } diff --git a/op-proposer/proposer/l2_output_submitter.go b/op-proposer/proposer/l2_output_submitter.go index dc560cb11ebc..695955e17c0f 100644 --- a/op-proposer/proposer/l2_output_submitter.go +++ b/op-proposer/proposer/l2_output_submitter.go @@ -17,7 +17,7 @@ import ( // This method returns a cliapp.LifecycleAction, to create an op-service CLI-lifecycle-managed L2Output-submitter func Main(version string) cliapp.LifecycleAction { return func(cliCtx *cli.Context, _ context.CancelCauseFunc) (cliapp.Lifecycle, error) { - if err := opaws.KeyManager(context.Background(), ctx, opaws.OP_PROPOSER_SIGN_KEY); err != nil { + if err := opaws.KeyManager(context.Background(), cliCtx, opaws.OP_PROPOSER_SIGN_KEY); err != nil { return nil, err } diff --git a/op-service/dummydial/dummydial.go b/op-service/dummydial/dummydial.go new file mode 100644 index 000000000000..7a1fb211ca46 --- /dev/null +++ b/op-service/dummydial/dummydial.go @@ -0,0 +1,85 @@ +// dummydial is copied from op-service/dial/dial.go but removed DialRollupClientWithTimeout, to workaround cycle imports issue +package dummydial + +import ( + "context" + "fmt" + "time" + + "github.com/ethereum-optimism/optimism/op-service/client" + "github.com/ethereum-optimism/optimism/op-service/retry" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" +) + +// DefaultDialTimeout is a default timeout for dialing a client. +const DefaultDialTimeout = 1 * time.Minute +const defaultRetryCount = 30 +const defaultRetryTime = 2 * time.Second + +const BatcherFallbackThreshold int64 = 10 +const ProposerFallbackThreshold int64 = 3 +const TxmgrFallbackThreshold int64 = 3 + +// DialEthClientWithTimeout attempts to dial the L1 provider using the provided +// URL. If the dial doesn't complete within defaultDialTimeout seconds, this +// method will return an error. +func DialEthClientWithTimeout(ctx context.Context, timeout time.Duration, log log.Logger, url string) (client.Client, error) { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + c, err := dialRPCClientWithBackoff(ctx, log, url) + if err != nil { + return nil, err + } + + return ethclient.NewClient(c), nil +} + +// DialRPCClientWithTimeout attempts to dial the RPC provider using the provided URL. +// If the dial doesn't complete within timeout seconds, this method will return an error. +func DialRPCClientWithTimeout(ctx context.Context, timeout time.Duration, log log.Logger, url string) (*rpc.Client, error) { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + return dialRPCClientWithBackoff(ctx, log, url) +} + +// Dials a JSON-RPC endpoint repeatedly, with a backoff, until a client connection is established. Auth is optional. +func dialRPCClientWithBackoff(ctx context.Context, log log.Logger, addr string) (*rpc.Client, error) { + bOff := retry.Fixed(defaultRetryTime) + return retry.Do(ctx, defaultRetryCount, bOff, func() (*rpc.Client, error) { + if !client.IsURLAvailable(addr) { + log.Warn("failed to dial address, but may connect later", "addr", addr) + return nil, fmt.Errorf("address unavailable (%s)", addr) + } + client, err := rpc.DialOptions(ctx, addr) + if err != nil { + return nil, fmt.Errorf("failed to dial address (%s): %w", addr, err) + } + return client, nil + }) +} + +// DialEthClientWithTimeoutAndFallback will try to dial within the timeout period and create an EthClient. +// If the URL is a multi URL, then a fallbackClient will be created to add the fallback capability to the client +func DialEthClientWithTimeoutAndFallback(ctx context.Context, url string, timeout time.Duration, l log.Logger, fallbackThreshold int64, m client.FallbackClientMetricer) (client.Client, error) { + isMultiUrl, urlList := client.MultiUrlParse(url) + if isMultiUrl { + firstEthClient, err := DialEthClientWithTimeout(ctx, timeout, l, urlList[0]) + if err != nil { + return nil, err + } + fallbackClient := client.NewFallbackClient(firstEthClient, urlList, l, fallbackThreshold, m, func(url string) (client.Client, error) { + ethClientNew, err := DialEthClientWithTimeout(ctx, timeout, l, url) + if err != nil { + return nil, err + } + return ethClientNew, nil + }) + return fallbackClient, nil + } + + return DialEthClientWithTimeout(ctx, timeout, l, url) +} diff --git a/op-service/sources/eth_client.go b/op-service/sources/eth_client.go index 4f500b85352a..e2db86dcddef 100644 --- a/op-service/sources/eth_client.go +++ b/op-service/sources/eth_client.go @@ -422,7 +422,7 @@ func (s *EthClient) Close() { } func (s *EthClient) bscFinalizedHeader(ctx context.Context, probabilisticFinalized int64) (eth.BlockInfo, error) { - var header *rpcHeader + var header *RPCHeader err := s.client.CallContext(ctx, &header, "eth_getFinalizedHeader", probabilisticFinalized) // headers are just blocks without txs if err != nil { return nil, err diff --git a/op-service/sources/fallback_client.go b/op-service/sources/fallback_client.go index 826cfb1ae480..60ff59f34ad8 100644 --- a/op-service/sources/fallback_client.go +++ b/op-service/sources/fallback_client.go @@ -272,8 +272,8 @@ func (l *FallbackClient) ChainID(ctx context.Context, rpc client.RPC) (*big.Int, return (*big.Int)(&id), nil } -func (l *FallbackClient) l1BlockRefByNumber(ctx context.Context, number uint64, newRpc client.RPC) (*rpcHeader, error) { - var header *rpcHeader +func (l *FallbackClient) l1BlockRefByNumber(ctx context.Context, number uint64, newRpc client.RPC) (*RPCHeader, error) { + var header *RPCHeader err := newRpc.CallContext(ctx, &header, "eth_getBlockByNumber", numberID(number).Arg(), false) // headers are just blocks without txs if err != nil { return nil, err diff --git a/op-service/sources/l1_client.go b/op-service/sources/l1_client.go index 75e963e2ff2b..a347a17272b3 100644 --- a/op-service/sources/l1_client.go +++ b/op-service/sources/l1_client.go @@ -150,7 +150,7 @@ func (s *L1Client) GoOrUpdatePreFetchReceipts(ctx context.Context, l1Start uint6 return case currentL1Block = <-s.preFetchReceiptsStartBlockChan: s.log.Debug("pre-fetching receipts currentL1Block changed", "block", currentL1Block) - s.receiptsCache.RemoveAll() + s.recProvider.GetReceiptsCache().RemoveAll() parentHash = common.Hash{} default: blockRef, err := s.L1BlockRefByLabel(ctx, eth.Unsafe) @@ -193,7 +193,7 @@ func (s *L1Client) GoOrUpdatePreFetchReceipts(ctx context.Context, l1Start uint6 time.Sleep(1 * time.Second) continue } - pair, ok := s.receiptsCache.Get(blockNumber, false) + pair, ok := s.recProvider.GetReceiptsCache().Get(blockNumber, false) if ok && pair.blockHash == blockInfo.Hash { blockInfoChan <- blockInfo return @@ -252,7 +252,7 @@ func (s *L1Client) GoOrUpdatePreFetchReceipts(ctx context.Context, l1Start uint6 func (s *L1Client) ClearReceiptsCacheBefore(blockNumber uint64) { s.log.Debug("clear receipts cache before", "blockNumber", blockNumber) - s.receiptsCache.RemoveLessThan(blockNumber) + s.recProvider.GetReceiptsCache().RemoveLessThan(blockNumber) } func (s *L1Client) Close() { diff --git a/op-service/sources/receipts.go b/op-service/sources/receipts.go index c8e7257f13b9..a64f070f13c9 100644 --- a/op-service/sources/receipts.go +++ b/op-service/sources/receipts.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/sources/caching" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/trie" @@ -20,6 +21,7 @@ type ReceiptsProvider interface { // It verifies the receipt hash in the block header against the receipt hash of the fetched receipts // to ensure that the execution engine did not fail to return any receipts. FetchReceipts(ctx context.Context, blockInfo eth.BlockInfo, txHashes []common.Hash, isForPreFetch bool) (types.Receipts, error, bool) + GetReceiptsCache() *caching.PreFetchCache[*ReceiptsHashPair] } type InnerReceiptsProvider interface { diff --git a/op-service/sources/receipts_caching.go b/op-service/sources/receipts_caching.go index 0aa9fb74d127..a7127ef22bb0 100644 --- a/op-service/sources/receipts_caching.go +++ b/op-service/sources/receipts_caching.go @@ -86,3 +86,7 @@ func (p *CachingReceiptsProvider) FetchReceipts(ctx context.Context, blockInfo e p.deleteFetchingLock(block.Hash) return r, nil, isFull } + +func (p *CachingReceiptsProvider) GetReceiptsCache() *caching.PreFetchCache[*ReceiptsHashPair] { + return p.cache +} diff --git a/op-service/txmgr/cli.go b/op-service/txmgr/cli.go index 44e03f93c5e9..18c2ca0dfe61 100644 --- a/op-service/txmgr/cli.go +++ b/op-service/txmgr/cli.go @@ -7,13 +7,13 @@ import ( "math/big" "time" - "github.com/ethereum-optimism/optimism/op-service/client" - txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" - opservice "github.com/ethereum-optimism/optimism/op-service" + "github.com/ethereum-optimism/optimism/op-service/client" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" + dial "github.com/ethereum-optimism/optimism/op-service/dummydial" "github.com/ethereum-optimism/optimism/op-service/eth" opsigner "github.com/ethereum-optimism/optimism/op-service/signer" + txmetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/urfave/cli/v2"