|
1 | 1 | --- |
2 | | -description: Deploy an Evolve EVM chain locally for development and testing using local-da and Docker Compose. |
| 2 | +description: Deploy an Evolve EVM chain locally for development and testing using ev-toolbox and local-da. |
3 | 3 | --- |
4 | 4 |
|
5 | 5 | # 🏠 Local Development Deployment |
6 | 6 |
|
7 | | -This guide walks you through deploying a complete Evolve EVM chain on your local machine for development and testing. Unlike testnet and mainnet deployments, local dev uses the **local-da** mock DA layer so you have zero external dependencies. |
| 7 | +This guide walks you through deploying a complete Evolve EVM chain on your local machine using [ev-toolbox](https://github.com/evstack/ev-toolbox). It uses the **local-da** mock DA layer so there are no external dependencies or token costs. |
8 | 8 |
|
9 | | -<!-- markdownlint-disable MD033 --> |
10 | | -<script setup> |
11 | | -import constants from '../../.vitepress/constants/constants.js' |
12 | | -</script> |
13 | | -<!-- markdownlint-enable MD033 --> |
| 9 | +## 🏗️ How it works |
14 | 10 |
|
15 | | -## 🏗️ Architecture Overview |
16 | | - |
17 | | -A local Evolve EVM deployment consists of three services running on your machine: |
| 11 | +The local stack is split into two Docker Compose stacks that share a Docker network (`evstack_shared`): |
18 | 12 |
|
19 | 13 | ```mermaid |
20 | 14 | graph TB |
21 | | - subgraph "Sequencer Stack" |
22 | | - SEQ_RETH[RETH Service<br/>:8545 JSON-RPC<br/>:8551 Engine API] |
23 | | - SEQ_EVOLVE[EVOLVE Service<br/>--aggregator=true] |
24 | | - SEQ_RETH <--> SEQ_EVOLVE |
25 | | - end |
26 | | -
|
27 | | - subgraph "Local DA" |
| 15 | + subgraph "da-local stack" |
28 | 16 | LOCAL_DA[local-da<br/>:7980] |
29 | 17 | end |
30 | 18 |
|
31 | | - SEQ_EVOLVE -->|Post Blobs| LOCAL_DA |
| 19 | + subgraph "single-sequencer stack" |
| 20 | + SEQ_RETH[ev-reth<br/>:8545 JSON-RPC] |
| 21 | + SEQ_EVM[ev-node-evm<br/>aggregator mode] |
| 22 | + SEQ_RETH <--> SEQ_EVM |
| 23 | + end |
32 | 24 |
|
33 | | - USERS[Dev / Tests] --> SEQ_RETH |
| 25 | + SEQ_EVM -->|Post blobs| LOCAL_DA |
34 | 26 |
|
35 | 27 | classDef sequencer fill:#e1f5fe |
36 | 28 | classDef da fill:#fff3e0 |
37 | | - classDef user fill:#e8f5e8 |
38 | | -
|
39 | | - class SEQ_RETH,SEQ_EVOLVE sequencer |
| 29 | + class SEQ_RETH,SEQ_EVM sequencer |
40 | 30 | class LOCAL_DA da |
41 | | - class USERS user |
42 | 31 | ``` |
43 | 32 |
|
44 | | -**Key differences from testnet/mainnet:** |
45 | | - |
46 | | -- `local-da` replaces Celestia — no tokens, no external network |
47 | | -- Single sequencer only — no full nodes required |
48 | | -- All services run on `localhost` |
| 33 | +The `da-local` stack is started first because it creates the shared Docker network that the sequencer stack joins. |
49 | 34 |
|
50 | 35 | ## 💻 Prerequisites {#prerequisites} |
51 | 36 |
|
52 | | -- [Go](https://golang.org/doc/install) {{ constants.golangVersion }} or later |
53 | | -- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) |
54 | | -- [just](https://github.com/casey/just#installation) (command runner) |
| 37 | +- [Docker](https://docs.docker.com/get-docker/) 20.10 or later |
| 38 | +- [Docker Compose](https://docs.docker.com/compose/install/) v2 or later |
55 | 39 | - [Git](https://git-scm.com/) |
56 | 40 |
|
57 | | -## 🛠️ Step 1 — Clone and Build {#clone-and-build} |
| 41 | +## 🛠️ Step 1 — Clone ev-toolbox {#clone} |
58 | 42 |
|
59 | 43 | ```bash |
60 | | -git clone --depth 1 --branch {{ constants.evolveLatestTag }} https://github.com/evstack/ev-node.git |
61 | | -cd ev-node |
62 | | - |
63 | | -# Build the EVM sequencer binary and local-da |
64 | | -just build-evm |
65 | | -just build-da |
| 44 | +git clone --depth 1 https://github.com/evstack/ev-toolbox.git |
| 45 | +cd ev-toolbox/ev-stacks/stacks |
66 | 46 | ``` |
67 | 47 |
|
68 | | -After building you will have: |
| 48 | +## 🌐 Step 2 — Start local-da {#start-local-da} |
69 | 49 |
|
70 | | -- `build/evm` — the Evolve EVM sequencer |
71 | | -- `build/local-da` — the mock DA node |
| 50 | +The `da-local` stack must be started first. It creates the `evstack_shared` Docker network that the sequencer stack joins. |
72 | 51 |
|
73 | | -## 🌐 Step 2 — Start local-da {#start-local-da} |
| 52 | +```bash |
| 53 | +cd da-local |
| 54 | +docker compose up -d |
| 55 | +``` |
74 | 56 |
|
75 | | -Open a terminal and start the local DA node: |
| 57 | +Verify it is running: |
76 | 58 |
|
77 | 59 | ```bash |
78 | | -./build/local-da |
| 60 | +docker logs local-da |
79 | 61 | ``` |
80 | 62 |
|
81 | | -You should see: |
| 63 | +Expected output: |
82 | 64 |
|
83 | 65 | ``` |
84 | 66 | INF NewLocalDA: initialized LocalDA component=da |
85 | | -INF Listening on component=da host=localhost maxBlobSize=1974272 port=7980 |
86 | | -INF server started component=da listening_on=localhost:7980 |
| 67 | +INF Listening on component=da host=0.0.0.0 maxBlobSize=1970176 port=7980 |
| 68 | +INF server started component=da listening_on=0.0.0.0:7980 |
87 | 69 | ``` |
88 | 70 |
|
89 | | -Leave this running in its own terminal tab. |
| 71 | +## 🔑 Step 3 — Create the passphrase file {#passphrase} |
90 | 72 |
|
91 | | -## ⚡ Step 3 — Start the EVM (RETH) Layer {#start-evm-layer} |
92 | | - |
93 | | -Clone the `ev-reth` repository and start RETH using Docker Compose: |
| 73 | +The sequencer signs blocks with a key protected by a passphrase. Create the passphrase file in the single-sequencer directory: |
94 | 74 |
|
95 | 75 | ```bash |
96 | | -git clone --depth 1 https://github.com/evstack/ev-reth.git |
97 | | -cd ev-reth |
98 | | -docker compose up -d |
| 76 | +cd ../single-sequencer |
| 77 | +echo -n "devpassword" > passphrase |
99 | 78 | ``` |
100 | 79 |
|
101 | | -Note the path to the JWT secret — you will need it in the next step: |
102 | | - |
103 | | -```bash |
104 | | -# Default location after docker compose starts |
105 | | -ls ev-reth/execution/evm/docker/jwttoken/jwt.hex |
106 | | -``` |
| 80 | +:::tip |
| 81 | +For local development, any string works as a passphrase. Keep it simple — you will not need it again unless you restart with a wiped volume. |
| 82 | +::: |
107 | 83 |
|
108 | | -## 🚀 Step 4 — Initialize and Start the Sequencer {#start-sequencer} |
| 84 | +## 🚀 Step 4 — Start the sequencer {#start-sequencer} |
109 | 85 |
|
110 | | -Back in the `ev-node` directory, initialize the sequencer: |
| 86 | +Make the entrypoint script executable (required after `git clone` on some systems), then start the stack using the local-DA variant of the compose file: |
111 | 87 |
|
112 | 88 | ```bash |
113 | | -./build/evm init \ |
114 | | - --evnode.node.aggregator=true \ |
115 | | - --evnode.signer.passphrase secret |
| 89 | +chmod +x entrypoint.sequencer.sh |
| 90 | +docker compose -f docker-compose.da.local.yml up -d |
116 | 91 | ``` |
117 | 92 |
|
118 | | -Then start it, pointing at local-da and the JWT secret from RETH: |
| 93 | +Monitor startup: |
119 | 94 |
|
120 | 95 | ```bash |
121 | | -./build/evm start \ |
122 | | - --evnode.node.aggregator=true \ |
123 | | - --evnode.signer.passphrase secret \ |
124 | | - --evnode.da.address http://localhost:7980 \ |
125 | | - --evnode.node.block_time 1s \ |
126 | | - --evm.jwt-secret /path/to/ev-reth/execution/evm/docker/jwttoken/jwt.hex |
| 96 | +docker compose -f docker-compose.da.local.yml logs -f |
127 | 97 | ``` |
128 | 98 |
|
129 | | -Replace `/path/to/ev-reth/` with the actual path to your cloned `ev-reth` directory. |
130 | | - |
131 | | -You should see block production logs like: |
| 99 | +A healthy startup looks like: |
132 | 100 |
|
133 | 101 | ``` |
134 | | -INF working in aggregator mode block_time=1000 component=main |
135 | | -INF using pending block component=BlockManager height=1 |
136 | | -INF block marked as DA included blockHash=... blockHeight=1 module=BlockManager |
| 102 | +single-sequencer | 🚀 INIT: Starting EVM Sequencer initialization |
| 103 | +single-sequencer | ✅ SUCCESS: Sequencer initialization completed |
| 104 | +single-sequencer | ✅ SUCCESS: Exported genesis.json to /volumes/sequencer_export/genesis.json |
| 105 | +single-sequencer | ✅ SUCCESS: Successfully retrieved genesis hash: 0x6aec2... |
| 106 | +single-sequencer | 🚀 INIT: Starting EVM sequencer with command: evm start ... |
| 107 | +single-sequencer | INF Starting aggregator node component=main |
| 108 | +single-sequencer | INF produced block component=executor height=1 |
| 109 | +single-sequencer | INF produced block component=executor height=2 |
137 | 110 | ``` |
138 | 111 |
|
| 112 | +:::info DA submission errors |
| 113 | +You may see errors like `DA layer submission failed: method 'blob.Submit' not found`. This is a known version mismatch between the current local-da Docker images and ev-node-evm. It is **non-fatal** — blocks are produced and the JSON-RPC is fully functional for local development. You can ignore these errors. |
| 114 | +::: |
| 115 | + |
139 | 116 | ## ✅ Step 5 — Verify {#verify} |
140 | 117 |
|
141 | | -Query the JSON-RPC endpoint to confirm the chain is producing blocks: |
| 118 | +The sequencer JSON-RPC port (`8545`) is not exposed to the host by default. To expose it for local testing, create an override file: |
| 119 | + |
| 120 | +```bash |
| 121 | +cat > docker-compose.override.yml << 'EOF' |
| 122 | +services: |
| 123 | + ev-reth-sequencer: |
| 124 | + ports: |
| 125 | + - "8545:8545" |
| 126 | +EOF |
| 127 | + |
| 128 | +docker compose -f docker-compose.da.local.yml -f docker-compose.override.yml up -d |
| 129 | +``` |
| 130 | + |
| 131 | +Then query the chain: |
142 | 132 |
|
143 | 133 | ```bash |
144 | 134 | curl -s -X POST http://localhost:8545 \ |
145 | 135 | -H 'Content-Type: application/json' \ |
146 | 136 | -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' |
147 | 137 | ``` |
148 | 138 |
|
149 | | -The `result` field should increment with each call as new blocks are produced. |
150 | | - |
151 | | -## 🐳 Alternative: Docker Compose (all-in-one) {#docker-compose} |
| 139 | +The `result` field (a hex block number) should increment with each call as new blocks are produced. |
152 | 140 |
|
153 | | -The [ev-toolbox](https://github.com/evstack/ev-toolbox/tree/main/ev-stacks) project provides a pre-configured Docker Compose stack that wires up RETH, the Evolve EVM sequencer, and local-da for you: |
| 141 | +## 🔍 Useful commands {#commands} |
154 | 142 |
|
155 | 143 | ```bash |
156 | | -git clone https://github.com/evstack/ev-toolbox.git |
157 | | -cd ev-toolbox/ev-stacks/local |
158 | | -docker compose up |
| 144 | +# Check container status |
| 145 | +docker ps |
| 146 | + |
| 147 | +# Follow all sequencer logs |
| 148 | +docker compose -f docker-compose.da.local.yml logs -f |
| 149 | + |
| 150 | +# Follow just the ev-node logs (block production) |
| 151 | +docker logs -f single-sequencer |
| 152 | + |
| 153 | +# Follow just the reth logs |
| 154 | +docker logs -f ev-reth-sequencer |
| 155 | + |
| 156 | +# Stop everything |
| 157 | +docker compose -f docker-compose.da.local.yml down |
| 158 | +cd ../da-local && docker compose down |
159 | 159 | ``` |
160 | 160 |
|
161 | | -This is the fastest way to get a fully functional local environment without manually coordinating services. |
| 161 | +## ⚙️ Configuration {#configuration} |
| 162 | + |
| 163 | +All configuration is via environment variables in `.env` and the `docker-compose.da.local.yml` file. |
162 | 164 |
|
163 | | -## ⚙️ Configuration Reference {#configuration} |
| 165 | +| Variable | Default | Description | |
| 166 | +| ----------------------------------- | ------- | -------------------------------------------------------- | |
| 167 | +| `SEQUENCER_EV_RETH_PROMETHEUS_PORT` | `9000` | Host port for ev-reth Prometheus metrics | |
| 168 | +| `SEQUENCER_EV_NODE_PROMETHEUS_PORT` | `26660` | Host port for ev-node Prometheus metrics | |
| 169 | +| `DA_SIGNING_ADDRESSES` | (empty) | DA signing addresses — leave empty for local-da | |
| 170 | +| `EVM_BLOCK_TIME` | `500ms` | How often the sequencer produces blocks (set in compose) | |
164 | 171 |
|
165 | | -| Flag | Default | Description | |
166 | | -|---|---|---| |
167 | | -| `--evnode.node.aggregator` | `false` | Must be `true` for the sequencer | |
168 | | -| `--evnode.signer.passphrase` | — | Passphrase protecting the signing key | |
169 | | -| `--evnode.da.address` | — | DA node endpoint (`http://localhost:7980` for local-da) | |
170 | | -| `--evnode.node.block_time` | `1s` | How often the sequencer produces blocks | |
171 | | -| `--evm.jwt-secret` | — | Path to the JWT secret shared with RETH | |
172 | | -| `--evm.eth-url` | `http://localhost:8545` | RETH JSON-RPC URL | |
173 | | -| `--evm.engine-url` | `http://localhost:8551` | RETH Engine API URL | |
| 172 | +To change the block time or other settings, edit `docker-compose.da.local.yml` directly or add them to your `docker-compose.override.yml`. |
174 | 173 |
|
175 | 174 | ## 🎉 Next Steps {#next-steps} |
176 | 175 |
|
177 | 176 | Once your local chain is running: |
178 | 177 |
|
179 | 178 | - [Testnet Deployment](./testnet.md) — deploy with real Celestia DA and a multi-node setup |
180 | | -- [Single Sequencer Guide](../evm/single.md) — detailed sequencer configuration options |
181 | | -- [Local DA Guide](../da/local-da.md) — more details on the `local-da` mock DA node |
| 179 | +- [Local DA Guide](../da/local-da.md) — more about the `local-da` mock DA node |
182 | 180 | - [Metrics](../metrics.md) — add Prometheus + Grafana monitoring |
183 | 181 |
|
184 | 182 | :::warning |
185 | | -This setup is for development only. Do not use `local-da` or a passphrase-protected key in any production environment. |
| 183 | +This setup is for development only. The `local-da` mock does not provide real data availability guarantees. Do not use it for any production environment. |
186 | 184 | ::: |
0 commit comments