Skip to content

Feature Request: Light Client API for Consensus Finality Proofs #1438

@yorhodes

Description

@yorhodes

Describe the feature

Summary

Tempo currently does not expose BLS finalization signatures or consensus proofs via any API, making it impossible to build trustless light clients or cross-chain bridges. This feature request proposes adding a consensus API (similar to Ethereum's Beacon API) to expose finality proofs.

Motivation

Use Cases

  1. Cross-chain bridges: Bridges need cryptographic proof that blocks were finalized by the validator set to trustlessly relay state to other chains
  2. Light clients: SPV-style clients need to verify finality without running a full consensus node
  3. Block explorers: Displaying finality status and validator signatures
  4. Audit and verification: Independent verification of chain finality

Current Limitation

While Tempo generates BLS threshold signatures for notarization and finalization internally, these signatures are:

  • ✅ Created during consensus
  • ✅ Verified by validators
  • Never stored or exposed
  • Discarded after verification

This means:

  • External observers cannot verify historical finality
  • Light clients must trust RPC providers
  • Bridges cannot prove finality cryptographically
  • --follow mode nodes have zero verification

Comparison: Ethereum Beacon API

Ethereum solved this with dedicated light client endpoints on the Beacon Chain API:

# Get finality checkpoint
GET /eth/v1/beacon/states/{state_id}/finality_checkpoints

# Light client finality update (includes BLS signatures)
GET /eth/v1/beacon/light_client/finality_update

# Light client bootstrap
GET /eth/v1/beacon/light_client/bootstrap/{block_root}

Response includes:

  • Finalized block header
  • BLS aggregate signature proving finality
  • Sync committee participation bits
  • Merkle proofs for verification

Additional context

Proposed Solution

1. Store Finalization Certificates

Uncomment and implement the existing skeleton in crates/commonware-node/src/consensus/block.rs:140-250:

pub struct Finalized {
    proof: Finalization,  // BLS signature
    block: Block,
}

impl Finalized {
    pub fn verify(&self, namespace: &[u8], identity: &BlsPublicKey) -> bool {
        self.proof.verify(namespace, identity)
    }
}

Store certificates in:

  • In-memory: Recent N blocks (for active bridges/light clients)
  • Database/Indexer: Historical proofs (for full verification)

2. Add Consensus RPC Namespace

Create tempo_consensus namespace with methods:

#[rpc(server, namespace = "tempo_consensus")]
pub trait TempoConsensusApi {
    /// Get BLS finalization signature for a block
    #[method(name = "getBlockFinalization")]
    async fn get_block_finalization(
        &self,
        block_hash: B256,
    ) -> RpcResult<Option<FinalizationCertificate>>;

    /// Get notarization certificate for a block
    #[method(name = "getBlockNotarization")]
    async fn get_block_notarization(
        &self,
        block_hash: B256,
    ) -> RpcResult<Option<NotarizationCertificate>>;

    /// Get validator set for an epoch
    #[method(name = "getEpochValidators")]
    async fn get_epoch_validators(
        &self,
        epoch: u64,
    ) -> RpcResult<ValidatorSet>;

    /// Get BLS public key for verification (from genesis/DKG)
    #[method(name = "getEpochPublicKey")]
    async fn get_epoch_public_key(
        &self,
        epoch: u64,
    ) -> RpcResult<BlsPublicKey>;
}

3. Response Format

interface FinalizationCertificate {
  blockHash: string;           // Block being finalized
  epoch: number;               // Epoch number
  round: number;               // Consensus round
  signature: string;           // BLS threshold signature (hex)
  participantBits: string;     // Bitfield of validators who signed
  publicKey: string;           // BLS public key for verification
}

4. Verification Flow

// Bridge verification example
const cert = await rpc.call('tempo_consensus_getBlockFinalization', [blockHash]);
const publicKey = await rpc.call('tempo_consensus_getEpochPublicKey', [cert.epoch]);

// Verify BLS signature cryptographically
const isValid = verifyBLS(cert.signature, blockHash, publicKey);

Implementation Notes

Minimal Implementation (Phase 1)

  1. Store finalization certificates in subblocks.rs actor (LruCache<BlockHash, Certificate>)
  2. Add RPC method to query from cache
  3. Expose last ~1000 finalized blocks

Full Implementation (Phase 2)

  1. Implement indexer that stores all historical certificates
  2. Add light client sync protocol (like Ethereum's)
  3. Support for merkle proofs and state verification

References

  • Existing code: crates/commonware-node/src/consensus/block.rs:140-250 (commented out)
  • Consensus signatures: Already created in crates/commonware-node/src/subblocks.rs:243-250
  • Ethereum Beacon API: https://ethereum.github.io/beacon-APIs/
  • BLS Public Key: Already stored in genesis.config.publicPolynomial (pre-allegretto) or boundary_block.extraData (post-allegretto)

Impact

Without this feature:

  • ❌ No trustless bridges possible
  • ❌ No light client support
  • ❌ Nodes in --follow mode cannot verify finality
  • ❌ External observers must trust RPC providers

With this feature:

  • ✅ Trustless cross-chain bridges
  • ✅ Light client protocols
  • ✅ Independent finality verification
  • ✅ Production-ready bridge infrastructure

Related

This addresses the TODO comment in the codebase about implementing an indexer for consensus proofs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions