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 setsys.dao_keysetwiththreshold < 2will 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
MarshalCanonicalcovers 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:
- Hash verification -- recompute the hash from the canonical encoding and compare to
block.Hash. - Duplicate check -- reject blocks with the same signer appearing twice.
- Keyset membership -- every signer's public key must be in the current
DAOKeyset.Keys. - Signature verification -- each
ed25519.Verify(pubkey, hash, signature)must pass. - 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_keysetmust 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, anr_sourcethat names the term that bound it, ans_millimatching the published phase, and a phase matching the chain's currentsys.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:
- Validates the genesis block.
- Creates an empty KV store.
- Applies genesis ops (must include
sys.dao_keyset). - Replays all stored blocks to rebuild KV state.
- Verifies a valid keyset exists after replay.
[!IMPORTANT] Replay is fail-loud Replay re-verifies every stored block -- index contiguity, hash,
prev_hashlinkage, and signatures against the state as of the previous block, exactly asAddBlocksaw them. Any gap or mismatch refuses to boot rather than starting the node into silent divergence.
AddBlock
func (c *Chain) AddBlock(b *Block) errorValidates and applies a new block:
- Verify hash -- recompute and compare.
- Duplicate/fork check -- if the block's index is at or below the tip, check for duplicate or fork.
- Gap check -- index must be exactly
tip.Index + 1. A gap is reported asErrGap, which the gossip ingest path uses to trigger a demand-driven resync. - PrevHash check -- must match the tip's hash.
- Timestamp monotonicity -- must be >= previous block's timestamp.
- Block must have ops, and the serialized block must not exceed
MaxBlockSize. - 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 examplesys.phasemay only move forwards). - Signature validation -- oracle keyset for oracle-scoped blocks, DAO keyset otherwise.
- 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:
- The incoming block's multisig is verified (to filter out garbage).
- If valid, the
forkCallbackis invoked with both the existing and incoming blocks. - 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