Hanzo

Consensus

Private BFT blockchain with dual-certificate Ed25519 + ML-DSA-65 finality for MPC cluster state management

Consensus (ConsensusKV)

MPC nodes form their own private BFT blockchain to manage cluster state. Every state mutation -- peer registration, key info, API keys, wallet ownership -- is proposed as a block, voted on with dual signatures, and finalized when the threshold is met.

This replaces external service discovery (Consul) with a blockchain-native approach: the MPC nodes themselves are the validators.

Dual-Certificate Finality

Every block and vote carries two signatures:

CertificateAlgorithmSizeStandardPurpose
ClassicalEd2551964 bytesFIPS 186-5Fast, battle-tested
Post-quantumML-DSA-653,309 bytesFIPS 204, NIST Level 3Quantum-resistant

A block is finalized only when both signature types reach the t-of-n threshold. This provides defense-in-depth:

  • If quantum computers break Ed25519, ML-DSA certificates still hold
  • If ML-DSA has a flaw, Ed25519 certificates still protect the chain
  • Both must be compromised simultaneously to forge a finalized block

ML-DSA-65 Key Sizes (FIPS 204)

ComponentSize
Public key1,952 bytes
Private key4,032 bytes
Signature3,309 bytes
Security levelNIST Level 3 (192-bit classical equivalent)
Packagegithub.com/luxfi/crypto/mldsa

ML-DSA is the NIST-standardized (FIPS 204) post-quantum digital signature scheme used for on-chain and consensus signature verification.

Consensus Protocol

 Proposer           Validators (all nodes)          Finalized State
    │                       │                              │
    │  1. Batch pending     │                              │
    │     KV mutations      │                              │
    │                       │                              │
    │  2. Create block      │                              │
    │     (height, parent,  │                              │
    │      mutations, time) │                              │
    │                       │                              │
    │  3. Dual-sign block   │                              │
    │     (Ed25519+ML-DSA)  │                              │
    │                       │                              │
    ├──4. Broadcast proposal──▶                            │
    │                       │                              │
    │  5. Verify block hash │                              │
    │     + proposer sigs   │                              │
    │                       │                              │
    │  ◀──6. Dual-signed vote──┤                           │
    │                       │                              │
    │  7. Collect votes     │                              │
    │     until threshold   │                              │
    │     met for BOTH      │                              │
    │     Ed25519 + ML-DSA  │                              │
    │                       │                              │
    ├──8. Broadcast finalized──▶                           │
    │                       │                              │
    │                       │  9. Verify dual-cert         │
    │                       │     threshold                │
    │                       │                              │
    │                       ├──10. Apply mutations──────────▶
    │                       │     (deterministic)          │

Block Structure

type ConsensusBlock struct {
    Height     uint64        // Sequential block number
    ParentHash [32]byte      // SHA-256 of parent block
    Timestamp  time.Time     // UTC proposal time
    Mutations  []KVMutation  // Ordered state changes
    ProposerID string        // Proposing validator
    EdSig      []byte        // Ed25519 signature (64 bytes)
    PQSig      []byte        // ML-DSA-65 signature (3,309 bytes)
}

type KVMutation struct {
    Op    OpType  // OpPut or OpDelete
    Key   string  // e.g. "ready/node0", "threshold_keyinfo/wallet1"
    Value []byte  // Payload (nil for delete)
}

The block hash is computed over Height + ParentHash + Timestamp + Mutations + ProposerID, excluding signatures. This ensures deterministic hashing regardless of signature order.

Vote Structure

type Vote struct {
    VoterID   string    // Validator identity
    BlockHash [32]byte  // Hash of the proposed block
    Height    uint64    // Must match block height
    EdSig     []byte    // Ed25519 vote signature
    PQSig     []byte    // ML-DSA-65 vote signature
}

A vote is valid only when both Ed25519 and ML-DSA signatures verify against the voter's registered public keys.

Finality Condition

finalized = (valid_ed25519_votes >= threshold) AND (valid_mldsa_votes >= threshold)

For a 3-of-5 cluster, a block needs at least 3 validators to cast valid dual-signed votes.

NATS Transport

Consensus communication uses three NATS topics per chain:

TopicDirectionContent
mpc.consensus.<chainID>.proposalsProposer → AllSerialized ConsensusBlock
mpc.consensus.<chainID>.votesVoter → ProposerSerialized Vote
mpc.consensus.<chainID>.finalizedProposer → AllSerialized FinalizedBlock

NATS JetStream provides reliable delivery. Consensus does not depend on NATS for ordering -- block height and parent hash enforce the canonical chain.

Bootstrap

Consensus requires validators to know each other before the first block. This creates a chicken-and-egg problem: peer state is stored in the consensus chain, but the chain needs peers to start.

The solution: NATS JetStream KV for bootstrap only.

1. Each node registers in NATS KV bucket "mpc-bootstrap"
   key: peer/<node-name>
   value: { ed_pubkey, pq_pubkey, endpoint }

2. Nodes read all peers from the bootstrap bucket

3. Build validator set with dual public keys

4. Start consensus chain with genesis block

5. All subsequent state goes through ConsensusKV

After genesis, the bootstrap bucket is only used for new node joins (state sync via snapshot).

State Sync

When a new node joins an existing cluster:

  1. The new node registers in the bootstrap NATS KV bucket
  2. An existing validator sends a state snapshot (full KV map + block height)
  3. The new node loads the snapshot and begins processing blocks from that height
  4. The new node participates in voting for subsequent blocks
// Snapshot is a point-in-time copy of the full KV state
snap := consensusKV.Snapshot()  // map[string][]byte

// New node loads snapshot at the height it was taken
newNode.LoadSnapshot(snap, height)

KMS/HSM Key Storage

In production, consensus signing keys should never exist as plaintext on disk. The Signer interface enables delegation to KMS or HSM:

type Signer interface {
    Sign(keyID string, message []byte) ([]byte, error)
}

When hsm.provider is configured (e.g., AWS CloudHSM, Azure HSM, Hanzo KMS), consensus uses the HSM signer adapter instead of loading local key files:

ConfigBehavior
hsm.provider: fileLoad Ed25519 + ML-DSA keys from local identity files
hsm.provider: aws-cloudhsmSign via AWS CloudHSM PKCS#11
hsm.provider: azure-hsmSign via Azure Managed HSM
hsm.provider: hanzo-kmsSign via Hanzo KMS API

Configuration

# config.yaml
kv_backend: consensus      # "consensus" (default), "nats", or "consul"

consensus:
  chain_id: m-chain         # Chain identifier

nats:
  url: nats://nats:4222
  kv_bucket: mpc-state      # Only used when kv_backend: nats

# Post-quantum identity keys (auto-generated if not provided)
# In production, store in KMS and reference by key ID
identity:
  ed_private_key: /app/identity/node_private.key
  pq_private_key: /app/identity/node_pq_private.key

Comparison with Alternatives

PropertyConsensusKVConsul RaftNATS KV
ValidatorsMPC nodes themselvesConsul server clusterNATS cluster
External dependencyNone (self-sovereign)Consul serversNATS servers
Post-quantumML-DSA-65 dual-certNoNo
Finality modelDual-cert thresholdRaft leader commitJetStream ack
Latency<100ms (typical)~50ms~10ms
Trust modelSame as MPC thresholdTrust Consul clusterTrust NATS cluster
State integrityBlockchain (hash chain)Raft logStream sequence

The key advantage: ConsensusKV uses the same trust model as MPC signing. If you trust 3-of-5 nodes to sign transactions, you can trust 3-of-5 nodes to finalize state -- no additional infrastructure needed.

Post-quantum cryptography

ConsensusKV's dual-certificate finality combines a classical Ed25519 certificate with a post-quantum ML-DSA-65 certificate, so cluster state stays verifiable even against a quantum adversary. The ML-DSA implementation supports the FIPS 204 parameter sets (ML-DSA-44/65/87); the cluster uses ML-DSA-65 (NIST Level 3) by default.

How is this guide?

Last updated on

On this page