Checkpoints
Checkpoints are vital components of the Polygon network, representing snapshots of the Bor chain state. These checkpoints are attested by a majority of the vali
Checkpoints are vital components of the Polygon network, representing snapshots of the Bor chain state. These checkpoints are attested by a majority of the validator set before being validated and submitted on Ethereum contracts.
Heimdall, an integral part of this process, manages checkpoint functionalities using the checkpoint module. It coordinates with the Bor chain to verify checkpoint root hashes when a new checkpoint is proposed.
Overview
Heimdall selects the next proposer using CometBFT’s leader selection algorithm.
The multi-stage checkpoint process is crucial due to potential failures when submitting checkpoints on the Ethereum chain caused by factors like gas limit, network traffic, or high gas fees.
Each checkpoint has a validator as the proposer.
The outcome of a checkpoint on the Ethereum chain (success or failure) triggers an ack (acknowledgment) or no-ack (no acknowledgment) transaction,
altering the proposer for the next checkpoint on Heimdall.

Flow
Checkpoint Proposal
A checkpoint proposal is initiated by a proposer, a validator with POL tokens staked on the L1 Ethereum root chain.
The checkpointing process is managed by the bridge processor which generates a MsgCheckpoint and broadcasts it as a transaction.
- The proposer derives the root hash from the Bor chain contract.
- Due to Bor’s finality time, the root hash may not always reflect the latest Bor tip.
Checkpoint Processing in Heimdall
Once the checkpoint message is included in a Heimdall block,
it undergoes processing through the message handling system.
Each validator node independently verifies the checkpoint
by checking the Bor root hash provided in the message against its local Bor chain.
ABCI++ Processing Flow for the checkpoint submission on Heimdall
Prepare Proposal: During the proposal phase, the checkpoint messageMsgCheckpointis included in the proposed block only if dry-running this tx does not return any errors.Process Proposal: The proposal is validated to ensure correctness.Pre-Commit: As part of the voting process, validators execute a side transaction to verify the checkpoint against their local Bor data. If the checkpoint is valid, validators include a vote extension confirming their approval.Verify Vote: Injected votes are verified.
•Next block - Finalize: In the next block, the finalized votes are processed, and the checkpoint is considered approved if a sufficient majority supports it.
ThepreBlockertriggers post-tx handlers performing the Heimdall state changes when the checkpoint is finally saved in the checkpoint buffer as the checkpoint that needs to be further bridged to the Ethereum L1 root chain.
Submission to Ethereum (L1)
Once approved, the checkpoint is added to a checkpoint buffer and an event is emitted. The bridge system, which listens for these events, submits the checkpoint data along with validator signatures to the Ethereum root chain.
Acknowledgment from Ethereum (L1)
After the checkpoint is successfully included on the Ethereum chain, an acknowledgment MsgCpAck is sent back to Heimdall from the bridge processor.
This acknowledgment, once processed through the ABCI++ flow with side and post-tx handlers: updates the state, flushes processed checkpoints from the buffer, and increments the number of ACK counters to track confirmations of checkpoints.
Additionally, the selection of the next checkpoint proposer is adjusted based on the updated state.
Missing Checkpoint Acknowledgment from Ethereum (L1)
The MsgCpNoAck message is broadcast by the bridge processor to indicate that a checkpoint was potentially transferred to the Ethereum chain but has not received an acknowledgment.
A background routine periodically checks for time elapsed and publishes the No-ACK signal. No-ACK is sent if a sufficient amount of time has passed since:
- the last checkpoint was created on the Heimdall-v2 chain and
- the last No-ACK was issued.
To conclude, the No-ACKs are triggered only when a checkpoint acknowledgment is overdue, ensuring they are not sent too frequently.
This message is broadcasted only by the proposer. This entire flow ensures that checkpoints are securely proposed, verified, and finalized across the Heimdall and Ethereum chains in a decentralized manner.

Messages
MsgCheckpoint
MsgCheckpoint defines a message for creating a checkpoint on the Ethereum chain.
message MsgCheckpoint {
option (cosmos.msg.v1.signer) = "proposer";
option (amino.name) = "heimdallv2/checkpoint/MsgCheckpoint";
option (gogoproto.equal) = true;
option (gogoproto.goproto_getters) = true;
string proposer = 1 [
(amino.dont_omitempty) = true,
(cosmos_proto.scalar) = "cosmos.AddressString"
];
uint64 start_block = 2 [ (amino.dont_omitempty) = true ];
uint64 end_block = 3 [ (amino.dont_omitempty) = true ];
bytes root_hash = 4 [ (amino.dont_omitempty) = true ];
bytes account_root_hash = 5 [ (amino.dont_omitempty) = true ];
string bor_chain_id = 6 [ (amino.dont_omitempty) = true ];
}MsgCpAck
MsgCpAck defines a message for creating the ack tx of a submitted checkpoint.
message MsgCpAck {
option (cosmos.msg.v1.signer) = "from";
option (amino.name) = "heimdallv2/checkpoint/MsgCpAck";
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = true;
string from = 1 [
(amino.dont_omitempty) = true,
(cosmos_proto.scalar) = "cosmos.AddressString"
];
uint64 number = 2 [ (amino.dont_omitempty) = true ];
string proposer = 3 [
(amino.dont_omitempty) = true,
(cosmos_proto.scalar) = "cosmos.AddressString"
];
uint64 start_block = 4 [ (amino.dont_omitempty) = true ];
uint64 end_block = 5 [ (amino.dont_omitempty) = true ];
bytes root_hash = 6 [ (amino.dont_omitempty) = true ];
bytes tx_hash = 7 [ (amino.dont_omitempty) = true ];
uint64 log_index = 8 [ (amino.dont_omitempty) = true ];
}MsgCheckpointNoAck
MsgCpNoAck defines a message for creating the no-ack tx of a checkpoint.
message MsgCpNoAck {
option (cosmos.msg.v1.signer) = "from";
option (amino.name) = "heimdallv2/checkpoint/MsgCpNoAck";
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = true;
string from = 1 [
(amino.dont_omitempty) = true,
(cosmos_proto.scalar) = "cosmos.AddressString"
];
}Interact with the Node
Tx Commands
Send checkpoint
heimdalld tx checkpoint send-checkpoint --proposer=<proposer-address> --start-block=<start-block-number> --end-block=<end-block-number> --root-hash=<root-hash> --account-root=<account-root> --bor-chain-id=<bor-chain-id> --auto-configure=true/falseSend checkpoint ack
heimdalld tx checkpoint send-ack --tx-hash=<checkpoint-tx-hash> --log-index=<log-index> --header=<header> --proposer=<proposer-address> --auto-configure=true/falseSend checkpoint no-ack
heimdalld tx checkpoint checkpoint-no-ack --from <from>CLI Query Commands
One can run the following query commands from the checkpoint module:
get-params- Get checkpoint paramsget-overview- Get checkpoint overviewget-ack-count- Get checkpoint ack countget-checkpoint- Get checkpoint based on its numberget-checkpoint-latest- Get the latest checkpointget-checkpoint-buffer- Get the checkpoint bufferget-last-no-ack- Get the last no ackget-next-checkpoint- Get the next checkpointget-current-proposer- Get the current proposerget-proposers- Get the proposersget-checkpoint-list- Get the list of checkpoints
heimdalld query checkpoint get-paramsheimdalld query checkpoint get-overviewheimdalld query checkpoint get-ack-countheimdalld query checkpoint get-checkpointheimdalld query checkpoint get-checkpoint-latestheimdalld query checkpoint get-checkpoint-bufferheimdalld query checkpoint get-last-no-ackheimdalld query checkpoint get-next-checkpointheimdalld query checkpoint get-current-proposerheimdalld query checkpoint get-proposersheimdalld query checkpoint get-checkpoint-listGRPC Endpoints
The endpoints and the params are defined in the checkpoint/query.proto file. Please refer to them for more information about the optional params.
grpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetCheckpointParamsgrpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetCheckpointOverviewgrpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetAckCountgrpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetCheckpointLatestgrpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetCheckpointBuffergrpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetLastNoAckgrpcurl -plaintext -d '{"bor_chain_id": <>}' localhost:9090 heimdallv2.checkpoint.Query/GetNextCheckpointgrpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetCurrentProposergrpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetProposersgrpcurl -plaintext -d '{}' localhost:9090 heimdallv2.checkpoint.Query/GetCheckpointListgrpcurl -plaintext -d '{"tx_hash": <>}' localhost:9090 heimdallv2.checkpoint.QueryGetCheckpointSignaturesgrpcurl -plaintext -d '{"number": <>}' localhost:9090 heimdallv2.checkpoint.Query/GetCheckpointREST Endpoints
The endpoints and the params are defined in the checkpoint/query.proto file. Please refer to them for more information about the optional params.
curl localhost:1317/checkpoints/paramscurl localhost:1317/checkpoints/overviewcurl localhost:1317/checkpoints/countcurl localhost:1317/checkpoints/latestcurl localhost:1317/checkpoints/buffercurl localhost:1317/checkpoints/last-no-ackcurl localhost:1317/checkpoints/prepare-nextcurl localhost:1317/checkpoint/proposers/currentcurl localhost:1317/checkpoint/proposers/{times}curl localhost:1317/checkpoints/listcurl localhost:1317/checkpoints/signatures/{tx_hash}curl localhost:1317/checkpoints/{number}Last updated on