Registry and FI state
leaf = H(FI_LEAF, fi_id, mpk.x, mpk.y, epk.x, epk.y, vk_root, params_commitment)
vk_root = H(FI_VKS, vk_salt, vk_hash_initiate, vk_hash_accept, vk_hash_reject)
Registry is a Poseidon2 Merkle tree of depth 16 indexed by FI index;
leaves are replaced in place. State is not in the leaf. The pool holds
fiState[fi_index], a state root it alone writes, checked by plain
equality against wrapper publics. The registry root therefore moves only on
admin operations, and the pool accepts any registry_root from a window of
the R_REG most recent roots. Revocation is checked directly against
revoked[fi_index], so the window does not delay it.
On-chain Registry contract:
| Function | Auth | Effect |
|---|---|---|
admit(fi_id, mpk, epk, vk_root, params_commitment, π_possess) | authority | new leaf; π_possess is a Schnorr signature under mpk over (chain_id, registry_address, fi_id), proving possession of msk so no FI can register another's key |
rotate(index, vk_root) | signature under current mpk | code change |
setParams(index, params_commitment) | signature under mpk | tuning change, no VK change |
rotateKey(index, new_mpk, new_epk, π_possess) | signature under current mpk | key rotation |
revoke(index) | authority | leaf zeroed, revoked[index] = true |
root(), recentRoots() | view | current root and the R_REG window |
The pool exposes fiState(index); it is written only by the pool after a
successful policy-carrying transition (§4.9). fiState == 0 is the
canonical empty state (§5.5).
A no-op policy is a fixed, published VK; an FI with no rule at a phase
registers the no-op's hash in that slot. vk_root is a salted commitment,
so which policies an FI runs stays private even when every slot is the
no-op; the leaf is public.