Skip to content

Governance

The state chain implements DAO governance through multisig blocks. A quorum of DAO members must sign each block before it can be applied to the chain. The DAO keyset -- the set of authorised signers and their quorum threshold -- is stored in the chain's own KV store, making governance self-referential: the keyset governs changes to itself.

DAOKeyset

type DAOKeyset struct {
    Keys      []string // hex-encoded ed25519 public keys (64 hex chars each)
    Threshold int      // minimum number of valid signatures required
}

The keyset is stored under the sys.dao_keyset key in the state chain's KV store. It is set in the genesis block and can be updated by subsequent blocks (signed by the current keyset).

Minimum threshold

The minimum allowed threshold is 2 (MinDAOThreshold). This prevents a single-signer takeover via threshold downgrade. A block that attempts to set sys.dao_keyset with threshold < 2 will be rejected.

Block structure

type Block struct {
    Index      uint64           `json:"index"`
    PrevHash   string           `json:"prev_hash"`
    Ops        []Op             `json:"ops"`
    Signatures []BlockSignature `json:"signatures"`
    Hash       string           `json:"hash"`
    Timestamp  int64            `json:"timestamp"`
}
Field Description
Index Sequential position in the chain (genesis = 0)
PrevHash SHA-256 hash of the previous block (empty string for genesis)
Ops Array of key-value operations to apply
Signatures Array of ed25519 signatures from DAO members
Hash SHA-256 hash computed over the operations only
Timestamp Unix nanoseconds; must be >= previous block's timestamp

Hash covers ops only

The block hash is computed from the operations only, not the signatures. This allows signatures to be collected independently -- different DAO members can sign the same block hash without needing to coordinate signature order.

BlockSignature

type BlockSignature struct {
    PublicKey string `json:"public_key"` // hex-encoded ed25519 public key
    Sig       string `json:"signature"`  // hex-encoded ed25519 signature
}

Each signature is over the block's Hash field. The signer must be a member of the current DAOKeyset.

Multisig validation

When AddBlock processes a new block, it validates the multisig:

  1. Hash verification -- recompute the hash from ops and compare to block.Hash.
  2. Duplicate check -- reject blocks with the same signer appearing twice.
  3. Keyset membership -- every signer's public key must be in the current DAOKeyset.Keys.
  4. Signature verification -- each ed25519.Verify(pubkey, hash, signature) must pass.
  5. Threshold check -- the count of valid signatures must be >= DAOKeyset.Threshold.
Block arrives
Verify block hash (recompute from ops)
For each signature:
    ├── Duplicate signer? → reject
    ├── Not in keyset? → reject
    └── Signature invalid? → reject
Valid count >= threshold? → accept

Keyset at validation time

Multisig validation uses the keyset from the KV state before the block's ops are applied. This means a block that updates sys.dao_keyset must still be signed by the current keyset, not the new one.

Chain management

NewChain

func NewChain(store StateChainStore, genesis *Block) (*Chain, error)

Creates a new chain instance:

  1. Validates the genesis block.
  2. Creates an empty KV store.
  3. Applies genesis ops (must include sys.dao_keyset).
  4. Replays all stored blocks to rebuild KV state.
  5. Verifies a valid keyset exists after replay.

AddBlock

func (c *Chain) AddBlock(b *Block) error

Validates and applies a new block:

  1. Verify hash -- recompute and compare.
  2. Duplicate/fork check -- if the block's index is at or below the tip, check for duplicate or fork.
  3. Gap check -- index must be exactly tip.Index + 1.
  4. PrevHash check -- must match the tip's hash.
  5. Timestamp monotonicity -- must be >= previous block's timestamp.
  6. Op validation -- each operation is validated (key format, value size, JSON validity).
  7. Block size check -- serialized block must not exceed MaxBlockSize.
  8. Multisig validation -- using the current keyset.
  9. Persist and apply -- store the block, update KV state, advance tip.

Fork detection

If a block arrives with the same index as an existing block but a different hash, a fork is detected:

  1. The incoming block's multisig is verified (to filter out garbage).
  2. If valid, the forkCallback is invoked with both the existing and incoming blocks.
  3. The fork is rejected -- the chain does not switch branches.

Fork = governance emergency

A fork in the state chain means two validly-signed blocks exist at the same index. This indicates either a coordination failure or a compromised signer. The fork callback should trigger an alert.

Chain struct

type Chain struct {
    store        StateChainStore
    kv           *KVStore           // in-memory key-value state
    tip          *Block             // latest block
    genesis      *Block             // genesis block (index 0)
    onBlock      func(*Block)       // callback on new block applied
    forkCallback func(existing, incoming *Block) // fork alert
}
Method Description
Tip() Returns the latest block
GetBlock(index) Retrieves a block by index
GetBlocks(start, limit) Retrieves a range of blocks
BlockCount() Total blocks including genesis
GetKV(key) Reads a value from the KV store
GetKVByPrefix(prefix) Reads all keys matching a prefix
GetAllKV() Dumps the entire KV store
DAOKeyset() Returns the current DAO keyset
SetOnBlock(fn) Registers a callback for new blocks
SetForkCallback(fn) Registers a callback for fork detection

Size limits

Limit Value Description
MaxKeyLength 128 bytes Maximum key length in an operation
MaxValueSize 65,536 bytes (64 KB) Maximum value size in a set operation
MaxBlockSize 262,144 bytes (256 KB) Maximum serialized block size
MinDAOThreshold 2 Minimum allowed DAO keyset threshold