Skip to content

Commit

Permalink
Merge pull request #110 from EspressoSystems/jh/escape-hatch
Browse files Browse the repository at this point in the history
Add SwitchSequencer
  • Loading branch information
ImJeremyHe authored Apr 11, 2024
2 parents 570aeee + 025b1cf commit d8d444f
Show file tree
Hide file tree
Showing 14 changed files with 337 additions and 46 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,13 @@ jobs:
if: matrix.test-mode == 'defaults'
run: |
packages=`go list ./...`
gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- -timeout 25m -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -skip TestEspressoE2E
gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- -timeout 25m -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -skip 'TestEspressoE2E|TestEspressoSwitch'
- name: run tests with race detection
if: matrix.test-mode == 'race'
run: |
packages=`go list ./...`
gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- --timeout 30m -race -skip TestEspressoE2E
gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- --timeout 30m -race -skip 'TestEspressoE2E|TestEspressoSwitch'
- name: run redis tests
if: matrix.test-mode == 'defaults'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/espresso-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,4 @@ jobs:
- name: Run test
run: |
packages=`go list ./... | grep system_tests`
gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- -v -timeout 25m ./... -run TestEspressoE2E
gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- -v -timeout 35m ./... -run 'TestEspressoE2E|TestEspressoSwitch'
13 changes: 5 additions & 8 deletions arbnode/batch_poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/rpc"

hotshotClient "github.com/EspressoSystems/espresso-sequencer-go/client"
lightclient "github.com/EspressoSystems/espresso-sequencer-go/light-client"

"github.com/offchainlabs/nitro/arbnode/dataposter"
"github.com/offchainlabs/nitro/arbnode/dataposter/storage"
Expand Down Expand Up @@ -75,10 +76,6 @@ type batchPosterPosition struct {
NextSeqNum uint64
}

type LightClientReaderInterface interface {
ValidatedHeight() (validatedHeight uint64, l1Height uint64, err error)
}

type BatchPoster struct {
stopwaiter.StopWaiter
l1Reader *headerreader.HeaderReader
Expand Down Expand Up @@ -111,7 +108,7 @@ type BatchPoster struct {
accessList func(SequencerInboxAccs, AfterDelayedMessagesRead int) types.AccessList

// Espresso readers
lightClientReader LightClientReaderInterface
lightClientReader lightclient.LightClientReaderInterface
hotshotClient *hotshotClient.Client
}

Expand Down Expand Up @@ -314,17 +311,17 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e
return nil, err
}
var hotShotClient *hotshotClient.Client
var lightClientReader LightClientReaderInterface
var lightClientReader lightclient.LightClientReaderInterface

hotShotUrl := opts.Config().HotShotUrl
lightClientAddr := opts.Config().LightClientAddress

if hotShotUrl != "" {
hotShotClient = hotshotClient.NewClient(log.New(), hotShotUrl)
hotShotClient = hotshotClient.NewClient(hotShotUrl)
}

if lightClientAddr != "" {
lightClientReader, err = NewMockLightClientReader(common.HexToAddress(lightClientAddr), opts.L1Reader.Client())
lightClientReader, err = arbos.NewMockLightClientReader(common.HexToAddress(lightClientAddr), opts.L1Reader.Client())
if err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package arbnode
package arbos

import (
"time"

"github.com/EspressoSystems/espresso-sequencer-go/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
Expand All @@ -17,3 +20,11 @@ func NewMockLightClientReader(lightClientAddr common.Address, l1client bind.Cont
func (l *MockLightClientReader) ValidatedHeight() (validatedHeight uint64, l1Height uint64, err error) {
return 18446744073709551615, 18446744073709551615, nil
}

func (l *MockLightClientReader) IsHotShotAvaliable(t time.Duration) bool {
return true
}

func (l *MockLightClientReader) FetchMerkleRootAtL1Block(l1BlockHeight uint64) (types.BlockMerkleRoot, error) {
return types.BlockMerkleRoot{}, nil
}
4 changes: 4 additions & 0 deletions execution/gethexec/arb_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type TransactionPublisher interface {
Start(context.Context) error
StopAndWait()
Started() bool

// This is only for testing the switch sequencer. Will be removed if the espresso light client
// contract is ready and we will use another way to trigger the mode switching.
SetMode(ctx context.Context, espresso bool) error
}

type ArbInterface struct {
Expand Down
5 changes: 2 additions & 3 deletions execution/gethexec/espresso_sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type HotShotState struct {

func NewHotShotState(log log.Logger, url string, startBlock uint64) *HotShotState {
return &HotShotState{
client: *espressoClient.NewClient(log, url),
client: *espressoClient.NewClient(url),
nextSeqBlockNum: startBlock,
}
}
Expand Down Expand Up @@ -112,14 +112,13 @@ func (s *EspressoSequencer) createBlock(ctx context.Context) (returnValue bool)
func (s *EspressoSequencer) Start(ctxIn context.Context) error {
s.StopWaiter.Start(ctxIn, s)
s.CallIteratively(func(ctx context.Context) time.Duration {
retryBlockTime := time.Now().Add(retryTime)
madeBlock := s.createBlock(ctx)
if madeBlock {
// Allow the sequencer to catch up to HotShot
return 0
}
// If we didn't make a block, try again in a bit
return time.Until(retryBlockTime)
return retryTime
})

return nil
Expand Down
24 changes: 15 additions & 9 deletions execution/gethexec/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,21 +171,27 @@ func CreateExecutionNode(
}
}

if config.Sequencer.Enable && !config.Sequencer.Espresso {
if config.Sequencer.Enable {
seqConfigFetcher := func() *SequencerConfig { return &configFetcher().Sequencer }
sequencer, err = NewSequencer(execEngine, parentChainReader, seqConfigFetcher)
if err != nil {
return nil, err
}
txPublisher = sequencer
} else if config.Sequencer.Enable && config.Sequencer.Espresso {
seqConfigFetcher := func() *SequencerConfig { return &configFetcher().Sequencer }
espressoSequencer, err := NewEspressoSequencer(execEngine, seqConfigFetcher)
if err != nil {
return nil, err
}
txPublisher = espressoSequencer
if config.Sequencer.Espresso {
seqConfigFetcher := func() *SequencerConfig { return &configFetcher().Sequencer }
espressoSequencer, err := NewEspressoSequencer(execEngine, seqConfigFetcher)
if err != nil {
return nil, err
}
switchSequencer, err := NewSwitchSequencer(sequencer, espressoSequencer, parentChainReader.Client(), seqConfigFetcher)
if err != nil {
return nil, err
}
txPublisher = switchSequencer

} else {
txPublisher = sequencer
}
} else {
if config.Forwarder.RedisUrl != "" {
txPublisher = NewRedisTxForwarder(config.forwardingTarget, &config.Forwarder)
Expand Down
16 changes: 12 additions & 4 deletions execution/gethexec/sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,13 @@ type SequencerConfig struct {
NonceFailureCacheExpiry time.Duration `koanf:"nonce-failure-cache-expiry" reload:"hot"`

// Espresso specific flags
Espresso bool `koanf:"espresso"`
HotShotUrl string `koanf:"hotshot-url"`
EspressoNamespace uint64 `koanf:"espresso-namespace"`
StartHotShotBlock uint64 `koanf:"start-hotshot-block"`
Espresso bool `koanf:"espresso"`
HotShotUrl string `koanf:"hotshot-url"`
LightClientAddress string `koanf:"light-client-address"`
EspressoNamespace uint64 `koanf:"espresso-namespace"`
StartHotShotBlock uint64 `koanf:"start-hotshot-block"`
MaxHotShotDriftTime time.Duration `koanf:"max-hotshot-drift-time"`
SwitchPollInterval time.Duration `koanf:"switch-poll-interval"`
}

func (c *SequencerConfig) Validate() error {
Expand All @@ -84,6 +87,9 @@ func (c *SequencerConfig) Validate() error {
return fmt.Errorf("sequencer sender whitelist entry \"%v\" is not a valid address", address)
}
}
if c.LightClientAddress == "" && c.Espresso {
log.Warn("LightClientAddress is empty, running the espresso test mode")
}
return nil
}

Expand Down Expand Up @@ -138,6 +144,8 @@ func SequencerConfigAddOptions(prefix string, f *flag.FlagSet) {
f.String(prefix+".hotshot-url", DefaultSequencerConfig.HotShotUrl, "")
f.Uint64(prefix+".espresso-namespace", DefaultSequencerConfig.EspressoNamespace, "espresso namespace that corresponds the L2 chain")
f.Uint64(prefix+".start-hotshot-block", DefaultSequencerConfig.StartHotShotBlock, "the starting block number of hotshot")
f.Duration(prefix+".max-hotshot-drift-time", DefaultSequencerConfig.MaxHotShotDriftTime, "maximum drift time of hotshot")
f.Duration(prefix+".switch-poll-interval", DefaultSequencerConfig.SwitchPollInterval, "the poll interval of checking the sequencer should be switched or not")
}

type txQueueItem struct {
Expand Down
160 changes: 160 additions & 0 deletions execution/gethexec/switch_sequencer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package gethexec

import (
"context"
"time"

lightClient "github.com/EspressoSystems/espresso-sequencer-go/light-client"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/arbitrum_types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/offchainlabs/nitro/arbos"
"github.com/offchainlabs/nitro/util/stopwaiter"
)

const (
SequencingMode_Espresso = 0
SequencingMode_Centralized = 1
)

type SwitchSequencer struct {
stopwaiter.StopWaiter

centralized *Sequencer
espresso *EspressoSequencer

maxHotShotDriftTime time.Duration
switchPollInterval time.Duration
lightClient lightClient.LightClientReaderInterface

mode int
}

func NewSwitchSequencer(centralized *Sequencer, espresso *EspressoSequencer, l1client bind.ContractBackend, configFetcher SequencerConfigFetcher) (*SwitchSequencer, error) {
config := configFetcher()
err := config.Validate()
if err != nil {
return nil, err
}

var lightClient lightClient.LightClientReaderInterface
if config.LightClientAddress != "" {
lightClient, err = arbos.NewMockLightClientReader(common.HexToAddress(config.LightClientAddress), l1client)
if err != nil {
return nil, err
}
}

return &SwitchSequencer{
centralized: centralized,
espresso: espresso,
lightClient: lightClient,
mode: SequencingMode_Espresso,
maxHotShotDriftTime: config.MaxHotShotDriftTime,
switchPollInterval: config.SwitchPollInterval,
}, nil
}

func (s *SwitchSequencer) IsRunningEspressoMode() bool {
return s.mode == SequencingMode_Espresso
}

func (s *SwitchSequencer) SwitchToEspresso(ctx context.Context) error {
if s.IsRunningEspressoMode() {
return nil
}
log.Info("Switching to espresso sequencer")

s.mode = SequencingMode_Espresso

s.centralized.StopAndWait()
return s.espresso.Start(ctx)
}

func (s *SwitchSequencer) SwitchToCentralized(ctx context.Context) error {
if !s.IsRunningEspressoMode() {
return nil
}
s.mode = SequencingMode_Centralized
log.Info("Switching to centrialized sequencer")

s.espresso.StopAndWait()
return s.centralized.Start(ctx)
}

func (s *SwitchSequencer) getRunningSequencer() TransactionPublisher {
if s.IsRunningEspressoMode() {
return s.espresso
}
return s.centralized
}

func (s *SwitchSequencer) PublishTransaction(ctx context.Context, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) error {
return s.getRunningSequencer().PublishTransaction(ctx, tx, options)
}

func (s *SwitchSequencer) CheckHealth(ctx context.Context) error {
return s.getRunningSequencer().CheckHealth(ctx)
}

func (s *SwitchSequencer) Initialize(ctx context.Context) error {
err := s.centralized.Initialize(ctx)
if err != nil {
return err
}

return s.espresso.Initialize(ctx)
}

func (s *SwitchSequencer) Start(ctx context.Context) error {
s.StopWaiter.Start(ctx, s)
err := s.getRunningSequencer().Start(ctx)
if err != nil {
return err
}

if s.lightClient != nil {
s.CallIteratively(func(ctx context.Context) time.Duration {
espresso := s.lightClient.IsHotShotAvaliable(s.maxHotShotDriftTime)

var err error
if s.IsRunningEspressoMode() && !espresso {
err = s.SwitchToCentralized(ctx)
} else if !s.IsRunningEspressoMode() && espresso {
err = s.SwitchToEspresso(ctx)
}

if err != nil {
return 0
}
return s.switchPollInterval
})
}

return nil
}

func (s *SwitchSequencer) StopAndWait() {
s.getRunningSequencer().StopAndWait()
s.StopWaiter.StopAndWait()
}

func (s *SwitchSequencer) Started() bool {
return s.getRunningSequencer().Started()
}

func (s *SwitchSequencer) SetMode(ctx context.Context, m bool) error {
if m {
return s.SwitchToEspresso(ctx)
} else {
return s.SwitchToCentralized(ctx)
}
}

func (s *Sequencer) SetMode(ctx context.Context, espresso bool) error { return nil }
func (s *EspressoSequencer) SetMode(ctx context.Context, m bool) error { return nil }
func (s *RedisTxForwarder) SetMode(ctx context.Context, m bool) error { return nil }
func (s *TxDropper) SetMode(ctx context.Context, m bool) error { return nil }
func (s *TxForwarder) SetMode(ctx context.Context, m bool) error { return nil }
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ replace github.com/VictoriaMetrics/fastcache => ./fastcache
replace github.com/ethereum/go-ethereum => ./go-ethereum

require (
github.com/EspressoSystems/espresso-sequencer-go v0.0.10
github.com/EspressoSystems/espresso-sequencer-go v0.0.11
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible
github.com/Shopify/toxiproxy v2.1.4+incompatible
github.com/alicebob/miniredis/v2 v2.21.0
Expand Down Expand Up @@ -71,7 +71,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.11.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.16.4 // indirect
github.com/aws/smithy-go v1.11.2 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.7.0 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
Expand Down
Loading

0 comments on commit d8d444f

Please sign in to comment.