| name | charon-guide |
| description | Use when porting Charon components to understand Go codebase architecture, workflow, design patterns, or component responsibilities |
Charon Architecture Guide
Reference for understanding the Charon Go codebase when porting to Pluto (Rust).
What is Charon?
Distributed validator middleware for Ethereum staking. Enables running a single validator across multiple independent nodes using threshold BLS signatures. Each validator duty flows through a multi-stage workflow with consensus and signature aggregation.
Knowledge Base
When you need deeper understanding:
| Topic | Location |
|---|
| High-level overview | charon/README.md |
| Detailed architecture | charon/docs/architecture.md |
| Go coding guidelines | charon/docs/goguidelines.md |
| Configuration options | charon/docs/configuration.md |
| DKG details | charon/docs/dkg.md |
| Consensus (QBFT) | charon/docs/consensus.md, charon/core/qbft/README.md |
| Metrics | charon/docs/metrics.md |
| Error reason codes | charon/docs/reasons.md |
| Package structure | charon/docs/structure.md |
| Product docs | https://docs.obol.org/next |
Reference Version
Use Charon v1.7.1 as the default Go reference for AI-assisted porting and review. For DKG, sync, reshare, FetchDefinition, and peer-indexed broadcast code, treat .claude/skills/pluto-review/references/trail-of-bits-charon-v2-audit.md as a required security overlay: preserve v1.7.1 compatibility unless the audit documents vulnerable behavior, then port the audited fix intent.
Core Workflow
Every validator duty (attestation, block proposal, etc.) flows through these components in order:
Scheduler → Fetcher → Consensus → DutyDB → ValidatorAPI → ParSigDB → ParSigEx → SigAgg → AggSigDB → Bcast
| Component | Responsibility | Go Package |
|---|
| Scheduler | Triggers duties at optimal times based on beacon chain state | core/scheduler/ |
| Fetcher | Fetches unsigned duty data from beacon node | core/fetcher/ |
| Consensus | QBFT consensus to agree on duty data across all nodes | core/consensus/, core/qbft/ |
| DutyDB | Persists unsigned data, slashing protection | core/dutydb/ |
| ValidatorAPI | Serves data to validator clients, receives partial signatures | core/validatorapi/ |
| ParSigDB | Stores partial threshold BLS signatures from local/remote VCs | core/parsigdb/ |
| ParSigEx | Exchanges partial signatures with peers via p2p | core/parsigex/ |
| SigAgg | Aggregates partial signatures when threshold reached | core/sigagg/ |
| AggSigDB | Persists aggregated signatures | core/aggsigdb/ |
| Bcast | Broadcasts final signatures to beacon node | core/bcast/ |
Porting note: When porting a component, trace its inputs and outputs through this pipeline.
Key Abstractions
| Concept | Definition | Go Type |
|---|
| Duty | Unit of work (slot + duty type). Cluster-level, not per-validator. | core.Duty |
| PubKey | DV root public key, validator identifier in workflow | core.PubKey |
| UnsignedData | Abstract type for attestation data, blocks, etc. | core.UnsignedData (interface) |
| SignedData | Fully signed duty data | core.SignedData (interface) |
| ParSignedData | Partially signed data from single threshold BLS share | core.ParSignedData (struct) |
Type encoding: Abstract types are encoded/decoded via core/encode.go. Always check this file when porting type serialization.
Design Patterns
| Pattern | Description | Porting Impact |
|---|
| Immutable values | Components consume and produce immutable values (actor-like) | Always .Clone() before sharing/caching in Rust |
| Callback subscriptions | Components decoupled via subscriptions, not direct calls | Use channels or callbacks in Rust |
| Type-safe encoding | Abstract types use custom encoding/decoding | Port core/encode.go logic carefully |
Consensus (QBFT)
Charon uses QBFT (Istanbul BFT) for consensus. Each duty requires consensus to ensure all nodes sign identical data (required for BLS threshold signatures and slashing protection).
Key files:
core/qbft/ - QBFT implementation
core/consensus/ - Consensus component integration
charon/docs/consensus.md - Design documentation
Porting note: QBFT is complex. Port incrementally and verify against Go test vectors.
Package Structure
| Go Package | Purpose | Rust Equivalent |
|---|
app/ | Application entrypoint, wiring, infrastructure (log, errors, tracer, lifecycle) | pluto/crates/app/ |
cluster/ | Cluster config, lock files, DKG artifacts | pluto/crates/cluster/ |
cmd/ | CLI commands (run, dkg, create, test, etc.) | pluto/crates/cli/ |
core/ | Core workflow business logic and components | pluto/crates/core/ |
dkg/ | Distributed Key Generation logic | pluto/crates/dkg/ |
eth2util/ | ETH2 utilities (signing, deposits, keystores) | pluto/crates/eth2util/ |
p2p/ | libp2p networking and discv5 peer discovery | pluto/crates/p2p/ |
tbls/ | Threshold BLS signature scheme | pluto/crates/crypto/ |
testutil/ | Test utilities, mocks, golden files | pluto/crates/testutil/ |
Common Porting Scenarios
Porting a Workflow Component
- Read
charon/docs/architecture.md for component interfaces
- Identify inputs (what triggers this component?)
- Identify outputs (what does it produce? who consumes it?)
- Check
core/<component>/ for implementation
- Check subscriptions in
app/app.go (wiring/stitching logic)
- Port logic, preserving immutability and callback patterns
Adding New Duty Types
When porting code that adds duty types:
- Add to
core/types.go (duty type constants)
- Add to
core/encode.go (encoding/decoding logic)
- Update Scheduler, Fetcher, and relevant components
Understanding Error Handling
Go style:
- Just return errors, don't log and return
- Wrap external errors:
errors.Wrap(err, "do something")
- Use
app/errors for structured errors with fields
Rust style: See rust-style skill and AGENTS.md.
Working with Cluster Lock Files
cluster-definition.json: Intended cluster config (operators, validators)
cluster-lock.json: Extends definition with DV public keys and shares (DKG output)
- See
cluster/ package for parsing/validation
Dependency Notes
Charon uses forked dependencies:
github.com/ObolNetwork/kryptology (security fixes)
github.com/ObolNetwork/go-eth2-client (kept up to date)
When porting code using these, check if Rust equivalents exist or if porting is needed.
Version Compatibility
Important for protocol compatibility:
- Compatible: Same MAJOR version, different MINOR/PATCH
- Incompatible: Different MAJOR version
- DKG: Requires matching MAJOR and MINOR versions (PATCH can differ)
Quick Reference Commands
When exploring Charon codebase:
grep -r "func FunctionName" charon/
ls -la charon/<directory>/
grep "^import" charon/path/to/file.go
cd charon && go test -run TestFunctionName ./path/to/package