Lattice Blockchain Core SDK

The consensus layer of the Lattice SDK. Hierarchical proof-of-work with merged mining — child chains inherit parent security without splitting hashrate. Cross-chain transfers verified by Merkle proof. No bridges, no multisigs, no trust assumptions.

The core idea
Traditional multi-chain architectures divide security: each chain has its own miners and its own hashrate. Lattice inverts this. A single proof of work secures the nexus chain and all its children simultaneously. Adding a child chain adds throughput without reducing security. The parent's block literally contains the child's block — merged mining is structural, not optional.

Blocks

A Lattice block is a Cashew Merkle structure containing transactions, state roots, child blocks, and proof-of-work metadata.

struct Block: Node {
    var previousBlock: HeaderImpl<Block>?
    var transactions: MerkleDictionaryImpl<Transaction>
    var difficulty: UInt256
    var nextDifficulty: UInt256
    var spec: HeaderImpl<ChainSpec>
    var homestead: HeaderImpl<LatticeState>    // state before execution
    var frontier: HeaderImpl<LatticeState>     // state after execution
    var childBlocks: MerkleDictionaryImpl<Block> // merged-mined children
    var parentHomestead: HeaderImpl<LatticeState>?
    var index: UInt64
    var timestamp: Int64
    var nonce: UInt256
}

Because blocks are Cashew Nodes, they are content-addressed (every block has a unique CID), lazily loadable (you can reference a block by CID without having its full data), and recursively storable in the Acorn CAS chain.

Block Types

TypeDescriptionConstraints
Genesis First block of any chain. No previous block, index 0. No withdrawals. Can contain deposits and genesis actions.
Nexus Block on the root chain (the nexus). No deposits or withdrawals. Standalone proof of work.
Child Block on a child chain, embedded in a parent block's childBlocks. Timestamp must match parent. Inherits parent's filter rules.

Validation Pipeline

Every block passes through a multi-stage validation:

  1. Structural: Index sequential, timestamp not in future, spec matches
  2. Difficulty: difficulty ≥ getDifficultyHash() — the block's hash meets the work requirement
  3. Transactions: All signatures valid, all nonces correct, count within spec limits
  4. State: Execute all actions against homestead; computed frontier must match declared frontier
  5. Spec limits: State growth within maxStateGrowth, block size within maxBlockSize
  6. Balances: Total balance changes = block reward + deposits - withdrawals (conservation of value)

ChainSpec

The economic and consensus parameters for a chain. Immutable once the chain launches.

struct ChainSpec: Node {
    var premine: Int                         // blocks by founders before public mining
    var initialRewardExponent: Int           // block reward = 2^exponent
    var halvingInterval: Int                 // blocks between reward halvings
    var targetBlockTime: Int                 // milliseconds between blocks
    var difficultyAdjustmentWindow: Int     // blocks averaged for difficulty
    var maxDifficultyChange: Int             // max 2x increase or decrease
    var maxNumberOfTransactionsPerBlock: Int
    var maxStateGrowth: Int                  // max bytes of state changes per block
    var maxBlockSize: Int                    // max serialized block bytes
    var filter: String?                      // JavaScript validator for transactions
    var actionFilter: String?               // JavaScript validator for actions
}

Block Reward

Rewards follow a halving schedule similar to Bitcoin:

baseReward = 2^initialRewardExponent
halvings = (blockIndex - premine) / halvingInterval
reward = baseReward >> halvings

When halvings ≥ 64, the reward is zero. The geometric series is designed so total supply asymptotically approaches UInt64.max.

Difficulty Adjustment

Difficulty adjusts to maintain the target block time using a windowed average of recent block timestamps:

  1. Compute actual time for the last difficultyAdjustmentWindow blocks
  2. Compare to expected time (window × targetBlockTime)
  3. Scale difficulty proportionally, clamped to maxDifficultyChange (max 2x up or down)

Predefined Specs

SpecBlock TimeRewardAdjustment Window
.bitcoin10 min210 = 10242016 blocks
.ethereum12 sec218 = 262144256 blocks
.development1 sec210 = 102420 blocks

Transactions

Transactions are signed bundles of actions that modify world state.

struct Transaction: Node {
    var signatures: [String: String]  // publicKeyHex → signatureHex
    var body: HeaderImpl<TransactionBody>
}

struct TransactionBody: Node {
    var accountActions: [AccountAction]
    var actions: [Action]
    var depositActions: [DepositAction]
    var withdrawalActions: [WithdrawalAction]
    var genesisActions: [GenesisAction]
    var peerActions: [PeerAction]
    var receiptActions: [ReceiptAction]
    var signers: [String]   // public keys that signed
    var fee: UInt64
    var nonce: UInt64       // replay protection
}

Signature Verification

Transactions use P256 ECDSA signatures. Each signer's public key maps to their signature over the transaction body's CID. Multi-signature transactions are natively supported — multiple signers can authorize the same transaction.

World State

LatticeState is an 8-component Merkle tree representing all state on a chain. Each component is a MerkleDictionary.

LatticeState (root CID) ├── AccountState balances: owner → UInt64 ├── GeneralState application KV: key → value ├── DepositState cross-chain deposits pending ├── WithdrawalState cross-chain withdrawals pending ├── GenesisState child chain genesis blocks: directory → Block ├── PeerState P2P peers: owner → (IP, fullNode, refreshed) ├── ReceiptState withdrawal receipts: composite-key → withdrawer └── TransactionState committed txs: (signers:nonce) → tx CID

State transitions are deterministic: given a homestead (state before) and a set of transactions, the frontier (state after) is uniquely determined. This is how nodes independently verify blocks without trusting anyone.

State Proofs

Because every state component is a Cashew Merkle structure, you can generate sparse Merkle proofs for any state claim. For example: "Alice's balance at block 50,000 was 1,000 tokens" can be proven with a proof containing only the path from the state root to Alice's account entry — a few hundred bytes regardless of how many accounts exist.

Actions

Actions are the atomic operations within transactions. Each action type modifies a specific component of world state.

ActionState ComponentDescription
AccountAction AccountState Balance transfers. Specifies owner, old balance, new balance. Creates or deletes accounts.
Action GeneralState Generic key-value state changes. Application-level storage.
DepositAction DepositState Records a cross-chain deposit request. Only valid in genesis blocks.
WithdrawalAction WithdrawalState Initiates a cross-chain withdrawal. Only valid on child chains.
ReceiptAction ReceiptState Records proof of a withdrawal on the parent chain.
GenesisAction GenesisState Creates a new child chain by embedding its genesis block.
PeerAction PeerState Registers, updates, or removes a P2P peer endpoint on-chain.

State Delta Tracking

Every action declares its state delta (bytes added or removed). The total delta across all actions in a block must be within maxStateGrowth. This prevents state bloat attacks.

Merged Mining

Lattice's defining feature. Parent blocks structurally contain child blocks — no auxiliary proof needed.

Nexus Block #1000 ├── transactions: [...] ├── frontier: (nexus state root) ├── nonce: 0x3a7f... ← single proof of work │ └── childBlocks: ├── "defi-chain" → Child Block │ ├── transactions: [...] │ ├── frontier: (defi state root) │ └── timestamp: must match parent │ └── "nft-chain" → Child Block ├── transactions: [...] ├── frontier: (nft state root) └── timestamp: must match parent

How It Works

  1. A miner builds a nexus block template with transactions
  2. The miner also builds child block templates for each child chain they want to mine
  3. Child blocks are inserted into the nexus block's childBlocks Merkle dictionary
  4. The miner finds a nonce that satisfies the nexus difficulty
  5. One proof of work secures all blocks simultaneously

Isolation Property

An invalid child block does not invalidate its parent. Child blocks are validated independently. If a child block fails validation, it's simply ignored — the parent block remains valid. This means a bug or attack on one child chain cannot compromise the nexus or any other child chain.

Child Chain Creation

New child chains are created via GenesisAction. The genesis block of the child chain is embedded in a nexus transaction, and the child chain's ChainSpec is locked at creation. The child chain inherits the parent's filter rules in addition to its own.

Cross-Chain Transfers

Lattice supports trustless cross-chain transfers via a deposit/withdrawal/receipt cycle. No bridges, no multisigs, no oracles.

1. DEPOSIT (nexus genesis) Nexus DepositState: record demand for tokens on child chain 2. WITHDRAWAL (child chain) Child WithdrawalState: claim tokens, referencing deposit proof 3. RECEIPT (parent chain) Parent ReceiptState: record that withdrawal was processed All steps verified by Merkle proof against the respective state roots.

The Flow

  1. Deposit: A DepositAction in the child chain's genesis block records the deposit demand (who wants tokens and how many)
  2. Withdrawal: A WithdrawalAction on the child chain claims tokens, providing proof that the deposit exists in the parent state
  3. Receipt: A ReceiptAction on the parent chain records proof that the withdrawal was processed on the child chain

Each step is verified by Merkle proof against the respective state root. The parent block contains the child block, so the parent node can verify the child's state root directly. No external validator or bridge contract is needed.

Consensus: Fork Choice

ChainState manages the consensus logic for each chain, tracking all known blocks and determining the canonical chain.

Fork Choice Rule

Lattice uses a two-tier fork choice rule:

  1. Parent chain anchoring (primary): A child block anchored by an earlier parent block wins. If block A's parent is at nexus height 1000 and block B's parent is at nexus height 1001, block A wins — regardless of child chain length.
  2. Longest chain (tiebreaker): If parent anchoring is tied, the longer chain wins (more cumulative work).
Why parent anchoring matters
Parent anchoring means child chain consensus inherits nexus finality. Once a nexus block is deeply buried, the child blocks it contains are final too. An attacker would need to reorg the nexus to reorg any child chain — and the nexus has the full network hashrate behind it.

Reorganization

When a fork accumulates more work than the current main chain:

  1. Find the common ancestor between the current tip and the fork
  2. Remove blocks from the current tip back to the ancestor
  3. Add blocks from the fork on top of the ancestor
  4. Recursively propagate the reorg to all affected child chains

Reorgs cascade: if a nexus reorg removes a block that contained child blocks, those child blocks are also removed from their respective chains.

Pruning

Consensus state keeps only the most recent 1000 blocks (configurable). Older blocks are pruned from the working set but remain available in the Acorn CAS chain for historical queries.

Mining

MinerLoop is the async mining engine that builds blocks and searches for valid nonces.

// Simplified mining loop
while running {
    let tip = await chain.tip()
    let txs = mempool.selectTransactions(limit: spec.maxNumberOfTransactionsPerBlock)
    let template = try BlockBuilder.extend(previous: tip, transactions: txs)

    // Batch nonce iteration (10,000 per yield)
    for batch in 0..<batchCount {
        if let block = template.mine(batchSize: 10_000) {
            await network.broadcastBlock(block)
            break
        }
        await Task.yield() // stay responsive
    }
}

Block Building

BlockBuilder constructs block templates:

  1. Carry forward the previous block's frontier as the new homestead
  2. Pack transactions selected from the mempool (sorted by fee)
  3. Execute all actions to compute the frontier state root
  4. Include child block templates in the childBlocks dictionary
  5. Set difficulty from the previous block's nextDifficulty

Network Layer

LatticeNode is the full node implementation that ties everything together.

public actor LatticeNode {
    // One ChainNetwork per chain (nexus + all children)
    var networks: [String: ChainNetwork]
    var lattice: Lattice
}

ChainNetwork

Each chain (nexus and every child) gets its own ChainNetwork wrapping an Ivy node:

Mempool

struct Mempool {
    func add(_ tx: Transaction) -> Bool
    func selectTransactions(limit: Int) -> [Transaction]  // sorted by fee
    func removeConfirmed(_ txIDs: Set<String>)
    var totalFees: UInt64
}

When full (10,000 transactions), the lowest-fee transaction is evicted to make room. Transactions expire after a configurable duration.

Persistence

Chain state is persisted to disk for crash recovery:

On restart, the node loads persisted consensus state and resumes from the last checkpoint. Block data is lazily loaded from disk as needed.

Cryptography

OperationAlgorithm
Key generationP256 ECDSA
Transaction signingP256 ECDSA
Content addressingSHA-256 (via swift-crypto)
Address derivation"1" + first32chars(SHA256(SHA256(publicKey)))
Difficulty hashingSHA-256 of serialized block

Package Integration

Lattice is the top-level package that brings the entire ecosystem together:

Latticeblockchain logic
uses
CashewMerkle state
+
AcornCAS storage
+
Tallypeer reputation
+
IvyP2P network