Skip to content

Commit dfa2204

Browse files
committed
Merge branch 'main' into alex/adr023_ha-failover_raft8
* main: chore: adding syncing tracing (#2981) feat(tracing): adding block production tracing (#2980) feat(tracing): Add Store, P2P and Config tracing (#2972) chore: fix upgrade test (#2979) build(deps): Bump github.com/ethereum/go-ethereum from 1.16.7 to 1.16.8 in /execution/evm/test in the go_modules group across 1 directory (#2974) feat(tracing): adding tracing to DA client (#2968) chore: create onboarding skill (#2971) test: add e2e tests for force inclusion (part 2) (#2970) feat(tracing): adding eth client tracing (#2960) test: add e2e tests for force inclusion (#2964) build(deps): Bump the all-go group across 4 directories with 10 updates (#2969)
2 parents d7fbf79 + 52825bf commit dfa2204

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+5612
-742
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
---
2+
name: ev-node-explainer
3+
description: Explains ev-node architecture, components, and internal workings. Use when the user asks how ev-node works, wants to understand the block package, DA layer, sequencing, namespaces, or needs architecture explanations. Covers block production, syncing, DA submission, forced inclusion, single vs based sequencer, and censorship resistance.
4+
---
5+
6+
# ev-node Architecture Explainer
7+
8+
ev-node is a sovereign rollup framework that allows building rollups on any Data Availability (DA) layer. It follows a modular architecture where components can be swapped.
9+
10+
**Reference files:**
11+
- [block-architecture.md](block-architecture.md) - Block package deep dive
12+
- [da-sequencing.md](da-sequencing.md) - DA and sequencing deep dive
13+
14+
## Core Principles
15+
16+
1. **Zero-dependency core** - `core/` contains only interfaces, no external deps
17+
2. **Modular components** - Executor, Sequencer, DA are pluggable
18+
3. **Two operating modes** - Aggregator (produces blocks) and Sync-only (follows chain)
19+
4. **Separation of concerns** - Block production, syncing, and DA submission are independent
20+
21+
## Package Overview
22+
23+
| Package | Responsibility |
24+
|---------|---------------|
25+
| `core/` | Interfaces only (Executor, Sequencer) |
26+
| `types/` | Data structures (Header, Data, State, SignedHeader) |
27+
| `block/` | Block lifecycle management |
28+
| `execution/` | Execution layer implementations (EVM, ABCI) |
29+
| `node/` | Node initialization and orchestration |
30+
| `pkg/p2p/` | libp2p-based networking |
31+
| `pkg/store/` | Persistent storage |
32+
| `pkg/da/` | DA layer abstraction |
33+
34+
## Block Package Deep Dive
35+
36+
The block package is the most complex part of ev-node. See [block-architecture.md](block-architecture.md) for the complete breakdown.
37+
38+
### Component Summary
39+
40+
```
41+
Components struct:
42+
├── Executor - Block production (Aggregator only)
43+
├── Reaper - Transaction scraping (Aggregator only)
44+
├── Syncer - Block synchronization
45+
├── Submitter - DA submission and inclusion
46+
└── Cache - Unified state caching
47+
```
48+
49+
### Entry Points
50+
51+
- `NewAggregatorComponents()` - Full node that produces and syncs blocks
52+
- `NewSyncComponents()` - Non-aggregator that only syncs
53+
54+
### Key Data Types
55+
56+
**Header** - Block metadata (height, time, hashes, proposer)
57+
**Data** - Transaction list with metadata
58+
**SignedHeader** - Header with proposer signature
59+
**State** - Chain state (last block, app hash, DA height)
60+
61+
## Block Production Flow (Aggregator)
62+
63+
```
64+
Sequencer.GetNextBatch()
65+
66+
67+
Executor.ExecuteTxs()
68+
69+
├──► SignedHeader + Data
70+
71+
├──► P2P Broadcast
72+
73+
└──► Submitter Queue
74+
75+
76+
DA Layer
77+
```
78+
79+
## Block Sync Flow (Non-Aggregator)
80+
81+
```
82+
┌─────────────────────────────────────┐
83+
│ Syncer │
84+
├─────────────┬─────────────┬─────────┤
85+
│ DA Worker │ P2P Worker │ Forced │
86+
│ │ │ Incl. │
87+
└──────┬──────┴──────┬──────┴────┬────┘
88+
│ │ │
89+
└─────────────┴───────────┘
90+
91+
92+
processHeightEvent()
93+
94+
95+
ExecuteTxs → Update State
96+
```
97+
98+
## Data Availability Layer
99+
100+
The DA layer abstracts blob storage. ev-node uses Celestia but the interface is pluggable. See [da-sequencing.md](da-sequencing.md) for full details.
101+
102+
### Namespaces
103+
104+
DA uses 29-byte namespaces (1 byte version + 28 byte ID). Three namespaces are used:
105+
106+
| Namespace | Purpose |
107+
|-----------|---------|
108+
| Header | Block headers |
109+
| Data | Transaction data (optional, can share with header) |
110+
| Forced Inclusion | User-submitted txs for censorship resistance |
111+
112+
### DA Client Interface
113+
114+
```go
115+
type Client interface {
116+
Submit(ctx, data [][]byte, gasPrice, namespace, options) ResultSubmit
117+
Retrieve(ctx, height uint64, namespace) ResultRetrieve
118+
Get(ctx, ids []ID, namespace) ([]Blob, error)
119+
}
120+
```
121+
122+
### Key Files
123+
124+
| File | Purpose |
125+
|------|---------|
126+
| `pkg/da/types/types.go` | Core types (Blob, ID, Commitment) |
127+
| `pkg/da/types/namespace.go` | Namespace handling |
128+
| `block/internal/da/client.go` | DA client wrapper |
129+
| `block/internal/da/forced_inclusion_retriever.go` | Forced tx retrieval |
130+
131+
---
132+
133+
## Sequencing
134+
135+
Sequencers order transactions for block production. See [da-sequencing.md](da-sequencing.md) for full details.
136+
137+
### Two Modes
138+
139+
| Mode | Mempool | Forced Inclusion | Use Case |
140+
|------|---------|------------------|----------|
141+
| **Single** | Yes | Yes | Traditional rollup |
142+
| **Based** | No | Only source | High liveness guarantee |
143+
144+
### Sequencer Interface
145+
146+
```go
147+
type Sequencer interface {
148+
SubmitBatchTxs(ctx, req) (*SubmitBatchTxsResponse, error)
149+
GetNextBatch(ctx, req) (*GetNextBatchResponse, error)
150+
VerifyBatch(ctx, req) (*VerifyBatchResponse, error)
151+
SetDAHeight(height uint64)
152+
GetDAHeight() uint64
153+
}
154+
```
155+
156+
### ForceIncludedMask
157+
158+
Batches include a mask distinguishing tx sources:
159+
160+
```go
161+
type Batch struct {
162+
Transactions [][]byte
163+
ForceIncludedMask []bool // true = from DA (must validate)
164+
}
165+
```
166+
167+
This allows the execution layer to skip validation for already-validated mempool txs.
168+
169+
### Key Files
170+
171+
| File | Purpose |
172+
|------|---------|
173+
| `core/sequencer/sequencing.go` | Core interface |
174+
| `pkg/sequencers/single/sequencer.go` | Hybrid sequencer |
175+
| `pkg/sequencers/based/sequencer.go` | Pure DA sequencer |
176+
| `pkg/sequencers/common/checkpoint.go` | Shared checkpoint logic |
177+
178+
---
179+
180+
## Forced Inclusion
181+
182+
Forced inclusion prevents sequencer censorship:
183+
184+
1. User submits tx directly to DA layer
185+
2. Syncer detects tx in forced-inclusion namespace
186+
3. Grace period starts (adjusts based on block fullness)
187+
4. If not included by sequencer within grace period → sequencer marked malicious
188+
5. Tx gets included regardless
189+
190+
## Key Files
191+
192+
| File | Purpose |
193+
|------|---------|
194+
| `block/public.go` | Exported types and factories |
195+
| `block/components.go` | Component creation |
196+
| `block/internal/executing/executor.go` | Block production |
197+
| `block/internal/syncing/syncer.go` | Sync orchestration |
198+
| `block/internal/submitting/submitter.go` | DA submission |
199+
| `block/internal/cache/manager.go` | Unified cache |
200+
201+
## Common Questions
202+
203+
### How does block production work?
204+
205+
The Executor runs `executionLoop()`:
206+
1. Wait for block time or new transactions
207+
2. Get batch from sequencer
208+
3. Execute via execution layer
209+
4. Create SignedHeader + Data
210+
5. Broadcast to P2P
211+
6. Queue for DA submission
212+
213+
### How does syncing work?
214+
215+
The Syncer coordinates three workers:
216+
- **DA Worker** - Fetches confirmed blocks from DA
217+
- **P2P Worker** - Receives gossiped blocks
218+
- **Forced Inclusion** - Monitors for censored txs
219+
220+
All feed into `processHeightEvent()` which validates and executes.
221+
222+
### What happens if DA submission fails?
223+
224+
Submitter has retry logic with exponential backoff. Status codes:
225+
- `TooBig` - Splits blob into chunks
226+
- `AlreadyInMempool` - Skips (duplicate)
227+
- `NotIncludedInBlock` - Retries with backoff
228+
- `ContextCanceled` - Request canceled
229+
230+
### How is state recovered after crash?
231+
232+
The Replayer syncs execution layer from disk:
233+
1. Load last committed height from store
234+
2. Check execution layer height
235+
3. Replay any missing blocks
236+
4. Ensure consistency before starting
237+
238+
## Architecture Diagrams
239+
240+
For detailed component diagrams and state machines, see [block-architecture.md](block-architecture.md).

0 commit comments

Comments
 (0)