diff --git a/common/version/version.go b/common/version/version.go index cb1ff7acc6..419c8716ad 100644 --- a/common/version/version.go +++ b/common/version/version.go @@ -5,7 +5,7 @@ import ( "runtime/debug" ) -var tag = "v4.5.16" +var tag = "v4.5.17" var commit = func() string { if info, ok := debug.ReadBuildInfo(); ok { diff --git a/rollup/internal/controller/relayer/l1_relayer.go b/rollup/internal/controller/relayer/l1_relayer.go index 0088b14102..d9f0835d52 100644 --- a/rollup/internal/controller/relayer/l1_relayer.go +++ b/rollup/internal/controller/relayer/l1_relayer.go @@ -179,13 +179,13 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { return } - hash, err := r.gasOracleSender.SendTransaction(block.Hash, &r.cfg.GasPriceOracleContractAddress, data, nil) + txHash, _, err := r.gasOracleSender.SendTransaction(block.Hash, &r.cfg.GasPriceOracleContractAddress, data, nil) if err != nil { log.Error("Failed to send gas oracle update tx to layer2", "block.Hash", block.Hash, "block.Height", block.Number, "block.BaseFee", baseFee, "block.BlobBaseFee", blobBaseFee, "err", err) return } - err = r.l1BlockOrm.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, block.Hash, types.GasOracleImporting, hash.String()) + err = r.l1BlockOrm.UpdateL1GasOracleStatusAndOracleTxHash(r.ctx, block.Hash, types.GasOracleImporting, txHash.String()) if err != nil { log.Error("UpdateGasOracleStatusAndOracleTxHash failed", "block.Hash", block.Hash, "block.Height", block.Number, "err", err) return @@ -195,7 +195,7 @@ func (r *Layer1Relayer) ProcessGasPriceOracle() { r.lastBlobBaseFee = blobBaseFee r.metrics.rollupL1RelayerLatestBaseFee.Set(float64(r.lastBaseFee)) r.metrics.rollupL1RelayerLatestBlobBaseFee.Set(float64(r.lastBlobBaseFee)) - log.Info("Update l1 base fee", "txHash", hash.String(), "baseFee", baseFee, "blobBaseFee", blobBaseFee) + log.Info("Update l1 base fee", "txHash", txHash.String(), "baseFee", baseFee, "blobBaseFee", blobBaseFee) } } } diff --git a/rollup/internal/controller/relayer/l2_relayer.go b/rollup/internal/controller/relayer/l2_relayer.go index 418762a470..8c74418fad 100644 --- a/rollup/internal/controller/relayer/l2_relayer.go +++ b/rollup/internal/controller/relayer/l2_relayer.go @@ -104,10 +104,15 @@ type StrategyParams struct { } // bestParams maps your 2h/5h/12h windows to their best rules. +// Timeouts are in seconds, 2, 5 and 12 hours (and same + 20 mins to account for +// time to create batch currently roughly, as time is measured from block creation) var bestParams = map[uint64]StrategyParams{ - 2 * 3600: {BaselineType: PctMin, BaselineParam: 0.10, Gamma: 0.4, Beta: 8, RelaxType: Exponential}, - 5 * 3600: {BaselineType: PctMin, BaselineParam: 0.30, Gamma: 0.6, Beta: 20, RelaxType: Sigmoid}, - 12 * 3600: {BaselineType: PctMin, BaselineParam: 0.50, Gamma: 0.5, Beta: 20, RelaxType: Sigmoid}, + 7200: {BaselineType: PctMin, BaselineParam: 0.10, Gamma: 0.4, Beta: 8, RelaxType: Exponential}, + 8400: {BaselineType: PctMin, BaselineParam: 0.10, Gamma: 0.4, Beta: 8, RelaxType: Exponential}, + 18000: {BaselineType: PctMin, BaselineParam: 0.30, Gamma: 0.6, Beta: 20, RelaxType: Sigmoid}, + 19200: {BaselineType: PctMin, BaselineParam: 0.30, Gamma: 0.6, Beta: 20, RelaxType: Sigmoid}, + 42800: {BaselineType: PctMin, BaselineParam: 0.50, Gamma: 0.5, Beta: 20, RelaxType: Sigmoid}, + 44400: {BaselineType: PctMin, BaselineParam: 0.50, Gamma: 0.5, Beta: 20, RelaxType: Sigmoid}, } // NewLayer2Relayer will return a new instance of Layer2RelayerClient @@ -147,6 +152,11 @@ func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm. return nil, fmt.Errorf("invalid service type for l2_relayer: %v", serviceType) } + strategy, ok := bestParams[uint64(cfg.BatchSubmission.TimeoutSec)] + if !ok { + return nil, fmt.Errorf("invalid timeout for batch submission: %v", cfg.BatchSubmission.TimeoutSec) + } + layer2Relayer := &Layer2Relayer{ ctx: ctx, db: db, @@ -164,7 +174,7 @@ func NewLayer2Relayer(ctx context.Context, l2Client *ethclient.Client, db *gorm. l1RollupABI: bridgeAbi.ScrollChainABI, l2GasOracleABI: bridgeAbi.L2GasPriceOracleABI, - batchStrategy: bestParams[uint64(cfg.BatchSubmission.TimeoutSec)], + batchStrategy: strategy, cfg: cfg, chainCfg: chainCfg, } @@ -271,11 +281,11 @@ func (r *Layer2Relayer) commitGenesisBatch(batchHash string, batchHeader []byte, } // submit genesis batch to L1 rollup contract - txHash, err := r.commitSender.SendTransaction(batchHash, &r.cfg.RollupContractAddress, calldata, nil) + txHash, _, err := r.commitSender.SendTransaction(batchHash, &r.cfg.RollupContractAddress, calldata, nil) if err != nil { return fmt.Errorf("failed to send import genesis batch tx to L1, error: %v", err) } - log.Info("importGenesisBatch transaction sent", "contract", r.cfg.RollupContractAddress, "txHash", txHash.String(), "batchHash", batchHash) + log.Info("importGenesisBatch transaction sent", "contract", r.cfg.RollupContractAddress, "txHash", txHash, "batchHash", batchHash) // wait for confirmation // we assume that no other transactions are sent before initializeGenesis completes @@ -336,11 +346,11 @@ func (r *Layer2Relayer) ProcessPendingBatches() { var forceSubmit bool startChunk, err := r.chunkOrm.GetChunkByIndex(r.ctx, dbBatches[0].StartChunkIndex) - oldestBlockTimestamp := time.Unix(int64(startChunk.StartBlockTime), 0) if err != nil { log.Error("failed to get first chunk", "err", err, "batch index", dbBatches[0].Index, "chunk index", dbBatches[0].StartChunkIndex) return } + oldestBlockTimestamp := time.Unix(int64(startChunk.StartBlockTime), 0) // if the batch with the oldest index is too old, we force submit all batches that we have so far in the next step if r.cfg.BatchSubmission.TimeoutSec > 0 && time.Since(oldestBlockTimestamp) > time.Duration(r.cfg.BatchSubmission.TimeoutSec)*time.Second { @@ -467,7 +477,7 @@ func (r *Layer2Relayer) ProcessPendingBatches() { return } - txHash, err := r.commitSender.SendTransaction(r.contextIDFromBatches(batchesToSubmit), &r.cfg.RollupContractAddress, calldata, blobs) + txHash, blobBaseFee, err := r.commitSender.SendTransaction(r.contextIDFromBatches(batchesToSubmit), &r.cfg.RollupContractAddress, calldata, blobs) if err != nil { if errors.Is(err, sender.ErrTooManyPendingBlobTxs) { r.metrics.rollupL2RelayerProcessPendingBatchErrTooManyPendingBlobTxsTotal.Inc() @@ -508,6 +518,7 @@ func (r *Layer2Relayer) ProcessPendingBatches() { r.metrics.rollupL2RelayerProcessPendingBatchSuccessTotal.Add(float64(len(batchesToSubmit))) r.metrics.rollupL2RelayerProcessBatchesPerTxCount.Set(float64(len(batchesToSubmit))) r.metrics.rollupL2RelayerCommitLatency.Set(time.Since(oldestBlockTimestamp).Seconds()) + r.metrics.rollupL2RelayerCommitPrice.Set(float64(blobBaseFee)) log.Info("Sent the commitBatches tx to layer1", "batches count", len(batchesToSubmit), "start index", firstBatch.Index, "start hash", firstBatch.Hash, "end index", lastBatch.Index, "end hash", lastBatch.Hash, "tx hash", txHash.String()) } @@ -691,7 +702,7 @@ func (r *Layer2Relayer) finalizeBundle(bundle *orm.Bundle, withProof bool) error return fmt.Errorf("unsupported codec version in finalizeBundle, bundle index: %v, version: %d", bundle.Index, bundle.CodecVersion) } - txHash, err := r.finalizeSender.SendTransaction("finalizeBundle-"+bundle.Hash, &r.cfg.RollupContractAddress, calldata, nil) + txHash, _, err := r.finalizeSender.SendTransaction("finalizeBundle-"+bundle.Hash, &r.cfg.RollupContractAddress, calldata, nil) if err != nil { log.Error("finalizeBundle in layer1 failed", "with proof", withProof, "index", bundle.Index, "start batch index", bundle.StartBatchIndex, "end batch index", bundle.EndBatchIndex, diff --git a/rollup/internal/controller/relayer/l2_relayer_metrics.go b/rollup/internal/controller/relayer/l2_relayer_metrics.go index 00e550588f..b6c883ceaa 100644 --- a/rollup/internal/controller/relayer/l2_relayer_metrics.go +++ b/rollup/internal/controller/relayer/l2_relayer_metrics.go @@ -31,6 +31,7 @@ type l2RelayerMetrics struct { rollupL2RelayerTargetBlobPrice prometheus.Gauge rollupL2RelayerCommitLatency prometheus.Gauge rollupL2RelayerBacklogCounts prometheus.Gauge + rollupL2RelayerCommitPrice prometheus.Gauge } var ( @@ -125,6 +126,10 @@ func initL2RelayerMetrics(reg prometheus.Registerer) *l2RelayerMetrics { Name: "rollup_l2_relayer_backlog_counts", Help: "The number of pending batches in the backlog", }), + rollupL2RelayerCommitPrice: promauto.With(reg).NewGauge(prometheus.GaugeOpts{ + Name: "rollup_l2_relayer_commit_price", + Help: "The commit price for the L2 relayer's submission strategy", + }), } }) return l2RelayerMetric diff --git a/rollup/internal/controller/sender/sender.go b/rollup/internal/controller/sender/sender.go index 1b8ccd3aa2..d5a4db5d3b 100644 --- a/rollup/internal/controller/sender/sender.go +++ b/rollup/internal/controller/sender/sender.go @@ -171,7 +171,7 @@ func (s *Sender) getFeeData(target *common.Address, data []byte, sidecar *gethTy } // SendTransaction send a signed L2tL1 transaction. -func (s *Sender) SendTransaction(contextID string, target *common.Address, data []byte, blobs []*kzg4844.Blob) (common.Hash, error) { +func (s *Sender) SendTransaction(contextID string, target *common.Address, data []byte, blobs []*kzg4844.Blob) (common.Hash, uint64, error) { s.metrics.sendTransactionTotal.WithLabelValues(s.service, s.name).Inc() var ( feeData *FeeData @@ -190,37 +190,37 @@ func (s *Sender) SendTransaction(contextID string, target *common.Address, data numPendingTransactions, err = s.pendingTransactionOrm.GetCountPendingTransactionsBySenderType(s.ctx, s.senderType) if err != nil { log.Error("failed to count pending transactions", "err: %w", err) - return common.Hash{}, fmt.Errorf("failed to count pending transactions, err: %w", err) + return common.Hash{}, 0, fmt.Errorf("failed to count pending transactions, err: %w", err) } if numPendingTransactions >= s.config.MaxPendingBlobTxs { - return common.Hash{}, ErrTooManyPendingBlobTxs + return common.Hash{}, 0, ErrTooManyPendingBlobTxs } } sidecar, err = makeSidecar(blobs) if err != nil { log.Error("failed to make sidecar for blob transaction", "error", err) - return common.Hash{}, fmt.Errorf("failed to make sidecar for blob transaction, err: %w", err) + return common.Hash{}, 0, fmt.Errorf("failed to make sidecar for blob transaction, err: %w", err) } } blockNumber, baseFee, blobBaseFee, err := s.getBlockNumberAndBaseFeeAndBlobFee(s.ctx) if err != nil { log.Error("failed to get block number and base fee", "error", err) - return common.Hash{}, fmt.Errorf("failed to get block number and base fee, err: %w", err) + return common.Hash{}, 0, fmt.Errorf("failed to get block number and base fee, err: %w", err) } if feeData, err = s.getFeeData(target, data, sidecar, baseFee, blobBaseFee); err != nil { s.metrics.sendTransactionFailureGetFee.WithLabelValues(s.service, s.name).Inc() log.Error("failed to get fee data", "from", s.transactionSigner.GetAddr().String(), "nonce", s.transactionSigner.GetNonce(), "err", err) - return common.Hash{}, fmt.Errorf("failed to get fee data, err: %w", err) + return common.Hash{}, 0, fmt.Errorf("failed to get fee data, err: %w", err) } signedTx, err := s.createTx(feeData, target, data, sidecar, s.transactionSigner.GetNonce()) if err != nil { s.metrics.sendTransactionFailureSendTx.WithLabelValues(s.service, s.name).Inc() log.Error("failed to create signed tx (non-resubmit case)", "from", s.transactionSigner.GetAddr().String(), "nonce", s.transactionSigner.GetNonce(), "err", err) - return common.Hash{}, fmt.Errorf("failed to create signed transaction, err: %w", err) + return common.Hash{}, 0, fmt.Errorf("failed to create signed transaction, err: %w", err) } // Insert the transaction into the pending transaction table. @@ -228,14 +228,14 @@ func (s *Sender) SendTransaction(contextID string, target *common.Address, data // This case will be handled by the checkPendingTransaction function. if err = s.pendingTransactionOrm.InsertPendingTransaction(s.ctx, contextID, s.getSenderMeta(), signedTx, blockNumber); err != nil { log.Error("failed to insert transaction", "from", s.transactionSigner.GetAddr().String(), "nonce", s.transactionSigner.GetNonce(), "err", err) - return common.Hash{}, fmt.Errorf("failed to insert transaction, err: %w", err) + return common.Hash{}, 0, fmt.Errorf("failed to insert transaction, err: %w", err) } if err := s.client.SendTransaction(s.ctx, signedTx); err != nil { // Delete the transaction from the pending transaction table if it fails to send. if updateErr := s.pendingTransactionOrm.DeleteTransactionByTxHash(s.ctx, signedTx.Hash()); updateErr != nil { log.Error("failed to delete transaction", "tx hash", signedTx.Hash().String(), "from", s.transactionSigner.GetAddr().String(), "nonce", signedTx.Nonce(), "err", updateErr) - return common.Hash{}, fmt.Errorf("failed to delete transaction, err: %w", updateErr) + return common.Hash{}, 0, fmt.Errorf("failed to delete transaction, err: %w", updateErr) } log.Error("failed to send tx", "tx hash", signedTx.Hash().String(), "from", s.transactionSigner.GetAddr().String(), "nonce", signedTx.Nonce(), "err", err) @@ -244,12 +244,12 @@ func (s *Sender) SendTransaction(contextID string, target *common.Address, data if strings.Contains(err.Error(), "nonce too low") { s.resetNonce(context.Background()) } - return common.Hash{}, fmt.Errorf("failed to send transaction, err: %w", err) + return common.Hash{}, 0, fmt.Errorf("failed to send transaction, err: %w", err) } s.transactionSigner.SetNonce(signedTx.Nonce() + 1) - return signedTx.Hash(), nil + return signedTx.Hash(), blobBaseFee, nil } func (s *Sender) createTx(feeData *FeeData, target *common.Address, data []byte, sidecar *gethTypes.BlobTxSidecar, nonce uint64) (*gethTypes.Transaction, error) { diff --git a/rollup/internal/controller/sender/sender_test.go b/rollup/internal/controller/sender/sender_test.go index 80164170a0..7ca8da5c8d 100644 --- a/rollup/internal/controller/sender/sender_test.go +++ b/rollup/internal/controller/sender/sender_test.go @@ -187,7 +187,7 @@ func testSendAndRetrieveTransaction(t *testing.T) { if txBlob[i] != nil { blobs = []*kzg4844.Blob{txBlob[i]} } - hash, err := s.SendTransaction("0", &common.Address{}, nil, blobs) + hash, _, err := s.SendTransaction("0", &common.Address{}, nil, blobs) assert.NoError(t, err) txs, err := s.pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), s.senderType, 1) assert.NoError(t, err) @@ -545,10 +545,10 @@ func testResubmitNonceGappedTransaction(t *testing.T) { if txBlob[i] != nil { blobs = []*kzg4844.Blob{txBlob[i]} } - _, err = s.SendTransaction("test-1", &common.Address{}, nil, blobs) + _, _, err = s.SendTransaction("test-1", &common.Address{}, nil, blobs) assert.NoError(t, err) - _, err = s.SendTransaction("test-2", &common.Address{}, nil, blobs) + _, _, err = s.SendTransaction("test-2", &common.Address{}, nil, blobs) assert.NoError(t, err) s.checkPendingTransaction() @@ -589,7 +589,7 @@ func testCheckPendingTransactionTxConfirmed(t *testing.T) { return nil }) - _, err = s.SendTransaction("test", &common.Address{}, nil, randBlobs(1)) + _, _, err = s.SendTransaction("test", &common.Address{}, nil, randBlobs(1)) assert.NoError(t, err) txs, err := s.pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), s.senderType, 1) @@ -631,7 +631,7 @@ func testCheckPendingTransactionResubmitTxConfirmed(t *testing.T) { return nil }) - originTxHash, err := s.SendTransaction("test", &common.Address{}, nil, randBlobs(1)) + originTxHash, _, err := s.SendTransaction("test", &common.Address{}, nil, randBlobs(1)) assert.NoError(t, err) txs, err := s.pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), s.senderType, 1) @@ -691,7 +691,7 @@ func testCheckPendingTransactionReplacedTxConfirmed(t *testing.T) { return nil }) - txHash, err := s.SendTransaction("test", &common.Address{}, nil, randBlobs(1)) + txHash, _, err := s.SendTransaction("test", &common.Address{}, nil, randBlobs(1)) assert.NoError(t, err) txs, err := s.pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), s.senderType, 1) @@ -761,7 +761,7 @@ func testCheckPendingTransactionTxMultipleTimesWithOnlyOneTxPending(t *testing.T return nil }) - _, err = s.SendTransaction("test", &common.Address{}, nil, randBlobs(1)) + _, _, err = s.SendTransaction("test", &common.Address{}, nil, randBlobs(1)) assert.NoError(t, err) txs, err := s.pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), s.senderType, 1) @@ -835,7 +835,7 @@ func testBlobTransactionWithBlobhashOpContractCall(t *testing.T) { assert.NoError(t, err) defer s.Stop() - _, err = s.SendTransaction("0", &testContractsAddress, data, blobs) + _, _, err = s.SendTransaction("0", &testContractsAddress, data, blobs) assert.NoError(t, err) var txHash common.Hash @@ -893,10 +893,10 @@ func testSendBlobCarryingTxOverLimit(t *testing.T) { assert.NoError(t, err) for i := 0; i < int(cfgCopy.MaxPendingBlobTxs); i++ { - _, err = s.SendTransaction("0", &common.Address{}, nil, randBlobs(1)) + _, _, err = s.SendTransaction("0", &common.Address{}, nil, randBlobs(1)) assert.NoError(t, err) } - _, err = s.SendTransaction("0", &common.Address{}, nil, randBlobs(1)) + _, _, err = s.SendTransaction("0", &common.Address{}, nil, randBlobs(1)) assert.ErrorIs(t, err, ErrTooManyPendingBlobTxs) s.Stop() }