Skip to main content
An attestor is a lightweight service (cosmos/ibc-attestor) that watches a chain and signs statements about its state on demand. When the Proof API needs to prove that a packet was committed on a chain, it queries the attestor for a signed attestation over the relevant block state. In this demo, one attestor is watching the EVM chain and one is watching the Cosmos chain. Each attestor signs with a secp256k1 key; the corresponding Ethereum address was registered with both light clients in the create-clients step. When a packet arrives, the light client recovers the signer address from the attestation signature and checks it against its registered set. Both attestors in this demo share a single signing key, so only one address needs to be registered. In production, you would run multiple attestors per chain with distinct keys and configure the light clients with a quorum threshold greater than one. Run the following:
./setup.sh attestors
The logic for this command is in lib/ibc.sh

What it does

Key generation

If no keystore exists, the script generates a new secp256k1 signing key and stores it at ibc/local/.ibc-attestor/ibc-attestor-keystore. The Ethereum address can be retrieved at any time:
ibc_attestor key show
This address is what gets registered with both light clients in the create-clients step. In this demo, the same keystore is mounted into both attestor containers so both sign with the same address.

Config rendering

The script renders two config files from templates: The EVM config requires ICS26_ROUTER_ADDR from the deploy step. See below for the configuration reference for both the EVM and Cosmos attestors.

Starting the containers

attestor        — ibc_attestor server --config /config/attestor-config.toml --chain-type evm --signer-type local
attestor-cosmos — ibc_attestor server --config /config/attestor-cosmos-config.toml --chain-type cosmos --signer-type local
Both containers mount the keystore from ibc/local/.ibc-attestor/ and listen on port 9101 within the Docker network.

Configuration

EVM attestor

Below is an example of the EVM attestor config for this demo:
[server]
listen_addr = "0.0.0.0:9101"   # gRPC — queried by the Proof API
health_addr = "0.0.0.0:9102"   # HTTP health check

[adapter]
url = "<EVM_JSON_RPC_ENDPOINT>"
router_address = "<ICS26_ROUTER_ADDR>"   # ICS26Router address from the deploy step
finality_offset = 0                       # set to N to attest latest-N instead of the finalized tag

[signer]
keystore_path = "/config/.ibc-attestor/ibc-attestor-keystore"
adapter.finality_offset determines at which block height the attestor reads and signs chain state. If omitted, the attestor uses the chain’s native finalized block tag. If set to N, it attests latest - N. This value must match finality_offset in the relayer config. See Finality offset for more details.

Cosmos attestor

The Cosmos attestor only requires the CometBFT RPC endpoint:
[server]
listen_addr = "0.0.0.0:9101"
health_addr = "0.0.0.0:9102"

[adapter]
url = "<COMETBFT_RPC_ENDPOINT>"

[signer]
keystore_path = "/config/.ibc-attestor/ibc-attestor-keystore"

Config field reference

FieldRequiredDescription
server.listen_addrYesgRPC address the Proof API connects to
server.health_addrYesHTTP health endpoint (GET /healthz)
adapter.urlYesChain RPC endpoint: EVM JSON-RPC or CometBFT RPC
adapter.router_addressEVM onlyAddress of the ICS26Router contract from the deploy step
adapter.finality_offsetNoBlocks to subtract from latest when determining finality — must match the relayer’s finality_offset (see Finality offset)
signer.keystore_pathYesPath to the keystore file

Production deployment

For more information on production deployment (remote signers, key rotation, multi-instance quorum, finality configuration, and health checking), see the IBC Attestor deployment guide.

Next steps

The next step is to configure and start the relayer and Proof API.