Skip to content

Commit b84fd96

Browse files
authored
Add origin height to Espresso streamer (#255)
1 parent 735d1af commit b84fd96

23 files changed

+223
-169
lines changed

.github/workflows/docker-images.yml

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,9 @@ name: Build and Push Docker Images
22

33
on:
44
push:
5-
branches: [main, celo*]
6-
paths:
7-
- "espresso/docker/**"
8-
- "espresso/docker-compose.yml"
9-
- "config/**"
5+
branches:
6+
- "celo-integration*"
107
pull_request:
11-
branches: [main, celo*, integration]
12-
paths:
13-
- "espresso/docker/**"
14-
- "espresso/docker-compose.yml"
15-
- "config/**"
168
workflow_dispatch:
179

1810
env:
@@ -45,7 +37,7 @@ jobs:
4537
- name: Install Foundry
4638
uses: foundry-rs/foundry-toolchain@v1
4739
with:
48-
version: nightly-654c8f01721e43dbc8a53c7a3b022548cb82b2f9 # same as for the nix environment
40+
version: nightly-654c8f01721e43dbc8a53c7a3b022548cb82b2f9 # same as for the nix environment
4941

5042
- name: Install dasel
5143
run: |
@@ -465,4 +457,3 @@ jobs:
465457
TARGET_BASE_IMAGE=alpine:3.22
466458
TARGETOS=linux
467459
TARGETARCH=amd64
468-

.github/workflows/espresso-devnet-tests.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
name: Run Espresso Devnet tests
22
on:
33
pull_request:
4-
branches:
5-
- "celo-integration*"
64
push:
75
branches:
8-
- "master"
96
- "celo-integration*"
107
workflow_dispatch:
118

@@ -57,7 +54,6 @@ jobs:
5754
- name: Run Challenge Game test
5855
run: go test -timeout 30m -p 1 -count 1 -run 'TestChallengeGame' -v ./espresso/devnet-tests/...
5956

60-
6157
- name: Run Withdraw test
6258
run: go test -timeout 30m -p 1 -count 1 -run 'TestWithdrawal' -v ./espresso/devnet-tests/...
6359

.github/workflows/espresso-enclave.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ name: Run enclave tests on EC2 instance
22

33
on:
44
pull_request:
5-
branches:
6-
- "celo-integration*"
75
push:
86
branches:
97
- "celo-integration*"
@@ -19,7 +17,6 @@ jobs:
1917
timeout-minutes: 40
2018

2119
steps:
22-
2320
- name: Checkout repository
2421
uses: actions/checkout@v4
2522

.github/workflows/espresso-integration.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
name: Run Espresso integration tests
22
on:
33
pull_request:
4-
branches:
5-
- "celo-integration*"
64
push:
75
branches:
8-
- "master"
96
- "celo-integration*"
107
workflow_dispatch:
118

espresso/cli.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import (
88

99
"github.com/ethereum/go-ethereum/common"
1010
"github.com/ethereum/go-ethereum/crypto"
11+
"github.com/ethereum/go-ethereum/ethclient"
12+
"github.com/ethereum/go-ethereum/log"
1113
"github.com/urfave/cli/v2"
14+
15+
espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client"
16+
espressoLightClient "github.com/EspressoSystems/espresso-network/sdks/go/light-client"
1217
)
1318

1419
// espressoFlags returns the flag names for espresso
@@ -28,6 +33,9 @@ var (
2833
LightClientAddrFlagName = espressoFlags("light-client-addr")
2934
L1UrlFlagName = espressoFlags("l1-url")
3035
TestingBatcherPrivateKeyFlagName = espressoFlags("testing-batcher-private-key")
36+
OriginHeight = espressoFlags("origin-height")
37+
NamespaceFlagName = espressoFlags("namespace")
38+
RollupL1UrlFlagName = espressoFlags("rollup-l1-url")
3139
)
3240

3341
func CLIFlags(envPrefix string, category string) []cli.Flag {
@@ -77,6 +85,24 @@ func CLIFlags(envPrefix string, category string) []cli.Flag {
7785
EnvVars: espressoEnvs(envPrefix, "TESTING_BATCHER_PRIVATE_KEY"),
7886
Category: category,
7987
},
88+
&cli.Uint64Flag{
89+
Name: OriginHeight,
90+
Usage: "Espresso transactions below this height will not be considered",
91+
EnvVars: espressoEnvs(envPrefix, "ORIGIN_HEIGHT"),
92+
Category: category,
93+
},
94+
&cli.Uint64Flag{
95+
Name: NamespaceFlagName,
96+
Usage: "Namespace of Espresso transactions",
97+
EnvVars: espressoEnvs(envPrefix, "NAMESPACE"),
98+
Category: category,
99+
},
100+
&cli.StringFlag{
101+
Name: RollupL1UrlFlagName,
102+
Usage: "RPC URL of L1 backing the Rollup we're streaming for",
103+
EnvVars: espressoEnvs(envPrefix, "ROLLUP_L1_URL"),
104+
Category: category,
105+
},
80106
}
81107
}
82108

@@ -87,7 +113,10 @@ type CLIConfig struct {
87113
QueryServiceURLs []string
88114
LightClientAddr common.Address
89115
L1URL string
116+
RollupL1URL string
90117
TestingBatcherPrivateKey *ecdsa.PrivateKey
118+
Namespace uint64
119+
OriginHeight uint64
91120
}
92121

93122
func (c CLIConfig) Check() error {
@@ -102,6 +131,12 @@ func (c CLIConfig) Check() error {
102131
if c.L1URL == "" {
103132
return fmt.Errorf("L1 URL is required when Espresso is enabled")
104133
}
134+
if c.RollupL1URL == "" {
135+
return fmt.Errorf("rollup L1 URL is required when Espresso is enabled")
136+
}
137+
if c.Namespace == 0 {
138+
return fmt.Errorf("namespace is required when Espresso is enabled")
139+
}
105140
}
106141
return nil
107142
}
@@ -112,6 +147,9 @@ func ReadCLIConfig(c *cli.Context) CLIConfig {
112147
PollInterval: c.Duration(PollIntervalFlagName),
113148
UseFetchAPI: c.Bool(UseFetchApiFlagName),
114149
L1URL: c.String(L1UrlFlagName),
150+
RollupL1URL: c.String(RollupL1UrlFlagName),
151+
Namespace: c.Uint64(NamespaceFlagName),
152+
OriginHeight: c.Uint64(OriginHeight),
115153
}
116154

117155
config.QueryServiceURLs = c.StringSlice(QueryServiceUrlsFlagName)
@@ -128,3 +166,48 @@ func ReadCLIConfig(c *cli.Context) CLIConfig {
128166

129167
return config
130168
}
169+
170+
func BatchStreamerFromCLIConfig[B Batch](
171+
cfg CLIConfig,
172+
log log.Logger,
173+
unmarshalBatch func([]byte) (*B, error),
174+
) (*BatchStreamer[B], error) {
175+
if !cfg.Enabled {
176+
return nil, fmt.Errorf("Espresso is not enabled")
177+
}
178+
179+
l1Client, err := ethclient.Dial(cfg.L1URL)
180+
if err != nil {
181+
return nil, fmt.Errorf("failed to dial L1 RPC at %s: %w", cfg.L1URL, err)
182+
}
183+
184+
RollupL1Client, err := ethclient.Dial(cfg.RollupL1URL)
185+
if err != nil {
186+
return nil, fmt.Errorf("failed to dial Rollup L1 RPC at %s: %w", cfg.RollupL1URL, err)
187+
}
188+
189+
espressoClient, err := espressoClient.NewMultipleNodesClient(cfg.QueryServiceURLs)
190+
if err != nil {
191+
return nil, fmt.Errorf("failed to create Espresso client: %w", err)
192+
}
193+
194+
espressoLightClient, err := espressoLightClient.NewLightclientCaller(cfg.LightClientAddr, l1Client)
195+
if err != nil {
196+
return nil, fmt.Errorf("failed to create Espresso light client")
197+
}
198+
199+
streamer := NewEspressoStreamer(
200+
cfg.Namespace,
201+
NewAdaptL1BlockRefClient(l1Client),
202+
NewAdaptL1BlockRefClient(RollupL1Client),
203+
espressoClient,
204+
espressoLightClient,
205+
log,
206+
unmarshalBatch,
207+
cfg.PollInterval,
208+
cfg.OriginHeight,
209+
)
210+
streamer.UseFetchApi = cfg.UseFetchAPI
211+
212+
return streamer, nil
213+
}

espresso/environment/2_espresso_liveness_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
espressoLightClient "github.com/EspressoSystems/espresso-network/sdks/go/light-client"
1414
"github.com/ethereum-optimism/optimism/espresso"
1515
env "github.com/ethereum-optimism/optimism/espresso/environment"
16-
"github.com/ethereum-optimism/optimism/op-batcher/batcher"
1716
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
1817
"github.com/ethereum-optimism/optimism/op-e2e/system/e2esys"
1918
"github.com/ethereum-optimism/optimism/op-e2e/system/helpers"
@@ -262,14 +261,16 @@ func TestE2eDevnetWithEspressoDegradedLivenessViaCaffNode(t *testing.T) {
262261
require.NoError(t, err, "light client creation failed")
263262
streamer := espresso.NewEspressoStreamer(
264263
system.RollupConfig.L2ChainID.Uint64(),
265-
batcher.NewAdaptL1BlockRefClient(l1Client),
264+
espresso.NewAdaptL1BlockRefClient(l1Client),
265+
espresso.NewAdaptL1BlockRefClient(l1Client),
266266
espressoClient.NewClient(server.URL),
267267
lightClient,
268268
l,
269269
func(b []byte) (*derive.EspressoBatch, error) {
270270
return derive.UnmarshalEspressoTransaction(b, system.RollupConfig.Genesis.SystemConfig.BatcherAddr)
271271
},
272272
100*time.Millisecond,
273+
0,
273274
)
274275

275276
l1Client, _ := client.NewRPC(streamBlocksCtx, l, system.NodeEndpoint(e2esys.RoleL1).RPC())

espresso/environment/enclave_helpers.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ func LaunchBatcherInEnclave() E2eDevnetLauncherOption {
9696
appendArg(&args, flags.L1EthRpcFlag.Name, l1Rpc)
9797
appendArg(&args, txmgr.L1RPCFlagName, l1Rpc)
9898
appendArg(&args, espresso.L1UrlFlagName, l1Rpc)
99+
appendArg(&args, espresso.RollupL1UrlFlagName, l1Rpc)
99100
l2EthRpc := sys.EthInstances[e2esys.RoleSeq].UserRPC().(endpoint.HttpRPC).HttpRPC()
100101
appendArg(&args, flags.L2EthRpcFlag.Name, l2EthRpc)
101102
rollupRpc := sys.RollupNodes[e2esys.RoleSeq].UserRPC().(endpoint.HttpRPC).HttpRPC()

espresso/environment/espresso_caff_node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ func LaunchCaffNode(t *testing.T, system *e2esys.System, espressoDevNode Espress
118118
// To create a valid multiple nodes client, we need to provide at least 2 URLs.
119119
QueryServiceURLs: []string{u.String(), u.String()},
120120
L1URL: system.L1.UserRPC().RPC(),
121+
RollupL1URL: system.L1.UserRPC().RPC(),
121122
LightClientAddr: common.HexToAddress(ESPRESSO_LIGHT_CLIENT_ADDRESS),
122123
}
123124

espresso/ethclient.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package espresso
2+
3+
import (
4+
"context"
5+
"math/big"
6+
7+
"github.com/ethereum/go-ethereum/common"
8+
"github.com/ethereum/go-ethereum/ethclient"
9+
)
10+
11+
// AdaptL1BlockRefClient is a wrapper around eth.L1BlockRef that implements the espresso.L1Client interface
12+
type AdaptL1BlockRefClient struct {
13+
L1Client *ethclient.Client
14+
}
15+
16+
// NewAdaptL1BlockRefClient creates a new L1BlockRefClient
17+
func NewAdaptL1BlockRefClient(L1Client *ethclient.Client) *AdaptL1BlockRefClient {
18+
return &AdaptL1BlockRefClient{
19+
L1Client: L1Client,
20+
}
21+
}
22+
23+
// HeaderHashByNumber implements the espresso.L1Client interface
24+
func (c *AdaptL1BlockRefClient) HeaderHashByNumber(ctx context.Context, number *big.Int) (common.Hash, error) {
25+
expectedL1BlockRef, err := c.L1Client.HeaderByNumber(ctx, number)
26+
if err != nil {
27+
return common.Hash{}, err
28+
}
29+
30+
return expectedL1BlockRef.Hash(), nil
31+
}

espresso/streamer.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type BatchStreamer[B Batch] struct {
7474
Namespace uint64
7575

7676
L1Client L1Client
77+
RollupL1Client L1Client
7778
EspressoClient EspressoClient
7879
EspressoLightClient LightClientCallerInterface
7980
Log log.Logger
@@ -87,6 +88,8 @@ type BatchStreamer[B Batch] struct {
8788
fallbackBatchPos uint64
8889
// HotShot position that we can fallback to, guaranteeing not to skip any unsafe batches
8990
fallbackHotShotPos uint64
91+
// HotShot position we start reading from, exclusive
92+
originHotShotPos uint64
9093
// Latest finalized block on the L1.
9194
FinalizedL1 eth.L1BlockRef
9295

@@ -110,14 +113,17 @@ var _ EspressoStreamer[Batch] = (*BatchStreamer[Batch])(nil)
110113
func NewEspressoStreamer[B Batch](
111114
namespace uint64,
112115
l1Client L1Client,
116+
rollupL1Client L1Client,
113117
espressoClient EspressoClient,
114118
lightClient LightClientCallerInterface,
115119
log log.Logger,
116120
unmarshalBatch func([]byte) (*B, error),
117121
pollingHotShotPollingInterval time.Duration,
122+
originHotShotPos uint64,
118123
) *BatchStreamer[B] {
119124
return &BatchStreamer[B]{
120125
L1Client: l1Client,
126+
RollupL1Client: rollupL1Client,
121127
EspressoClient: espressoClient,
122128
EspressoLightClient: lightClient,
123129
Log: log,
@@ -127,6 +133,9 @@ func NewEspressoStreamer[B Batch](
127133
PollingHotShotPollingInterval: pollingHotShotPollingInterval,
128134
RemainingBatches: make(map[common.Hash]B),
129135
unmarshalBatch: unmarshalBatch,
136+
originHotShotPos: originHotShotPos,
137+
fallbackHotShotPos: originHotShotPos,
138+
hotShotPos: originHotShotPos,
130139
}
131140
}
132141

@@ -147,15 +156,18 @@ func (s *BatchStreamer[B]) RefreshSafeL1Origin(safeL1Origin eth.BlockID) error {
147156
s.Reset()
148157
}
149158

150-
return err
159+
if err != nil {
160+
return fmt.Errorf("failed to confirm espresso block height: %w", err)
161+
}
162+
return nil
151163
}
152164

153165
// Update streamer state based on L1 and L2 sync status
154166
func (s *BatchStreamer[B]) Refresh(ctx context.Context, finalizedL1 eth.L1BlockRef, safeBatchNumber uint64, safeL1Origin eth.BlockID) error {
155167
s.FinalizedL1 = finalizedL1
156168

157169
if err := s.RefreshSafeL1Origin(safeL1Origin); err != nil {
158-
return err
170+
return fmt.Errorf("failed to refresh safe L1 origin: %w", err)
159171
}
160172

161173
// NOTE: be sure to update s.finalizedL1 before checking this condition and returning
@@ -187,7 +199,7 @@ func (s *BatchStreamer[B]) CheckBatch(ctx context.Context, batch B) (BatchValidi
187199
return BatchUndecided, 0
188200
}
189201

190-
l1headerHash, err := s.L1Client.HeaderHashByNumber(ctx, new(big.Int).SetUint64(origin.Number))
202+
l1headerHash, err := s.RollupL1Client.HeaderHashByNumber(ctx, new(big.Int).SetUint64(origin.Number))
191203
if err != nil {
192204
// Signal to resync to be able to fetch the L1 header.
193205
s.Log.Warn("Failed to fetch the L1 header, pending resync", "error", err)
@@ -263,7 +275,7 @@ func (s *BatchStreamer[B]) Update(ctx context.Context) error {
263275
// the current block height available to process.
264276
currentBlockHeight, err := s.EspressoClient.FetchLatestBlockHeight(ctx)
265277
if err != nil {
266-
return err
278+
return fmt.Errorf("failed to fetch latest block height: %w", err)
267279
}
268280

269281
// Streaming API implementation
@@ -544,10 +556,10 @@ func (s *BatchStreamer[B]) confirmEspressoBlockHeight(safeL1Origin eth.BlockID)
544556
hotshotState, err := s.EspressoLightClient.
545557
FinalizedState(&bind.CallOpts{BlockNumber: new(big.Int).SetUint64(safeL1Origin.Number)})
546558
if errors.Is(err, bind.ErrNoCode) {
547-
s.fallbackHotShotPos = 0
559+
s.fallbackHotShotPos = s.originHotShotPos
548560
return false, nil
549561
} else if err != nil {
550-
return false, err
562+
return false, fmt.Errorf("failed to get finalized state from light client: %w", err)
551563
}
552564

553565
shouldReset = hotshotState.BlockHeight < s.fallbackHotShotPos

0 commit comments

Comments
 (0)