Skip to content
Open
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
85 changes: 69 additions & 16 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
"net/url"
"os"
"proxy/proxy"
verifier "proxy/verifier/op"
nitroVerifier "proxy/verifier/nitro"
opVerifier "proxy/verifier/op"
"time"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -25,19 +26,30 @@ type OPConfig struct {
BatchAuthenticatorAddress string `json:"batch_authenticator_address"`
}

type NitroConfig struct {
Enable bool `json:"enable"`
FeedURL string `json:"feed_url"`
VerificationInterval time.Duration `json:"verification_interval"`
QueryServiceURL string `json:"query_service_url"`
Namespace uint64 `json:"namespace"`
InitialHotshotBlock uint64 `json:"initial_hotshot_block"`
ValidBatcherAddresses []string `json:"valid_batcher_addresses"`
}

type Config struct {
FullNodeExecutionRPC string `json:"full_node_execution_rpc"`
L1RPC string `json:"l1_rpc"`
ListenAddr string `json:"listen_addr"`
EspressoTag string `json:"espresso_tag"`
StoreFilePath string `json:"store_file_path"`
InitialHotshotHeight uint64 `json:"initial_hotshot_height"`
MaxBatchSize int `json:"max_batch_size"`
MaxRequestBodySize int `json:"max_request_body_size"`
OPConfig OPConfig `json:"op"`
LogLevel string `json:"log_level"`
LogFormat string `json:"log_format"`
TrackBatchLatency bool `json:"track_batch_latency"`
FullNodeExecutionRPC string `json:"full_node_execution_rpc"`
L1RPC string `json:"l1_rpc"`
ListenAddr string `json:"listen_addr"`
EspressoTag string `json:"espresso_tag"`
StoreFilePath string `json:"store_file_path"`
InitialHotshotHeight uint64 `json:"initial_hotshot_height"`
MaxBatchSize int `json:"max_batch_size"`
MaxRequestBodySize int `json:"max_request_body_size"`
OPConfig OPConfig `json:"op"`
NitroConfig NitroConfig `json:"nitro"`
LogLevel string `json:"log_level"`
LogFormat string `json:"log_format"`
TrackBatchLatency bool `json:"track_batch_latency"`
}

func defaultConfig() *Config {
Expand All @@ -52,6 +64,9 @@ func defaultConfig() *Config {
OPConfig: OPConfig{
VerificationInterval: 10 * time.Millisecond,
},
NitroConfig: NitroConfig{
VerificationInterval: 10 * time.Millisecond,
},
}
}

Expand Down Expand Up @@ -94,6 +109,14 @@ func parseConfig() *Config {
pflag.StringVar(&cfg.OPConfig.BatcherAddress, "op.batcher-address", cfg.OPConfig.BatcherAddress, "OP batcher address")
pflag.StringVar(&cfg.OPConfig.BatchAuthenticatorAddress, "op.batch-authenticator-address", cfg.OPConfig.BatchAuthenticatorAddress, "Espresso batch authenticator contract address")

pflag.BoolVar(&cfg.NitroConfig.Enable, "nitro.enable", cfg.NitroConfig.Enable, "enable Nitro mode")
pflag.StringVar(&cfg.NitroConfig.FeedURL, "nitro.feed-url", cfg.NitroConfig.FeedURL, "Nitro sequencer feed WebSocket URL")
pflag.DurationVar(&cfg.NitroConfig.VerificationInterval, "nitro.verification-interval", cfg.NitroConfig.VerificationInterval, "Nitro verification interval")
pflag.StringVar(&cfg.NitroConfig.QueryServiceURL, "nitro.query-service-url", cfg.NitroConfig.QueryServiceURL, "Espresso query service URL for Nitro")
pflag.Uint64Var(&cfg.NitroConfig.Namespace, "nitro.namespace", cfg.NitroConfig.Namespace, "Nitro namespace")
pflag.Uint64Var(&cfg.NitroConfig.InitialHotshotBlock, "nitro.initial-hotshot-block", cfg.NitroConfig.InitialHotshotBlock, "initial HotShot block for Nitro streamer")
pflag.StringArrayVar(&cfg.NitroConfig.ValidBatcherAddresses, "nitro.valid-batcher-addresses", cfg.NitroConfig.ValidBatcherAddresses, "valid batcher addresses for Nitro verifier")

pflag.Parse()

if err := cfg.validate(); err != nil {
Expand Down Expand Up @@ -134,16 +157,34 @@ func (c *Config) validate() error {
var errs []error

errs = append(errs, validateURL("full-node-execution-rpc", c.FullNodeExecutionRPC))
errs = append(errs, validateURL("l1-rpc", c.L1RPC))

if c.OPConfig.Enable && c.NitroConfig.Enable {
errs = append(errs, fmt.Errorf("cannot enable both op and nitro verifiers simultaneously"))
}

if c.OPConfig.Enable {
errs = append(errs, validateURL("l1-rpc", c.L1RPC))
errs = append(errs, validateURL("op.full-node-consensus-rpc", c.OPConfig.FullNodeConsensusRPC))
errs = append(errs, validateURL("op.query-service-url", c.OPConfig.QueryServiceURL))
errs = append(errs, validateAddress("op.light-client-address", c.OPConfig.LightClientAddress))
errs = append(errs, validateAddress("op.batcher-address", c.OPConfig.BatcherAddress))
errs = append(errs, validateAddress("op.batch-authenticator-address", c.OPConfig.BatchAuthenticatorAddress))
}

if c.NitroConfig.Enable {
errs = append(errs, validateURL("nitro.feed-url", c.NitroConfig.FeedURL))
errs = append(errs, validateURL("nitro.query-service-url", c.NitroConfig.QueryServiceURL))
if c.NitroConfig.Namespace == 0 {
errs = append(errs, fmt.Errorf("nitro.namespace: must not be zero"))
}
if c.NitroConfig.VerificationInterval <= 0 {
errs = append(errs, fmt.Errorf("nitro.verification-interval: must not be zero"))
}
if len(c.NitroConfig.ValidBatcherAddresses) == 0 {
errs = append(errs, fmt.Errorf("nitro.valid-batcher-addresses: at least one address required"))
}
}

if c.ListenAddr == "" {
errs = append(errs, fmt.Errorf("listen-addr: must not be empty"))
}
Expand All @@ -169,8 +210,8 @@ func (c *Config) toProxyConfig() *proxy.ProxyConfig {
}
}

func (c *Config) toOPVerifierConfig() *verifier.OPEspressoBatchVerifierConfig {
return &verifier.OPEspressoBatchVerifierConfig{
func (c *Config) toOPVerifierConfig() *opVerifier.OPEspressoBatchVerifierConfig {
return &opVerifier.OPEspressoBatchVerifierConfig{
FullNodeExecutionRPC: c.FullNodeExecutionRPC,
FullNodeConsensusRPC: c.OPConfig.FullNodeConsensusRPC,
VerificationInterval: c.OPConfig.VerificationInterval,
Expand All @@ -180,3 +221,15 @@ func (c *Config) toOPVerifierConfig() *verifier.OPEspressoBatchVerifierConfig {
TrackBatchLatency: c.TrackBatchLatency,
}
}

func (c *Config) toNitroVerifierConfig() *nitroVerifier.NitroEspressoBatchVerifierConfig {
return &nitroVerifier.NitroEspressoBatchVerifierConfig{
FeedURL: c.NitroConfig.FeedURL,
FullNodeExecutionRPC: c.FullNodeExecutionRPC,
VerificationInterval: c.NitroConfig.VerificationInterval,
QueryServiceURL: c.NitroConfig.QueryServiceURL,
Namespace: c.NitroConfig.Namespace,
InitialHotshotBlock: c.NitroConfig.InitialHotshotBlock,
ValidBatcherAddresses: c.NitroConfig.ValidBatcherAddresses,
}
}
4 changes: 0 additions & 4 deletions espresso_e2e/mock-l1-beacon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,10 +306,6 @@ func (fb *FakeBeacon) recordBlock(block blockInfo) {
fb.mu.Lock()
defer fb.mu.Unlock()
fb.history = append(fb.history, block)
// cap entries to 500
if len(fb.history) > 500 {
fb.history = fb.history[len(fb.history)-500:]
}
}

func (fb *FakeBeacon) advance() error {
Expand Down
32 changes: 32 additions & 0 deletions espresso_e2e/nitro/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
ESPRESSO_DEV_NODE_L1_DEPLOYMENT=skip
ESPRESSO_SEQUENCER_API_PORT=24000
ESPRESSO_DEV_NODE_PORT=24002
ESPRESSO_BUILDER_PORT=31003
ESPRESSO_DEV_NODE_IMAGE=ghcr.io/espressosystems/espresso-sequencer/espresso-dev-node:release-20251120-lip2p-tcp-3855

# Contract addresses (from integration allocs, used by dev node)
ESPRESSO_SEQUENCER_FEE_CONTRACT_ADDRESS=0x0165878a594ca255338adfa4d48449f69242eb8f
ESPRESSO_SEQUENCER_ESP_TOKEN_ADDRESS=0x2279b7a0a67db372996a5fab50d91eaa73d2ebe6
ESPRESSO_SEQUENCER_PLONK_VERIFIER_ADDRESS=0x5fbdb2315678afecb367f032d93f642f64180aa3
ESPRESSO_SEQUENCER_STAKE_TABLE_ADDRESS=0x610178da211fef7d417bc0e6fed39f05609ad788
ESPRESSO_SEQUENCER_ESP_TOKEN_PROXY_ADDRESS=0x8a791620dd6260079bf849dc5567adc3f2fdc318
ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS=0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0
ESPRESSO_SEQUENCER_FEE_CONTRACT_PROXY_ADDRESS=0xa513e6e4b8f2a923d98304ec87f64353c4d5c853
ESPRESSO_SEQUENCER_STAKE_TABLE_PROXY_ADDRESS=0xb7f8bc63bbcad18155201308c8f3540b07f84f5e
ESPRESSO_SEQUENCER_PLONK_VERIFIER_V2_ADDRESS=0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9

L1_GETH_IMAGE=ghcr.io/espressosystems/optimism-espresso-integration/l1-geth:celo-integration-rebase-16.3
LIGHTHOUSE_IMAGE=sigp/lighthouse:v7.1.0

L1_HTTP_PORT=8545
L1_ENGINE_PORT=8551
L1_BEACON_PORT=5052
L1_CHAIN_ID=11155111

OPERATOR_ADDRESS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
OPERATOR_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
BATCH_POSTER_PRIVATE_KEY=b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659
DEPLOYER_PRIVATE_KEY=0x4f3edf983ac636a65a842ce7c78d9aa706d3b113b37f60c1b6d9d1f0a5b2b5ff

NITRO_IMAGE=ghcr.io/espressosystems/nitro-espresso-integration/nitro-node:v3.9.8-espresso-v1.1.0
ROLLUP_CREATOR_IMAGE=ghcr.io/espressosystems/timeboost/rollup-creator:v2.1.3-legacy
157 changes: 157 additions & 0 deletions espresso_e2e/nitro/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
services:
l1-geth:
image: ghcr.io/espressosystems/timeboost/geth-l1:rollup-creator-round2
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we using timeboost image?

Copy link
Copy Markdown
Contributor Author

@lukeiannucci lukeiannucci May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was a prebuilt l1 image we used for timeboost. i will move away from this.

command: --dev --dev.period=1 --verbosity=2
ports:
- 8545:8545
- 8546:8546

espresso-dev-node:
image: ${ESPRESSO_DEV_NODE_IMAGE}
depends_on:
l1-geth:
condition: service_healthy
ports:
- "${ESPRESSO_SEQUENCER_API_PORT}:${ESPRESSO_SEQUENCER_API_PORT}"
- "${ESPRESSO_DEV_NODE_PORT}:${ESPRESSO_DEV_NODE_PORT}"
- "${ESPRESSO_BUILDER_PORT}:${ESPRESSO_BUILDER_PORT}"
volumes:
- espresso-data:/data
env_file:
- ./.env
environment:
RUST_LOG: warn
ESPRESSO_DEV_NODE_L1_DEPLOYMENT: skip
RUST_BACKTRACE: 1
ESPRESSO_SEQUENCER_STORAGE_PATH: /data/espresso
ESPRESSO_SEQUENCER_L1_PROVIDER: http://l1-geth:${L1_HTTP_PORT}
ESPRESSO_DEPLOYER_ACCOUNT_INDEX: 0
ESPRESSO_DEV_NODE_EPOCH_HEIGHT: "4294967295"
ESPRESSO_SEQUENCER_ETH_MNEMONIC: "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should go in .env


rollup-creator:
image: ${ROLLUP_CREATOR_IMAGE}
volumes:
- "config:/config"
- "./nitro-config:/nitro-config"
env_file:
- ./.env
environment:
AUTHORIZE_VALIDATORS: 10
CHAIN_DEPLOYMENT_INFO: "/config/deployment.json"
CHILD_CHAIN_CONFIG_PATH: "/nitro-config/l2_chain_config.json"
CHILD_CHAIN_INFO: "/config/deployed_chain_info.json"
CHILD_CHAIN_NAME: "arb-dev-test"
DEPLOYER_PRIVKEY: ${DEPLOYER_PRIVATE_KEY}
MAX_DATA_SIZE: 117964
OWNER_ADDRESS: 0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E
PARENT_CHAIN_ID: 1337
PARENT_CHAIN_RPC: "http://l1-geth:8545"
SEQUENCER_ADDRESS: 0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E
WASM_MODULE_ROOT: 0xdb698a2576298f25448bc092e52cf13b1e24141c997135d70f217d674bbeb69a
command: ["create-rollup-testnode"]
depends_on:
l1-geth:
condition: service_healthy

sequencer:
image: ${NITRO_IMAGE}
entrypoint: /usr/local/bin/nitro
ports:
- "8547:8547"
- "8548:8548"
- "9642:9642"
volumes:
- "nitro-sequencer:/home/user/.arbitrum/local/nitro"
- "l1keystore:/home/user/l1keystore"
- "config:/config"
- "./nitro-config:/nitro-config"
command:
- --conf.file=/nitro-config/sequencer_config.json
- --http.api=net,web3,eth,txpool,debug,timeboost,auctioneer
- --graphql.enable
- --graphql.vhosts=*
- --graphql.corsdomain=*
env_file:
- ./.env
depends_on:
rollup-creator:
condition: service_completed_successfully
healthcheck:
test:
[
"CMD",
"curl",
"-s",
"-X",
"POST",
"--data",
'{\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1}',
"-H",
"Content-Type: application/json",
"http://localhost:8547",
]
interval: 5s
timeout: 3s
retries: 12

batch-poster:
image: ${NITRO_IMAGE}
entrypoint: /usr/local/bin/nitro
volumes:
- "nitro-batch-poster:/home/user/.arbitrum/local/nitro"
- "l1keystore:/home/user/l1keystore"
- "config:/config"
- "./nitro-config:/nitro-config"
command:
- --conf.file=/nitro-config/batch_poster_config.json
env_file:
- ./.env
depends_on:
sequencer:
condition: service_healthy

full-node:
image: ${NITRO_IMAGE}
entrypoint: /usr/local/bin/nitro
ports:
- "8549:8547"
- "8550:8548"
- "9643:9642"
volumes:
- "nitro-full-node:/home/user/.arbitrum/local/nitro"
- "config:/config"
- "./nitro-config:/nitro-config"
command:
- --conf.file=/nitro-config/full_node_config.json
- --http.api=net,web3,eth,debug
env_file:
- ./.env
depends_on:
sequencer:
condition: service_healthy

tx-generator:
image: ghcr.io/foundry-rs/foundry:v1.5.1
depends_on:
sequencer:
condition: service_healthy
entrypoint: ["sh", "-c"]
command:
- |
while true; do
cast send \
--rpc-url http://sequencer:8547 \
--private-key ${BATCH_POSTER_PRIVATE_KEY} \
0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E \
--value 1 2>&1 || true
sleep 2
done

volumes:
config:
l1keystore:
espresso-data:
nitro-sequencer:
nitro-batch-poster:
nitro-full-node:
Loading
Loading