This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Gean is a Go consensus client for Lean Ethereum, implementing a simplified consensus layer with post-quantum signature support (XMSS via leanSig). It targets devnet-1 of the Lean Ethereum specification.
make build # Build binary to bin/gean
make test # Run all tests
make test-race # Run tests with race detector
make lint # go vet + staticcheck
make fmt # go fmt
make run # Build and run locally with config files
make run-devnet # Spin up devnet node via lean-quickstart
make docker-build # Build Docker imageRun a single test:
go test -run TestName ./path/to/packageThe leansig CGo bindings require the Rust FFI library to be built first:
cd leansig-ffi && cargo build --release- Go 1.24.6+ (toolchain 1.24.12)
- Rust 1.87+ (for leansig-ffi)
- staticcheck (optional, for linting)
The node follows a reactor pattern with sequential core logic and concurrency only at boundaries (networking):
cmd/gean/main.go Entry point, CLI flags, node init
│
node/ Orchestrator: lifecycle, clock, sync, validator duties
│
┌────┴──────────────┐
│ │
chain/ network/
├── forkchoice/ ├── host.go libp2p host (QUIC, secp256k1)
│ ├── store.go ├── gossipsub/ Block/attestation gossip
│ ├── block.go │ ├── gossip.go Topic pub/sub
│ ├── attestation.go│ ├── handler.go Message handlers
│ ├── ghost.go │ └── encoding.go SSZ + Snappy encoding
│ ├── produce.go └── reqresp/ /status, /blocks_by_root
│ └── time.go
└── statetransition/
├── transition.go Main state transition function
├── genesis.go Genesis state initialization
└── proposer.go Proposer duty helpers
Other packages:
types/— SSZ-encoded core data structures (State, Block, Attestation) withssz-*struct tagsstorage/— Storage interface with in-memory implementation (storage/memory/)config/— YAML config loaders for genesis, bootnodes, validator registryleansig/— Go CGo bindings for XMSS post-quantum signaturesleansig-ffi/— Rust FFI library wrapping the leanSig crateobservability/— Structured logging (slog) and Prometheus metrics
Block received: validate parent state → state transition → store block+state → process attestations as votes → update head (LMD GHOST)
Attestation received: validate → store in fork choice → update head
Key entry points: forkchoice.Store.ProcessBlock(), forkchoice.Store.ProcessAttestation(), statetransition.StateTransition()
Tests live in test/unit/, test/interop/, and test/integration/. Some packages also have colocated test files. Tests use standard testing.T with helper functions like makeGenesisFC() and makeTestValidators().
- Commit messages: conventional commits —
feat(scope):,fix:,refactor:,test:,docs: - Error wrapping:
fmt.Errorf("context: %w", err) - Logging: structured slog via
logging.NewComponentLogger(logging.CompXxx) - Receiver names: single letter (
n *Node,s *Store,st *State) - SSZ types: generated encoding code lives in
*_encoding.gofiles alongside type definitions
- Readable over clever — linear control flow, explicit naming
- Minimal dependencies — few external imports
- No premature abstraction — concrete types, interfaces only when duplication is real
- Flat and direct — shallow package hierarchy