Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 3 additions & 12 deletions .github/workflows/docker-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,9 @@ name: Build and Push Docker Images

on:
push:
branches: [main, celo*]
paths:
- "espresso/docker/**"
- "espresso/docker-compose.yml"
- "config/**"
branches:
- "celo-integration*"
pull_request:
branches: [main, celo*, integration]
paths:
- "espresso/docker/**"
- "espresso/docker-compose.yml"
- "config/**"
workflow_dispatch:

env:
Expand Down Expand Up @@ -45,7 +37,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly-654c8f01721e43dbc8a53c7a3b022548cb82b2f9 # same as for the nix environment
version: nightly-654c8f01721e43dbc8a53c7a3b022548cb82b2f9 # same as for the nix environment

- name: Install dasel
run: |
Expand Down Expand Up @@ -465,4 +457,3 @@ jobs:
TARGET_BASE_IMAGE=alpine:3.22
TARGETOS=linux
TARGETARCH=amd64

4 changes: 0 additions & 4 deletions .github/workflows/espresso-devnet-tests.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
name: Run Espresso Devnet tests
on:
pull_request:
branches:
- "celo-integration*"
push:
branches:
- "master"
- "celo-integration*"
workflow_dispatch:

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


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

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/espresso-enclave.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: Run enclave tests on EC2 instance

on:
pull_request:
branches:
- "celo-integration*"
push:
branches:
- "celo-integration*"
Expand All @@ -19,7 +17,6 @@ jobs:
timeout-minutes: 40

steps:

- name: Checkout repository
uses: actions/checkout@v4

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/espresso-integration.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
name: Run Espresso integration tests
on:
pull_request:
branches:
- "celo-integration*"
push:
branches:
- "master"
- "celo-integration*"
workflow_dispatch:

Expand Down
83 changes: 83 additions & 0 deletions espresso/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"

espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client"
espressoLightClient "github.com/EspressoSystems/espresso-network/sdks/go/light-client"
)

// espressoFlags returns the flag names for espresso
Expand All @@ -28,6 +33,9 @@ var (
LightClientAddrFlagName = espressoFlags("light-client-addr")
L1UrlFlagName = espressoFlags("l1-url")
TestingBatcherPrivateKeyFlagName = espressoFlags("testing-batcher-private-key")
OriginHeight = espressoFlags("origin-height")
NamespaceFlagName = espressoFlags("namespace")
RollupL1UrlFlagName = espressoFlags("rollup-l1-url")
)

func CLIFlags(envPrefix string, category string) []cli.Flag {
Expand Down Expand Up @@ -77,6 +85,24 @@ func CLIFlags(envPrefix string, category string) []cli.Flag {
EnvVars: espressoEnvs(envPrefix, "TESTING_BATCHER_PRIVATE_KEY"),
Category: category,
},
&cli.Uint64Flag{
Name: OriginHeight,
Usage: "Espresso transactions below this height will not be considered",
EnvVars: espressoEnvs(envPrefix, "ORIGIN_HEIGHT"),
Category: category,
},
&cli.Uint64Flag{
Name: NamespaceFlagName,
Usage: "Namespace of Espresso transactions",
EnvVars: espressoEnvs(envPrefix, "NAMESPACE"),
Category: category,
},
&cli.StringFlag{
Name: RollupL1UrlFlagName,
Usage: "RPC URL of L1 backing the Rollup we're streaming for",
EnvVars: espressoEnvs(envPrefix, "ROLLUP_L1_URL"),
Category: category,
},
}
}

Expand All @@ -87,7 +113,10 @@ type CLIConfig struct {
QueryServiceURLs []string
LightClientAddr common.Address
L1URL string
RollupL1URL string
TestingBatcherPrivateKey *ecdsa.PrivateKey
Namespace uint64
OriginHeight uint64
}

func (c CLIConfig) Check() error {
Expand All @@ -102,6 +131,12 @@ func (c CLIConfig) Check() error {
if c.L1URL == "" {
return fmt.Errorf("L1 URL is required when Espresso is enabled")
}
if c.RollupL1URL == "" {
return fmt.Errorf("rollup L1 URL is required when Espresso is enabled")
}
if c.Namespace == 0 {
return fmt.Errorf("namespace is required when Espresso is enabled")
}
}
return nil
}
Expand All @@ -112,6 +147,9 @@ func ReadCLIConfig(c *cli.Context) CLIConfig {
PollInterval: c.Duration(PollIntervalFlagName),
UseFetchAPI: c.Bool(UseFetchApiFlagName),
L1URL: c.String(L1UrlFlagName),
RollupL1URL: c.String(RollupL1UrlFlagName),
Namespace: c.Uint64(NamespaceFlagName),
OriginHeight: c.Uint64(OriginHeight),
}

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

return config
}

func BatchStreamerFromCLIConfig[B Batch](
cfg CLIConfig,
log log.Logger,
unmarshalBatch func([]byte) (*B, error),
) (*BatchStreamer[B], error) {
if !cfg.Enabled {
return nil, fmt.Errorf("Espresso is not enabled")
}

l1Client, err := ethclient.Dial(cfg.L1URL)
if err != nil {
return nil, fmt.Errorf("failed to dial L1 RPC at %s: %w", cfg.L1URL, err)
}

RollupL1Client, err := ethclient.Dial(cfg.RollupL1URL)
if err != nil {
return nil, fmt.Errorf("failed to dial Rollup L1 RPC at %s: %w", cfg.RollupL1URL, err)
}

espressoClient, err := espressoClient.NewMultipleNodesClient(cfg.QueryServiceURLs)
if err != nil {
return nil, fmt.Errorf("failed to create Espresso client: %w", err)
}

espressoLightClient, err := espressoLightClient.NewLightclientCaller(cfg.LightClientAddr, l1Client)
if err != nil {
return nil, fmt.Errorf("failed to create Espresso light client")
}

streamer := NewEspressoStreamer(
cfg.Namespace,
NewAdaptL1BlockRefClient(l1Client),
NewAdaptL1BlockRefClient(RollupL1Client),
espressoClient,
espressoLightClient,
log,
unmarshalBatch,
cfg.PollInterval,
cfg.OriginHeight,
)
streamer.UseFetchApi = cfg.UseFetchAPI

return streamer, nil
}
5 changes: 3 additions & 2 deletions espresso/environment/2_espresso_liveness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
espressoLightClient "github.com/EspressoSystems/espresso-network/sdks/go/light-client"
"github.com/ethereum-optimism/optimism/espresso"
env "github.com/ethereum-optimism/optimism/espresso/environment"
"github.com/ethereum-optimism/optimism/op-batcher/batcher"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-e2e/system/e2esys"
"github.com/ethereum-optimism/optimism/op-e2e/system/helpers"
Expand Down Expand Up @@ -262,14 +261,16 @@ func TestE2eDevnetWithEspressoDegradedLivenessViaCaffNode(t *testing.T) {
require.NoError(t, err, "light client creation failed")
streamer := espresso.NewEspressoStreamer(
system.RollupConfig.L2ChainID.Uint64(),
batcher.NewAdaptL1BlockRefClient(l1Client),
espresso.NewAdaptL1BlockRefClient(l1Client),
espresso.NewAdaptL1BlockRefClient(l1Client),
espressoClient.NewClient(server.URL),
lightClient,
l,
func(b []byte) (*derive.EspressoBatch, error) {
return derive.UnmarshalEspressoTransaction(b, system.RollupConfig.Genesis.SystemConfig.BatcherAddr)
},
100*time.Millisecond,
0,
)

l1Client, _ := client.NewRPC(streamBlocksCtx, l, system.NodeEndpoint(e2esys.RoleL1).RPC())
Expand Down
1 change: 1 addition & 0 deletions espresso/environment/enclave_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func LaunchBatcherInEnclave() E2eDevnetLauncherOption {
appendArg(&args, flags.L1EthRpcFlag.Name, l1Rpc)
appendArg(&args, txmgr.L1RPCFlagName, l1Rpc)
appendArg(&args, espresso.L1UrlFlagName, l1Rpc)
appendArg(&args, espresso.RollupL1UrlFlagName, l1Rpc)
l2EthRpc := sys.EthInstances[e2esys.RoleSeq].UserRPC().(endpoint.HttpRPC).HttpRPC()
appendArg(&args, flags.L2EthRpcFlag.Name, l2EthRpc)
rollupRpc := sys.RollupNodes[e2esys.RoleSeq].UserRPC().(endpoint.HttpRPC).HttpRPC()
Expand Down
1 change: 1 addition & 0 deletions espresso/environment/espresso_caff_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ func LaunchCaffNode(t *testing.T, system *e2esys.System, espressoDevNode Espress
// To create a valid multiple nodes client, we need to provide at least 2 URLs.
QueryServiceURLs: []string{u.String(), u.String()},
L1URL: system.L1.UserRPC().RPC(),
RollupL1URL: system.L1.UserRPC().RPC(),
LightClientAddr: common.HexToAddress(ESPRESSO_LIGHT_CLIENT_ADDRESS),
}

Expand Down
31 changes: 31 additions & 0 deletions espresso/ethclient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package espresso

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)

// AdaptL1BlockRefClient is a wrapper around eth.L1BlockRef that implements the espresso.L1Client interface
type AdaptL1BlockRefClient struct {
L1Client *ethclient.Client
}

// NewAdaptL1BlockRefClient creates a new L1BlockRefClient
func NewAdaptL1BlockRefClient(L1Client *ethclient.Client) *AdaptL1BlockRefClient {
return &AdaptL1BlockRefClient{
L1Client: L1Client,
}
}

// HeaderHashByNumber implements the espresso.L1Client interface
func (c *AdaptL1BlockRefClient) HeaderHashByNumber(ctx context.Context, number *big.Int) (common.Hash, error) {
expectedL1BlockRef, err := c.L1Client.HeaderByNumber(ctx, number)
if err != nil {
return common.Hash{}, err
}

return expectedL1BlockRef.Hash(), nil
}
24 changes: 18 additions & 6 deletions espresso/streamer.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type BatchStreamer[B Batch] struct {
Namespace uint64

L1Client L1Client
RollupL1Client L1Client
EspressoClient EspressoClient
EspressoLightClient LightClientCallerInterface
Log log.Logger
Expand All @@ -87,6 +88,8 @@ type BatchStreamer[B Batch] struct {
fallbackBatchPos uint64
// HotShot position that we can fallback to, guaranteeing not to skip any unsafe batches
fallbackHotShotPos uint64
// HotShot position we start reading from, exclusive
originHotShotPos uint64
// Latest finalized block on the L1.
FinalizedL1 eth.L1BlockRef

Expand All @@ -110,14 +113,17 @@ var _ EspressoStreamer[Batch] = (*BatchStreamer[Batch])(nil)
func NewEspressoStreamer[B Batch](
namespace uint64,
l1Client L1Client,
rollupL1Client L1Client,
espressoClient EspressoClient,
lightClient LightClientCallerInterface,
log log.Logger,
unmarshalBatch func([]byte) (*B, error),
pollingHotShotPollingInterval time.Duration,
originHotShotPos uint64,
) *BatchStreamer[B] {
return &BatchStreamer[B]{
L1Client: l1Client,
RollupL1Client: rollupL1Client,
EspressoClient: espressoClient,
EspressoLightClient: lightClient,
Log: log,
Expand All @@ -127,6 +133,9 @@ func NewEspressoStreamer[B Batch](
PollingHotShotPollingInterval: pollingHotShotPollingInterval,
RemainingBatches: make(map[common.Hash]B),
unmarshalBatch: unmarshalBatch,
originHotShotPos: originHotShotPos,
fallbackHotShotPos: originHotShotPos,
hotShotPos: originHotShotPos,
}
}

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

return err
if err != nil {
return fmt.Errorf("failed to confirm espresso block height: %w", err)
}
return nil
}

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

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

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

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

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

shouldReset = hotshotState.BlockHeight < s.fallbackHotShotPos
Expand Down
Loading