Skip to content

[runtime, tesseract]: Tempo consensus client - #1090

Draft
seunlanlege wants to merge 2 commits into
mainfrom
seun/tempo-consensus-client
Draft

[runtime, tesseract]: Tempo consensus client#1090
seunlanlege wants to merge 2 commits into
mainfrom
seun/tempo-consensus-client

Conversation

@seunlanlege

@seunlanlege seunlanlege commented Aug 1, 2026

Copy link
Copy Markdown
Member

Adds a light client for Tempo, the Reth-based EVM chain finalized by commonware's simplex consensus with BLS12-381 threshold certificates.

Why it fits

Tempo turns out to be one of the cleanest integrations available to us:

  • One pairing check finalizes a block. Each finalized block carries a ~134-byte certificate — a threshold signature pair over the finalize vote and the per-view VRF seed — verifiable against a single static group public key (the "network identity"). Compare the sync-committee client, which aggregates 512 keys per update.
  • The certificate digest is the block hash. So one verification finalizes an Ethereum-style header carrying a standard MPT state root. State proofs reuse EvmStateMachine unchanged; eth_getProof works as-is.
  • Key rotations are rare and announced. The identity is the constant term of the DKG public polynomial. Reshares provably preserve it, so validator churn does not rotate the key — mainnet is at epoch ~1500 still using its epoch-0 identity. Only a governance-scheduled full DKG mints a new one, committed on-chain in the epoch-boundary block's extra_data and finalized under the outgoing key.
  • Relayer data is public. The node exposes a consensus RPC namespace (consensus_getFinalization, consensus_subscribe) backed by a permanent archive, so no beacon-API-style proof assembly is needed.

What's here

Crate Role
modules/consensus/tempo/verifier no_std verifier: commonware wire-format decoders, MinSig BLS verification, header RLP, epoch/rotation rules, fraud proofs
modules/consensus/tempo/prover std-only JSON-RPC prover over the node's consensus and eth namespaces
modules/ismp/clients/tempo ConsensusClient (TMPO) for Evm(4217) mainnet and Evm(42431) testnet
tesseract/consensus/tempo Relayer host: update construction and boundary walking

Registered in both gargantua and nexus.

Note the BLS orientation: commonware uses MinSig (48-byte G1 signatures, 96-byte G2 public keys), the mirror image of Ethereum's MinPk. We reuse bls_on_arkworks' IETF point serialization but hash to G1 and pair accordingly. The wire-format decoders are hand-rolled rather than imported, because commonware-cryptography is built on blst and its consensus types are unconditionally std — neither survives the runtime's no_std wasm target.

Security properties

Two rules keep the identity handoff unbreakable, both mirroring what Tempo's own nodes enforce before following a new epoch:

  1. An update may never cross an epoch boundary the client has not verified. The boundary block is the only place the next epoch's DKG outcome is committed, so without this a relayer could hop over a full-DKG rotation and leave the client trusting a key the chain had already retired — permanently, since every later certificate would still check against it.
  2. A boundary may only change the key when the previous boundary announced a full DKG (is_next_full_dkg, tracked as ConsensusState::full_dkg_pending). Every rotation therefore gets an epoch of on-chain advance warning, and an unannounced one is rejected. This matters because a bad rotation is uniquely unrecoverable: it overwrites the identity, after which no honest certificate verifies and no fraud proof can be satisfied.

Fraud proofs carry headers and accept same-round-different-digest or same-height-different-digest. The second form is necessary because a simplex safety violation is not always a same-round double vote — a Byzantine quorum can nullify its way to a second notarization of a conflicting block at the same height.

One deliberate leniency: decode_dkg_outcome tolerates trailing bytes. Tempo's nodes decode extra_data prefix-only and compare the decoded struct, so a boundary block with trailing garbage is a valid finalized chain state; rejecting it would let a single Byzantine proposer freeze identity rotation forever. The bytes are committed under a certificate-verified header hash either way.

Trust assumptions are documented in the crate docs. The load-bearing one: because reshares preserve the constant term, a quorum of any past epoch's shares reconstructs the same group secret — security rests on no such quorum ever colluding, not on the current validator set alone.

Testing

38 tests in the verifier, run against captured mainnet certificates and headers, including an epoch-boundary block and its DKG outcome (which decodes to exactly the identity pinned in Tempo's chainspec).

Rotation and equivocation paths — which mainnet has never exercised, since it has never done a full DKG — are covered with synthetic certificates. A recovered threshold signature is indistinguishable from a plain BLS signature under the group key, so tests forge certificates with a 1-of-1 group key. That covers the full rotation lifecycle (rotate → retired key now rejected → new key accepted), unannounced-rotation rejection, and both fraud-proof forms.

A live end-to-end test against mainnet RPC runs with:

cargo test -p tempo-prover -- --ignored

Follow-ups (not in this PR)

  • EvmHosts host-executive entry for Evm(4217) once contracts are deployed on Tempo.
  • The reverse direction (Hyperbridge → Tempo) needs an EvmHost deployment. Worth knowing: Tempo has no native gas token — fees are paid in USD-pegged TIP-20 stablecoins, defaulting to pathUSD at 0x20C0…0000. That suits our existing stablecoin feeToken model, but deployment/fee config there needs its own look.
  • Commonware is pre-1.0 and Tempo pins a git rev, so certificate encodings are stable per-rev rather than per-semver. The mainnet vectors in test-data/ are what will fail loudly if either side drifts; worth rechecking on Tempo node upgrades.

Adds a light client for Tempo (tempoxyz/tempo), a Reth-based EVM chain
finalized by commonware's simplex consensus with BLS12-381 threshold
certificates.

Every finalized Tempo block carries a ~134-byte certificate: a threshold
signature pair over the finalize vote and the per-view VRF seed, verifiable
against a single static group public key (the "network identity") with two
pairings. The certificate's payload digest is the block hash, so one
verification finalizes an Ethereum-style header carrying a standard MPT state
root — state proofs reuse EvmStateMachine unchanged.

The network identity is the constant term of the DKG public polynomial.
Reshares provably preserve it, so validator churn does not rotate the key;
only a governance-scheduled full DKG does, and that is committed on-chain in
the epoch-boundary block's extra_data and finalized under the outgoing key.
The client follows that handoff, subject to two rules that keep it
unbreakable: an update may never cross a boundary the client has not verified,
and a boundary may only change the key when the previous boundary announced a
full DKG.

- modules/consensus/tempo/verifier: no_std verifier. Decoders for the
  commonware wire formats (certificate, on-chain DKG outcome) written against
  the rev Tempo pins, MinSig BLS verification over arkworks (hash-to-G1,
  subgroup and infinity checks), Tempo header RLP, epoch and rotation rules,
  fraud proofs.
- modules/ismp/clients/tempo: ConsensusClient (TMPO) for Evm(4217) mainnet and
  Evm(42431) testnet.
- tesseract/consensus/tempo: relayer host over the node's consensus RPC
  namespace, walking boundary blocks so rotations are never skipped.

Fraud proofs carry headers and accept same-round-different-digest or
same-height-different-digest: a simplex safety violation is not always a
same-round double vote, since a Byzantine quorum can nullify its way to a
second notarization at the same height.

Tested against captured mainnet certificates and headers, including an
epoch-boundary block and its DKG outcome. Rotation and equivocation paths —
which mainnet has never exercised — are covered with synthetic certificates,
exploiting that a recovered threshold signature is indistinguishable from a
plain BLS signature under the group key. A live end-to-end test against
mainnet RPC runs with `cargo test -p tesseract-tempo -- --ignored`.
@seunlanlege
seunlanlege marked this pull request as draft August 1, 2026 13:36
@seunlanlege seunlanlege changed the title [runtime]: Tempo consensus client [runtime, tesseract]: Tempo consensus client Aug 1, 2026
@seunlanlege
seunlanlege force-pushed the seun/tempo-consensus-client branch from 136de72 to ca9e746 Compare August 1, 2026 13:38
Follows repo conventions:

- Derive the verifier's Error with thiserror instead of a hand-written
  Display impl, and convert into ismp::error::Error through the AnyHow
  variant rather than Custom(String). The unsupported-state-machine case
  becomes an error variant so every ismp error goes through that one path.
- Split the JSON-RPC prover out of the relayer into tempo-prover, a std-only
  sibling of tempo-verifier, matching modules/consensus/<chain>/{verifier,
  prover}. The live mainnet test moves with it, so it now runs as
  `cargo test -p tempo-prover -- --ignored`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant