dig-block is the DIG Network L2 block format, production, and validation library. It
owns the canonical on-wire shape of every L2 block, the builder pipeline that assembles blocks
from spend bundles, and the three-tier validation pipeline (structural → execution → state)
that rejects any block that does not match consensus rules.
This crate is single-block scoped: every public function operates on one block (or one checkpoint) in isolation. External state (coin set, chain tip, wall clock, validator set) is injected through two traits:
CoinLookup— coin-set queries for Tier 3 state validation.BlockSigner— proposer signing hook for block production.
dig-block never reads from disk, never makes network calls, and never maintains state across
blocks. Downstream crates (dig-coinstore, dig-epoch, dig-gossip) supply the trait
implementations, storage, chain management, and networking.
[dependencies]
dig-block = "0.1"Rust edition 2021; minimum supported Rust version 1.75.
| Concern | Key items |
|---|---|
| Block types | L2BlockHeader, L2Block, AttestedBlock, Checkpoint, CheckpointSubmission, Receipt, ReceiptList, SignerBitmap, BlockStatus, CheckpointStatus |
| Block production | BlockBuilder, CheckpointBuilder |
| Validation | L2Block::validate_structure, L2Block::validate_execution, L2Block::validate_state, L2Block::validate_full |
| Validation output | ExecutionResult, PendingAssertion, AssertionKind |
| Integration traits | CoinLookup, BlockSigner |
| Hashing | hash_leaf, hash_node, compute_spends_root, compute_additions_root, compute_removals_root, compute_filter_hash, compute_receipts_root, compute_state_root_from_delta |
| Errors | BlockError, CheckpointError, BuilderError, SignerBitmapError, ReceiptError |
| Constants | EMPTY_ROOT, ZERO_HASH, MAX_BLOCK_SIZE, MAX_COST_PER_BLOCK, MAX_SLASH_PROPOSALS_PER_BLOCK, MAX_SLASH_PROPOSAL_PAYLOAD_BYTES, DFSP_ACTIVATION_HEIGHT, MAX_FUTURE_TIMESTAMP_SECONDS, HASH_LEAF_PREFIX, HASH_TREE_PREFIX, VERSION_V1, VERSION_V2, MAX_VALIDATORS |
| Primitives | Bytes32, Cost, Signature, PublicKey (re-exported from chia-protocol / chia-bls) |
A convenience glob import is provided:
use dig_block::prelude::*;use dig_block::prelude::*;
use dig_block::traits::SignerError;
// 1. Implement BlockSigner for your key backend.
struct MySigner;
impl BlockSigner for MySigner {
fn sign_block(&self, _header_hash: &Bytes32) -> Result<Signature, SignerError> {
Ok(Signature::default())
}
}
// 2. Build an anchored builder.
let parent = Bytes32::default();
let l1_hash = Bytes32::default();
let mut builder = BlockBuilder::new(
/*height=*/ 1, /*epoch=*/ 0, parent,
/*l1_height=*/ 100, l1_hash, /*proposer_index=*/ 0,
);
// 3. (Optional) add spend bundles / slash proposals / set L1 proofs etc.
// builder.add_spend_bundle(bundle, cost, fee)?;
// 4. Finalize and sign.
let state_root = Bytes32::default();
let receipts_root = Bytes32::default();
let block = builder.build(state_root, receipts_root, &MySigner)
.expect("well-formed builder output");The output is structurally valid by construction: block.validate_structure() always passes
on anything BlockBuilder::build produces (BLD-007).
use dig_block::prelude::*;
use chia_protocol::CoinState;
use dig_clvm::ValidationConfig;
struct MyCoinLookup;
impl CoinLookup for MyCoinLookup {
fn get_coin_state(&self, _id: &Bytes32) -> Option<CoinState> { None }
fn get_chain_height(&self) -> u64 { 0 }
fn get_chain_timestamp(&self) -> u64 { 0 }
}
fn validate(block: &L2Block, pk: &PublicKey) -> Result<Bytes32, BlockError> {
// Runs Tier 1 → Tier 2 → Tier 3 with short-circuit on failure.
// Returns the computed state root on success.
block.validate_full(
&ValidationConfig::default(),
&Bytes32::default(), // genesis challenge
&MyCoinLookup,
pk,
)
}┌─────────────────────────────────────────────────────────────────────┐
│ L2Block │
│ │ │
│ ▼ │
│ Tier 1: L2Block::validate_structure() │
│ • no external state │
│ • SVL-001..006: version, DFSP roots, cost/size, timestamp, │
│ count agreement, Merkle roots, duplicates │
│ │ │
│ ▼ │
│ Tier 2: L2Block::validate_execution(config, genesis_challenge) │
│ • dig_clvm::validate_spend_bundle per SpendBundle │
│ • EXE-002..007: puzzle hash, CLVM, conditions, BLS, conservation, │
│ cost │
│ • Produces ExecutionResult → PendingAssertion vec │
│ │ │
│ ▼ │
│ Tier 3: L2Block::validate_state(exec, coins, pubkey) │
│ • CoinLookup for persistent coin state │
│ • STV-002..007: coin existence, puzzle-hash cross-check, │
│ addition uniqueness, height/time locks, proposer signature, │
│ state root recompute │
│ │ │
│ ▼ │
│ Ok(computed_state_root) / Err(BlockError) │
└─────────────────────────────────────────────────────────────────────┘
Each tier can also be invoked independently, e.g. for light clients that only need structural
verification, or for validators that want to cache ExecutionResult for replay.
dig-block reuses the Chia Rust ecosystem and does not reimplement CLVM, BLS, or Merkle primitives:
| Concern | Crate |
|---|---|
Core protocol types (Bytes32, Coin, SpendBundle, CoinSpend, CoinState) |
chia-protocol |
BLS12-381 signatures (Signature, PublicKey, verify) |
chia-bls |
| CLVM execution + condition parsing | dig-clvm (wraps chia-consensus) |
| Merkle set roots (additions, removals) | chia-consensus |
| Binary Merkle trees (spends, receipts, slash proposals) | chia-sdk-types |
| SHA-256 | chia-sha2 |
| CLVM tree hashing | clvm-utils |
| Bincode serialization | bincode + serde |
| BIP-158 compact block filter | bitcoin::bip158 |
CLVM execution is always routed through dig_clvm::validate_spend_bundle; dig-block never
calls chia-consensus::run_spendbundle directly. This architectural boundary is enforced by a
grep-based lint in tests/test_exe_003_clvm_delegation.rs.
74 normative requirements; one dedicated integration test file per requirement. The full suite:
cargo test --releaseRuns ~600 tests against the public API (no unit tests reach into private modules). Property-based
coverage via proptest in tests/test_ser_005_roundtrip_integrity.rs.
All behavior is derived from the authoritative crate specification:
docs/resources/SPEC.md.
Each requirement has a three-document trace:
docs/requirements/domains/{domain}/NORMATIVE.md— authoritative MUST/SHOULD statementsdocs/requirements/domains/{domain}/specs/{PREFIX-NNN}.md— detailed spec + test plandocs/requirements/domains/{domain}/VERIFICATION.md/TRACKING.yaml— status + test refs
MIT. See LICENSE.
Semantic versioning. Wire-format changes to the block header bump the minor version pre-1.0 and
the major version post-1.0. Adding new protocol versions (VERSION_V3, etc.) follows the same
rule.