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 implementationcore/consensus/- Consensus component integrationcharon/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.mdfor 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/errorsfor 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:
# Find a function
grep -r "func FunctionName" charon/
# See package structure
ls -la charon/<directory>/
# Check imports
grep "^import" charon/path/to/file.go
# Run specific Go test (to understand behavior)
cd charon && go test -run TestFunctionName ./path/to/package