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:
- Hash verification -- recompute the hash from ops 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 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¶
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.
AddBlock¶
Validates 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. - PrevHash check -- must match the tip's hash.
- Timestamp monotonicity -- must be >= previous block's timestamp.
- Op validation -- each operation is validated (key format, value size, JSON validity).
- Block size check -- serialized block must not exceed
MaxBlockSize. - Multisig validation -- using the current keyset.
- 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.
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 |