Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Repository Guidelines

## Project Structure & Module Organization

This is a Rust binary crate for the `aleo-devnode` CLI. The entrypoint is `src/main.rs`; command modules live in `src/accounts.rs`, `src/advance.rs`, `src/start.rs`, and `src/restore.rs`. REST routing is under `src/rest/`, with shared helpers in `src/rest/helpers/`. Integration tests are in `tests/integration.rs`, and the bundled genesis block is in `resources/`.

## Architecture (the big picture)

Single Rust binary crate built on **snarkVM `TestnetV0`**. The defining trait: **the devnode skips proof verification** (snarkVM `dev_skip_checks`/`test_*` features in `Cargo.toml`), so clients can broadcast transactions built with placeholder proofs/verifying keys. Any code touching transaction validation must preserve this "no real proofs" assumption.

**CLI dispatch** (`src/main.rs`): a clap enum with four subcommands — `start`, `advance`, `restore`, `accounts` — each owning its module. `--private-key` is a *global* arg resolved from the flag or the `PRIVATE_KEY` env var (`start::resolve_private_key`). Keep each command's behavior inside its own module.

**Ledger storage is generic over `ConsensusStorage<N>`.** `src/start.rs` picks the concrete backend at runtime: `ConsensusMemory` (in-memory, default) or `ConsensusDB` (RocksDB, when `--storage <DIR>` is given). Everything downstream — including the whole REST layer — is written generically as `<N: Network, C: ConsensusStorage<N>>` so both backends share one code path.

**REST layer** (`src/rest/`): `Rest<N, C>` (`mod.rs`) holds the ledger plus a `Mutex<Vec<Transaction>>` buffer, shutdown channel, and in-flight verification counters. `build_routes()` defines the route table **once** and is mounted under three prefixes in `spawn_server`:
- `/<network>` and `/v1/<network>` — wrapped in `v1_error_middleware`, which flattens any error into a plain-string HTTP 500 (legacy behavior).
- `/v2/<network>` — returns structured JSON errors (`helpers/error.rs`, `RestError`).

So a single handler change automatically affects all three prefixes; error *shape* differs only via the middleware. Route handlers live in `src/rest/routes.rs`; error types/helpers in `src/rest/helpers/`.

**Block creation has two modes** (`routes.rs`):
- Default (auto): `transaction/broadcast` immediately calls `prepare_advance_to_next_beacon_block` + `advance_to_next_block`, producing one block per transaction.
- `--manual-block-creation`: broadcasts only push into the buffer; blocks are minted on demand via `POST /block/create` (`{"num_blocks": N}`), draining the buffer into the first block.

On startup (auto mode only), `run_devnode` advances the ledger to the last height in `TEST_CONSENSUS_VERSION_HEIGHTS` so the node boots at the latest consensus version. Blocking ledger work is wrapped in `tokio::task::spawn_blocking`.

**Snapshots & restore** (`routes.rs` + `src/restore.rs`): snapshots require `--storage` and are written to a sibling dir `{storage}-snapshots/{name}` (`snapshots_sibling_dir` is the shared source of that layout — reuse it, don't recompute the path). Restore is **offline**: the server must be stopped; `restore` clears the storage dir, copies the snapshot in, and with `--restart` re-execs the binary as `start` (via `exec` on Unix, same PID).

**Genesis**: a 40-validator genesis block is embedded at compile time from `resources/` via `include_bytes!`; `--genesis-path` overrides it.

## Build, Test, and Development Commands

Native build prerequisite: `clang`/`libclang` (snarkVM's `rocks`/RocksDB feature links it). CI installs `libclang-dev`; on macOS it comes with the Xcode Command Line Tools (`xcode-select --install`).

- `cargo build --release`: builds the production CLI binary.
- `cargo test --locked`: runs the same locked-dependency test command used by CI.
- `cargo test <name>`: runs a single test by substring; `cargo test --test integration <name>` runs one integration test.
- `cargo fmt --all -- --check`: verifies formatting against `rustfmt.toml`. CI does **not** enforce fmt — run it yourself.
- `cargo run -- accounts`: lists built-in funded development accounts.
- `cargo run -- start --private-key <DEV_PRIVATE_KEY>`: starts a local node on `127.0.0.1:3030`.

Use the standard development private key from `README.md` only for local testing.

CI (`.github/workflows/ci.yml`) installs `libclang-dev`, verifies `Cargo.lock` is present, and runs `cargo test --locked` on stable. The release workflow refuses to build unless the git tag version matches the `Cargo.toml` version — bump `Cargo.toml` before tagging.

## Coding Style & Naming Conventions

Follow Rust 2021 crate semantics and `rustfmt.toml`: 4-space indentation, 120-column width, shorthand field/try syntax, and crate-granular imports. Use `snake_case` for modules, functions, and tests; `PascalCase` for types; and `SCREAMING_SNAKE_CASE` for constants. Keep command behavior in its owning command module.

## Testing Guidelines

Tests use Rust's built-in test harness plus `reqwest` and `tempfile`. Integration tests spawn the compiled `aleo-devnode` binary through `CARGO_BIN_EXE_aleo-devnode`, allocate local ports, and clean up child processes through `DevnodeGuard`. Name new integration tests `test_*` and avoid depending on port `3030`.

## Domain Deep Dives

Use these docs as selective disclosure. Read them only when touching the related area:

- `docs/cli-usage.md`: commands, flags, env vars, and examples.
- `docs/rest-api.md`: route prefixes, endpoint patterns, and response behavior.
- `docs/storage-and-snapshots.md`: persistent storage, snapshot layout, and restore rules.
- `docs/testing.md`: integration harness, CI checks, ports, and child process cleanup.
- `docs/genesis-and-accounts.md`: bundled genesis data and development account limits.

## Commit & Pull Request Guidelines

Recent commits use short, imperative summaries, usually lowercase, such as `bump version to 0.1.1`. Keep commits focused and include `Cargo.lock` when dependency resolution changes. Pull requests should follow `.github/PULL_REQUEST_TEMPLATE.md`: motivation, test plan, and related PRs or issues.

## Security & Configuration Tips

The devnode skips proof verification by design. Treat all pre-funded keys and placeholder proof workflows as development-only. Never document or commit production keys, private ledgers, or generated snapshot data.
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

See **@AGENTS.md** for everything: project structure, the big-picture architecture, build/test/dev commands, coding style, testing model, commit/PR conventions, and security notes. The `docs/` files it links are selective-disclosure reference — read the matching one only when touching that area.
112 changes: 112 additions & 0 deletions docs/cli-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# CLI Usage Deep Dive

Read this when changing `src/main.rs`, any command module, README command examples, or user-facing flag behavior.

## Command Map

- `start`: starts the REST devnode and owns server, storage, genesis, logging, and block creation flags.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document the block creation flags more here as they are core to how to interact with devnode.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DONE

- `advance`: a thin client that POSTs to `/testnet/block/create`; defaults to advancing one block. It surfaces failures — a connection error or a non-success HTTP status returns an error and exits nonzero.
- `restore`: copies a named snapshot back into a storage directory and can re-exec as `start`.
- `accounts`: prints the 50 built-in development accounts for the bundled genesis block.
- `update`: self-updates the binary from GitHub releases (`ProvableHQ/aleo-devnode`).

## Global flags

These are defined on the top-level CLI (`src/main.rs`):

- `-v` / `-V` / `--version`: print the version and exit. Top-level only (parsed before the subcommand). **Caution:** `-v` at the top level means *version*, but `-v` after `start` or `restore` means *verbosity* — clap disambiguates by position.
- `--private-key <KEY>`: marked `global = true`, so clap accepts it positionally alongside `start` (e.g. `start --private-key ...` or `--private-key ... start`). However, `src/main.rs` only forwards it into `start`. **It is ignored by `restore`** — `restore --restart` reads its own `--private-key` (or `PRIVATE_KEY`), not the top-level one. See Private Key Rules below.

## `start` flags

| Flag | Short | Default | Notes |
| --- | --- | --- | --- |
| `--private-key <KEY>` | | `PRIVATE_KEY` env | Global. Required (flag or env). Used to sign every block. |
| `--verbosity <0-2>` | `-v` | `2` | Logging level; values outside `0..=2` are rejected by clap. |
| `--socket-addr <ADDR>` | `-a` | `127.0.0.1:3030` | REST API bind address. Must parse as a `SocketAddr`. |
| `--genesis-path <PATH>` | `-g` | `blank` | `blank` is a sentinel meaning the bundled 40-validator genesis; any other value is read as a genesis block file. |
| `--manual-block-creation` | `-m` | off | Switches block creation from automatic to manual — see below. |
| `--storage [DIR]` | `-s` | in-memory | Persist the ledger to disk (RocksDB). See storage behavior below. |
| `--clear-storage` | `-c` | off | Wipe the storage dir before starting. **Requires `--storage`** (clap enforces this). |

### Storage flag behavior (`--storage`)

`--storage` takes an optional value (`num_args = 0..=1`):

- **Omitted entirely** → ledger runs in memory (`ConsensusMemory`); nothing persists across restarts.
- **`-s` / `--storage` with no value** → defaults to the directory `devnode` (`default_missing_value`).
- **`--storage <DIR>`** → persists to `<DIR>` (`ConsensusDB` / RocksDB).

Snapshots (`POST /snapshot`) and the `restore` command only work when `--storage` is set — in-memory nodes return `400` for snapshot routes. See `docs/storage-and-snapshots.md`.

## Block creation

This is the core of how clients drive the ledger. There are two modes, selected at `start` time:

### Automatic (default)

Every successful `POST /testnet/transaction/broadcast` **immediately mints one block** containing that transaction — no extra call needed. Additionally, on startup in this mode `run_devnode` auto-advances the ledger to the last height in `TEST_CONSENSUS_VERSION_HEIGHTS` (by calling `/block/create` once internally), so the node boots at the latest consensus version.

You can still mint blocks explicitly in automatic mode — `advance <N>` (or `POST /block/create`) works here too. Since the broadcast buffer is always empty in this mode, those blocks are empty; use it to advance height or trigger height/time-gated logic without any transactions.

### Manual (`--manual-block-creation` / `-m`)

Broadcasts are only **buffered**; they do not land on the ledger until a block is minted explicitly. Use this when a test needs several transactions in one block, deterministic block boundaries, or control over block timing. Two ways to mint:

- `cargo run -- advance <N>` — the `advance` subcommand (a thin client that POSTs to `/block/create`).
- `POST /testnet/block/create` with `{"num_blocks": N}` directly (see `docs/rest-api.md`).

The buffered transactions are drained into the **first** block created; any further blocks in the same call are empty. `num_blocks` is bounded server-side to `1..=1000`.

Note: in manual mode the startup consensus-version auto-advance is **skipped**, so the node remains at the loaded ledger height — genesis height for a fresh ledger, or the persisted height when restarting against existing `--storage`. Advance manually if you need a later consensus version.

## `advance` flags

| Arg | Default | Notes |
| --- | --- | --- |
| `<num_blocks>` (positional) | `1` | Number of blocks to mint. |
| `--socket-addr <ADDR>` | `127.0.0.1:3030` | Target devnode. |

A thin client against a running server — it does not share the ledger, it just calls `/block/create`. Connection failures and non-success HTTP responses (status + body) are returned as errors and exit nonzero.

## `restore` flags

`restore` is **offline** — the server must be stopped first. Flags forwarded to `start` only apply with `--restart`, and **only the ones in the table below are forwarded** (`--storage`, `--private-key`, `--socket-addr`, `--verbosity`, `--manual-block-creation`). Notably `--genesis-path` and `--clear-storage` are **not** forwarded — if you restored a snapshot built from a custom genesis, restart with a manual `start --genesis-path ...` instead of `--restart`.

| Flag | Short | Default | Notes |
| --- | --- | --- | --- |
| `--snapshot <NAME>` | | (required) | Snapshot directory name under `{storage}-snapshots/`. |
| `--storage <DIR>` | | `devnode` | Storage dir to restore into; must match the `--storage` used at `start`. |
| `--restart` | | off | Re-exec as `start` after restoring (via `exec` on Unix, same PID). |
| `--private-key <KEY>` | | `PRIVATE_KEY` env | Forwarded to `start` on `--restart`. |
| `--socket-addr <ADDR>` | `-a` | `127.0.0.1:3030` | Forwarded to `start` on `--restart`. |
| `--verbosity <0-2>` | `-v` | `2` | Forwarded to `start` on `--restart`. |
| `--manual-block-creation` | `-m` | off | Forwarded to `start` on `--restart`. |

## `update` flags

| Flag | Short | Notes |
| --- | --- | --- |
| `--list` | `-l` | List available releases instead of updating. |
| `--name <TAG>` | `-n` | Update to a specific release tag rather than latest. |
| `--quiet` | `-q` | Suppress download/progress output. |

## Private Key Rules

`start` requires a private key. Prefer `--private-key` in examples because it is explicit and overrides the `PRIVATE_KEY` environment fallback. If neither is present, startup exits with an error. Invalid or blank keys should fail before the REST server starts.

`restore --restart` has its own forwarded `--private-key`; without it, restart relies on `PRIVATE_KEY`. The key is passed to the re-exec'd process via the `PRIVATE_KEY` env var to keep it out of the process argument list.

## Examples To Keep Current

```sh
cargo run -- accounts
cargo run -- start --private-key <DEV_PRIVATE_KEY>
cargo run -- start --private-key <DEV_PRIVATE_KEY> --manual-block-creation
cargo run -- start --private-key <DEV_PRIVATE_KEY> --storage devnode --clear-storage
cargo run -- advance 5 --socket-addr 127.0.0.1:3030
cargo run -- restore --snapshot before-deploy --storage devnode
cargo run -- update --list
```

When adding or renaming flags, update the owning command module, `README.md`, and this file together.
29 changes: 29 additions & 0 deletions docs/genesis-and-accounts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Genesis And Accounts Deep Dive

Read this when changing `resources/`, `src/accounts.rs`, genesis loading, or documentation that lists funded development accounts.

## Bundled Genesis

The default genesis block is compiled into the binary from:

```text
resources/genesis_8d710d7e2_40val_snarkos_dev_network.bin
```

`start --genesis-path <file>` replaces the built-in block with a custom genesis file. Do not assume the built-in funded account list applies when a custom genesis path is used.

## Funded Accounts

`src/accounts.rs` defines `FUNDED_ACCOUNTS`, the 50 development address/private-key pairs seeded by the bundled genesis block. The `accounts` command prints this list for local setup convenience.

The first development key is also used by README examples and integration tests:

```text
APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH
```

These keys are public development fixtures. Never present them as safe for production, private deployments, or funded real assets.

## Documentation Rules

If the bundled genesis block or funded account list changes, update `src/accounts.rs`, `README.md`, and tests that assume the standard development private key. For custom genesis workflows, direct users to inspect block 0 through the REST API instead of relying on `accounts`.
Loading