> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metalntwx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Finality Certificates

> How to verify Metal state on foreign chains

When Simplex finalizes a Metal block, the validator quorum produces a finality certificate: an aggregate BLS12-381 signature over the block hash, together with a bitmap identifying which validators signed. The certificate is the complete, portable proof that a block is final — any party holding the validator public keys can verify it without trusting the node that served it.

Nodes retain recent finality certificates and serve them over JSON-RPC. The
default retention limit is 100,000 certificates.

## Fetch a certificate

`metal_getFinalityCertificate` takes a finalized block hash. Pair it with `consensus_status`, which reports the latest finalized block:

Use the endpoint URL from the [Metal console](https://console-testnet.metalntwx.com).
See [RPC endpoint setup](https://docs.metalntwx.com/start/connect-to-testnet.md#2-create-an-rpc-endpoint) for the setup steps.

```bash theme={null}
export METAL_RPC_URL="https://rpc-testnet.metalntwx.com/v1/<endpoint-id>"
curl "$METAL_RPC_URL" \
  -H 'content-type: application/json' \
  --data '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "metal_getFinalityCertificate",
    "params": ["0x4f06f3c2aebd79d3e85b577ad25d0643f469cf8d7a65c5fb29f96de0c4594870"]
  }'
```

The response carries the canonical certificate, the full header encoding, and parsed convenience fields:

```json theme={null}
{
  "blockHash": "0x4f06...4870",
  "blockNumber": 4242,
  "headerRlp": "0xf902...",
  "certificate": "0x002a29...",
  "epoch": 0,
  "view": 42,
  "parentView": 41,
  "participants": 3,
  "signerCount": 3,
  "signersBitmap": "0x07",
  "signatureEipUncompressed": "0x0000...",
  "namespace": "0x6d6574616c2d636f6e73656e73757301..."
}
```

| Field | Meaning |
| - | - |
| `headerRlp` | The block header's full RLP encoding. `keccak256(headerRlp)` equals `blockHash` — this is the exact preimage the certificate signs over |
| `certificate` | The canonical certificate bytes: `varint(epoch) ‖ varint(view) ‖ varint(parentView) ‖ blockHash ‖ uint64_be(participants) ‖ signerBitmap ‖ signature` |
| `participants` | Size of the validator set the signer bitmap indexes into |
| `signerCount` | Number of validators that signed |
| `signersBitmap` | Canonically decoded signer bitmap, ready for `MetalLightClient.update` |
| `signatureEipUncompressed` | Aggregate signature converted to the 256-byte EIP-2537 G2 encoding |
| `namespace` | Hex-encoded base signing namespace: `"metal-consensus" ‖ verifierVersion ‖ uint256_be(chainId) ‖ genesisHash`; finalize votes append `_FINALIZE` |

The result is `null` when the node has no retained certificate or cannot yet
read its corresponding header. This can happen before finality, during local
import or persistence, or after retention pruning.

The message tree is cumulative, so a recent certificate can authenticate an
older message once its path and leaf count are rebuilt for that checkpoint.
An existing proof remains tied to its original checkpoint root.

> [!NOTE]
>
> Parsed fields are conveniences. The light client verifies the bitmap and signature onchain. External verifiers reconstruct the signing namespace from their trusted verifier version, chain ID, and genesis hash.

## What validators sign

Each finalize vote signs the same message, so the aggregate verifies with a single pairing check:

```text theme={null}
base      = "metal-consensus" ‖ uint8(verifierVersion)
          ‖ uint256_be(chainId) ‖ genesisHash
namespace = base ‖ "_FINALIZE"
preimage  = varint(len(namespace)) ‖ namespace
          ‖ varint(epoch) ‖ varint(view) ‖ varint(parentView)
          ‖ blockHash
```

* Varints are LEB128: seven bits per byte, most-significant-bit continuation.
* `blockHash = keccak256(headerRlp)` — the certificate transitively commits to every header field, including the state root, the receipts root, and the interop message root the cross-chain message path verifies against.
* Signatures are BLS12-381 **MinPk** (public keys in G1, signatures in G2), hashed to the curve per RFC 9380 with SHA-256 and the standard DST `BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_`.

## Verifying a certificate

A certificate proves finality for one Metal network and epoch validator set.
Pin the verifier version, chain ID, and genesis hash, and authenticate the
validator set for the certificate's epoch. Do not accept these trust roots from
the RPC endpoint that served the certificate.

A verifier holding the ordered validator public-key list checks:

1. **Network domain.** Reconstruct the namespace from the pinned verifier version, chain ID, and genesis hash.
2. **Bitmap shape.** The bitmap indexes the validator set in ascending order of compressed public-key bytes, least-significant bit first within each byte (validator `i` is bit `i % 8` of byte `i / 8`); its bit length equals the set size, and trailing bits are zero.
3. **Quorum.** At least `n - (n - 1) / 3` distinct validators signed.
4. **Signature.** Sum the signers' G1 public keys and check the pairing `e(aggregateKey, hashToG2(preimage)) == e(g1, signature)`.
5. **Header binding.** `keccak256(headerRlp)` equals the block hash inside the signed preimage. Any header field the verifier then reads — state root, receipts root, block number, timestamp — is authenticated by the certificate.

The current `MetalLightClient` contract performs these checks using the
EIP-2537 precompiles, with a validator set fixed at deployment. It accepts only
epoch `0` and requires each accepted checkpoint to advance both block number
and view. It cannot verify later-epoch certificates or follow validator-set
transitions.

> [card] [Consensus and finality](https://docs.metalntwx.com/learn/architecture.md)
>
> How validators check and agree on candidate blocks.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.