```solidity=
struct Validator {
address addr.
bytes blskey;
uint votingPower
}
struct Checkpoint {
uint256 epochId
uint256 blockNumber
uint256 startEpochBlockNumber
bytes32 eventRoot
}
mapping(uint256 => Checkpoint) public checkpoints;
function verifyCheckpoint(
//supernet id to avoid replay attack
uint chainID,
//@todo Do we need to send checkpointHash or to derive from data?
bytes aggregatedSignature,
bytes validatorsBitmap, //which validators signed checkpoint
uint epochId,
uint blockNumber, //checkpoint identifier
bytes32 blockHash,
uint round,
bytes32 eventRoot,
bytes32 currentValidatorSetHash,
Validator[] nextValidators,
) public {
//check that checkpoint have enough voting power
checkQuorum(validatorsBitmap);
nextValidatorSetHash = currentValidatorSetHash
// need to update validator set. Do we need new epoch id there?
if (len(nextValidators)>0) {
nextValidatorSetHash = calculateNextValidatorSetHash(nextValidators)
}
// derive checkpoint hash from checkpoint data
checkpointHash = verifyCheckpointHash(blockNumber, blockHash, round, epochId, eventRoot, currentValidatorSetHash, nextValidatorSetHash);
// verify checkpoint signature
verifySignature(checkpointHash, aggregatedSignature, validatorsBitmap);
// store intermediatory checkpoints and override latest epoch id
if (checkpoints[epochId] == null) {
checkpoints[epochId] = eventRootHash;
} else if (checkpoints[epochId].blockNumber < blockNumber) {
checkpoints[epochId] = eventRootHash;
}
if (len(nextValidators)>0) {
updateValidatorSet(nextValidators);
epoch++
}
}
```
```solidity=
struct L2Event {
uint counter;
address sender;
address receiver;
bytes data;
L2StateSynced(++counter, msg.sender, receiver, data)
}
function doExit(uint epochID, L2Event exitEvent, bytes exitProof) public {
checkProof(checkpoints[epochID], exitEvent, exitProof);
saveExit(exitEvent);
makeExit();
}
```
```solidity=
struct Checkpoint {
uint256 epochId,
uint256 endBlock,
bytes32 eventRoot
}
uint256 public currentEpochId;
bytes32 public currentValidatorSetHash;
Validator[] public currentValidators;
mapping(uint256 => Checkpoint) public checkpoints;
submitCheckpoint(
uint256 chainID, //supernet id to avoid replay attack
bytes aggregatedSignature,
bytes validatorsBitmap, //which validators signed checkpoint
uint epochId,
uint blockNumber, //checkpoint identifier
bytes32 blockHash,
uint round,
bytes32 eventRoot,
Validator[] nextValidators,
) {
// Epoch Id will always be next one
require(currentEpochId + 1 = epochId);
// create hash of next validators
// will it be same as currentValidators if it's not epoch change
byte32 nextValidatorSetHash = currentValidatorSetHash;
if (len(nextValidators) != 0) {
nextValidatorSetHash = hash(nextValidators);
}
//check that checkpoint have enough voting power
checkQuorum(validatorsBitmap);
// derive checkpoint hash from checkpoint data
// Hash will have new validator set along with epoch id, start block, end block, event root
checkpointHash = verifyCheckpointHash(chainID, blockNumber, blockHash, round, epochId, eventRoot, currentValidatorSetHash, nextValidatorSetHash);
// verify checkpoint signature
// sigs will be current validator set's sig, so verify against the current validator set
verifySignature(checkpointHash, aggregatedSignature, validatorsBitmap);
// store intermediatory checkpoints and override it with latest epoch id
if (
checkpoints[epochId] == null || checkpoints[epochId].endBlock < endBlock
) {
checkpoints[epochId] = { epochId, blockNumber as endBlock, eventRootHash };
// epoch changed - update stored validator set and validator set hash
if (nextValidatorSetHash != currentValidatorSetHash) {
currentValidators = nextValidators;
currentValidatorSetHash = nextValidatorSetHash;
currentEpochId = currentEpochId + 1;
}
return;
}
// Code should never reach here
assert(false);
}
```