// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /** * @title MirrorRegistry * @notice Stores Merkle commitment roots for DBIS/Alltra transaction mirroring to public mainnets. * @dev submitCommit(chainId, startBlock, endBlock, root, uri, ts) + event CommitSubmitted. * Publisher allowlist and optional rate limiting. */ contract MirrorRegistry { address public admin; bool public paused; mapping(address => bool) public publishers; uint256 public commitsCount; uint256 public constant MAX_COMMITS_PER_BLOCK = 5; uint256 public commitsThisBlock; uint256 public lastBlock; event CommitSubmitted( uint256 indexed sourceChainId, uint64 startBlock, uint64 endBlock, bytes32 root, string uri, uint64 ts ); event AdminChanged(address indexed newAdmin); event Paused(); event Unpaused(); event PublisherSet(address indexed publisher, bool allowed); modifier onlyAdmin() { require(msg.sender == admin, "only admin"); _; } modifier whenNotPaused() { require(!paused, "paused"); _; } modifier onlyPublisher() { require(publishers[msg.sender], "not publisher"); _; } constructor(address _admin) { require(_admin != address(0), "zero admin"); admin = _admin; publishers[_admin] = true; } function setAdmin(address newAdmin) external onlyAdmin { require(newAdmin != address(0), "zero admin"); admin = newAdmin; emit AdminChanged(newAdmin); } function pause() external onlyAdmin { paused = true; emit Paused(); } function unpause() external onlyAdmin { paused = false; emit Unpaused(); } function setPublisher(address publisher, bool allowed) external onlyAdmin { publishers[publisher] = allowed; emit PublisherSet(publisher, allowed); } /** * @notice Submit a Merkle commitment for a block range on source chain (138 or 651940). * @param sourceChainId Chain ID (e.g. 138, 651940). * @param startBlock Start block (inclusive). * @param endBlock End block (inclusive). * @param root Merkle root of leaves (tx_hash, block_number, receipt_root, payload_hash, sal_hash). * @param uri URI to leaf set (e.g. S3 or API). * @param ts Timestamp of commit (epoch seconds). */ function submitCommit( uint256 sourceChainId, uint64 startBlock, uint64 endBlock, bytes32 root, string calldata uri, uint64 ts ) external onlyPublisher whenNotPaused { require(startBlock <= endBlock, "invalid range"); require(root != bytes32(0), "zero root"); if (block.number != lastBlock) { lastBlock = block.number; commitsThisBlock = 0; } require(commitsThisBlock < MAX_COMMITS_PER_BLOCK, "rate limit"); commitsThisBlock++; commitsCount++; emit CommitSubmitted(sourceChainId, startBlock, endBlock, root, uri, ts); } }