Skip to main content
Minichain is built with a clean, modular architecture consisting of 7 Rust crates. Each crate has a single, well-defined responsibility, making the codebase easy to understand and extend.

The 7 Crates

Minichain’s architecture follows a layered design where higher-level crates depend on lower-level ones:

1. Core (minichain-core)

The foundation of the entire blockchain. Provides primitive types used throughout the system. Key Components:
  • Cryptography: Blake3 hashing, Ed25519 signatures, 20-byte addresses
  • Accounts: Balance, nonce, code hash, storage root
  • Transactions: Transfer, deploy, and call transaction types
  • Blocks: Block headers and full blocks with merkle roots
  • Merkle Trees: Efficient state verification
Core Types:
pub struct Account {
    pub nonce: u64,              // Transaction count
    pub balance: u64,            // Native token balance
    pub code_hash: Option<Hash>, // Contract bytecode hash (None for EOAs)
    pub storage_root: Hash,      // Root of account's storage trie
}

pub struct Transaction {
    pub nonce: u64,
    pub from: Address,
    pub to: Option<Address>,     // None for contract deployment
    pub value: u64,
    pub data: Vec<u8>,
    pub gas_limit: u64,
    pub gas_price: u64,
    pub signature: Signature,
}

pub struct BlockHeader {
    pub height: u64,
    pub timestamp: u64,
    pub prev_hash: Hash,
    pub merkle_root: Hash,       // Merkle root of transactions
    pub state_root: Hash,        // Root of world state
    pub author: Address,         // PoA authority
    pub difficulty: u64,         // Always 1 for PoA
    pub nonce: u64,
}
The core crate has zero blockchain-specific dependencies — it only uses cryptography and serialization libraries. This makes it highly portable and testable.

2. Storage (minichain-storage)

Persistent state layer built on sled, an embedded key-value database. Responsibilities:
  • Account state (balances, nonces, contract code)
  • Block storage (by hash and height)
  • Contract storage (SLOAD/SSTORE operations)
  • State root computation
Architecture:
┌─────────────────────────────────────────────────────────┐
│                    Application Layer                     │
│            (Chain, VM, Transaction Execution)            │
└────────────────────────┬────────────────────────────────┘

┌────────────────────────▼────────────────────────────────┐
│                   Storage Layer                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐  │
│  │ StateManager│  │ ChainStore  │  │ Storage (DB)    │  │
│  │  - Accounts │  │  - Blocks   │  │  - sled wrapper │  │
│  │  - Balances │  │  - Height   │  │  - serialization│  │
│  │  - Contract │  │  - Genesis  │  │  - key helpers  │  │
│  │    Storage  │  │             │  │                 │  │
│  └─────────────┘  └─────────────┘  └─────────────────┘  │
└────────────────────────┬────────────────────────────────┘

┌────────────────────────▼────────────────────────────────┐
│                    sled Database                         │
│              (Embedded Key-Value Store)                  │
└─────────────────────────────────────────────────────────┘

3. VM (minichain-vm)

Register-based virtual machine with gas metering. Executes contract bytecode. Features:
  • 16 general-purpose registers (R0-R15)
  • 40+ opcodes (arithmetic, logic, memory, storage, control flow)
  • Separate RAM (memory) and disk (storage) operations
  • Gas metering on every operation
  • Execution tracing for debugging
Key Modules:
  • executor.rs: Main VM execution loop
  • opcodes.rs: Opcode definitions and decoding
  • gas.rs: Gas costs and metering
  • memory.rs: Register and memory management

4. Assembler (minichain-assembler)

Compiles human-readable assembly language into VM bytecode. Features:
  • Labels and jump instructions
  • Entry point declarations (.entry)
  • Immediate values and register operations
  • Error reporting with line numbers
Example:
.entry main

main:
    LOADI R0, 0          ; storage slot 0
    SLOAD R1, R0         ; load counter
    ADDI R1, R1, 1       ; increment
    SSTORE R0, R1        ; save back
    HALT

5. Consensus (minichain-consensus)

Proof of Authority (PoA) consensus implementation. Components:
  • PoAConfig: Authority list and block time settings
  • Authority: Round-robin block production scheduling
  • BlockProposer: Creates and signs blocks
  • BlockValidator: Validates block structure and signatures
  • TransactionValidator: Validates transaction signatures and nonces
Round-Robin Scheduling:
// Authority at height H = authorities[H % authority_count]
pub fn authority_at_height(&self, height: u64) -> Result<Address> {
    let index = (height as usize) % self.authorities.len();
    Ok(self.authorities[index])
}

6. Chain (minichain-chain)

Orchestrates all components into a functional blockchain. Key Components:
  • Blockchain: Main orchestrator, manages state and blocks
  • Mempool: Transaction pool with gas-price ordering
  • Executor: Executes blocks and transactions
  • BlockchainConfig: Configuration and authority management
Responsibilities:
  • Transaction submission and validation
  • Block production and execution
  • State management and persistence
  • Gas tracking and refunds

7. CLI (minichain-cli)

Command-line interface for interacting with the blockchain. Commands:
  • init: Initialize new blockchain
  • account: Manage accounts and balances
  • tx: Submit transactions
  • block: Produce and query blocks
  • deploy: Deploy smart contracts
  • call: Call contract functions

Transaction Flow

Let’s trace a complete transaction through the system:

Step-by-Step Breakdown

1. Transaction Creation (CLI)

// User signs transaction with their keypair
let mut tx = Transaction::transfer(
    from_address,
    to_address,
    amount,
    nonce,
    gas_price,
);
tx.sign(&keypair);

2. Transaction Validation (Blockchain)

// Verify signature
tx.verify(&sender_public_key)?;

// Check nonce matches account
if tx.nonce != account.nonce {
    return Err(ValidationError::InvalidNonce);
}

// Check sufficient balance for value + gas
if account.balance < tx.max_cost() {
    return Err(ValidationError::InsufficientBalance);
}

3. Mempool Ordering (Mempool)

// Transactions ordered by gas_price (highest first)
transactions.sort_by(|a, b| b.gas_price.cmp(&a.gas_price));

4. Block Production (Authority)

// Check if it's our turn
let expected = config.authority_at_height(height)?;
if expected != self.address() {
    return Err(ConsensusError::NotTurn { expected, got });
}

// Create and sign block
let block = Block::new(
    height,
    prev_hash,
    transactions,
    state_root,
    self.address(),
).signed(&self.keypair);

5. Transaction Execution (Executor + VM)

// Deduct max gas cost upfront
account.debit(tx.gas_limit * tx.gas_price)?;

// Execute in VM
let result = vm.execute(bytecode, gas_limit)?;

// Refund unused gas
let refund = (gas_limit - result.gas_used) * tx.gas_price;
account.credit(refund);

// Update states
if result.success {
    storage.commit_state()
} else {
    storage.revert_state()
}

6. State Persistence (Storage)

// Atomically persist all state changes
storage.batch_write([
    (account_key, account_data),
    (block_hash_key, block_data),
    (height_key, block_hash),
    (head_key, new_height),
])?;

Dependency Graph

Here’s the complete dependency graph showing how crates depend on each other:
          ┌─────────────┐
          │     CLI     │ (User Interface)
          └──────┬──────┘

    ┌────────────┼────────────┐
    │            │            │
    ▼            ▼            ▼
┌────────┐  ┌───────┐  ┌──────────┐
│Assembler│  │ Chain │  │          │
└────┬───┘  └───┬───┘  │          │
     │          │       │          │
     │     ┌────┼───────┤          │
     │     │    │       │          │
     │     ▼    ▼       ▼          │
     │  ┌────┐ ┌──┐ ┌─────────┐   │
     │  │ VM │ │  │ │Consensus│   │
     │  └─┬──┘ │  │ └────┬────┘   │
     │    │    │  │      │        │
     │    │    ▼  │      │        │
     │    │ ┌───────┐   │        │
     │    │ │Storage│   │        │
     │    │ └───┬───┘   │        │
     │    │     │       │        │
     └────┼─────┼───────┼────────┘
          │     │       │
          ▼     ▼       ▼
       ┌──────────────────┐
       │      CORE        │ (Primitives)
       └──────────────────┘
All crates depend on core, but core depends on no other Minichain crates. This creates a clean dependency hierarchy with no circular dependencies.

Design Principles

1. Separation of Concerns

Each crate has a single, well-defined responsibility:
  • Core: Types and primitives
  • Storage: Persistence only
  • VM: Execution only
  • Consensus: Validation only
  • Chain: Orchestration only

2. Dependency Flow

Dependencies flow downward:
  • High-level crates (CLI, Chain) depend on low-level crates
  • Low-level crates (Core) have no internal dependencies
  • No circular dependencies

3. Testability

Each crate is independently testable:
  • Core types have unit tests
  • VM can be tested without blockchain
  • Storage can be tested with in-memory database
  • Integration tests verify full system

4. Extensibility

New features can be added without modifying core:
  • New opcodes: Update VM only
  • New transaction types: Update Core and Executor
  • New consensus: Implement consensus interface
  • New storage backend: Implement storage trait

Real-World Example

Let’s see how all components work together for a contract deployment:
# 1. User writes assembly
cat > counter.asm << EOF
.entry main
main:
    LOADI R0, 0
    SLOAD R1, R0
    ADDI R1, R1, 1
    SSTORE R0, R1
    HALT
EOF

# 2. CLI compiles assembly → bytecode (Assembler)
minichain deploy --from alice --source counter.asm

# 3. CLI creates deployment transaction (Core)
# Transaction { to: None, data: bytecode, ... }

# 4. Blockchain validates & adds to mempool (Chain)
# - Signature verification
# - Nonce check
# - Balance check

# 5. Authority produces block (Consensus)
minichain block produce --authority authority_0
# - Round-robin: Check if it's our turn
# - Collect transactions from mempool
# - Create & sign block

# 6. Executor runs transactions (Chain + VM)
# - Load sender account (Storage)
# - Deduct gas upfront
# - Execute bytecode in VM
# - Update state (Storage)

# 7. Storage persists changes (Storage)
# - Account states (sender balance, nonce)
# - Contract account (code hash, storage)
# - Block data (header, transactions)
# - Chain head (new height)

Performance Characteristics

ComponentComplexityNotes
Block validationO(n)n = number of transactions
Transaction validationO(1)Signature verification
Mempool insertionO(log n)Maintain gas-price order
Account lookupO(log n)sled B-tree
Contract executionO(m)m = number of opcodes
State persistenceO(k)k = modified accounts
The VM uses registers instead of a stack, which provides better cache locality and simpler instruction encoding. This makes execution faster and bytecode more compact.

Next Steps

Accounts & Transactions

Learn about the account model and transaction types

Blocks & Consensus

Understand block structure and PoA consensus

Gas System

Explore gas metering and operation costs

Virtual Machine

Deep dive into the register-based VM

Build docs developers (and LLMs) love