[runtime, tesseract]: Kaia consensus client - #1098
Draft
seunlanlege wants to merge 1 commit into
Draft
Conversation
Adds a light client for Kaia (kaiachain/kaia, ex-Klaytn), an EVM chain finalized instantly by Istanbul BFT. Every Kaia header carries its own finality artifact in extra_data: the proposer's secp256k1 seal plus the committee's committed seals, all plain ecrecover. Verification mirrors the chain's own import check in blockchain/block_validator.go — the author must be a council member, and 2f+1 distinct council members must have committed, where f = ceil(min(|council|, committee_size)/3) - 1. Kaia uses standard MPT state commitments, so state proofs reuse EvmStateMachine unchanged. The council is tracked from governance.addvalidator / removevalidator votes carried in header vote fields, and the committee size / governing node from governance ratified at epoch blocks. Vote values follow Kaia's canonicalizer exactly, including the form where the value is the ASCII text of an address rather than its bytes — mainnet blocks 75038594 and 90897408 did that, and the address it yields is in the canonical council. Council freshness is a trust assumption on whoever assembles updates: updates skip blocks (Kaia produces 86,400 a day) and no header proves the blocks before it carried no votes. Drift in the dangerous direction is caught rather than trusted — a header declaring a validator the client does not know proves a skipped addvalidator, and is rejected. Kaia's Permissionless hardfork makes the header's validator list consensus enforced, which retires this machinery entirely; it is unscheduled on both mainnet and Kairos, and the client fails closed if it activates before an upgrade. - modules/consensus/kaia/verifier: no_std verifier. Kaia header RLP (no uncles, difficulty, gas limit or nonce; adds rewardbase, block_score, timestamp_fos, governance and vote), Istanbul extra parsing, the filtered-header hashes, and seal recovery. Note the proposer seal signs keccak256(sig_hash) — Istanbul hashes the payload a second time inside its signing helper. - modules/consensus/kaia/prover: assembles updates over the kaia_* RPC namespace, which the eth_* block queries cannot serve since they omit the governance header fields. Reconstructed headers are checked against the node's reported block hash before use. - modules/ismp/clients/kaia: the ConsensusClient impl. - tesseract/consensus/kaia: relayer host, config type = "kaia". Tests pin the RLP layout, both seal preimages and the historical vote blocks against captured mainnet data offline; a live suite runs with cargo test -p kaia-prover -- --ignored.
seunlanlege
force-pushed
the
seun/kaia-consensus-client
branch
from
August 4, 2026 09:19
2455799 to
7c3ddce
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft — parked pending Kaia's Permissionless hardfork (see Why this is a draft below).
Adds a light client for Kaia (ex-Klaytn, mainnet chain id 8217, Kairos 1001), an EVM chain finalized instantly by Istanbul BFT.
How Kaia finality works
~33 known companies (the Governance Council) take turns proposing blocks and signing off on each other's. A block only exists once roughly two-thirds have committed to it, so there are no reorgs and no confirmations — 1s blocks, final on arrival.
Every header carries its own finality artifact in
extra_data: the proposer's secp256k1 seal plus the committee's committed seals. All plainecrecover, no BLS. Verification mirrors the chain's own import check inblockchain/block_validator.go:2f + 1distinct council members must have committed,f = ceil(min(|council|, committee_size)/3) - 1Mainnet today: 33 council members, blocks carry 22 seals, 21 required. State commitments are standard MPT, so state proofs reuse
EvmStateMachineunchanged.What's here
modules/consensus/kaia/verifierno_stdverifier — header RLP, Istanbul extra, seal recovery, council trackingmodules/consensus/kaia/proverkaia_*RPC namespacemodules/ismp/clients/kaiaConsensusClientimpl (KAIA)tesseract/consensus/kaiatype = "kaia"Plus registration in both runtimes, the tesseract consensus config, the fisherman's exhaustive match, the EVM registry, and relayer docs.
Things worth a reviewer's attention
The proposer seal is a double hash. Istanbul's
GetSignatureAddresskeccaks its payload, so the seal signskeccak256(sig_hash), notsig_hash. Recovering with the single hash yields a plausible-looking wrong address. Pinned by a fixture test.Kaia headers are not Ethereum headers. No uncles, difficulty, gas limit or nonce; they add
rewardbase,block_score,timestamp_fos,governanceandvote. Thegeth-primitivesheader cannot reproduce Kaia block hashes, hence the separate type.Validator votes have three encodings and the chain rejects none of them. Besides a single address and concatenated addresses, any other length is canonicalized via
BytesToAddress— the last 20 bytes, left-padded — with no hex decoding. Mainnet blocks 75038594 and 90897408 carried the 42-byte ASCII text of an address, and the address that yields (0x6332386239...) is in the canonical council to this day. A parser that rejects these silently diverges from the chain. Both blocks are pinned as fixtures.Governance parameters are read from live state, not genesis.
istanbul.committeesizeis 22 in the genesis config and 50 on-chain today; the governing node moved too. The client tracks both from ratified epoch-block governance.Known limitations
Council freshness is a trust assumption. Updates skip blocks — Kaia produces 86,400 a day, so following every one would cost ~122 MB/day of ancestry — and no header proves the blocks before it carried no votes. Whoever assembles updates is trusted to include every vote-bearing header.
Drift in the dangerous direction is caught rather than trusted: a header declaring a validator the client doesn't know proves a skipped
addvalidator(which would understate the quorum), and is rejected. A skippedremovevalidatoris not detectable from headers.The quorum denominator is conservative. The chain derives
ffrom qualified validators (council minus those below the 5M KAIA minimum stake); that's staking state, invisible to a header-only client, so the council size stands in. It's never smaller, so the client can only demand more seals than the chain — but blocks carry ~ceil(2N/3), so with 33 council / 22 seals / 21 required the margin is one. Roughly three demotions would stall the client until its state is refreshed.Why this is a draft
Kaia's Permissionless hardfork retires most of this. Post-fork the chain enforces that a header's declared validator list matches state (
kaiax/valset/impl/consensus.go— the check exists today but returns early), which turns the list from an unchecked hint into a consensus-validated claim. A quorum signature then is a quorum attestation of the roster, so the client reads it off the header: no vote parsing, no epoch tracking, no ancestry, and the quorum denominator becomes exactly right sincequalified = committeepost-fork. Both limitations above dissolve, and the verifier gets smaller.It is unscheduled —
PermissionlessCompatibleBlockisnilwith aTODOon both mainnet and Kairos as ofkaiachain/kaia@d6eb00a, and the latest release schedules only Osaka. If it activates before an upgrade the client fails closed: seals recover to non-council addresses and every header is rejected, so it stalls rather than accepting anything unsound. Kairos getting a fork block is the signal to start the rewrite.Testing
37 unit and fixture tests. The fixtures are captured mainnet responses, so the RLP layout, block hash, both seal preimages and the historical vote blocks are pinned offline — silent drift fails the build.
cargo test -p kaia-verifierA live suite verifies real mainnet finality end to end (bootstrap a trusted state from RPC, then verify sequential and batched updates):
cargo test -p kaia-prover -- --ignoredOne caveat found while writing these:
kaia_getBlockWithConsensusInfoByNumber'scommittersfield is not a reliable oracle — it returns different sets for the same finalized block across calls (a stale signature-address cache). An independenteth_keysrecovery agreed with this implementation, not with the node. The tests assert against the seals themselves.Not included
Contracts aren't deployed on Kaia yet, so there's no
EvmHostsentry,chain.tsentry, or indexer wiring. Also outstanding: testsuite-runtime registration, a CI job, and integration tests.