Tutorials → 02

Child Chains

Create an application-specific child chain anchored to a parent. Inherited security through merged mining, independent state and throughput.

Why Child Chains?

Lattice's key insight: you don't need one chain for everything. Child chains let you:

How merged mining works

When a miner finds a valid block for the parent chain, that same proof-of-work can simultaneously validate a child chain block. The child chain doesn't need its own dedicated miners — it piggybacks on the parent's security.

Parent blocks reference child block hashes, and child blocks reference their parent anchor. This bidirectional linking creates a verifiable hierarchy.

Step 1: Set Up the Parent Chain

Start with a standard parent node (from Tutorial 1):

main.swift
import Foundation
import Lattice
import AcornMemoryWorker

let keyPair = CryptoUtils.generateKeyPair()

// Parent chain: the root of our chain hierarchy
let parentSpec = ChainSpec(
    directory: "Nexus",
    maxNumberOfTransactionsPerBlock: 100,
    maxStateGrowth: 100_000,
    premine: 0,
    targetBlockTime: 2_000,
    initialRewardExponent: 10
)

let genesisConfig = GenesisConfig.standard(spec: parentSpec)
let nodeConfig = LatticeNodeConfig(
    publicKey: keyPair.publicKey,
    privateKey: keyPair.privateKey,
    listenPort: 4001,
    storagePath: URL(filePath: "/tmp/multi-chain"),
    enableLocalDiscovery: true
)

let node = try await LatticeNode(config: nodeConfig, genesisConfig: genesisConfig)
try await node.start()

Step 2: Define the Child Chain Spec

A child chain has its own ChainSpec with a unique directory. You can tune it for your application's specific needs:

// Child chain: optimized for a high-frequency token application
let tokenChainSpec = ChainSpec(
    directory: "TokenLedger",             // unique identifier for this child chain
    maxNumberOfTransactionsPerBlock: 500, // higher throughput than parent
    maxStateGrowth: 200_000,              // more state space
    premine: 0,
    targetBlockTime: 500,                // 500ms blocks — 4x faster than parent
    initialRewardExponent: 8              // smaller rewards (2^8 = 256)
)
Child chain design choices

Faster blocks (500ms vs 2s): your token transfers confirm quickly.

Higher throughput (500 txns vs 100): the child chain handles volume without congesting the parent.

Smaller rewards: the child chain's token has a different economic model than the parent.

Step 3: Register the Child Chain via GenesisAction

Child chains are born through a GenesisAction in a parent chain transaction. This embeds the child's genesis block reference into the parent, creating a permanent cryptographic anchor.

// Create the child chain's genesis block
let childGenesisConfig = GenesisConfig.standard(spec: tokenChainSpec)

// The GenesisAction tells the parent chain: "a new child chain exists"
let genesisAction = GenesisAction(
    directory: "TokenLedger",
    spec: tokenChainSpec
)

// Wrap it in a transaction and submit to the parent chain
let body = TransactionBody(
    accountActions: [],
    actions: [],
    depositActions: [],
    genesisActions: [genesisAction],
    peerActions: [],
    receiptActions: [],
    withdrawalActions: [],
    signers: [CryptoUtils.sha256(keyPair.publicKey)],
    fee: 1,
    nonce: 0
)

let bodyData = body.toData()
let bodyHash = CryptoUtils.sha256(String(data: bodyData, encoding: .utf8)!)
let signature = CryptoUtils.sign(message: bodyHash, privateKeyHex: keyPair.privateKey)!

let tx = Transaction(
    signatures: [keyPair.publicKey: signature],
    body: body
)

await node.submitTransaction(directory: "Nexus", transaction: tx)
print("Child chain genesis submitted to parent")

Step 4: Register the Network and Mine

After the genesis action is mined into a parent block, register the child chain's network layer:

// Register child chain network on the same node
let childIvyConfig = IvyConfig(
    publicKey: keyPair.publicKey,
    listenPort: 4002,               // different port than parent
    bootstrapPeers: []
)

try await node.registerChainNetwork(
    directory: "TokenLedger",
    config: childIvyConfig
)

// Start mining on both chains — merged mining shares work
await node.startMining(directory: "Nexus")
await node.startMining(directory: "TokenLedger")

print("Mining on parent (Nexus) and child (TokenLedger)")

Step 5: Submit Transactions to the Child Chain

Transactions to the child chain work identically — just target the child's directory:

try await Task.sleep(for: .seconds(5))

// Create a token balance entry on the child chain
let tokenAction = Action(
    key: "balance:\(CryptoUtils.createAddress(from: keyPair.publicKey))",
    oldValue: nil,
    newValue: "1000000"
)

let tokenBody = TransactionBody(
    accountActions: [],
    actions: [tokenAction],
    depositActions: [],
    genesisActions: [],
    peerActions: [],
    receiptActions: [],
    withdrawalActions: [],
    signers: [CryptoUtils.sha256(keyPair.publicKey)],
    fee: 1,
    nonce: 1
)

let tokenBodyData = tokenBody.toData()
let tokenHash = CryptoUtils.sha256(String(data: tokenBodyData, encoding: .utf8)!)
let tokenSig = CryptoUtils.sign(message: tokenHash, privateKeyHex: keyPair.privateKey)!

let tokenTx = Transaction(
    signatures: [keyPair.publicKey: tokenSig],
    body: tokenBody
)

// Submit to the child chain, not the parent
await node.submitTransaction(directory: "TokenLedger", transaction: tokenTx)
print("Token balance set on child chain")

The Chain Hierarchy

After these steps, your node maintains two independent chains:

Nexus (Parent)TokenLedger (Child)
Block Time2 seconds500ms
Txns/Block100500
MiningIndependent PoWMerged with parent
StateGeneral purposeToken-specific
SecurityOwn hashrateInherited from parent

Each chain has its own state tree, mempool, and block history. They share the same P2P identity and storage layer, but are logically independent — a congested child chain doesn't slow down the parent.

Scaling pattern
This is Lattice's answer to the scalability trilemma. Instead of making one chain faster, you create purpose-built chains that inherit security from their parent. You can go arbitrarily deep — a child chain can have its own children.