Governance

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).

[!WARNING] 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, hex-encoded; 64 zero characters on genesis

Ops

Array of key-value operations to apply

Signatures

Array of ed25519 signatures from DAO members

Hash

SHA-256 over the canonical encoding of the block (index, prev_hash, ops, timestamp) -- signatures excluded

Timestamp

Unix nanoseconds; must be >= previous block's timestamp

[!NOTE] Hash excludes signatures The canonical encoding hashed by MarshalCanonical covers the index, previous hash, every op, and the timestamp -- but 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 the canonical encoding 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 canonical encoding)


For each signature:
    ├── Duplicate signer? → reject
    ├── Not in keyset? → reject
    └── Signature invalid? → reject


Valid count >= threshold? → accept

[!IMPORTANT] 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.

Oracle-scoped blocks

The DAO keyset is not the only signer set. When sys.oracle is present in the KV store it names an oracle keyset, a threshold, and an allowed_prefix -- the key namespace that keyset is authorised to write:

type OracleConfig struct {
    Keys          []string `json:"keys"`
    Threshold     int      `json:"threshold"`
    AllowedPrefix string   `json:"allowed_prefix"`
}

AddBlock chooses the signer set per block:

  • If an oracle config exists and every op in the block targets a key with the oracle's allowed_prefix, the block is validated against the oracle keyset and threshold.
  • Otherwise the block is validated against the DAO keyset. A DAO block is additionally rejected if any of its ops writes into the oracle prefix.

A block that mixes oracle-prefix and non-oracle-prefix ops therefore fails: it is validated as a DAO block and then rejected for touching the oracle prefix.

Oracle-scoped set ops carry two extra rules:

  • Schema validation. The value must be a well-formed epoch record -- consistent r_effective, an r_source that names the term that bound it, an s_milli matching the published phase, and a phase matching the chain's current sys.phase.
  • Write-once. A key that already exists in state cannot be overwritten by an oracle block.

sys.oracle itself is an ordinary sys.* key: it is set and changed by DAO multisig like any other governance value.

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.

[!IMPORTANT] Replay is fail-loud Replay re-verifies every stored block -- index contiguity, hash, prev_hash linkage, and signatures against the state as of the previous block, exactly as AddBlock saw them. Any gap or mismatch refuses to boot rather than starting the node into silent divergence.

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. A gap is reported as ErrGap, which the gossip ingest path uses to trigger a demand-driven resync.
  4. PrevHash check -- must match the tip's hash.
  5. Timestamp monotonicity -- must be >= previous block's timestamp.
  6. Block must have ops, and the serialized block must not exceed MaxBlockSize.
  7. Op validation -- each operation is validated (key format, value size, JSON validity, sys.* schema), followed by transition rules that depend on the existing value (for example sys.phase may only move forwards).
  8. Signature validation -- oracle keyset for oracle-scoped blocks, DAO keyset otherwise.
  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.

[!DANGER] 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 {
    mu           sync.Mutex
    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

    gapSyncMu   sync.Mutex          // gap-resync throttle
    lastGapSync time.Time
}

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

Phase()

Returns the current tokenomics phase from sys.phase (1 when unset)

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