Protocol Architecture
Crate graph, data structures, UTXO model, block format, and network layer design.
Crate Graph
RillCoin is implemented as a Cargo workspace with six library crates and three binary crates. Dependencies flow strictly from lower to higher layers — no circular dependencies.
rill-coreDefines all shared types (Transaction, Block, BlockHeader, UTXO, Address, TxInput, TxOutput), protocol constants, error types, and cryptographic primitives. All other crates depend on rill-core. No external blockchain dependencies.
rill-decayImplements the sigmoid lookup table, fixed-point decay rate calculation, cluster balance aggregation, lineage tracking, and the decay pool state machine. Exposes pure functions — no I/O, no side effects.
rill-consensusBlock and transaction validation logic, proof-of-work verification, difficulty adjustment algorithm, Merkle root computation, coinbase maturity checks, and fee validation. The single source of truth for what constitutes a valid block.
rill-networklibp2p-based networking: TCP transport, Noise handshake, Gossipsub block/tx propagation, Kademlia peer discovery, and connection management. Handles peer scoring and ban lists.
rill-walletHD wallet derivation (BIP-32-style with Ed25519), address generation, decay-aware coin selection (spends highest-decay UTXOs first), transaction construction, and signing.
rill-nodeIntegrates all library crates. Hosts the JSON-RPC 2.0 server, manages RocksDB storage, drives the mempool, coordinates block sync, and runs the decay application engine.
Transaction Structure
Transactions use a standard UTXO model. Each transaction consumes one or more UTXOs (inputs) and creates one or more new UTXOs (outputs).
pub struct Transaction {
pub version: u64,
pub inputs: Vec<TxInput>,
pub outputs: Vec<TxOutput>,
pub lock_time: u64,
}
pub struct TxInput {
pub prev_txid: [u8; 32], // BLAKE3 hash of previous transaction
pub prev_index: u32, // Index of the output being spent
pub signature: [u8; 64], // Ed25519 signature
pub pubkey: [u8; 32], // Ed25519 public key
}
pub struct TxOutput {
pub value: u64, // Amount in rills (1 RILL = 100_000_000)
pub pubkey_hash: [u8; 20], // BLAKE3(pubkey)[..20] — address payload
pub cluster_id: [u8; 32], // BLAKE3 cluster identifier
}Transaction ID
A transaction ID (txid) is computed as BLAKE3(bincode::serialize(transaction)) — the BLAKE3 hash of the bincode-serialized transaction bytes. This is a 32-byte value, displayed as lowercase hex.
Signing
Each input is signed independently. The signing message is the serialized transaction with all input signatures zeroed out, committed to with the signer's Ed25519 key. The signature and public key are embedded in the input itself.
UTXO Model & Cluster Tracking
RillCoin extends the standard UTXO model with cluster tracking. Every UTXO carries a cluster_id — a 32-byte BLAKE3 hash that groups economically related UTXOs for decay calculation. All UTXOs sharing a cluster_id are aggregated when computing concentration.
The cluster_id is set at UTXO creation time. Wallet implementations should assign the same cluster_id to all outputs controlled by the same economic actor, or use the default derivation:
default_cluster_id = BLAKE3(wallet_root_pubkey)This prevents decay evasion through address splitting: even if a holder distributes coins across thousands of addresses, as long as they share a cluster_id, the full aggregate is subject to decay.
Block Structure
pub struct Block {
pub header: BlockHeader,
pub transactions: Vec<Transaction>,
}
pub struct BlockHeader {
pub version: u32,
pub prev_hash: [u8; 32], // SHA-256 hash of previous header
pub merkle_root: [u8; 32], // BLAKE3 Merkle root of transactions
pub timestamp: u64, // Unix seconds
pub difficulty_target: u32, // Compact nBits format
pub nonce: u64, // Proof-of-work nonce
}Block Hash
The block hash is computed as SHA-256(SHA-256(bincode(block_header))). A block is valid if its hash, interpreted as a 256-bit big-endian integer, is less than the target derived from difficulty_target.
Coinbase Transaction
The first transaction in every block must be a coinbase transaction. Coinbase transactions have no inputs (or a single input with a null prevout). The output value must equal the block subsidy plus transaction fees plus the decay pool release for that block. Coinbase outputs cannot be spent until the UTXO has been buried by COINBASE_MATURITY = 100 additional blocks.
Address Encoding
RillCoin addresses use Bech32m encoding (BIP-350). The address payload is a 20-byte prefix of the BLAKE3 hash of the Ed25519 public key.
address_payload = BLAKE3(ed25519_pubkey_bytes)[0..20]
address_string = bech32m_encode(hrp, address_payload)
where hrp:
mainnet → "rill1"
testnet → "trill1"Example addresses
trill1qw5r3k8d9...rill1qw5r3k8d9...Network Magic Bytes
Each network is identified by a 4-byte magic value prepended to all P2P messages. This prevents cross-network contamination.
| Network | Magic (ASCII) | Magic (hex) | P2P Port | RPC Port |
|---|---|---|---|---|
| Mainnet | RILL | 0x52494C4C | 18333 | 18332 |
| Testnet | TEST | 0x54455354 | 28333 | 28332 |
| Regtest | REGT | 0x52454754 | 38333 | 38332 |