Imported from p2poolv2/p2poolv2 (
AGENTS.md). Install upstream withnpx skills add p2poolv2/p2poolv2. Copyright stays with the author.
This file provides guidance to AI coding agents working with code in this repository.
Project overview
P2Poolv2 is a peer-to-peer Bitcoin mining pool where miners coordinate directly without centralized operators. Miners submit shares via Stratum, which form a DAG-based share chain with uncle support. Payouts are calculated via PPLNS and paid directly from coinbase transactions.
Project name is P2Poolv2 (no whitespace). Rust edition 2024, minimum rust-version 1.88.
Build and test commands
cargo build --workspace # Use --workspace unless working on a single crate.
cargo nextest run --workspace # always use nextest, not cargo test
cargo nextest run --workspace test_name # single test by name substring
cargo nextest run -p p2poolv2_lib # single crate
cargo clippy --workspace --all-targets # must pass with zero warnings
cargo fmt --all -- --check # must pass
cargo bench -p p2poolv2_lib --bench pplns_window --features test-utils
cargo build -p p2poolv2_sim --features sim # sim binary
A task is not done until cargo clippy --workspace --all-targets and
cargo fmt --all -- --check both pass cleanly (no new warnings, zero
errors).
Architecture docs
Read docs/architecture/ when working in the relevant area:
share-processing-pipeline.md-- emission through validation, organization, and P2P broadcastasync-flow.md-- worker tasks, channel topology, backpressurestore-schema.md-- RocksDB column families, key encoding, merge opsstore-architecture.md-- read/write separation, StoreWriter threadpruning.md-- TTL-based share pruningmetrics.md-- Prometheus exposition, metric sources, Grafana panelsaddress-format.md-- share chain address encoding, parser rules, test vectors, and how a miner supplies one over stratum
Also: docs/simulation/ (sim feature), docs/difficulty_adjustment/
(ASERT algorithm).
Never infer architecture from filenames alone. Read the relevant architecture document first.
Workspace crates
| Crate | Purpose |
|---|---|
p2poolv2_lib |
Core library: shares, P2P, stratum, storage, validation, accounting |
p2poolv2_node |
Main binary: builds and runs the full node |
p2poolv2_cli |
CLI utilities: share queries, address generation, auth signing |
p2poolv2_api |
REST API + WebSocket server (axum) |
p2poolv2_config |
TOML config parsing with phantom-type state machine (Raw -> Parsed) |
p2poolv2_address |
Share chain address type: bech32m over a witness v1 (P2TR) output key, HRP p2pool/tp2pool/sp2pool/rp2pool |
p2poolv2_tests |
Integration tests (multi-node P2P, stratum, API) |
p2poolv2_sim |
No-PoW load-test simulation binary (feature-gated with sim) |
bitcoindrpc |
JSON-RPC 1.0 client for Bitcoin Core |
Key entry points in p2poolv2_lib
Most modules are discoverable by name; these are the non-obvious ones:
node/actor.rs-- NodeActor: main event loop driving libp2p swarm and orchestrating all workersshares/handle_stratum_share.rs-- converts a stratum Emission into a ShareBlock; the bridge between stratum and the share chainstore/writer/-- all RocksDB writes are serialized through a dedicated OS thread; reads bypass itstore/organise/-- candidate/confirmed chain indexing, reorg logic, WriteBatch atomicityshares/chain/chain_store_handle.rs-- async trait over chain ops; the seam used for mocking in tests
Domain concepts
- Share chain: DAG of share blocks with parent + uncle references. Uncle is an ancestor (not sibling) that is NOT on the confirmed chain.
- Candidate chain: best-work chain of share headers, updated on each new header
- Confirmed chain: subset of candidate chain promoted after block validation
- Emission: a stratum share submission packaged for the node
pipeline (defined in
stratum/emission.rs) - Two miner addresses: a share header carries both. Never derive one
from the other, and never substitute one for the other.
miner_bitcoin_address(bitcoin::Address) receives the PPLNS payout from a found block's coinbase;miner_address(p2poolv2_address::Address, stored as aWitnessProgram) owns the share coinbase output and must be spend-capable, because shares are traded and the share chain runs real script validation. - ASERT: difficulty adjustment algorithm for share target
- PPLNS: pay-per-last-N-shares payout scheme
- Pool modes: P2Poolv2 (sharechain PPLNS) vs Hydrapool (time-window PPLNS)
Testing
- Set mock expectations inline in each test -- no mock setup helpers. This keeps each test self-contained and readable.
- Test helpers belong in
test_utilsmodule only if they are generic and reusable. No helpers local to a test module -- they inevitably diverge and hide setup differences between tests. - No loops over collections in tests. Do not iterate a table of
cases inside a test. Write each case as its own
#[test]with a name that says what it covers, or as explicit sequential assertions. Looping adds indirection to read, hides which case failed, and lets one case's setup silently drift from the others. TestShareBlockBuilderfor constructing test shares; well-known pubkeysPUBKEY_GthroughPUBKEY_5Gin test_utils.- Feature
test-utilsgates shared test infrastructure.
Do not touch
- Fixture JSON files in
p2poolv2_tests/test_data/. Override values in test code instead. - Genesis block definitions in
shares/genesis/. Genesis shares define a share chain starting point.
Workflow
- For multi-step plans, ask to commit after each step.
- Never create commits yourself. After completing a logical step, ask the user to review and commit before continuing.
- Code reviews stay local -- do not post to GitHub.
Rust conventions
- Imports: All
usedeclarations at top of module (or top of#[cfg(test)] mod tests). Never inside function bodies. Import items and refer by short name; at mostparent_module::function, never full crate paths inline. - Preallocate collections: Use
HashMap::with_capacity(n)/Vec::with_capacity(n)when the size is known or estimable at construction. Flag bareHashMap::new()as a potential problem. - Error handling: Do not replace fallible operations with default values unless that behaviour is explicitly intended. If error handling is unclear, ask.
- Update docstrings for changed functions.
Hard constraints
- No
unsafe. - ASCII only in all src and comments (no unicode emdashes, arrows, etc.).
Preferred style
- No abbreviations or single-character variable names.
- Concurrency: Prefer the actor/channel pattern over locks. If you must lock, evaluate deadlock risk and document lock ordering.
- Functions: One function does one thing. Extract when a block needs a comment to explain it, unless extraction forces lifetimes or unnecessary copies.
- Avoid continue/break. Prefer structured control flow because the distributed algorithms are easier to reason about without non-local jumps.
Prefer existing code
Before introducing a new utility, helper, or dependency:
- search the workspace for an existing implementation
- reuse existing abstractions
- avoid duplicate helper functions
Performance matters.
- Avoid unnecessary cloning, temporary allocations, and repeated RocksDB lookups.
- When performance and readability conflict, ask before making a large refactor.
Security
- Never add support for generating private keys. This should be done
by external third party tools and wallets. Tools that generate
private keys end up eventually having a security flaw. The only use
of
randcrate is for generating session nonces, compact block salt and sim jitter.