dig-rpc-types 0.1.0

JSON-RPC wire types shared by the DIG Network fullnode + validator RPC servers and their clients. Pure types — no I/O, no async, no logic.
Documentation

dig-rpc-types

Pure serde-derived JSON-RPC wire types for the DIG Network fullnode / validator / future wallet RPC surface. The single source of truth for every request and response shape exchanged between a DIG server and its clients.

  • No I/O — no tokio, no reqwest, no Axum in the default build.
  • No business logic — a method's wire shape is defined; dispatch lives elsewhere.
  • Stable error codesErrorCode numeric values never change once assigned.
  • Additive enum evolution — every enum is #[non_exhaustive].
  • JSON-RPC 2.0 strict — request/response envelopes match the spec exactly.

See docs/resources/SPEC.md for the design doc.


Table of contents

  1. Install
  2. At-a-glance
  3. Quick reference
  4. Envelope types
  5. ErrorCode
  6. Shared domain types
  7. Fullnode methods
  8. Validator methods
  9. Stability contract
  10. Feature flags
  11. License

Install

[dependencies]
dig-rpc-types = "0.1"

Minimum Rust: 1.70. Zero optional features are required for the core wire types.


At-a-glance

Module Re-exported at crate root Role
envelope JSON-RPC 2.0 request / response / error / id / version
errors Stable numeric error codes (ErrorCode)
types ✓ (partial) Shared domain types (HashHex, PubkeyHex, SignatureHex, Amount, BlockSummary, ValidatorSummary, ValidatorStatus)
fullnode 21 fullnode RPC method pairs
validator 9 validator RPC method pairs

Every method's request carries a pub const METHOD: &'static str for the wire name (GetBlockchainStateRequest::METHOD == "get_blockchain_state").


Quick reference

use dig_rpc_types::{
    envelope::{JsonRpcRequest, JsonRpcResponse, JsonRpcResponseBody, RequestId, Version},
    fullnode::{GetBlockchainStateRequest, GetBlockchainStateResponse},
};

// Build a request.
let req: JsonRpcRequest<()> = JsonRpcRequest {
    jsonrpc: Version,
    id: RequestId::Num(1),
    method: GetBlockchainStateRequest::METHOD.to_string(),
    params: None,
};

// Decode a typed response off the wire.
let raw = r#"{"jsonrpc":"2.0","id":1,"result":{
    "height": 100, "tip_hash":"0x1111111111111111111111111111111111111111111111111111111111111111",
    "synced":true,"sync_progress":1.0,"finalized_epoch": 3, "sealed_height": 95
}}"#;
let _resp: JsonRpcResponse<GetBlockchainStateResponse> = serde_json::from_str(raw).unwrap();

Envelope types

Module: dig_rpc_types::envelope. All re-exported at the crate root.

JsonRpcRequest<P = serde_json::Value>

pub struct JsonRpcRequest<P = serde_json::Value> {
    pub jsonrpc: Version,     // serialises to literal "2.0"
    pub id: RequestId,
    pub method: String,
    pub params: Option<P>,    // skip_serializing_if = "Option::is_none"
}

Inputs to construct: any P that implements Serialize. For typed requests, use the method-specific {Method}Request types under fullnode:: / validator:: as P. For generic proxies, leave P = serde_json::Value.

Output on the wire: {"jsonrpc":"2.0","id":...,"method":"...","params":...}params is omitted entirely when None.

JsonRpcResponse<R = serde_json::Value>

pub struct JsonRpcResponse<R = serde_json::Value> {
    pub jsonrpc: Version,
    pub id: RequestId,
    #[serde(flatten)]
    pub body: JsonRpcResponseBody<R>,
}

pub enum JsonRpcResponseBody<R> {              // #[serde(untagged)]
    Success { result: R },
    Error   { error: JsonRpcError },
}

Output on the wire: exactly one of {"jsonrpc":"2.0","id":...,"result":...} or {"jsonrpc":"2.0","id":...,"error":{...}} — never both, never neither. This is enforced by the untagged enum.

JsonRpcError

pub struct JsonRpcError {
    pub code: ErrorCode,
    pub message: String,
    pub data: Option<serde_json::Value>,   // skip_serializing_if = "Option::is_none"
}

Inputs: code is any ErrorCode variant; message is a human-readable short description; data carries optional structured context.

RequestId

pub enum RequestId {                       // #[serde(untagged)]
    Num(u64),
    Str(String),
    Null,                                  // spec-allowed for notifications
}

Echoed unchanged in the matching response.

Version

pub struct Version;      // ZST; serde impls hard-code "2.0"

Rejecting any string other than "2.0" at deserialize time is structural — no hand-coded check needed.


ErrorCode

Module: dig_rpc_types::errors. Re-exported at the crate root.

#[repr(i32)], #[non_exhaustive], Serialize_repr / Deserialize_repr — serialises as a bare integer on the wire, matching JSON-RPC 2.0.

Variants and wire values

Variant Value Range When
ParseError -32700 JSON-RPC reserved Invalid JSON received
InvalidRequest -32600 JSON-RPC reserved JSON valid but not a Request
MethodNotFound -32601 JSON-RPC reserved Unknown method
InvalidParams -32602 JSON-RPC reserved Params don't match schema
InternalError -32603 JSON-RPC reserved Server bug / panic caught
NotSynced 10001 DIG sync/auth Server not yet synced; data may be stale
PeerUnauthorized 10002 DIG sync/auth Client cert unresolved to a role
PermissionDenied 10003 DIG sync/auth Role below method's min_role
RateLimited 10004 DIG sync/auth Per-peer token bucket exhausted
ResourceNotFound 10005 DIG sync/auth Block / coin / validator not found
SlashingGuardBlocked 10010 DIG crypto Validator's local slashing DB vetoed the sig
InvalidSignature 10011 DIG crypto BLS signature failed verification
InvalidProof 10012 DIG crypto Groth16 / other ZK proof failed
L1Unavailable 10020 DIG L1 Cannot reach Chia L1
WalletLocked 10030 DIG wallet Wallet must unlock before signing
ShutdownPending 10040 DIG lifecycle Server shutting down
NetworkMismatch 10050 DIG protocol Client/server network id mismatch
VersionMismatch 10051 DIG protocol Client/server schema major mismatch

Helpers

Method Signature Purpose
code self -> i32 Raw numeric value
is_jsonrpc_reserved self -> bool In -32768..=-32000
is_dig_specific self -> bool In 10000..

Shared domain types

Module: dig_rpc_types::types. HashHex, PubkeyHex, SignatureHex, Amount, BlockSummary, ValidatorSummary, ValidatorStatus are re-exported at the crate root.

Hex-encoded byte arrays

Type Byte length Wire form Use
HashHex 32 "0x" + 64 lowercase hex chars Block hash, coin id, state root, beacon-block root
PubkeyHex 48 "0x" + 96 lowercase hex chars BLS12-381 G1 compressed pubkey
SignatureHex 96 "0x" + 192 lowercase hex chars BLS12-381 G2 compressed signature

All three share the same contract:

  • ConstructT::new([u8; N]) is const fn.
  • Serialize"0x" + lowercase hex.
  • Deserialize — accepts upper / lower / mixed case, optional 0x prefix.
  • FromStr — returns HexParseError on length mismatch or invalid hex.
  • as_bytes() -> &[u8; N] — borrow the underlying bytes.

HexParseError

pub enum HexParseError {
    WrongLength { expected: usize, expected_hex_chars: usize, got: usize },
    InvalidHex(hex::FromHexError),
}

Amount

#[serde(transparent)]
pub struct Amount(pub u64);              // mojos; 1 XCH = 10^12 mojos
impl Amount { pub const ZERO: Self = Self(0); }

Serialises as a bare JSON number. Safe for amounts up to 2^53 − 1 mojos (~9000 XCH) before JavaScript's float precision kicks in.

BlockSummary

pub struct BlockSummary {
    pub height: u64,
    pub hash: HashHex,
    pub parent_hash: HashHex,
    pub timestamp: u64,
    pub proposer: PubkeyHex,
    pub tx_count: u32,
    pub weight: u64,
}

Returned by get_block_records and embedded in get_blockchain_state.

ValidatorStatus

#[non_exhaustive]
#[serde(rename_all = "snake_case")]
pub enum ValidatorStatus {
    PendingRegister, Active, ExitingVoluntary, ExitingForced,
    Exited, WithdrawalPending, Withdrawn,
}

Wire form: "pending_register", "active", etc.

ValidatorSummary

pub struct ValidatorSummary {
    pub pubkey: PubkeyHex,
    pub status: ValidatorStatus,
    pub validator_index: Option<u32>,
    pub effective_balance: Amount,
    pub slashed_amount: Amount,
    pub activation_epoch: Option<u64>,
    pub exit_epoch: Option<u64>,
}

Fullnode methods

Module: dig_rpc_types::fullnode. Every method has a {Method}Request + {Method}Response pair and a const METHOD: &'static str on the request.

Blockchain state

Method Request fields Response fields
get_blockchain_state height: u64, tip_hash: HashHex, synced: bool, sync_progress: f32, finalized_epoch: u64, sealed_height: u64
get_network_info network_id: String, genesis_challenge: HashHex, chain_age_seconds: u64
healthz ok: bool

Blocks

Method Request fields Response fields
get_block hash: HashHex block: Option<BlockFull>
get_block_by_height height: u64 block: Option<BlockFull>
get_block_records start_height: u64, count: u32 records: Vec<BlockSummary>

BlockFull carries {header: BlockHeaderWire, body_hex: String, canonical: bool}. BlockHeaderWire carries {height, hash, parent_hash, timestamp, proposer, state_root, receipts_root, weight, total_iters: u128, signature: SignatureHex}.

Coins

Method Request fields Response fields
get_coin_record coin_id: HashHex record: Option<CoinRecordWire>
get_coin_records_by_hint hint: HashHex, include_spent_coins: bool, start_height?: u64, end_height?: u64 records: Vec<CoinRecordWire>
get_coin_records_by_puzzle_hash puzzle_hash: HashHex, include_spent_coins: bool, start_height?: u64, end_height?: u64 records: Vec<CoinRecordWire>

CoinRecordWire = {coin_id, parent_coin_info, puzzle_hash, amount: Amount, confirmed_block_height, spent_block_height, coinbase: bool, timestamp}.

Mempool

Method Request fields Response fields
get_mempool total_cost: u64, total_fees: Amount, items: Vec<MempoolItem>
push_tx spend_bundle_hex: String status: PushTxStatus, details?: String

MempoolItem = {spend_bundle_name: HashHex, cost: u64, fee: Amount, is_cpfp: bool}. PushTxStatus = "success" / "rejected" / "already_exists".

Peers

Method Request fields Response fields
get_connections peers: Vec<PeerInfoWire>
ban_peer peer_id: HashHex, reason: String, duration_secs: u64 banned: bool

PeerInfoWire = {peer_id, remote_addr, node_type, connected_since: u64, penalty: u32}.

Checkpoint

Method Request fields Response fields
submit_partial_checkpoint_signature epoch: u64, validator_index: u32, partial_sig: SignatureHex accepted: bool, reason?: String
get_checkpoint_pool epoch?: u64 epochs: Vec<CheckpointEpochStatus>

CheckpointEpochStatus = {epoch, partials_count, threshold, aggregated}.

Validator set

Method Request fields Response fields
get_validator pubkey: PubkeyHex validator: Option<ValidatorSummary>
get_active_validators limit: u32, offset: u32 validators: Vec<ValidatorSummary>, total: u32
get_current_proposer height: u64 proposer: Option<ValidatorSummary>

Admin

Method Request fields Response fields
stop_node reason?: String accepted: bool
get_recovery_status mode: RecoveryMode, last_anomaly?: String, attempts: u32
get_version version: String, build_commit: String

RecoveryMode = "running" / "recovering" / "loop_breaker_open".


Validator methods

Module: dig_rpc_types::validator. Smaller, operator-facing surface.

Status

Method Request fields Response fields
get_status pubkey: PubkeyHex, validator_index?: u32, can_participate: bool, last_duty_at?: u64, active_connections: u32
get_duty_history limit: u32, since?: u64 entries: Vec<DutyEntry>

DutyEntry = {at: u64, kind: DutyKind, ok: bool, detail?: String}. DutyKind = "propose" / "attest" / "sign_checkpoint" / "observe_l1".

Helper: validator::now_unix_seconds() -> u64 for populating since.

Slashing DB

Method Request fields Response fields
get_slashing_db last_proposed_slot: u64, last_attested_source: u64, last_attested_target: u64, last_attested_root: HashHex
export_slashing_db path: String written_bytes: u64
reset_slashing_db confirm_token: String reset: bool

Admin

Method Request fields Response fields
stop_node reason?: String accepted: bool
reload_config accepted: bool, applied_changes: Vec<String>
healthz ok: bool
get_version version: String, build_commit: String

Stability contract

  1. Every enum is #[non_exhaustive] — adding variants is additive.
  2. ErrorCode numeric values never change once assigned. New variants take the next unused integer.
  3. Method names are snake_case and stable.
  4. Hex-encoded fields emit 0x + lowercase; accept case-insensitive + optional 0x on parse.
  5. Response envelopes are valid JSON-RPC 2.0 — exactly one of result or error.
  6. SCHEMA_VERSION constant ("1") is bumped on wire-breaking changes.

Versioning policy

  • Additive changes (new methods, new optional fields, new enum variants) → bump minor. Older clients continue to work.
  • Removing or reshaping a field → bump major. Servers may serve multiple majors behind /v1/, /v2/ URL prefixes.

Feature flags

Flag Default Effect
client off Optional blocking reqwest-backed client (not implemented in v0.1)
schema-export off JSON-Schema dumps via schemars for Go/TS codegen

License

Licensed under either of Apache-2.0 or MIT at your option.