Skip to content

Commit 69984fc

Browse files
committed
refactor: rename EthereumISM to EvmISM and introduce IEvmLightClient interface
1 parent 40f9e52 commit 69984fc

7 files changed

Lines changed: 166 additions & 271 deletions

File tree

contracts/script/Deploy.s.sol

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ pragma solidity 0.8.30;
44
import {Script, console} from "forge-std/src/Script.sol";
55
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
66
import {EthereumLightClient} from "../src/light-client/EthereumLightClient.sol";
7-
import {EthereumISM} from "../src/ism/EthereumISM.sol";
7+
import {EvmISM} from "../src/ism/EvmISM.sol";
88

99
/**
1010
* @title Deploy
11-
* @notice Deploys the EthereumLightClient and EthereumISM behind UUPS proxies.
11+
* @notice Deploys the EthereumLightClient and EvmISM behind UUPS proxies.
1212
*
1313
* @dev Required environment variables:
1414
* ETHEREUM_LIGHT_CLIENT_VK - SP1 verification key for the Helios circuit
@@ -77,13 +77,13 @@ contract Deploy is Script {
7777

7878
// ============ ISM ============
7979

80-
console.log("=== Deploying EthereumISM ===");
80+
console.log("=== Deploying EvmISM ===");
8181

82-
EthereumISM ismImpl = new EthereumISM();
82+
EvmISM ismImpl = new EvmISM();
8383
console.log(" Implementation:", address(ismImpl));
8484

8585
ERC1967Proxy ismProxy = new ERC1967Proxy(
86-
address(ismImpl), abi.encodeCall(EthereumISM.initialize, (merkleVk, sp1Verifier, address(lcProxy)))
86+
address(ismImpl), abi.encodeCall(EvmISM.initialize, (merkleVk, sp1Verifier, address(lcProxy)))
8787
);
8888
console.log(" Proxy:", address(ismProxy));
8989

@@ -98,9 +98,9 @@ contract Deploy is Script {
9898
require(lc.headers(initialSlot) == initialHeader, "header mismatch");
9999
require(lc.stateRoots(initialBlockNumber) == initialStateRoot, "stateRoot mismatch");
100100

101-
EthereumISM ism = EthereumISM(address(ismProxy));
101+
EvmISM ism = EvmISM(address(ismProxy));
102102
require(ism.programVk() == merkleVk, "ism programVk mismatch");
103-
require(address(ism.ethereumLightClient()) == address(lcProxy), "light client address mismatch");
103+
require(address(ism.lightClient()) == address(lcProxy), "light client address mismatch");
104104
require(address(ism.verifier()) == sp1Verifier, "ism verifier mismatch");
105105

106106
console.log("=== Deployment verified successfully ===");

contracts/src/interfaces/IEthereumLightClient.sol

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity >=0.8.20;
33

44
import {IVersioned} from "./IVersioned.sol";
55
import {IVkUpdatable} from "./IVkUpdatable.sol";
6+
import {IEvmLightClient} from "./IEvmLightClient.sol";
67

78
/**
89
* @title IEthereumLightClient
@@ -11,7 +12,7 @@ import {IVkUpdatable} from "./IVkUpdatable.sol";
1112
* proofs. It maintains a chain of finalized headers verified through sync committee signatures and enables
1213
* trustless cross-chain verification of Ethereum state.
1314
*/
14-
interface IEthereumLightClient is IVersioned, IVkUpdatable {
15+
interface IEthereumLightClient is IEvmLightClient, IVersioned, IVkUpdatable {
1516
/// @notice Thrown when an invalid address (e.g., zero address) is provided.
1617
error InvalidAddress();
1718

@@ -101,13 +102,6 @@ interface IEthereumLightClient is IVersioned, IVkUpdatable {
101102
*/
102103
function headers(uint256 slot) external view returns (bytes32);
103104

104-
/**
105-
* @notice Returns the execution state root for a given block number.
106-
* @param blockNumber The execution layer block number.
107-
* @return The state root hash, or zero if not set.
108-
*/
109-
function stateRoots(uint256 blockNumber) external view returns (bytes32);
110-
111105
/**
112106
* @notice Returns the sync committee hash for a given period.
113107
* @param period The sync committee period index.
@@ -121,18 +115,6 @@ interface IEthereumLightClient is IVersioned, IVkUpdatable {
121115
*/
122116
function latestSlot() external view returns (uint256);
123117

124-
/**
125-
* @notice Returns the execution state root for the latest finalized block.
126-
* @return The state root hash that can be used for verifying storage proofs.
127-
*/
128-
function latestStateRoot() external view returns (bytes32);
129-
130-
/**
131-
* @notice Returns the latest finalized execution block number.
132-
* @return The most recent execution block number with a verified state root.
133-
*/
134-
function latestBlockNumber() external view returns (uint256);
135-
136118
/**
137119
* @notice Calculates the sync committee period for a given slot.
138120
* @dev Each period spans 8192 slots (~27 hours). The sync committee rotates at period boundaries.
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import {IVersioned} from "./IVersioned.sol";
77
import {IVkUpdatable} from "./IVkUpdatable.sol";
88

99
/**
10-
* @title IEthereumISM
10+
* @title IEvmISM
1111
* @notice Interface for a Hyperlane Interchain Security Module that verifies messages using SP1 ZK proofs
12-
* of Ethereum's Merkle tree hook roots.
12+
* of Merkle tree hook roots from EVM chains.
1313
* @dev This ISM verifies messages in two steps:
14-
* 1. Accept SP1 proofs of Ethereum's Merkle tree hook roots via update()
14+
* 1. Accept SP1 proofs of the source chain's Merkle tree hook roots via update()
1515
* 2. Validate Hyperlane messages against these roots via verify()
1616
*/
17-
interface IEthereumISM is IInterchainSecurityModule, IVersioned, IVkUpdatable {
17+
interface IEvmISM is IInterchainSecurityModule, IVersioned, IVkUpdatable {
1818
/// @notice Thrown when an invalid address (e.g., zero address) is provided.
1919
error InvalidAddress();
2020

@@ -24,17 +24,17 @@ interface IEthereumISM is IInterchainSecurityModule, IVersioned, IVkUpdatable {
2424
/**
2525
* @notice Emitted when a new Merkle tree root is verified and added to the valid roots set.
2626
* @param root The Merkle tree hook root that was verified.
27-
* @param blockNumber The Ethereum block number at which the root was proven.
28-
* @param stateRoot The Ethereum execution layer state root at the given block.
27+
* @param blockNumber The source chain block number at which the root was proven.
28+
* @param stateRoot The source chain execution state root at the given block.
2929
*/
3030
event Updated(bytes32 root, uint64 blockNumber, bytes32 stateRoot);
3131

3232
/**
3333
* @notice Output structure from the Merkle tree verification circuit.
34-
* @dev This struct contains the data needed to validate a Merkle tree root against Ethereum state.
34+
* @dev This struct contains the data needed to validate a Merkle tree root against the source chain's state.
3535
* @param root The Merkle tree hook root from the Hyperlane contract.
36-
* @param stateRoot The Ethereum execution layer state root at the given block.
37-
* @param blockNumber The Ethereum block number where the Merkle root was proven.
36+
* @param stateRoot The source chain execution state root at the given block.
37+
* @param blockNumber The source chain block number where the Merkle root was proven.
3838
*/
3939
struct CircuitOutput {
4040
bytes32 root;
@@ -56,7 +56,7 @@ interface IEthereumISM is IInterchainSecurityModule, IVersioned, IVkUpdatable {
5656

5757
/**
5858
* @notice Updates the set of valid Merkle roots by verifying an SP1 proof.
59-
* @dev Verifies a ZK proof that attests to a valid Merkle tree hook root from Ethereum,
59+
* @dev Verifies a ZK proof that attests to a valid Merkle tree hook root from the source EVM chain,
6060
* validates the state root against the light client, and adds the root to the valid set.
6161
* @param proof The SP1 proof bytes generated by the Merkle tree circuit.
6262
* @param publicValues The ABI-encoded CircuitOutput containing the proof data.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity >=0.8.20;
3+
4+
/**
5+
* @title IEvmLightClient
6+
* @notice Minimal interface for light clients that provide verified EVM execution state roots.
7+
* @dev Any chain-specific light client (Ethereum, Noble, etc.) should implement this.
8+
*/
9+
interface IEvmLightClient {
10+
/**
11+
* @notice Returns the execution state root for a given block number.
12+
* @param blockNumber The execution layer block number.
13+
* @return The state root hash, or zero if not set.
14+
*/
15+
function stateRoots(uint256 blockNumber) external view returns (bytes32);
16+
17+
/**
18+
* @notice Returns the execution state root for the latest finalized block.
19+
* @return The state root hash that can be used for verifying storage proofs.
20+
*/
21+
function latestStateRoot() external view returns (bytes32);
22+
23+
/**
24+
* @notice Returns the latest finalized execution block number.
25+
* @return The most recent execution block number with a verified state root.
26+
*/
27+
function latestBlockNumber() external view returns (uint256);
28+
}
Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,30 @@ import {OwnableUpgradeable} from "@openzeppelin-contracts-upgradeable/access/Own
55
import {UUPSUpgradeable} from "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
66
import {PausableUpgradeable} from "@openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
77
import {ISP1Verifier} from "succinctlabs-sp1-contracts/src/ISP1Verifier.sol";
8-
import {IEthereumISM} from "../interfaces/IEthereumISM.sol";
9-
import {IEthereumLightClient} from "../interfaces/IEthereumLightClient.sol";
8+
import {IEvmISM} from "../interfaces/IEvmISM.sol";
9+
import {IEvmLightClient} from "../interfaces/IEvmLightClient.sol";
1010
import {IInterchainSecurityModule} from "hyperlane/interfaces/IInterchainSecurityModule.sol";
1111
import {Message} from "hyperlane/libs/Message.sol";
1212
import {MerkleLib} from "hyperlane/libs/Merkle.sol";
1313

1414
/**
15-
* @title EthereumISM
15+
* @title EvmISM
1616
* @author NASD Inc.
1717
* @notice Hyperlane Interchain Security Module that uses SP1 zk proofs to verify Hyperlane
18-
* Merkle tree hook roots from the Ethereum blockchain
19-
* @dev This EthereumISM verifies messages by:
20-
* 1. Accepting SP1 proofs of Ethereum's Merkle tree hook roots via update()
18+
* Merkle tree hook roots from an EVM source chain.
19+
* @dev This ISM verifies messages by:
20+
* 1. Accepting SP1 proofs of the source chain's Merkle tree hook roots via update()
2121
* 2. Validating Hyperlane messages against these roots via verify(). This will be called by the Hyperlane mailbox.
2222
*/
23-
contract EthereumISM is OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable, IEthereumISM {
23+
contract EvmISM is OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable, IEvmISM {
2424
/// @notice The verification key for the EVM Hyperlane Merkle Tree SP1 program circuit
2525
bytes32 public programVk;
2626

2727
/// @notice The SP1 verifier contract used to verify the proofs
2828
ISP1Verifier public verifier;
2929

30-
/// @notice The Ethereum light client contract for validating app hashes
31-
IEthereumLightClient public ethereumLightClient;
30+
/// @notice The light client contract for validating source chain state roots
31+
IEvmLightClient public lightClient;
3232

3333
/// @notice Mapping of verified Merkle tree roots that can be used for message verification
3434
/// @dev A root is added when an SP1 proof is successfully verified via update()
@@ -43,25 +43,22 @@ contract EthereumISM is OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable
4343
}
4444

4545
/**
46-
* @notice Initializes the EthereumISM contract with required addresses and verification key
46+
* @notice Initializes the EvmISM contract with required addresses and verification key
4747
* @dev Can only be called once due to initializer modifier
4848
* @param _programVk The SP1 program verification key for the Merkle tree circuit
4949
* @param verifierAddress Address of the SP1 verifier contract
50-
* @param ethereumLightClientAddress Address of the Ethereum light client contract
50+
* @param lightClientAddress Address of the EVM light client contract
5151
*/
52-
function initialize(bytes32 _programVk, address verifierAddress, address ethereumLightClientAddress)
53-
public
54-
initializer
55-
{
52+
function initialize(bytes32 _programVk, address verifierAddress, address lightClientAddress) public initializer {
5653
__Ownable_init(msg.sender);
5754
__Pausable_init();
5855

5956
programVk = _programVk;
6057
require(verifierAddress != address(0), InvalidAddress());
6158
verifier = ISP1Verifier(verifierAddress);
6259

63-
require(ethereumLightClientAddress != address(0), InvalidAddress());
64-
ethereumLightClient = IEthereumLightClient(ethereumLightClientAddress);
60+
require(lightClientAddress != address(0), InvalidAddress());
61+
lightClient = IEvmLightClient(lightClientAddress);
6562
}
6663

6764
/**
@@ -71,14 +68,14 @@ contract EthereumISM is OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable
7168
*/
7269
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
7370

74-
/// @inheritdoc IEthereumISM
71+
/// @inheritdoc IEvmISM
7572
function update(bytes calldata proof, bytes calldata publicValues) external override whenNotPaused {
7673
verifier.verifyProof(programVk, publicValues, proof);
7774

7875
CircuitOutput memory output = abi.decode(publicValues, (CircuitOutput));
7976

80-
// Validate that the state root matches the Ethereum light client's state at this block number
81-
bytes32 expectedStateRoot = ethereumLightClient.stateRoots(output.blockNumber);
77+
// Validate that the state root matches the light client's state at this block number
78+
bytes32 expectedStateRoot = lightClient.stateRoots(output.blockNumber);
8279
require(expectedStateRoot != bytes32(0) && output.stateRoot == expectedStateRoot, InvalidStateRoot());
8380

8481
// Mark this Merkle hook root as valid for message verification
@@ -87,7 +84,7 @@ contract EthereumISM is OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable
8784
emit Updated(output.root, output.blockNumber, output.stateRoot);
8885
}
8986

90-
/// @inheritdoc IEthereumISM
87+
/// @inheritdoc IEvmISM
9188
function verify(bytes calldata _metadata, bytes calldata _message)
9289
external
9390
view
@@ -143,7 +140,7 @@ contract EthereumISM is OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable
143140
}
144141

145142
/**
146-
* @notice Returns the version of the EthereumISM contract
143+
* @notice Returns the version of the EvmISM contract
147144
* @return A string representing the semantic version
148145
*/
149146
function version() external pure override returns (string memory) {

contracts/src/light-client/EthereumLightClient.sol

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,10 @@ contract EthereumLightClient is OwnableUpgradeable, UUPSUpgradeable, PausableUpg
199199
*/
200200
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
201201

202-
/// @inheritdoc IEthereumLightClient
202+
/**
203+
* @notice Returns the execution state root for the latest finalized block.
204+
* @return The state root hash that can be used for verifying storage proofs.
205+
*/
203206
function latestStateRoot() public view returns (bytes32) {
204207
return stateRoots[latestBlockNumber];
205208
}

0 commit comments

Comments
 (0)