Skip to content

Commit

Permalink
feat(voyager): evm state lens modules
Browse files Browse the repository at this point in the history
  • Loading branch information
hussein-aitlahcen committed Dec 27, 2024
1 parent 2ff517d commit a20ef25
Show file tree
Hide file tree
Showing 21 changed files with 1,082 additions and 46 deletions.
93 changes: 93 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ members = [
"lib/tendermint-light-client-types",
"lib/linea-light-client-types",
"lib/movement-light-client-types",
"lib/state-lens/evm-light-client-types",

# these will all be re enabled and updated once ethereum-light-client is updated

Expand Down Expand Up @@ -114,16 +115,19 @@ members = [
"voyager/modules/client/ethereum",
"voyager/modules/client/movement",
"voyager/modules/client/tendermint",
"voyager/modules/client/state-lens/evm",

"voyager/modules/consensus/cometbls",
"voyager/modules/consensus/ethereum",
"voyager/modules/consensus/movement",
"voyager/modules/consensus/tendermint",
"voyager/modules/consensus/state-lens/evm",

"voyager/plugins/client-update/cometbls",
"voyager/plugins/client-update/ethereum",
"voyager/plugins/client-update/movement",
"voyager/plugins/client-update/tendermint",
"voyager/plugins/client-update/state-lens/evm",

"voyager/plugins/periodic-client-update",

Expand Down Expand Up @@ -204,6 +208,8 @@ scroll-rpc = { path = "lib/scroll-rpc", default-features = false }
berachain-light-client = { path = "cosmwasm/union-ibc/light-clients/light-clients/berachain-light-client", default-features = false }
berachain-light-client-types = { path = "lib/berachain-light-client-types", default-features = false }

evm-state-lens-light-client-types = { path = "lib/state-lens/evm-light-client-types", default-features = false }

tendermint-light-client = { path = "cosmwasm/union-ibc/light-clients/tendermint", default-features = false }
tendermint-light-client-types = { path = "lib/tendermint-light-client-types", default-features = false }
tendermint-verifier = { path = "lib/tendermint-verifier", default-features = false }
Expand Down
80 changes: 35 additions & 45 deletions evm/contracts/clients/EvmInCosmosClient.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import "../core/02-client/ILightClient.sol";
import "../core/24-host/IBCStore.sol";
import "../core/24-host/IBCCommitment.sol";
import "../lib/ICS23.sol";
import "../lib/Common.sol";
import "../lib/MPTVerifier.sol";

struct Header {
Expand All @@ -22,9 +21,8 @@ struct Header {

struct ClientState {
uint32 l1ClientId;
uint32 l2ChainId;
uint32 l2ClientId;
uint64 latestHeight;
uint64 l2LatestHeight;
uint16 timestampOffset;
uint16 stateRootOffset;
uint16 storageRootOffset;
Expand All @@ -44,7 +42,7 @@ library EvmInCosmosLib {
error ErrClientFrozen();
error ErrInvalidL1Proof();
error ErrInvalidInitialConsensusState();
error ErrInvalidMisbehaviour();
error ErrUnsupported();

function encode(
ConsensusState memory consensusState
Expand All @@ -69,6 +67,15 @@ library EvmInCosmosLib {
) internal pure returns (bytes32) {
return keccak256(encode(clientState));
}

function extract(
bytes calldata input,
uint16 offset
) internal pure returns (bytes32 val) {
assembly {
val := calldataload(add(input.offset, offset))
}
}
}

contract EvmInCosmosClient is
Expand All @@ -84,8 +91,6 @@ contract EvmInCosmosClient is

mapping(uint32 => ClientState) private clientStates;
mapping(uint32 => mapping(uint64 => ConsensusState)) private consensusStates;
mapping(uint32 => mapping(uint64 => ProcessedMoment)) private
processedMoments;

constructor() {
_disableInitializers();
Expand All @@ -112,20 +117,15 @@ contract EvmInCosmosClient is
assembly {
consensusState := consensusStateBytes.offset
}
if (clientState.latestHeight == 0 || consensusState.timestamp == 0) {
if (clientState.l2LatestHeight == 0 || consensusState.timestamp == 0) {
revert EvmInCosmosLib.ErrInvalidInitialConsensusState();
}
clientStates[clientId] = clientState;
consensusStates[clientId][clientState.latestHeight] = consensusState;
// Normalize to nanosecond because ibc-go recvPacket expects nanos...
processedMoments[clientId][clientState.latestHeight] = ProcessedMoment({
timestamp: block.timestamp * 1e9,
height: block.number
});
consensusStates[clientId][clientState.l2LatestHeight] = consensusState;
return ConsensusStateUpdate({
clientStateCommitment: clientState.commit(),
consensusStateCommitment: consensusState.commit(),
height: clientState.latestHeight
height: clientState.l2LatestHeight
});
}

Expand All @@ -141,7 +141,7 @@ contract EvmInCosmosClient is
assembly {
header := clientMessageBytes.offset
}
ClientState memory clientState = clientStates[clientId];
ClientState storage clientState = clientStates[clientId];
ILightClient l1Client =
IBCStore(ibcHandler).getClient(clientState.l1ClientId);
// L₂[H₂] ∈ L₁[H₁]
Expand All @@ -162,23 +162,22 @@ contract EvmInCosmosClient is
}

bytes calldata rawL2ConsensusState = header.l2ConsensusState;
uint64 timestampOffset = clientState.timestampOffset;
uint64 stateRootOffset = clientState.stateRootOffset;
uint64 storageRootOffset = clientState.storageRootOffset;
uint64 l2Timestamp;
bytes32 l2StateRoot;
bytes32 l2StorageRoot;
assembly {
l2Timestamp :=
calldataload(add(rawL2ConsensusState.offset, timestampOffset))
l2StateRoot :=
calldataload(add(rawL2ConsensusState.offset, stateRootOffset))
l2StorageRoot :=
calldataload(add(rawL2ConsensusState.offset, storageRootOffset))
}
uint64 l2Timestamp = uint64(
uint256(
EvmInCosmosLib.extract(
rawL2ConsensusState, clientState.timestampOffset
)
)
);
bytes32 l2StateRoot = EvmInCosmosLib.extract(
rawL2ConsensusState, clientState.stateRootOffset
);
bytes32 l2StorageRoot = EvmInCosmosLib.extract(
rawL2ConsensusState, clientState.storageRootOffset
);

if (header.l2Height > clientState.latestHeight) {
clientState.latestHeight = header.l2Height;
if (header.l2Height > clientState.l2LatestHeight) {
clientState.l2LatestHeight = header.l2Height;
}

// L₂[H₂] = S₂
Expand All @@ -189,12 +188,6 @@ contract EvmInCosmosClient is
consensusState.stateRoot = l2StateRoot;
consensusState.storageRoot = l2StorageRoot;

// P[H₂] = now()
ProcessedMoment storage processed =
processedMoments[clientId][header.l2Height];
processed.timestamp = block.timestamp * 1e9;
processed.height = block.number;

// commit(S₂)
return ConsensusStateUpdate({
clientStateCommitment: clientState.commit(),
Expand All @@ -207,7 +200,7 @@ contract EvmInCosmosClient is
uint32 clientId,
bytes calldata clientMessageBytes
) external override onlyIBC {
revert EvmInCosmosLib.ErrInvalidMisbehaviour();
revert EvmInCosmosLib.ErrUnsupported();
}

function verifyMembership(
Expand All @@ -227,9 +220,8 @@ contract EvmInCosmosClient is
EvmInCosmosLib.EVM_IBC_COMMITMENT_SLOT
)
);
(bool exists, bytes calldata provenValue) = MPTVerifier.verifyTrieValue(
proof, keccak256(abi.encodePacked(slot)), storageRoot
);
(bool exists, bytes calldata provenValue) =
MPTVerifier.verifyTrieValue(proof, slot, storageRoot);
return exists && keccak256(value) == keccak256(provenValue);
}

Expand All @@ -249,9 +241,7 @@ contract EvmInCosmosClient is
EvmInCosmosLib.EVM_IBC_COMMITMENT_SLOT
)
);
(bool exists, bytes calldata provenValue) = MPTVerifier.verifyTrieValue(
proof, keccak256(abi.encodePacked(slot)), storageRoot
);
(bool exists,) = MPTVerifier.verifyTrieValue(proof, slot, storageRoot);
return !exists;
}

Expand All @@ -278,7 +268,7 @@ contract EvmInCosmosClient is
function getLatestHeight(
uint32 clientId
) external view override returns (uint64) {
return clientStates[clientId].latestHeight;
return clientStates[clientId].l2LatestHeight;
}

function isFrozen(
Expand Down
1 change: 0 additions & 1 deletion evm/tests/src/02-client/CosmosInCosmosClient.t.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.27;

import "forge-std/Test.sol";
Expand Down
29 changes: 29 additions & 0 deletions evm/tests/src/02-client/EvmInCosmosClient.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pragma solidity ^0.8.27;

import "forge-std/Test.sol";
import "../core/IBCHandler.sol";
import "../core/Relay.sol";
import "../../../contracts/clients/EvmInCosmosClient.sol";
import "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol";

contract EvmInCosmosClientTest is Test {
EvmInCosmosClient client;
address admin = address(0xABCD);
address ibcHandler;

function setUp() public {
ibcHandler = address(0xC0DE);
EvmInCosmosClient implementation = new EvmInCosmosClient();
ERC1967Proxy proxy = new ERC1967Proxy(
address(implementation),
abi.encodeWithSelector(
EvmInCosmosClient.initialize.selector, ibcHandler, admin
)
);
client = EvmInCosmosClient(address(proxy));
}

function test_initialize_ok() public {
assertEq(client.owner(), admin);
}
}
17 changes: 17 additions & 0 deletions lib/state-lens/evm-light-client-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
edition = "2021"
name = "evm-state-lens-light-client-types"
version = "0.1.0"

[dependencies]
alloy = { workspace = true, features = ["sol-types"], optional = true }
protos = { workspace = true, optional = true, features = ["union+ibc+lightclients+berachain+v1"] }
serde = { workspace = true, optional = true, features = ["derive"] }
thiserror = { workspace = true }
unionlabs = { workspace = true }

[features]
default = []
ethabi = ["unionlabs/ethabi", "dep:alloy"]
proto = ["dep:protos"]
serde = ["dep:serde"]
Loading

0 comments on commit a20ef25

Please sign in to comment.