Files
smom-dbis-138/contracts/mainnet-checkpoint/Chain138MainnetCheckpoint.sol
defiQUG c336809676
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m3s
CI/CD Pipeline / Security Scanning (push) Successful in 2m18s
CI/CD Pipeline / Lint and Format (push) Failing after 34s
CI/CD Pipeline / Terraform Validation (push) Failing after 20s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 22s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 40s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 49s
OMNL reconcile anchor / Run omnl:reconcile and upload artifacts (push) Failing after 21s
Validation / validate-genesis (push) Successful in 25s
Validation / validate-terraform (push) Failing after 21s
Validation / validate-kubernetes (push) Failing after 8s
Validation / validate-smart-contracts (push) Failing after 8s
Validation / validate-security (push) Failing after 1m11s
Validation / validate-documentation (push) Failing after 14s
Verify Deployment / Verify Deployment (push) Failing after 45s
Add mainnet checkpoint stack: ISO attestation, participant Etherscan surface, and services.
Ship AddressActivityRegistry V1/V2, ISO20022IntakeGateway, Chain138ParticipantSurface,
checkpoint hub contracts, checkpoint-core package, aggregator/indexer/sdk services,
relay profile guards, M00 diamond bridge facet, and OMNL compliance contracts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:30:45 -07:00

550 lines
24 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "../vendor/openzeppelin/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../vendor/openzeppelin/ReentrancyGuardUpgradeable.sol";
import "../ccip/IRouterClient.sol";
import {CheckpointStorage} from "./storage/CheckpointStorage.sol";
import {CheckpointLeaf} from "./libraries/CheckpointLeaf.sol";
import {CheckpointFlags} from "./libraries/CheckpointFlags.sol";
import {ICheckpointExtension} from "./interfaces/ICheckpointExtension.sol";
import {IChain138MainnetCheckpoint} from "./interfaces/IChain138MainnetCheckpoint.sol";
import {CheckpointEIP712} from "./libraries/CheckpointEIP712.sol";
import {CheckpointHubConfig} from "./libraries/CheckpointHubConfig.sol";
import {CheckpointErrors} from "./libraries/CheckpointErrors.sol";
import {CheckpointPaymentsLib} from "./libraries/CheckpointPaymentsLib.sol";
import {ExtensionIds} from "./libraries/ExtensionIds.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/**
* @title Chain138MainnetCheckpoint
* @notice Upgradeable hub: state + payment batches from Chain 138 (default 10 txs).
* @dev UUPS + EIP-7201 storage + extension registry. cW mint remains on CWMultiTokenBridge.
*/
contract Chain138MainnetCheckpoint is
Initializable,
AccessControlUpgradeable,
ReentrancyGuardUpgradeable,
UUPSUpgradeable,
IChain138MainnetCheckpoint
{
using CheckpointStorage for CheckpointStorage.CheckpointStorageStruct;
uint256 public constant IMPLEMENTATION_VERSION = 4;
uint64 public constant CHAIN_138 = 138;
bytes32 public constant SUBMITTER_ROLE = keccak256("SUBMITTER_ROLE");
bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
bytes32 public constant EXTENSION_ADMIN_ROLE = keccak256("EXTENSION_ADMIN_ROLE");
bytes32 public constant EMERGENCY_ROLE = keccak256("EMERGENCY_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant CHECKPOINT_ATTEST_TYPEHASH =
keccak256("BatchAttestation(uint64 chainId,uint64 batchId,uint256 checkpointBlock,bytes32 blockHash,bytes32 stateRoot,bytes32 paymentsRoot,uint64 previousBatchId)");
uint32 public constant HOOK_BEFORE_SUBMIT = 1 << 0;
uint32 public constant HOOK_AFTER_SUBMIT = 1 << 1;
uint32 public constant HOOK_ON_CCIP = 1 << 2;
uint32 public constant HOOK_VERIFY_LEAF = 1 << 3;
event HubConfigApplied(CheckpointHubConfig.HubConfig config);
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(
address admin,
address ccipRouter,
uint64 sourceChainSelector,
address batchEmitterOnSource
) external initializer {
__AccessControl_init();
__ReentrancyGuard_init();
__UUPSUpgradeable_init();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(UPGRADER_ROLE, admin);
_grantRole(SUBMITTER_ROLE, admin);
_grantRole(EXTENSION_ADMIN_ROLE, admin);
_grantRole(PAUSER_ROLE, admin);
_grantRole(EMERGENCY_ROLE, admin);
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
$.chainId = CHAIN_138;
$.batchSize = 10;
$.maxBatchWaitSeconds = 300;
$.requireValidatorSigs = true;
$.allowCalldataOnlySubmit = true;
$.allowCCIPIngress = true;
$.enforcePreviousBatchId = true;
$.ccipRouter = ccipRouter;
$.expectedSourceChainSelector = sourceChainSelector;
$.batchEmitterOnSource = batchEmitterOnSource;
}
function initializeV2(address legacyMirror, address legacyTether, address attestationSigner) external reinitializer(2) {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
$.legacyMirrorV1 = legacyMirror;
$.legacyTetherV1 = legacyTether;
$.submitterAttestationSigner = attestationSigner;
}
// --- views ---
function getConfig()
external
view
returns (
uint16 batchSize,
uint32 maxBatchWaitSeconds,
uint256 minPaymentValueWei,
bool requireValidatorSigs,
bool allowCalldataOnlySubmit,
bool allowCCIPIngress
)
{
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
return (
$.batchSize,
$.maxBatchWaitSeconds,
$.minPaymentValueWei,
$.requireValidatorSigs,
$.allowCalldataOnlySubmit,
$.allowCCIPIngress
);
}
function getFullConfig() external view returns (CheckpointHubConfig.HubConfig memory cfg) {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
cfg.chainId = $.chainId;
cfg.batchSize = $.batchSize;
cfg.maxBatchWaitSeconds = $.maxBatchWaitSeconds;
cfg.minPaymentValueWei = $.minPaymentValueWei;
cfg.requireValidatorSigs = $.requireValidatorSigs;
cfg.allowCalldataOnlySubmit = $.allowCalldataOnlySubmit;
cfg.allowCCIPIngress = $.allowCCIPIngress;
cfg.enforcePreviousBatchId = $.enforcePreviousBatchId;
cfg.ccipRouter = $.ccipRouter;
cfg.sourceChainSelector = $.expectedSourceChainSelector;
cfg.batchEmitterOnSource = $.batchEmitterOnSource;
cfg.legacyMirrorV1 = $.legacyMirrorV1;
cfg.legacyTetherV1 = $.legacyTetherV1;
cfg.submitterAttestationSigner = $.submitterAttestationSigner;
}
function applyConfig(CheckpointHubConfig.HubConfig calldata cfg) external onlyRole(DEFAULT_ADMIN_ROLE) {
CheckpointHubConfig.validate(cfg);
_applyHubConfig(cfg);
emit HubConfigApplied(cfg);
}
function extensionCount() external view returns (uint256) {
return CheckpointStorage.get().extensionList.length;
}
function getExtension(bytes32 extensionId)
external
view
returns (address module, uint32 hooks, bool active)
{
CheckpointStorage.ExtensionConfig storage cfg = CheckpointStorage.get().extensions[extensionId];
return (cfg.module, cfg.hooks, cfg.active);
}
function getExtensionList() external view returns (bytes32[] memory) {
return CheckpointStorage.get().extensionList;
}
function getLatestCheckpoint() external view returns (CheckpointStorage.CheckpointHeader memory) {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
return $.checkpoints[$.latestBatchId];
}
function getCheckpoint(uint64 batchId) external view returns (CheckpointStorage.CheckpointHeader memory) {
return CheckpointStorage.get().checkpoints[batchId];
}
function getLatestBatchId() external view returns (uint64) {
return CheckpointStorage.get().latestBatchId;
}
function latestCheckpointBlock() external view returns (uint256) {
return CheckpointStorage.get().latestCheckpointBlock;
}
function verifyPaymentInBatch(
uint64 batchId,
CheckpointLeaf.PaymentLeafV1 calldata leaf,
bytes32[] calldata proof
) external view returns (bool) {
CheckpointStorage.CheckpointHeader storage h = CheckpointStorage.get().checkpoints[batchId];
if (h.batchId == 0) revert CheckpointErrors.UnknownBatch();
bytes32 leafHash = CheckpointLeaf.paymentLeafV1(h.chainId, leaf);
CheckpointStorage.CheckpointHeader memory hm = h;
if (_extensionVerifyLeaf(hm, leaf, proof)) return true;
return CheckpointLeaf.verifyMerkle(h.paymentsRoot, leafHash, proof);
}
function isTxIncluded(bytes32 txHash) external view returns (bool included, uint64 batchId) {
batchId = CheckpointStorage.get().txHashToBatchId[txHash];
included = batchId != 0;
}
// --- submit ---
function paused() external view returns (bool) {
return CheckpointStorage.get().paused;
}
function pause() external onlyRole(PAUSER_ROLE) {
CheckpointStorage.get().paused = true;
}
function unpause() external onlyRole(PAUSER_ROLE) {
CheckpointStorage.get().paused = false;
}
function submitCheckpoint(
CheckpointStorage.CheckpointHeader calldata header,
bytes calldata validatorSignatures,
bytes32[] calldata txHashes,
bytes calldata extensionData
) external onlyRole(SUBMITTER_ROLE) nonReentrant {
_requireNotPaused();
_runBeforeSubmitExtensions(header, validatorSignatures, extensionData);
_submit(header, validatorSignatures, txHashes, bytes32(0), false, false, extensionData);
_runExtensions(HOOK_AFTER_SUBMIT, header, extensionData);
}
function submitCheckpointByRelayer(
CheckpointStorage.CheckpointHeader calldata header,
bytes calldata validatorSignatures,
bytes32[] calldata txHashes,
bytes calldata extensionData,
bytes calldata submitterSignature
) external onlyRole(RELAYER_ROLE) nonReentrant {
_requireNotPaused();
_verifySubmitterAttestation(header, submitterSignature);
CheckpointStorage.CheckpointHeader memory h = header;
h.flags |= CheckpointFlags.RELAYER_SUBMIT;
_runBeforeSubmitExtensions(h, validatorSignatures, extensionData);
_submit(h, validatorSignatures, txHashes, bytes32(0), false, false, extensionData);
_runExtensions(HOOK_AFTER_SUBMIT, h, extensionData);
}
function submitCheckpointWithLeaves(
CheckpointStorage.CheckpointHeader calldata header,
bytes calldata validatorSignatures,
bytes32[] calldata txHashes,
CheckpointLeaf.PaymentLeafV1[] calldata leaves
) external onlyRole(SUBMITTER_ROLE) nonReentrant {
_requireNotPaused();
CheckpointPaymentsLib.assertPaymentsRootV1(header.chainId, header.paymentsRoot, leaves);
bytes memory leafPayload = abi.encode(leaves);
_runBeforeSubmitExtensions(header, validatorSignatures, leafPayload);
_submit(header, validatorSignatures, txHashes, bytes32(0), false, false, leafPayload);
_runExtensions(HOOK_AFTER_SUBMIT, header, leafPayload);
}
function forceCheckpoint(
CheckpointStorage.CheckpointHeader calldata header,
bytes calldata validatorSignatures,
bytes32[] calldata txHashes,
bytes calldata extensionData
) external onlyRole(EMERGENCY_ROLE) nonReentrant {
CheckpointStorage.CheckpointHeader memory h = header;
h.flags |= CheckpointFlags.EMERGENCY;
_runBeforeSubmitExtensions(h, validatorSignatures, extensionData);
_submit(h, validatorSignatures, txHashes, bytes32(0), false, true, extensionData);
_runExtensions(HOOK_AFTER_SUBMIT, h, extensionData);
}
function submitCheckpointCommitment(
CheckpointStorage.CheckpointHeader calldata header,
bytes calldata validatorSignatures,
bytes32 contentURI,
bytes calldata extensionData
) external onlyRole(SUBMITTER_ROLE) nonReentrant {
_requireNotPaused();
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
if (!$.allowCalldataOnlySubmit) revert CheckpointErrors.CalldataOnlyDisabled();
_runBeforeSubmitExtensions(header, validatorSignatures, extensionData);
_submit(header, validatorSignatures, new bytes32[](0), contentURI, true, false, extensionData);
_runExtensions(HOOK_AFTER_SUBMIT, header, extensionData);
}
function ccipReceive(IRouterClient.Any2EVMMessage calldata message) external nonReentrant {
_requireNotPaused();
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
if (!$.allowCCIPIngress) revert CheckpointErrors.CcipDisabled();
if (msg.sender != $.ccipRouter) revert CheckpointErrors.OnlyRouter();
if (message.sourceChainSelector != $.expectedSourceChainSelector) revert CheckpointErrors.BadSelector();
address sender = message.sender.length >= 32
? address(uint160(uint256(bytes32(message.sender))))
: address(bytes20(message.sender));
if (sender != $.batchEmitterOnSource) revert CheckpointErrors.BadEmitter();
(
CheckpointStorage.CheckpointHeader memory header,
bytes memory validatorSignatures,
bytes32[] memory txHashes,
bytes32 contentURI,
bytes memory extensionData
) = abi.decode(
message.data,
(CheckpointStorage.CheckpointHeader, bytes, bytes32[], bytes32, bytes)
);
header.flags |= CheckpointFlags.CCIP_INGRESS;
_runExtensions(HOOK_ON_CCIP, header, message.data);
_runBeforeSubmitExtensions(header, validatorSignatures, extensionData);
_submit(
header,
validatorSignatures,
txHashes,
contentURI,
CheckpointFlags.has(header.flags, CheckpointFlags.CALLDATA_ONLY),
false,
extensionData
);
_runExtensions(HOOK_AFTER_SUBMIT, header, extensionData);
}
// --- extensions ---
function registerExtension(bytes32 extensionId, address module, uint32 hooks) external onlyRole(EXTENSION_ADMIN_ROLE) {
if (module == address(0)) revert CheckpointErrors.ZeroModule();
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
if ($.extensions[extensionId].active) revert CheckpointErrors.ExtensionExists();
$.extensions[extensionId] = CheckpointStorage.ExtensionConfig({module: module, hooks: hooks, active: true});
$.extensionList.push(extensionId);
emit ExtensionRegistered(extensionId, module, hooks);
}
function revokeExtension(bytes32 extensionId) external onlyRole(EXTENSION_ADMIN_ROLE) {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
if (!$.extensions[extensionId].active) revert CheckpointErrors.ExtensionMissing();
delete $.extensions[extensionId];
bytes32[] storage list = $.extensionList;
for (uint256 i = 0; i < list.length; i++) {
if (list[i] == extensionId) {
list[i] = list[list.length - 1];
list.pop();
break;
}
}
emit ExtensionRevoked(extensionId);
}
function setConfig(
uint16 batchSize,
uint32 maxBatchWaitSeconds,
uint256 minPaymentValueWei,
bool requireValidatorSigs,
bool allowCalldataOnlySubmit,
bool allowCCIPIngress
) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (batchSize == 0 || batchSize > 256) revert CheckpointErrors.BatchSize();
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
$.batchSize = batchSize;
$.maxBatchWaitSeconds = maxBatchWaitSeconds;
$.minPaymentValueWei = minPaymentValueWei;
$.requireValidatorSigs = requireValidatorSigs;
$.allowCalldataOnlySubmit = allowCalldataOnlySubmit;
$.allowCCIPIngress = allowCCIPIngress;
}
function setExtensionActive(bytes32 extensionId, bool active) external onlyRole(EXTENSION_ADMIN_ROLE) {
CheckpointStorage.ExtensionConfig storage cfg = CheckpointStorage.get().extensions[extensionId];
if (cfg.module == address(0)) revert CheckpointErrors.ExtensionMissing();
cfg.active = active;
}
function updateExtensionHooks(bytes32 extensionId, uint32 hooks) external onlyRole(EXTENSION_ADMIN_ROLE) {
CheckpointStorage.ExtensionConfig storage cfg = CheckpointStorage.get().extensions[extensionId];
if (cfg.module == address(0)) revert CheckpointErrors.ExtensionMissing();
cfg.hooks = hooks;
}
function _authorizeUpgrade(address) internal override onlyRole(UPGRADER_ROLE) {}
function _applyHubConfig(CheckpointHubConfig.HubConfig calldata cfg) internal {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
$.chainId = cfg.chainId;
$.batchSize = cfg.batchSize;
$.maxBatchWaitSeconds = cfg.maxBatchWaitSeconds;
$.minPaymentValueWei = cfg.minPaymentValueWei;
$.requireValidatorSigs = cfg.requireValidatorSigs;
$.allowCalldataOnlySubmit = cfg.allowCalldataOnlySubmit;
$.allowCCIPIngress = cfg.allowCCIPIngress;
$.enforcePreviousBatchId = cfg.enforcePreviousBatchId;
if (cfg.ccipRouter != address(0)) $.ccipRouter = cfg.ccipRouter;
if (cfg.sourceChainSelector != 0) $.expectedSourceChainSelector = cfg.sourceChainSelector;
if (cfg.batchEmitterOnSource != address(0)) $.batchEmitterOnSource = cfg.batchEmitterOnSource;
if (cfg.legacyMirrorV1 != address(0)) $.legacyMirrorV1 = cfg.legacyMirrorV1;
if (cfg.legacyTetherV1 != address(0)) $.legacyTetherV1 = cfg.legacyTetherV1;
if (cfg.submitterAttestationSigner != address(0)) {
$.submitterAttestationSigner = cfg.submitterAttestationSigner;
}
}
// --- internal ---
function _requireNotPaused() internal view {
if (CheckpointStorage.get().paused) revert CheckpointErrors.Paused();
}
function _submit(
CheckpointStorage.CheckpointHeader memory header,
bytes memory validatorSignatures,
bytes32[] memory txHashes,
bytes32 contentURI,
bool calldataOnly,
bool emergency,
bytes memory extensionData
) internal {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
if ($.processedBatchIds[header.batchId]) revert CheckpointErrors.BatchDone();
if (header.chainId != $.chainId) revert CheckpointErrors.BadChain();
if (header.batchId <= $.latestBatchId) revert CheckpointErrors.BatchOrder();
if ($.enforcePreviousBatchId && $.latestBatchId > 0 && header.previousBatchId != $.latestBatchId) {
revert CheckpointErrors.PrevBatch();
}
if (header.endBlock > 0 && header.endBlock < header.startBlock) revert CheckpointErrors.BadBlocks();
if (header.txCount == 0 || header.txCount > $.batchSize) revert CheckpointErrors.TxCount();
if (!emergency && !CheckpointFlags.has(header.flags, CheckpointFlags.PARTIAL_BATCH) && header.txCount != $.batchSize) {
revert CheckpointErrors.IncompleteBatch();
}
if (header.paymentsRoot == bytes32(0)) revert CheckpointErrors.PaymentsRoot();
if (header.stateRoot == bytes32(0)) revert CheckpointErrors.StateRoot();
if (header.blockHash == bytes32(0)) revert CheckpointErrors.BlockHash();
if ($.requireValidatorSigs && validatorSignatures.length == 0) revert CheckpointErrors.Signatures();
if ($.minPaymentValueWei > 0 && extensionData.length > 0) {
CheckpointPaymentsLib.enforceMinPaymentValueV1(extensionData, $.minPaymentValueWei);
}
bytes32 proofHash = keccak256(
abi.encodePacked(
header.batchId,
header.paymentsRoot,
header.stateRoot,
header.checkpointBlock,
validatorSignatures
)
);
if ($.processedProofHashes[proofHash]) revert CheckpointErrors.Replay();
$.processedProofHashes[proofHash] = true;
if (calldataOnly) {
header.flags |= CheckpointFlags.CALLDATA_ONLY;
}
if (contentURI != bytes32(0)) {
header.flags |= CheckpointFlags.HAS_CONTENT_URI;
header.contentURI = contentURI;
}
header.submittedAt = uint64(block.timestamp);
header.submitter = msg.sender;
for (uint256 i = 0; i < txHashes.length; i++) {
if (txHashes[i] == bytes32(0)) revert CheckpointErrors.ZeroTx();
if ($.txHashToBatchId[txHashes[i]] != 0) revert CheckpointErrors.TxSeen();
$.txHashToBatchId[txHashes[i]] = header.batchId;
}
$.checkpoints[header.batchId] = header;
$.processedBatchIds[header.batchId] = true;
$.latestBatchId = header.batchId;
$.latestCheckpointBlock = header.checkpointBlock;
$.latestPaymentsRoot = header.paymentsRoot;
emit CheckpointSubmitted(
header.batchId,
header.checkpointBlock,
header.paymentsRoot,
header.txCount,
header.flags,
header.contentURI
);
}
/// @dev Validator extension expects ECDSA bytes; other extensions expect leaf/extension payload.
function _runBeforeSubmitExtensions(
CheckpointStorage.CheckpointHeader memory header,
bytes memory validatorSignatures,
bytes memory extensionData
) internal {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
bytes32[] storage list = $.extensionList;
bytes32 validatorId = ExtensionIds.VALIDATOR_SIG;
CheckpointStorage.ExtensionConfig storage validatorCfg = $.extensions[validatorId];
if (validatorCfg.active && (validatorCfg.hooks & HOOK_BEFORE_SUBMIT) != 0) {
ICheckpointExtension(validatorCfg.module).beforeSubmit(header, validatorSignatures);
}
for (uint256 i = 0; i < list.length; i++) {
bytes32 id = list[i];
if (id == validatorId) continue;
CheckpointStorage.ExtensionConfig storage cfg = $.extensions[id];
if (!cfg.active || (cfg.hooks & HOOK_BEFORE_SUBMIT) == 0) continue;
ICheckpointExtension(cfg.module).beforeSubmit(header, extensionData);
}
}
function _runExtensions(
uint32 hook,
CheckpointStorage.CheckpointHeader memory header,
bytes memory data
) internal {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
bytes32[] storage list = $.extensionList;
for (uint256 i = 0; i < list.length; i++) {
CheckpointStorage.ExtensionConfig storage cfg = $.extensions[list[i]];
if (!cfg.active || (cfg.hooks & hook) == 0) continue;
if (hook == HOOK_BEFORE_SUBMIT) {
ICheckpointExtension(cfg.module).beforeSubmit(header, data);
} else if (hook == HOOK_AFTER_SUBMIT) {
ICheckpointExtension(cfg.module).afterSubmit(header, data);
} else if (hook == HOOK_ON_CCIP) {
ICheckpointExtension(cfg.module).onCCIPReceive(data);
}
}
}
function _verifySubmitterAttestation(
CheckpointStorage.CheckpointHeader calldata header,
bytes calldata submitterSignature
) internal view {
if (submitterSignature.length != 65) revert CheckpointErrors.SubmitterSigLen();
bytes32 digest = CheckpointEIP712.digest(address(this), block.chainid, header);
address signer = ECDSA.recover(digest, submitterSignature);
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
if ($.submitterAttestationSigner != address(0)) {
if (signer != $.submitterAttestationSigner) revert CheckpointErrors.AttestSigner();
return;
}
if (!hasRole(SUBMITTER_ROLE, signer)) revert CheckpointErrors.SubmitterRole();
}
function _extensionVerifyLeaf(
CheckpointStorage.CheckpointHeader memory h,
CheckpointLeaf.PaymentLeafV1 calldata leaf,
bytes32[] calldata proof
) internal view returns (bool) {
CheckpointStorage.CheckpointStorageStruct storage $ = CheckpointStorage.get();
bytes32[] storage list = $.extensionList;
for (uint256 i = 0; i < list.length; i++) {
CheckpointStorage.ExtensionConfig storage cfg = $.extensions[list[i]];
if (!cfg.active || (cfg.hooks & HOOK_VERIFY_LEAF) == 0) continue;
if (ICheckpointExtension(cfg.module).verifyLeaf(h, leaf, proof)) return true;
}
return false;
}
}