From b8544755c760e8f073a9aef15e9f8f750781f8a4 Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Wed, 18 Dec 2024 19:55:37 -0500 Subject: [PATCH 01/10] Add UseEscapeHatch and replace the existing UseEscapeHatch with EnableEscapeHatch --- arbnode/batch_poster.go | 16 +++++++------- arbnode/transaction_streamer.go | 25 +++++++++++++--------- system_tests/espresso_escape_hatch_test.go | 4 ++-- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index db18b0c035..4737b45f0e 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -179,7 +179,7 @@ type BatchPosterConfig struct { // Espresso specific flags LightClientAddress string `koanf:"light-client-address"` HotShotUrl string `koanf:"hotshot-url"` - UseEscapeHatch bool `koanf:"use-escape-hatch"` + EnableEscapeHatch bool `koanf:"use-escape-hatch"` EspressoTxnsPollingInterval time.Duration `koanf:"espresso-txns-polling-interval"` EspressoSwitchDelayThreshold uint64 `koanf:"espresso-switch-delay-threshold"` EspressoMaxTransactionSize uint64 `koanf:"espresso-max-transaction-size"` @@ -240,7 +240,7 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Uint64(prefix+".gas-estimate-base-fee-multiple-bips", uint64(DefaultBatchPosterConfig.GasEstimateBaseFeeMultipleBips), "for gas estimation, use this multiple of the basefee (measured in basis points) as the max fee per gas") f.Duration(prefix+".reorg-resistance-margin", DefaultBatchPosterConfig.ReorgResistanceMargin, "do not post batch if its within this duration from layer 1 minimum bounds. Requires l1-block-bound option not be set to \"ignore\"") f.Bool(prefix+".check-batch-correctness", DefaultBatchPosterConfig.CheckBatchCorrectness, "setting this to true will run the batch against an inbox multiplexer and verifies that it produces the correct set of messages") - f.Bool(prefix+".use-escape-hatch", DefaultBatchPosterConfig.UseEscapeHatch, "if true, batches will be posted without doing the espresso verification when hotshot is down. If false, wait for hotshot being up") + f.Bool(prefix+".use-escape-hatch", DefaultBatchPosterConfig.EnableEscapeHatch, "if true, batches will be posted without doing the espresso verification when hotshot is down. If false, wait for hotshot being up") f.Duration(prefix+".espresso-txns-polling-interval", DefaultBatchPosterConfig.EspressoTxnsPollingInterval, "interval between polling for transactions to be included in the block") f.Uint64(prefix+".espresso-switch-delay-threshold", DefaultBatchPosterConfig.EspressoSwitchDelayThreshold, "specifies the switch delay threshold used to determine hotshot liveness") f.String(prefix+".espresso-tee-verifier-address", DefaultBatchPosterConfig.EspressoTEEVerifierAddress, "") @@ -276,7 +276,7 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ GasEstimateBaseFeeMultipleBips: arbmath.OneInUBips * 3 / 2, ReorgResistanceMargin: 10 * time.Minute, CheckBatchCorrectness: true, - UseEscapeHatch: false, + EnableEscapeHatch: false, EspressoTxnsPollingInterval: time.Millisecond * 500, EspressoSwitchDelayThreshold: 350, LightClientAddress: "", @@ -314,7 +314,7 @@ var TestBatchPosterConfig = BatchPosterConfig{ UseAccessLists: true, GasEstimateBaseFeeMultipleBips: arbmath.OneInUBips * 3 / 2, CheckBatchCorrectness: true, - UseEscapeHatch: false, + EnableEscapeHatch: false, EspressoTxnsPollingInterval: time.Millisecond * 500, EspressoSwitchDelayThreshold: 10, LightClientAddress: "", @@ -381,7 +381,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e return nil, err } opts.Streamer.lightClientReader = lightClientReader - opts.Streamer.UseEscapeHatch = opts.Config().UseEscapeHatch + opts.Streamer.EnableEscapeHatch = opts.Config().EnableEscapeHatch opts.Streamer.espressoTxnsPollingInterval = opts.Config().EspressoTxnsPollingInterval opts.Streamer.espressoSwitchDelayThreshold = opts.Config().EspressoSwitchDelayThreshold opts.Streamer.espressoMaxTransactionSize = opts.Config().EspressoMaxTransactionSize @@ -578,7 +578,7 @@ func (b *BatchPoster) checkEspressoValidation() error { return nil } - if b.streamer.UseEscapeHatch { + if b.streamer.EnableEscapeHatch { skip, err := b.streamer.getSkipVerificationPos() if err != nil { log.Error("failed call to get skip verification pos", "err", err) @@ -594,7 +594,7 @@ func (b *BatchPoster) checkEspressoValidation() error { } } - if b.streamer.HotshotDown && b.streamer.UseEscapeHatch { + if b.streamer.HotshotDown && b.streamer.EnableEscapeHatch { log.Warn("skipped espresso verification due to hotshot failure", "pos", b.building.msgCount) return nil } @@ -1418,7 +1418,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) // Submit message positions to pending queue shouldSubmit := b.streamer.shouldSubmitEspressoTransaction() - if !b.streamer.UseEscapeHatch || shouldSubmit { + if !b.streamer.EnableEscapeHatch || shouldSubmit { for p := b.building.msgCount; p < msgCount; p += 1 { err = b.submitEspressoTransactionPos(p) if err != nil { diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 70a9eee20c..ee006a4db4 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -87,8 +87,10 @@ type TransactionStreamer struct { espressoSwitchDelayThreshold uint64 espressoMaxTransactionSize uint64 // Public these fields for testing - HotshotDown bool - UseEscapeHatch bool + HotshotDown bool + EnableEscapeHatch bool + UseEscapeHatch bool + espressoTEEVerifierAddress common.Address } @@ -1727,7 +1729,7 @@ func (s *TransactionStreamer) checkEspressoLiveness(ctx context.Context) error { s.HotshotDown = true } - if !s.UseEscapeHatch { + if !s.EnableEscapeHatch { return nil } @@ -1810,14 +1812,17 @@ func (s *TransactionStreamer) espressoSwitch(ctx context.Context, ignored struct retryRate := s.espressoTxnsPollingInterval * 50 enabledEspresso := s.espressoTEEVerifierAddress != common.Address{} if enabledEspresso { - err := s.checkEspressoLiveness(ctx) - if err != nil { - if ctx.Err() != nil { - return 0 + var err error + if s.UseEscapeHatch { + err = s.checkEspressoLiveness(ctx) + if err != nil { + if ctx.Err() != nil { + return 0 + } + logLevel := getLogLevel(err) + logLevel("error checking escape hatch, will retry", "err", err) + return retryRate } - logLevel := getLogLevel(err) - logLevel("error checking escape hatch, will retry", "err", err) - return retryRate } err = s.pollSubmittedTransactionForFinality(ctx) if err != nil { diff --git a/system_tests/espresso_escape_hatch_test.go b/system_tests/espresso_escape_hatch_test.go index 735bcaab77..8be0c7612b 100644 --- a/system_tests/espresso_escape_hatch_test.go +++ b/system_tests/espresso_escape_hatch_test.go @@ -55,7 +55,7 @@ func TestEspressoEscapeHatch(t *testing.T) { address := common.HexToAddress(lightClientAddress) txOpts := builder.L1Info.GetDefaultTransactOpts("Faucet", ctx) - if builder.L2.ConsensusNode.TxStreamer.UseEscapeHatch { + if builder.L2.ConsensusNode.TxStreamer.EnableEscapeHatch { t.Fatal("testing not using escape hatch first") } log.Info("Checking turning off the escape hatch") @@ -115,7 +115,7 @@ func TestEspressoEscapeHatch(t *testing.T) { log.Info("testing escape hatch") // Modify it manually - builder.L2.ConsensusNode.TxStreamer.UseEscapeHatch = true + builder.L2.ConsensusNode.TxStreamer.EnableEscapeHatch = true err = lightclientmock.FreezeL1Height(t, builder.L1.Client, address, &txOpts) Require(t, err) From 84e60acbfd01ef7b88ef2dce1fe76903850a4738 Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Wed, 18 Dec 2024 20:05:45 -0500 Subject: [PATCH 02/10] fix --- arbnode/batch_poster.go | 11 ++++++++--- arbnode/transaction_streamer.go | 18 ++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 4737b45f0e..76de9a7736 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -180,6 +180,7 @@ type BatchPosterConfig struct { LightClientAddress string `koanf:"light-client-address"` HotShotUrl string `koanf:"hotshot-url"` EnableEscapeHatch bool `koanf:"use-escape-hatch"` + UseEscapeHatch bool `koanf:"use-escape-hatch"` EspressoTxnsPollingInterval time.Duration `koanf:"espresso-txns-polling-interval"` EspressoSwitchDelayThreshold uint64 `koanf:"espresso-switch-delay-threshold"` EspressoMaxTransactionSize uint64 `koanf:"espresso-max-transaction-size"` @@ -241,6 +242,7 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Duration(prefix+".reorg-resistance-margin", DefaultBatchPosterConfig.ReorgResistanceMargin, "do not post batch if its within this duration from layer 1 minimum bounds. Requires l1-block-bound option not be set to \"ignore\"") f.Bool(prefix+".check-batch-correctness", DefaultBatchPosterConfig.CheckBatchCorrectness, "setting this to true will run the batch against an inbox multiplexer and verifies that it produces the correct set of messages") f.Bool(prefix+".use-escape-hatch", DefaultBatchPosterConfig.EnableEscapeHatch, "if true, batches will be posted without doing the espresso verification when hotshot is down. If false, wait for hotshot being up") + f.Bool(prefix+".use-escape-hatch", DefaultBatchPosterConfig.UseEscapeHatch, "if true, Escape Hatch functionality will be used") f.Duration(prefix+".espresso-txns-polling-interval", DefaultBatchPosterConfig.EspressoTxnsPollingInterval, "interval between polling for transactions to be included in the block") f.Uint64(prefix+".espresso-switch-delay-threshold", DefaultBatchPosterConfig.EspressoSwitchDelayThreshold, "specifies the switch delay threshold used to determine hotshot liveness") f.String(prefix+".espresso-tee-verifier-address", DefaultBatchPosterConfig.EspressoTEEVerifierAddress, "") @@ -277,6 +279,7 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ ReorgResistanceMargin: 10 * time.Minute, CheckBatchCorrectness: true, EnableEscapeHatch: false, + UseEscapeHatch: false, EspressoTxnsPollingInterval: time.Millisecond * 500, EspressoSwitchDelayThreshold: 350, LightClientAddress: "", @@ -315,6 +318,7 @@ var TestBatchPosterConfig = BatchPosterConfig{ GasEstimateBaseFeeMultipleBips: arbmath.OneInUBips * 3 / 2, CheckBatchCorrectness: true, EnableEscapeHatch: false, + UseEscapeHatch: false, EspressoTxnsPollingInterval: time.Millisecond * 500, EspressoSwitchDelayThreshold: 10, LightClientAddress: "", @@ -382,6 +386,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e } opts.Streamer.lightClientReader = lightClientReader opts.Streamer.EnableEscapeHatch = opts.Config().EnableEscapeHatch + opts.Streamer.UseEscapeHatch = opts.Config().UseEscapeHatch opts.Streamer.espressoTxnsPollingInterval = opts.Config().EspressoTxnsPollingInterval opts.Streamer.espressoSwitchDelayThreshold = opts.Config().EspressoSwitchDelayThreshold opts.Streamer.espressoMaxTransactionSize = opts.Config().EspressoMaxTransactionSize @@ -578,7 +583,7 @@ func (b *BatchPoster) checkEspressoValidation() error { return nil } - if b.streamer.EnableEscapeHatch { + if b.streamer.UseEscapeHatch && b.streamer.EnableEscapeHatch { skip, err := b.streamer.getSkipVerificationPos() if err != nil { log.Error("failed call to get skip verification pos", "err", err) @@ -594,7 +599,7 @@ func (b *BatchPoster) checkEspressoValidation() error { } } - if b.streamer.HotshotDown && b.streamer.EnableEscapeHatch { + if b.streamer.UseEscapeHatch && b.streamer.HotshotDown && b.streamer.EnableEscapeHatch { log.Warn("skipped espresso verification due to hotshot failure", "pos", b.building.msgCount) return nil } @@ -1418,7 +1423,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) // Submit message positions to pending queue shouldSubmit := b.streamer.shouldSubmitEspressoTransaction() - if !b.streamer.EnableEscapeHatch || shouldSubmit { + if (b.streamer.UseEscapeHatch && !b.streamer.EnableEscapeHatch) || shouldSubmit { for p := b.building.msgCount; p < msgCount; p += 1 { err = b.submitEspressoTransactionPos(p) if err != nil { diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index ee006a4db4..34ad0a7ac0 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -1729,7 +1729,7 @@ func (s *TransactionStreamer) checkEspressoLiveness(ctx context.Context) error { s.HotshotDown = true } - if !s.EnableEscapeHatch { + if s.UseEscapeHatch && !s.EnableEscapeHatch { return nil } @@ -1813,16 +1813,14 @@ func (s *TransactionStreamer) espressoSwitch(ctx context.Context, ignored struct enabledEspresso := s.espressoTEEVerifierAddress != common.Address{} if enabledEspresso { var err error - if s.UseEscapeHatch { - err = s.checkEspressoLiveness(ctx) - if err != nil { - if ctx.Err() != nil { - return 0 - } - logLevel := getLogLevel(err) - logLevel("error checking escape hatch, will retry", "err", err) - return retryRate + err = s.checkEspressoLiveness(ctx) + if err != nil { + if ctx.Err() != nil { + return 0 } + logLevel := getLogLevel(err) + logLevel("error checking escape hatch, will retry", "err", err) + return retryRate } err = s.pollSubmittedTransactionForFinality(ctx) if err != nil { From 93bf3946f7c1506a32e1e375e58720ab93bf63f3 Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Thu, 19 Dec 2024 11:18:28 +0800 Subject: [PATCH 03/10] rename the flag and if escape hatch is not used, not to check the hotshot livenesses --- arbnode/batch_poster.go | 4 +- arbnode/transaction_streamer.go | 58 ++++++++++------------ system_tests/espresso_escape_hatch_test.go | 4 +- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 76de9a7736..a877e60430 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -583,7 +583,7 @@ func (b *BatchPoster) checkEspressoValidation() error { return nil } - if b.streamer.UseEscapeHatch && b.streamer.EnableEscapeHatch { + if b.streamer.UseEscapeHatch { skip, err := b.streamer.getSkipVerificationPos() if err != nil { log.Error("failed call to get skip verification pos", "err", err) @@ -599,7 +599,7 @@ func (b *BatchPoster) checkEspressoValidation() error { } } - if b.streamer.UseEscapeHatch && b.streamer.HotshotDown && b.streamer.EnableEscapeHatch { + if b.streamer.EnableEscapeHatch { log.Warn("skipped espresso verification due to hotshot failure", "pos", b.building.msgCount) return nil } diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 34ad0a7ac0..496ff92c57 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -87,7 +87,6 @@ type TransactionStreamer struct { espressoSwitchDelayThreshold uint64 espressoMaxTransactionSize uint64 // Public these fields for testing - HotshotDown bool EnableEscapeHatch bool UseEscapeHatch bool @@ -1347,7 +1346,7 @@ func (s *TransactionStreamer) pollSubmittedTransactionForFinality(ctx context.Co batch := s.db.NewBatch() if err := s.cleanEspressoSubmittedData(batch); err != nil { - return nil + return err } lastConfirmedPos := submittedTxnPos[len(submittedTxnPos)-1] if err := s.setEspressoLastConfirmedPos(batch, &lastConfirmedPos); err != nil { @@ -1709,16 +1708,17 @@ func (s *TransactionStreamer) submitEspressoTransactions(ctx context.Context) ti return s.espressoTxnsPollingInterval } +// Make sure useEscapeHatch is true func (s *TransactionStreamer) checkEspressoLiveness(ctx context.Context) error { live, err := s.lightClientReader.IsHotShotLive(s.espressoSwitchDelayThreshold) if err != nil { return err } - // If hotshot is down, escape hatch is activated, the only thing is to check if hotshot is live again - if s.HotshotDown { + // If escape hatch is acitivated, the only thing is to check if hotshot is live again + if s.EnableEscapeHatch { if live { log.Info("HotShot is up, disabling the escape hatch") - s.HotshotDown = false + s.EnableEscapeHatch = false } return nil } @@ -1726,13 +1726,10 @@ func (s *TransactionStreamer) checkEspressoLiveness(ctx context.Context) error { // If hotshot was previously up, now it is down if !live { log.Warn("enabling the escape hatch, hotshot is down") - s.HotshotDown = true - } - - if s.UseEscapeHatch && !s.EnableEscapeHatch { - return nil + s.EnableEscapeHatch = true } + // Check if the submitted transaction should skip the espresso verification submittedHash, err := s.getEspressoSubmittedHash() if err != nil { return err @@ -1810,9 +1807,8 @@ func getLogLevel(err error) func(string, ...interface{}) { func (s *TransactionStreamer) espressoSwitch(ctx context.Context, ignored struct{}) time.Duration { retryRate := s.espressoTxnsPollingInterval * 50 - enabledEspresso := s.espressoTEEVerifierAddress != common.Address{} - if enabledEspresso { - var err error + var err error + if s.UseEscapeHatch { err = s.checkEspressoLiveness(ctx) if err != nil { if ctx.Err() != nil { @@ -1822,31 +1818,29 @@ func (s *TransactionStreamer) espressoSwitch(ctx context.Context, ignored struct logLevel("error checking escape hatch, will retry", "err", err) return retryRate } - err = s.pollSubmittedTransactionForFinality(ctx) - if err != nil { - if ctx.Err() != nil { - return 0 - } - logLevel := getLogLevel(err) - logLevel("error polling finality, will retry", "err", err) - return retryRate - } else { - espressoMerkleProofEphemeralErrorHandler.Reset() - } - - shouldSubmit := s.shouldSubmitEspressoTransaction() - if shouldSubmit { - return s.submitEspressoTransactions(ctx) + espressoTransactionEphemeralErrorHandler.Reset() + } + err = s.pollSubmittedTransactionForFinality(ctx) + if err != nil { + if ctx.Err() != nil { + return 0 } - - return s.espressoTxnsPollingInterval - } else { + logLevel := getLogLevel(err) + logLevel("error polling finality, will retry", "err", err) return retryRate } + espressoMerkleProofEphemeralErrorHandler.Reset() + + shouldSubmit := s.shouldSubmitEspressoTransaction() + if shouldSubmit { + return s.submitEspressoTransactions(ctx) + } + + return s.espressoTxnsPollingInterval } func (s *TransactionStreamer) shouldSubmitEspressoTransaction() bool { - return !s.HotshotDown + return !(s.UseEscapeHatch && s.EnableEscapeHatch) } func (s *TransactionStreamer) Start(ctxIn context.Context) error { diff --git a/system_tests/espresso_escape_hatch_test.go b/system_tests/espresso_escape_hatch_test.go index 8be0c7612b..89a5283a91 100644 --- a/system_tests/espresso_escape_hatch_test.go +++ b/system_tests/espresso_escape_hatch_test.go @@ -68,7 +68,7 @@ func TestEspressoEscapeHatch(t *testing.T) { log.Info("waiting for light client to report hotshot is down") err = waitForWith(ctx, 10*time.Minute, 10*time.Second, func() bool { log.Info("waiting for hotshot down") - return builder.L2.ConsensusNode.TxStreamer.HotshotDown + return builder.L2.ConsensusNode.TxStreamer.EnableEscapeHatch }) Require(t, err) @@ -122,7 +122,7 @@ func TestEspressoEscapeHatch(t *testing.T) { err = waitForWith(ctx, 10*time.Minute, 10*time.Second, func() bool { log.Info("waiting for hotshot down") - return builder.L2.ConsensusNode.TxStreamer.HotshotDown + return builder.L2.ConsensusNode.TxStreamer.EnableEscapeHatch }) Require(t, err) From d039534fa82e76c725c08f5b2d4e8dd40af7f1bb Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Thu, 19 Dec 2024 11:23:58 +0800 Subject: [PATCH 04/10] Rename error --- arbnode/batch_poster.go | 6 +++--- arbnode/transaction_streamer.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index a877e60430..be92708cf6 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -561,7 +561,7 @@ func AccessList(opts *AccessListOpts) types.AccessList { return l } -var EspressoFetchMerkleRootErr = errors.New("failed to fetch the espresso merkle roof") +var EspressoValidationErr = errors.New("failed to check espresso validation") var EspressoFetchTransactionErr = errors.New("failed to fetch the espresso transaction") // Adds a block merkle proof to an Espresso justification, providing a proof that a set of transactions @@ -604,7 +604,7 @@ func (b *BatchPoster) checkEspressoValidation() error { return nil } - return fmt.Errorf("%w (height: %d)", EspressoFetchMerkleRootErr, b.building.msgCount) + return fmt.Errorf("%w (height: %d)", EspressoValidationErr, b.building.msgCount) } func (b *BatchPoster) submitEspressoTransactionPos(pos arbutil.MessageIndex) error { @@ -1759,7 +1759,7 @@ func (b *BatchPoster) Start(ctxIn context.Context) { storageRaceEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, storage.ErrStorageRace.Error(), time.Minute) normalGasEstimationFailedEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, ErrNormalGasEstimationFailed.Error(), time.Minute) accumulatorNotFoundEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, AccumulatorNotFoundErr.Error(), time.Minute) - espressoEphemeralErrorHandler := util.NewEphemeralErrorHandler(80*time.Minute, EspressoFetchMerkleRootErr.Error(), time.Hour) + espressoEphemeralErrorHandler := util.NewEphemeralErrorHandler(80*time.Minute, EspressoValidationErr.Error(), time.Hour) resetAllEphemeralErrs := func() { commonEphemeralErrorHandler.Reset() exceedMaxMempoolSizeEphemeralErrorHandler.Reset() diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 496ff92c57..7b366ccbf2 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -1283,7 +1283,7 @@ func (s *TransactionStreamer) pollSubmittedTransactionForFinality(ctx context.Co // Verify the merkle proof snapshot, err := s.lightClientReader.FetchMerkleRoot(height, nil) if err != nil { - return fmt.Errorf("%w (height: %d): %w", EspressoFetchMerkleRootErr, height, err) + return fmt.Errorf("%w (height: %d): %w", EspressoValidationErr, height, err) } if snapshot.Height <= height { @@ -1795,7 +1795,7 @@ func (s *TransactionStreamer) checkEspressoLiveness(ctx context.Context) error { return nil } -var espressoMerkleProofEphemeralErrorHandler = util.NewEphemeralErrorHandler(80*time.Minute, EspressoFetchMerkleRootErr.Error(), time.Hour) +var espressoMerkleProofEphemeralErrorHandler = util.NewEphemeralErrorHandler(80*time.Minute, EspressoValidationErr.Error(), time.Hour) var espressoTransactionEphemeralErrorHandler = util.NewEphemeralErrorHandler(3*time.Minute, EspressoFetchTransactionErr.Error(), time.Minute) func getLogLevel(err error) func(string, ...interface{}) { From fd64f0852ca69059b1adc3fd1722f0785d38ca02 Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Thu, 19 Dec 2024 11:44:11 +0800 Subject: [PATCH 05/10] escape hatch --- arbnode/batch_poster.go | 2 +- arbnode/transaction_streamer.go | 48 +++++++-------------------------- 2 files changed, 11 insertions(+), 39 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index be92708cf6..a7fdaac685 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -1423,7 +1423,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) // Submit message positions to pending queue shouldSubmit := b.streamer.shouldSubmitEspressoTransaction() - if (b.streamer.UseEscapeHatch && !b.streamer.EnableEscapeHatch) || shouldSubmit { + if shouldSubmit { for p := b.building.msgCount; p < msgCount; p += 1 { err = b.submitEspressoTransactionPos(p) if err != nil { diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 7b366ccbf2..73fda8d457 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -1709,7 +1709,7 @@ func (s *TransactionStreamer) submitEspressoTransactions(ctx context.Context) ti } // Make sure useEscapeHatch is true -func (s *TransactionStreamer) checkEspressoLiveness(ctx context.Context) error { +func (s *TransactionStreamer) checkEspressoLiveness() error { live, err := s.lightClientReader.IsHotShotLive(s.espressoSwitchDelayThreshold) if err != nil { return err @@ -1723,50 +1723,22 @@ func (s *TransactionStreamer) checkEspressoLiveness(ctx context.Context) error { return nil } - // If hotshot was previously up, now it is down - if !live { - log.Warn("enabling the escape hatch, hotshot is down") - s.EnableEscapeHatch = true - } - - // Check if the submitted transaction should skip the espresso verification - submittedHash, err := s.getEspressoSubmittedHash() - if err != nil { - return err - } - - // No transaction is waiting for espresso finalization - if submittedHash == nil { + // If escape hatch is disabled, hotshot is live, everything is fine + if live { return nil } - // If a submitted transaction is waiting for being finalized, check if hotshot is live at - // the corresponding L1 height. - data, err := s.espressoClient.FetchTransactionByHash(ctx, submittedHash) - if err != nil { - return err - } + // If escape hatch is on, and hotshot is down + log.Warn("enabling the escape hatch, hotshot is down") + s.EnableEscapeHatch = true - header, err := s.espressoClient.FetchHeaderByHeight(ctx, data.BlockHeight) - if err != nil { - return err - } - - l1Height := header.Header.GetL1Head() - hotshotLive, err := s.lightClientReader.IsHotShotLiveAtHeight(l1Height, s.espressoSwitchDelayThreshold) - if err != nil { - return err - } - if hotshotLive { - // This transaction will be still finalized - return nil - } + // Skip the espresso verification for the submitted messages submitted, err := s.getEspressoSubmittedPos() if err != nil { return err } if len(submitted) == 0 { - return fmt.Errorf("submitted messages should not have the length of 0") + return nil } last := submitted[len(submitted)-1] @@ -1809,7 +1781,7 @@ func (s *TransactionStreamer) espressoSwitch(ctx context.Context, ignored struct retryRate := s.espressoTxnsPollingInterval * 50 var err error if s.UseEscapeHatch { - err = s.checkEspressoLiveness(ctx) + err = s.checkEspressoLiveness() if err != nil { if ctx.Err() != nil { return 0 @@ -1840,7 +1812,7 @@ func (s *TransactionStreamer) espressoSwitch(ctx context.Context, ignored struct } func (s *TransactionStreamer) shouldSubmitEspressoTransaction() bool { - return !(s.UseEscapeHatch && s.EnableEscapeHatch) + return !s.EnableEscapeHatch } func (s *TransactionStreamer) Start(ctxIn context.Context) error { From 62ef054df1bf3e525e1c155c9f246473665f6dc6 Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Thu, 19 Dec 2024 11:48:50 +0800 Subject: [PATCH 06/10] Rename and the enabled flag should be set by users --- arbnode/batch_poster.go | 7 +------ arbnode/transaction_streamer.go | 13 +++++++------ system_tests/espresso_escape_hatch_test.go | 8 ++++---- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index a7fdaac685..b352d8eba8 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -179,7 +179,6 @@ type BatchPosterConfig struct { // Espresso specific flags LightClientAddress string `koanf:"light-client-address"` HotShotUrl string `koanf:"hotshot-url"` - EnableEscapeHatch bool `koanf:"use-escape-hatch"` UseEscapeHatch bool `koanf:"use-escape-hatch"` EspressoTxnsPollingInterval time.Duration `koanf:"espresso-txns-polling-interval"` EspressoSwitchDelayThreshold uint64 `koanf:"espresso-switch-delay-threshold"` @@ -241,7 +240,6 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Uint64(prefix+".gas-estimate-base-fee-multiple-bips", uint64(DefaultBatchPosterConfig.GasEstimateBaseFeeMultipleBips), "for gas estimation, use this multiple of the basefee (measured in basis points) as the max fee per gas") f.Duration(prefix+".reorg-resistance-margin", DefaultBatchPosterConfig.ReorgResistanceMargin, "do not post batch if its within this duration from layer 1 minimum bounds. Requires l1-block-bound option not be set to \"ignore\"") f.Bool(prefix+".check-batch-correctness", DefaultBatchPosterConfig.CheckBatchCorrectness, "setting this to true will run the batch against an inbox multiplexer and verifies that it produces the correct set of messages") - f.Bool(prefix+".use-escape-hatch", DefaultBatchPosterConfig.EnableEscapeHatch, "if true, batches will be posted without doing the espresso verification when hotshot is down. If false, wait for hotshot being up") f.Bool(prefix+".use-escape-hatch", DefaultBatchPosterConfig.UseEscapeHatch, "if true, Escape Hatch functionality will be used") f.Duration(prefix+".espresso-txns-polling-interval", DefaultBatchPosterConfig.EspressoTxnsPollingInterval, "interval between polling for transactions to be included in the block") f.Uint64(prefix+".espresso-switch-delay-threshold", DefaultBatchPosterConfig.EspressoSwitchDelayThreshold, "specifies the switch delay threshold used to determine hotshot liveness") @@ -278,7 +276,6 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ GasEstimateBaseFeeMultipleBips: arbmath.OneInUBips * 3 / 2, ReorgResistanceMargin: 10 * time.Minute, CheckBatchCorrectness: true, - EnableEscapeHatch: false, UseEscapeHatch: false, EspressoTxnsPollingInterval: time.Millisecond * 500, EspressoSwitchDelayThreshold: 350, @@ -317,7 +314,6 @@ var TestBatchPosterConfig = BatchPosterConfig{ UseAccessLists: true, GasEstimateBaseFeeMultipleBips: arbmath.OneInUBips * 3 / 2, CheckBatchCorrectness: true, - EnableEscapeHatch: false, UseEscapeHatch: false, EspressoTxnsPollingInterval: time.Millisecond * 500, EspressoSwitchDelayThreshold: 10, @@ -385,7 +381,6 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e return nil, err } opts.Streamer.lightClientReader = lightClientReader - opts.Streamer.EnableEscapeHatch = opts.Config().EnableEscapeHatch opts.Streamer.UseEscapeHatch = opts.Config().UseEscapeHatch opts.Streamer.espressoTxnsPollingInterval = opts.Config().EspressoTxnsPollingInterval opts.Streamer.espressoSwitchDelayThreshold = opts.Config().EspressoSwitchDelayThreshold @@ -599,7 +594,7 @@ func (b *BatchPoster) checkEspressoValidation() error { } } - if b.streamer.EnableEscapeHatch { + if b.streamer.EscapeHatchEnabled { log.Warn("skipped espresso verification due to hotshot failure", "pos", b.building.msgCount) return nil } diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 73fda8d457..b0ba87bde8 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -87,8 +87,8 @@ type TransactionStreamer struct { espressoSwitchDelayThreshold uint64 espressoMaxTransactionSize uint64 // Public these fields for testing - EnableEscapeHatch bool - UseEscapeHatch bool + EscapeHatchEnabled bool + UseEscapeHatch bool espressoTEEVerifierAddress common.Address } @@ -143,6 +143,7 @@ func NewTransactionStreamer( fatalErrChan: fatalErrChan, config: config, snapSyncConfig: snapSyncConfig, + EscapeHatchEnabled: false, } err := streamer.cleanupInconsistentState() @@ -1715,10 +1716,10 @@ func (s *TransactionStreamer) checkEspressoLiveness() error { return err } // If escape hatch is acitivated, the only thing is to check if hotshot is live again - if s.EnableEscapeHatch { + if s.EscapeHatchEnabled { if live { log.Info("HotShot is up, disabling the escape hatch") - s.EnableEscapeHatch = false + s.EscapeHatchEnabled = false } return nil } @@ -1730,7 +1731,7 @@ func (s *TransactionStreamer) checkEspressoLiveness() error { // If escape hatch is on, and hotshot is down log.Warn("enabling the escape hatch, hotshot is down") - s.EnableEscapeHatch = true + s.EscapeHatchEnabled = true // Skip the espresso verification for the submitted messages submitted, err := s.getEspressoSubmittedPos() @@ -1812,7 +1813,7 @@ func (s *TransactionStreamer) espressoSwitch(ctx context.Context, ignored struct } func (s *TransactionStreamer) shouldSubmitEspressoTransaction() bool { - return !s.EnableEscapeHatch + return !s.EscapeHatchEnabled } func (s *TransactionStreamer) Start(ctxIn context.Context) error { diff --git a/system_tests/espresso_escape_hatch_test.go b/system_tests/espresso_escape_hatch_test.go index 89a5283a91..f13259d4ca 100644 --- a/system_tests/espresso_escape_hatch_test.go +++ b/system_tests/espresso_escape_hatch_test.go @@ -55,7 +55,7 @@ func TestEspressoEscapeHatch(t *testing.T) { address := common.HexToAddress(lightClientAddress) txOpts := builder.L1Info.GetDefaultTransactOpts("Faucet", ctx) - if builder.L2.ConsensusNode.TxStreamer.EnableEscapeHatch { + if builder.L2.ConsensusNode.TxStreamer.EscapeHatchEnabled { t.Fatal("testing not using escape hatch first") } log.Info("Checking turning off the escape hatch") @@ -68,7 +68,7 @@ func TestEspressoEscapeHatch(t *testing.T) { log.Info("waiting for light client to report hotshot is down") err = waitForWith(ctx, 10*time.Minute, 10*time.Second, func() bool { log.Info("waiting for hotshot down") - return builder.L2.ConsensusNode.TxStreamer.EnableEscapeHatch + return builder.L2.ConsensusNode.TxStreamer.EscapeHatchEnabled }) Require(t, err) @@ -115,14 +115,14 @@ func TestEspressoEscapeHatch(t *testing.T) { log.Info("testing escape hatch") // Modify it manually - builder.L2.ConsensusNode.TxStreamer.EnableEscapeHatch = true + builder.L2.ConsensusNode.TxStreamer.EscapeHatchEnabled = true err = lightclientmock.FreezeL1Height(t, builder.L1.Client, address, &txOpts) Require(t, err) err = waitForWith(ctx, 10*time.Minute, 10*time.Second, func() bool { log.Info("waiting for hotshot down") - return builder.L2.ConsensusNode.TxStreamer.EnableEscapeHatch + return builder.L2.ConsensusNode.TxStreamer.EscapeHatchEnabled }) Require(t, err) From 59d043b0cb2de81cc7b06eeb3d935be710e68d39 Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Thu, 19 Dec 2024 12:11:09 +0800 Subject: [PATCH 07/10] typo --- arbnode/transaction_streamer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index b0ba87bde8..c1f1a6867b 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -1715,7 +1715,7 @@ func (s *TransactionStreamer) checkEspressoLiveness() error { if err != nil { return err } - // If escape hatch is acitivated, the only thing is to check if hotshot is live again + // If escape hatch is activated, the only thing is to check if hotshot is live again if s.EscapeHatchEnabled { if live { log.Info("HotShot is up, disabling the escape hatch") From 7288f94f27f40dd33e27042f346f3179e0e3fd16 Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Thu, 19 Dec 2024 12:31:57 +0800 Subject: [PATCH 08/10] Remove TEE address in batch poster --- arbnode/batch_poster.go | 4 ---- arbnode/transaction_streamer.go | 2 -- system_tests/espresso_e2e_test.go | 3 --- system_tests/espresso_sovereign_sequencer_test.go | 1 - 4 files changed, 10 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index b352d8eba8..5346a4ba3d 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -183,7 +183,6 @@ type BatchPosterConfig struct { EspressoTxnsPollingInterval time.Duration `koanf:"espresso-txns-polling-interval"` EspressoSwitchDelayThreshold uint64 `koanf:"espresso-switch-delay-threshold"` EspressoMaxTransactionSize uint64 `koanf:"espresso-max-transaction-size"` - EspressoTEEVerifierAddress string `koanf:"espresso-tee-verifier-address"` } func (c *BatchPosterConfig) Validate() error { @@ -243,7 +242,6 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".use-escape-hatch", DefaultBatchPosterConfig.UseEscapeHatch, "if true, Escape Hatch functionality will be used") f.Duration(prefix+".espresso-txns-polling-interval", DefaultBatchPosterConfig.EspressoTxnsPollingInterval, "interval between polling for transactions to be included in the block") f.Uint64(prefix+".espresso-switch-delay-threshold", DefaultBatchPosterConfig.EspressoSwitchDelayThreshold, "specifies the switch delay threshold used to determine hotshot liveness") - f.String(prefix+".espresso-tee-verifier-address", DefaultBatchPosterConfig.EspressoTEEVerifierAddress, "") redislock.AddConfigOptions(prefix+".redis-lock", f) dataposter.DataPosterConfigAddOptions(prefix+".data-poster", f, dataposter.DefaultDataPosterConfig) genericconf.WalletConfigAddOptions(prefix+".parent-chain-wallet", f, DefaultBatchPosterConfig.ParentChainWallet.Pathname) @@ -282,7 +280,6 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ LightClientAddress: "", HotShotUrl: "", EspressoMaxTransactionSize: 900 * 1024, - EspressoTEEVerifierAddress: "", } var DefaultBatchPosterL1WalletConfig = genericconf.WalletConfig{ @@ -385,7 +382,6 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e opts.Streamer.espressoTxnsPollingInterval = opts.Config().EspressoTxnsPollingInterval opts.Streamer.espressoSwitchDelayThreshold = opts.Config().EspressoSwitchDelayThreshold opts.Streamer.espressoMaxTransactionSize = opts.Config().EspressoMaxTransactionSize - opts.Streamer.espressoTEEVerifierAddress = common.HexToAddress(opts.Config().EspressoTEEVerifierAddress) } b := &BatchPoster{ diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index c1f1a6867b..62cd6a5a8c 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -89,8 +89,6 @@ type TransactionStreamer struct { // Public these fields for testing EscapeHatchEnabled bool UseEscapeHatch bool - - espressoTEEVerifierAddress common.Address } type TransactionStreamerConfig struct { diff --git a/system_tests/espresso_e2e_test.go b/system_tests/espresso_e2e_test.go index 8a6784eb29..f4e9601cbe 100644 --- a/system_tests/espresso_e2e_test.go +++ b/system_tests/espresso_e2e_test.go @@ -24,9 +24,6 @@ var workingDir = "./espresso-e2e" // light client proxy var lightClientAddress = "0x60571c8f4b52954a24a5e7306d435e951528d963" -// TODO: espresso TEE verifier address -var verifierAddress = "0x60571c8f4b52954a24a5e7306d435e951528d963" - var hotShotUrl = "http://127.0.0.1:41000" var ( diff --git a/system_tests/espresso_sovereign_sequencer_test.go b/system_tests/espresso_sovereign_sequencer_test.go index f532b7e4be..53cee6f161 100644 --- a/system_tests/espresso_sovereign_sequencer_test.go +++ b/system_tests/espresso_sovereign_sequencer_test.go @@ -34,7 +34,6 @@ func createL1AndL2Node( builder.nodeConfig.BatchPoster.MaxDelay = -1000 * time.Hour builder.nodeConfig.BatchPoster.LightClientAddress = lightClientAddress builder.nodeConfig.BatchPoster.HotShotUrl = hotShotUrl - builder.nodeConfig.BatchPoster.EspressoTEEVerifierAddress = verifierAddress // validator config builder.nodeConfig.BlockValidator.Enable = true From dd733c4e09235ea15052f6ee8fea7ebc44b717cc Mon Sep 17 00:00:00 2001 From: ImJeremyHe Date: Thu, 19 Dec 2024 15:54:38 +0800 Subject: [PATCH 09/10] Fix escape hatch test --- system_tests/espresso_escape_hatch_test.go | 12 ++++++++---- system_tests/espresso_sovereign_sequencer_test.go | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/system_tests/espresso_escape_hatch_test.go b/system_tests/espresso_escape_hatch_test.go index f13259d4ca..f58c9e9761 100644 --- a/system_tests/espresso_escape_hatch_test.go +++ b/system_tests/espresso_escape_hatch_test.go @@ -55,10 +55,10 @@ func TestEspressoEscapeHatch(t *testing.T) { address := common.HexToAddress(lightClientAddress) txOpts := builder.L1Info.GetDefaultTransactOpts("Faucet", ctx) - if builder.L2.ConsensusNode.TxStreamer.EscapeHatchEnabled { + if builder.L2.ConsensusNode.TxStreamer.UseEscapeHatch { t.Fatal("testing not using escape hatch first") } - log.Info("Checking turning off the escape hatch") + log.Info("Checking turning on the escape hatch") // Start to check the escape hatch @@ -68,7 +68,11 @@ func TestEspressoEscapeHatch(t *testing.T) { log.Info("waiting for light client to report hotshot is down") err = waitForWith(ctx, 10*time.Minute, 10*time.Second, func() bool { log.Info("waiting for hotshot down") - return builder.L2.ConsensusNode.TxStreamer.EscapeHatchEnabled + live, err := lightclientmock.IsHotShotLive(t, builder.L1.Client, address, 1) + if err != nil { + log.Error("error checking hotshot live", "err", err) + } + return !live }) Require(t, err) @@ -115,7 +119,7 @@ func TestEspressoEscapeHatch(t *testing.T) { log.Info("testing escape hatch") // Modify it manually - builder.L2.ConsensusNode.TxStreamer.EscapeHatchEnabled = true + builder.L2.ConsensusNode.TxStreamer.UseEscapeHatch = true err = lightclientmock.FreezeL1Height(t, builder.L1.Client, address, &txOpts) Require(t, err) diff --git a/system_tests/espresso_sovereign_sequencer_test.go b/system_tests/espresso_sovereign_sequencer_test.go index 53cee6f161..f636ee6ee0 100644 --- a/system_tests/espresso_sovereign_sequencer_test.go +++ b/system_tests/espresso_sovereign_sequencer_test.go @@ -34,6 +34,7 @@ func createL1AndL2Node( builder.nodeConfig.BatchPoster.MaxDelay = -1000 * time.Hour builder.nodeConfig.BatchPoster.LightClientAddress = lightClientAddress builder.nodeConfig.BatchPoster.HotShotUrl = hotShotUrl + builder.nodeConfig.BatchPoster.UseEscapeHatch = false // validator config builder.nodeConfig.BlockValidator.Enable = true From 78444a6e48f76ed5a59f16a50d5f2b7521846036 Mon Sep 17 00:00:00 2001 From: Sneh Koul Date: Thu, 19 Dec 2024 08:40:28 -0500 Subject: [PATCH 10/10] Add hotshot height log --- arbnode/transaction_streamer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 62cd6a5a8c..c20a65ea64 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -1271,7 +1271,6 @@ func (s *TransactionStreamer) pollSubmittedTransactionForFinality(ctx context.Co if err != nil { return fmt.Errorf("failed to fetch the submitted transaction hash (hash: %s): %w", submittedTxHash.String(), err) } - height := data.BlockHeight header, err := s.espressoClient.FetchHeaderByHeight(ctx, height) @@ -1279,6 +1278,7 @@ func (s *TransactionStreamer) pollSubmittedTransactionForFinality(ctx context.Co return fmt.Errorf("could not get the header (height: %d): %w", height, err) } + log.Info("Fetching Merkle Root at hotshot height: ", height) // Verify the merkle proof snapshot, err := s.lightClientReader.FetchMerkleRoot(height, nil) if err != nil {