// SPDX-License-Identifier: MIT pragma solidity 0.8.23; // Import ONLY the V2 history anchor. It transitively imports the V1 history // anchor (and its single IAereStorageProofVerifier declaration), so this // compilation closure sees exactly one copy of that interface. It deliberately // does NOT import AereStateRootAnchor.sol, which declares a same-named interface // and would collide. import "../AereHistoryStateRootAnchorV2.sol"; /** * @dev Test-only subclass of the ORIGINAL history anchor (V1) that injects a * canonical block hash for a block number, standing in for the EIP-2935 * ring buffer without a live fork. Everything else is the production V1 * logic, including the redundant double verification in proveAgainstAnchor. */ contract MockHistoryAnchorV1 is AereHistoryStateRootAnchor { mapping(uint256 => bytes32) public injected; constructor(address verifier) AereHistoryStateRootAnchor(verifier) {} function setHistoryBlockHash(uint256 blockNumber, bytes32 h) external { injected[blockNumber] = h; } function _historyBlockHash(uint256 blockNumber) internal view override returns (bytes32) { bytes32 h = injected[blockNumber]; if (h == bytes32(0)) revert OutOfHistoryWindow(blockNumber, block.number); return h; } } /** * @dev Test-only subclass of the corrected history anchor (V2) with the same * injectable canonical block hash. proveAgainstAnchor runs the single * corrected path. */ contract MockHistoryAnchorV2 is AereHistoryStateRootAnchorV2 { mapping(uint256 => bytes32) public injected; constructor(address verifier) AereHistoryStateRootAnchorV2(verifier) {} function setHistoryBlockHash(uint256 blockNumber, bytes32 h) external { injected[blockNumber] = h; } function _historyBlockHash(uint256 blockNumber) internal view override returns (bytes32) { bytes32 h = injected[blockNumber]; if (h == bytes32(0)) revert OutOfHistoryWindow(blockNumber, block.number); return h; } } /** * @dev Counting stand-in for the LIVE AereStorageProofVerifier used to prove * how many SP1 verifications a caller triggers. * * submitProofWithExpectedRoot is the ONE verification the corrected anchor * must run: it validates, binds the committed root to the expected root, * records, and increments `submitCalls`. * * verify() reverts with RedundantVerifyCall. The live verify() is a * stateless view whose whole cost is the SP1 Groth16 pairing; the original * anchor calls it purely to read the block number, which the corrected * anchor derives locally. Making verify() revert here means any code path * that still calls it (the V1 double-verify) fails loudly, while the * corrected path (which never calls verify) is unaffected. `submitCalls` * then witnesses that exactly one verification ran. */ contract CountingStorageProofVerifier is IAereStorageProofVerifier { bool public shouldValidate = true; uint256 public submitCalls; /// @notice keccak256(publicValues) => recorded, for assertions. mapping(bytes32 => bool) public recorded; error RedundantVerifyCall(); function setValidate(bool v) external { shouldValidate = v; } function verify(bytes calldata, bytes calldata) external pure returns (uint256, bytes32, address, bytes32, bytes32) { revert RedundantVerifyCall(); } function submitProofWithExpectedRoot( bytes calldata publicValues, bytes calldata /*proof*/, bytes32 expectedStateRoot ) external returns (bytes32 value) { require(shouldValidate, "InvalidProof"); require(publicValues.length == 192, "BadPublicValuesLength"); (, , bytes32 stateRoot, , , bytes32 v) = abi.decode( publicValues, (uint256, uint256, bytes32, address, bytes32, bytes32) ); require(stateRoot == expectedStateRoot, "StateRootMismatch"); recorded[keccak256(publicValues)] = true; submitCalls += 1; return v; } }