Skip to main content

Checkpoint

checkpoint module manages checkpoint related functionalities for Heimdall. It needs Bor chain when a new checkpoint is proposed on Heimdall to verify checkpoint root hash.

All related to checkpoint data is explained in details here.

Checkpoint life-cycle

Heimdall uses the same leader selection algorithm as Tendermint to select the next proposer. While submitting checkpoints on the Ethereum chain, it may fail due to multiple reasons like gas limit, traffic on Ethereum, high gas fees. That's why the multi-stage checkpoint process is required.

Each checkpoint has validator as proposer. If checkpoint on Ethereum chain fails or succeeds, ack and no-ack transaction would change the proposer on Heimdall for next checkpoint. The following flow chart represents the life cycle of the checkpoint:

Messages

MsgCheckpoint

MsgCheckpoint handles checkpoint verification on Heimdall. Only this message uses RLP encoding as it needs to be verified on Ethereum chain.

// MsgCheckpoint represents checkpoint transaction
type MsgCheckpoint struct {
Proposer types.HeimdallAddress `json:"proposer"`
StartBlock uint64 `json:"startBlock"`
EndBlock uint64 `json:"endBlock"`
RootHash types.HeimdallHash `json:"rootHash"`
AccountRootHash types.HeimdallHash `json:"accountRootHash"`
}

Once this transaction gets processed on Heimdall, the proposer takes votes and sigs from Tendermint for this transaction and sends checkpoint on the Ethereum chain.

Since block contains multiple transactions and verifies this particular transaction on the Ethereum chain, Merkle proof is required. To avoid extra Merkle proof verification on Ethereum, Heimdall only allows one transaction in the block if the transaction type is MsgCheckpoint

To allow this mechanism, Heimdall sets MsgCheckpoint transaction as high gas consumed transaction. Check https://github.com/maticnetwork/heimdall/blob/develop/auth/ante.go#L104-L106

// fee wanted for checkpoint transaction
gasWantedPerCheckpoinTx sdk.Gas = 10000000

// checkpoint gas limit
if stdTx.Msg.Type() == "checkpoint" && stdTx.Msg.Route() == "checkpoint" {
gasForTx = gasWantedPerCheckpoinTx
}

This transaction will store proposed checkpoint on checkpointBuffer state instead of actual checkpoint list state.

MsgCheckpointAck

MsgCheckpointAck handles successful checkpoint submission. Here HeaderBlock is a checkpoint counter;

// MsgCheckpointAck represents checkpoint ack transaction if checkpoint is successful
type MsgCheckpointAck struct {
From types.HeimdallAddress `json:"from"`
HeaderBlock uint64 `json:"headerBlock"`
TxHash types.HeimdallHash `json:"tx_hash"`
LogIndex uint64 `json:"log_index"`
}

For valid TxHash and LogIndex for the submitted checkpoint, this transaction verifies the following event and validates checkpoint in checkpointBuffer state: https://github.com/maticnetwork/contracts/blob/develop/contracts/root/RootChainStorage.sol#L7-L14

event NewHeaderBlock(
address indexed proposer,
uint256 indexed headerBlockId,
uint256 indexed reward,
uint256 start,
uint256 end,
bytes32 root
);

On successful event verification, it updates the actual count of checkpoint, also known as ackCount and clears the checkpointBuffer.

MsgCheckpointNoAck

MsgCheckpointNoAck handles un-successful checkpoints or offline proposers. This transaction is only valid after CheckpointBufferTime has passed from the following events:

  • Last successful ack transaction
  • Last successful no-ack transaction
// MsgCheckpointNoAck represents checkpoint no-ack transaction
type MsgCheckpointNoAck struct {
From types.HeimdallAddress `json:"from"`
}

This transaction gives the timeout period for the current proposer to send checkpoint/ack before Heimdall chooses a new proposer for the next checkpoint.

Parameters

The checkpoint module contains the following parameters:

KeyTypeDefault value
CheckpointBufferTimeuint641000 * time.Second

CLI Commands

Params

To print all params:

heimdallcli query checkpoint params --trust-node

Expected Result:

checkpoint_buffer_time: 16m40s

Send Checkpoint

Following command sends checkpoint transaction on Heimdall:

heimdallcli tx checkpoint send-checkpoint \
--start-block=<start-block> \
--end-block=<end-block> \
--root-hash=<root-hash> \
--account-root-hash=<account-root-hash> \
--chain-id=<chain-id>

Send ack

Following command sends ack transaction on Heimdall if checkpoint is successful on Ethereum:

heimdallcli tx checkpoint send-ack \
--tx-hash=<checkpoint-tx-hash>
--log-index=<checkpoint-event-log-index>
--header=<checkpoint-index> \
--chain-id=<chain-id>

Send no-ack

Following command send no-ack transaction on Heimdall:

heimdallcli tx checkpoint send-noack --chain-id <chain-id>

REST APIs

NameMethodEndpoint
It returns the prepared msg for ack checkpointPOST/checkpoint/ack
It returns the prepared msg for new checkpointPOST/checkpoint/new
It returns the prepared msg for no-ack checkpointPOST/checkpoint/no-ack
Checkpoint by numberGET/checkpoints/<checkpoint-number>
Get current checkpoint buffer stateGET/checkpoints/buffer
Get checkpoint countsGET/checkpoints/count
Get last no-ack detailsGET/checkpoints/last-no-ack
Get latest checkpointGET/checkpoints/latest
All checkpointsGET/checkpoints/list
It returns the checkpoint parametersGET/checkpoints/parama
It returns the prepared checkpointGET/checkpoints/prepare
Get ack count, buffer, validator set, validator count and last-no-ack detailsGET/overview

More details about the query and response of the above requests can be found here.

All query APIs will provide result in following format:

{
"height": "1",
"result": {
...
}
}