Skip to content

Commit 80473b8

Browse files
committed
Merge branch 'unstable' into bing/blst-z
2 parents 5a2a13f + f225223 commit 80473b8

File tree

81 files changed

+3958
-1393
lines changed

Some content is hidden

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

81 files changed

+3958
-1393
lines changed

AGENTS.md

Lines changed: 142 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ pnpm install
5353
# Build all packages
5454
pnpm build
5555

56+
# Build a specific package (faster iteration)
57+
pnpm --filter @lodestar/beacon-node build
58+
5659
# Run linter (biome)
5760
pnpm lint
5861

@@ -62,15 +65,17 @@ pnpm lint:fix
6265
# Type check all packages
6366
pnpm check-types
6467

68+
# Type check a specific package
69+
pnpm --filter @lodestar/beacon-node check-types
70+
6571
# Run unit tests (fast, minimal preset)
6672
pnpm test:unit
6773

68-
# Run specific test file (faster - run from package directory)
69-
cd packages/beacon-node
70-
pnpm vitest run test/unit/path/to/test.test.ts
74+
# Run specific test file with project filter
75+
pnpm vitest run --project unit test/unit/path/to/test.test.ts
7176

7277
# Run tests matching a pattern
73-
pnpm vitest run -t "pattern"
78+
pnpm vitest run --project unit -t "pattern"
7479

7580
# Run spec tests (requires downloading first)
7681
pnpm download-spec-tests
@@ -81,6 +86,13 @@ pnpm test:spec
8186
pnpm test:e2e
8287
```
8388

89+
**Tip:** For faster iteration, run tests from the specific package directory:
90+
91+
```bash
92+
cd packages/beacon-node
93+
pnpm vitest run test/unit/chain/validation/block.test.ts
94+
```
95+
8496
## Code style
8597

8698
Lodestar uses [Biome](https://biomejs.dev/) for linting and formatting.
@@ -94,6 +106,7 @@ Lodestar uses [Biome](https://biomejs.dev/) for linting and formatting.
94106
- **Types**: All functions must have explicit parameter and return types
95107
- **No `any`**: Avoid TypeScript `any` type
96108
- **Private fields**: No underscore prefix (use `private dirty`, not `private _dirty`)
109+
- **Named exports only**: No default exports
97110

98111
### Import organization
99112

@@ -126,6 +139,94 @@ Metrics are critical for production monitoring:
126139
- Do NOT suffix code variables with units (no `Sec` suffix)
127140
- Time-based metrics must use seconds
128141

142+
## Architecture patterns
143+
144+
### Fork-aware code
145+
146+
Code that varies by fork uses fork guards and type narrowing:
147+
148+
```typescript
149+
import {isForkPostElectra, isForkPostFulu} from "@lodestar/params";
150+
151+
// Check fork before accessing fork-specific fields
152+
if (isForkPostElectra(fork)) {
153+
// electra and later forks
154+
}
155+
```
156+
157+
The fork progression is: `phase0``altair``bellatrix``capella`
158+
`deneb``electra``fulu``gloas`.
159+
160+
### Configuration
161+
162+
`ChainForkConfig` combines base chain config with computed fork information:
163+
164+
```typescript
165+
// Access config values
166+
config.SLOTS_PER_EPOCH; // from params
167+
config.getForkName(slot); // computed fork for a slot
168+
config.getForkTypes(fork); // SSZ types for a fork
169+
```
170+
171+
`@lodestar/params` holds constants (`SLOTS_PER_EPOCH`, etc.).
172+
`@lodestar/config` holds runtime chain configuration.
173+
174+
### State access
175+
176+
- Get current state via `chain.getHeadState()` — returns a tree-backed state
177+
- **Never hold references to old states** — they consume memory and can go stale
178+
- For read-only access, use the state directly; for mutations, use `state.clone()`
179+
- Beacon state is tree-backed (persistent data structure), making cloning cheap
180+
181+
### SSZ types
182+
183+
Types use `@chainsafe/ssz` and come in two forms:
184+
185+
- **Value types**: Plain JS objects. Easy to work with, higher memory usage.
186+
- **View/ViewDU types**: Tree-backed. Memory-efficient, used for beacon state.
187+
188+
```typescript
189+
// Type definition
190+
const MyContainer = new ContainerType(
191+
{
192+
field1: UintNum64,
193+
field2: Root,
194+
},
195+
{typeName: "MyContainer"}
196+
);
197+
198+
// Value usage
199+
const value = MyContainer.defaultValue();
200+
value.field1 = 42;
201+
202+
// View usage (tree-backed)
203+
const view = MyContainer.toViewDU(value);
204+
view.field1 = 42;
205+
view.commit();
206+
```
207+
208+
### Fork choice
209+
210+
The fork choice store uses proto-array for efficient head computation:
211+
212+
- `getHead()` returns a **cached** `ProtoBlock` — may be stale after mutations
213+
- After modifying proto-array node state (e.g., execution status), call
214+
`recomputeForkChoiceHead()` to refresh the cache
215+
- This applies to any code that modifies proto-array outside normal block import
216+
217+
### Logging
218+
219+
Use structured logging with metadata objects:
220+
221+
```typescript
222+
this.logger.debug("Processing block", {slot, root: toRootHex(root)});
223+
this.logger.warn("Peer disconnected", {peerId: peer.toString(), reason});
224+
```
225+
226+
- Prefer structured fields over string concatenation
227+
- Use appropriate levels: `error` > `warn` > `info` > `verbose` > `debug` > `trace`
228+
- Include relevant context (slot, root, peer) as structured fields
229+
129230
## Testing guidelines
130231

131232
### Test organization
@@ -157,11 +258,18 @@ for (const block of blocks) {
157258

158259
### Running specific tests
159260

160-
For faster iteration, run tests from the package directory:
261+
Use vitest project filters for targeted test runs:
161262

162263
```bash
264+
# Unit tests only (from repo root)
265+
pnpm vitest run --project unit test/unit/chain/validation/block.test.ts
266+
267+
# With pattern matching
268+
pnpm vitest run --project unit -t "should reject"
269+
270+
# From package directory (no project filter needed)
163271
cd packages/beacon-node
164-
pnpm vitest run test/unit/chain/validation/block.test.ts -t "should reject"
272+
pnpm vitest run test/unit/chain/validation/block.test.ts
165273
```
166274

167275
For spec tests with minimal preset (faster):
@@ -215,7 +323,8 @@ refactor(reqresp)!: support byte based handlers
215323
- Keep PRs as drafts until ready for review
216324
- Don't force push after review starts (use incremental commits)
217325
- Close stale PRs rather than letting them sit
218-
- Respond to review feedback promptly
326+
- Respond to review feedback promptly — reply to every comment, including bot reviewers
327+
- When updating based on feedback, respond in-thread to acknowledge
219328

220329
## Common tasks
221330

@@ -241,6 +350,14 @@ refactor(reqresp)!: support byte based handlers
241350
3. The type will be automatically aggregated (no central `sszTypes` to modify)
242351
4. Run `pnpm check-types` to verify
243352

353+
### Adding a new API endpoint
354+
355+
1. Define the route in `packages/api/src/beacon/routes/<resource>.ts`
356+
2. Add request/response SSZ codecs alongside the route definition
357+
3. Implement the server handler in `packages/beacon-node/src/api/impl/beacon/<resource>.ts`
358+
4. Add tests for the new endpoint
359+
5. Reference the [Beacon APIs spec](https://github.com/ethereum/beacon-APIs) for the endpoint contract
360+
244361
## Style learnings from reviews
245362

246363
### Prefer inline logic over helper functions
@@ -348,3 +465,21 @@ Edit source files in `packages/*/src/` instead.
348465

349466
The `specrefs/` directory contains pinned consensus spec versions.
350467
When implementing spec changes, reference the exact spec version.
468+
469+
## Common pitfalls
470+
471+
- **Forgetting `pnpm lint` before pushing**: Biome enforces formatting. Always
472+
run it before committing. CI will catch it, but it wastes a round-trip.
473+
- **Forgetting `pnpm docs:lint` after editing docs**: Markdown files are
474+
formatted by Prettier. Run `pnpm docs:lint` (or `pnpm docs:lint:fix` to
475+
auto-fix) before pushing changes to `.md` files.
476+
- **Editing `lib/` instead of `src/`**: Files in `packages/*/lib/` are build
477+
outputs. Always edit in `packages/*/src/`.
478+
- **Stale fork choice head**: After modifying proto-array execution status,
479+
the cached head from `getHead()` is stale. Call `recomputeForkChoiceHead()`.
480+
- **Holding state references**: Beacon state objects are large. Don't store
481+
references beyond their immediate use — let them be garbage collected.
482+
- **Missing `.js` extension**: Relative imports must use `.js` even though
483+
source files are `.ts`. This is required for Node.js ESM resolution.
484+
- **Force pushing after review**: Never force push once a reviewer has started.
485+
Use incremental commits — reviewers track changes between reviews.

0 commit comments

Comments
 (0)