Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change counterpartyInfo to channel #100

Merged
merged 17 commits into from
Dec 2, 2024
Merged
2 changes: 1 addition & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ test-e2e testname: clean

# Install the sp1-ics07-tendermint operator for use in the e2e tests
install-operator:
cargo install --git https://github.com/cosmos/sp1-ics07-tendermint --rev {{sp1_operator_rev}} sp1-ics07-tendermint-operator --bin operator --locked
cargo +nightly install --git https://github.com/cosmos/sp1-ics07-tendermint --rev {{sp1_operator_rev}} sp1-ics07-tendermint-operator --bin operator --locked
srdtrk marked this conversation as resolved.
Show resolved Hide resolved

# Generate the fixtures for the Solidity tests using the e2e tests
generate-fixtures: clean
Expand Down
40 changes: 25 additions & 15 deletions src/ICS26Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ pragma solidity ^0.8.28;
import { IIBCApp } from "./interfaces/IIBCApp.sol";
import { IICS26Router } from "./interfaces/IICS26Router.sol";
import { IICS02Client } from "./interfaces/IICS02Client.sol";
import { ICS02Client } from "./ICS02Client.sol";
import { IICS04Channel } from "./interfaces/IICS04Channel.sol";
import { ICSCore } from "./ICSCore.sol";
import { IIBCStore } from "./interfaces/IIBCStore.sol";
import { IICS24HostErrors } from "./errors/IICS24HostErrors.sol";
import { IBCStore } from "./utils/IBCStore.sol";
Expand All @@ -16,6 +17,8 @@ import { IIBCAppCallbacks } from "./msgs/IIBCAppCallbacks.sol";
import { ICS24Host } from "./utils/ICS24Host.sol";
import { ILightClientMsgs } from "./msgs/ILightClientMsgs.sol";
import { IICS02ClientMsgs } from "./msgs/IICS02ClientMsgs.sol";
import { IICS04ChannelMsgs } from "./msgs/IICS04ChannelMsgs.sol";
import { ReentrancyGuard } from "@openzeppelin/utils/ReentrancyGuard.sol";
import { ReentrancyGuardTransient } from "@openzeppelin/utils/ReentrancyGuardTransient.sol";
import { Multicall } from "@openzeppelin/utils/Multicall.sol";

Expand All @@ -24,13 +27,17 @@ import { Multicall } from "@openzeppelin/utils/Multicall.sol";
contract ICS26Router is IICS26Router, IICS26RouterErrors, Ownable, ReentrancyGuardTransient, Multicall {
/// @dev portId => IBC Application contract
mapping(string portId => IIBCApp app) private apps;

/// @inheritdoc IICS26Router
IICS02Client public immutable ICS02_CLIENT;
/// @dev ICS04Channel contract
IICS04Channel public immutable ICS04_CHANNEL;
srdtrk marked this conversation as resolved.
Show resolved Hide resolved
/// @inheritdoc IICS26Router
IIBCStore public immutable IBC_STORE;

constructor(address owner) Ownable(owner) {
ICS02_CLIENT = new ICS02Client(owner); // using the same owner
ICS02_CLIENT = new ICSCore(owner); // using the same owner
ICS04_CHANNEL = IICS04Channel(address(ICS02_CLIENT)); // using the same contract
IBC_STORE = new IBCStore(address(this)); // using this contract as the owner
}

Expand Down Expand Up @@ -75,7 +82,7 @@ contract ICS26Router is IICS26Router, IICS26RouterErrors, Ownable, ReentrancyGua
require(msg_.payloads.length == 1, IBCMultiPayloadPacketNotSupported());
Payload calldata payload = msg_.payloads[0];

string memory counterpartyId = ICS02_CLIENT.getCounterparty(msg_.sourceChannel).clientId;
string memory counterpartyId = ICS04_CHANNEL.getChannel(msg_.sourceChannel).counterpartyId;

// TODO: validate all identifiers
require(
Expand Down Expand Up @@ -112,15 +119,17 @@ contract ICS26Router is IICS26Router, IICS26RouterErrors, Ownable, ReentrancyGua
/// @param msg_ The message for receiving packets
/// @inheritdoc IICS26Router
function recvPacket(MsgRecvPacket calldata msg_) external nonReentrant {

// TODO: Support multi-payload packets #93
require(msg_.packet.payloads.length == 1, IBCMultiPayloadPacketNotSupported());
Payload calldata payload = msg_.packet.payloads[0];

IICS02ClientMsgs.CounterpartyInfo memory cInfo = ICS02_CLIENT.getCounterparty(msg_.packet.destChannel);
IICS04ChannelMsgs.Channel memory channel = ICS04_CHANNEL.getChannel(msg_.packet.destChannel);
require(
keccak256(bytes(cInfo.clientId)) == keccak256(bytes(msg_.packet.sourceChannel)),
IBCInvalidCounterparty(cInfo.clientId, msg_.packet.sourceChannel)
keccak256(bytes(channel.counterpartyId)) == keccak256(bytes(msg_.packet.sourceChannel)),
IBCInvalidCounterparty(channel.counterpartyId, msg_.packet.sourceChannel)
);

require(
msg_.packet.timeoutTimestamp > block.timestamp,
IBCInvalidTimeoutTimestamp(msg_.packet.timeoutTimestamp, block.timestamp)
Expand All @@ -133,7 +142,7 @@ contract ICS26Router is IICS26Router, IICS26RouterErrors, Ownable, ReentrancyGua
ILightClientMsgs.MsgMembership memory membershipMsg = ILightClientMsgs.MsgMembership({
proof: msg_.proofCommitment,
proofHeight: msg_.proofHeight,
path: ICS24Host.prefixedPath(cInfo.merklePrefix, commitmentPath),
path: ICS24Host.prefixedPath(channel.merklePrefix, commitmentPath),
value: abi.encodePacked(commitmentBz)
});

Expand Down Expand Up @@ -171,10 +180,10 @@ contract ICS26Router is IICS26Router, IICS26RouterErrors, Ownable, ReentrancyGua
require(msg_.packet.payloads.length == 1, IBCMultiPayloadPacketNotSupported());
Payload calldata payload = msg_.packet.payloads[0];

IICS02ClientMsgs.CounterpartyInfo memory cInfo = ICS02_CLIENT.getCounterparty(msg_.packet.sourceChannel);
IICS04ChannelMsgs.Channel memory channel = ICS04_CHANNEL.getChannel(msg_.packet.sourceChannel);
require(
keccak256(bytes(cInfo.clientId)) == keccak256(bytes(msg_.packet.destChannel)),
IBCInvalidCounterparty(cInfo.clientId, msg_.packet.destChannel)
keccak256(bytes(channel.counterpartyId)) == keccak256(bytes(msg_.packet.destChannel)),
IBCInvalidCounterparty(channel.counterpartyId, msg_.packet.destChannel)
);

bytes memory commitmentPath =
Expand All @@ -187,7 +196,7 @@ contract ICS26Router is IICS26Router, IICS26RouterErrors, Ownable, ReentrancyGua
ILightClientMsgs.MsgMembership memory membershipMsg = ILightClientMsgs.MsgMembership({
proof: msg_.proofAcked,
proofHeight: msg_.proofHeight,
path: ICS24Host.prefixedPath(cInfo.merklePrefix, commitmentPath),
path: ICS24Host.prefixedPath(channel.merklePrefix, commitmentPath),
value: abi.encodePacked(commitmentBz)
});

Expand Down Expand Up @@ -221,22 +230,23 @@ contract ICS26Router is IICS26Router, IICS26RouterErrors, Ownable, ReentrancyGua
/// @param msg_ The message for timing out packets
/// @inheritdoc IICS26Router
function timeoutPacket(MsgTimeoutPacket calldata msg_) external nonReentrant {

// TODO: Support multi-payload packets #93
require(msg_.packet.payloads.length == 1, IBCMultiPayloadPacketNotSupported());
Payload calldata payload = msg_.packet.payloads[0];

IICS02ClientMsgs.CounterpartyInfo memory cInfo = ICS02_CLIENT.getCounterparty(msg_.packet.sourceChannel);
IICS04ChannelMsgs.Channel memory channel = ICS04_CHANNEL.getChannel(msg_.packet.sourceChannel);
require(
keccak256(bytes(cInfo.clientId)) == keccak256(bytes(msg_.packet.destChannel)),
IBCInvalidCounterparty(cInfo.clientId, msg_.packet.destChannel)
keccak256(bytes(channel.counterpartyId)) == keccak256(bytes(msg_.packet.destChannel)),
IBCInvalidCounterparty(channel.counterpartyId, msg_.packet.destChannel)
);

bytes memory receiptPath =
ICS24Host.packetReceiptCommitmentPathCalldata(msg_.packet.destChannel, msg_.packet.sequence);
ILightClientMsgs.MsgMembership memory nonMembershipMsg = ILightClientMsgs.MsgMembership({
proof: msg_.proofTimeout,
proofHeight: msg_.proofHeight,
path: ICS24Host.prefixedPath(cInfo.merklePrefix, receiptPath),
path: ICS24Host.prefixedPath(channel.merklePrefix, receiptPath),
value: bytes("")
});

Expand Down
35 changes: 19 additions & 16 deletions src/ICS02Client.sol → src/ICSCore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
pragma solidity ^0.8.28;

import { IICS02Client } from "./interfaces/IICS02Client.sol";
import { IICS04Channel } from "./interfaces/IICS04Channel.sol";
import { Strings } from "@openzeppelin/utils/Strings.sol";
import { IBCIdentifiers } from "./utils/IBCIdentifiers.sol";
import { ILightClient } from "./interfaces/ILightClient.sol";
import { IICS02ClientErrors } from "./errors/IICS02ClientErrors.sol";
import { Ownable } from "@openzeppelin/access/Ownable.sol";

contract ICS02Client is IICS02Client, IICS02ClientErrors, Ownable {
/// @dev clientId => counterpartyInfo
mapping(string clientId => CounterpartyInfo info) private counterpartyInfos;
contract ICSCore is IICS02Client, IICS04Channel, IICS02ClientErrors, Ownable {
/// @dev channelId => counterpartyInfo
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
mapping(string channelId => Channel channel) private channels;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CounterpartyInfo feels more descriptive ngl 😅. Because the same light client cannot have multiple channels on top in our implementation I think

/// @dev clientId => light client contract
mapping(string clientId => ILightClient client) private clients;
/// @dev clientType => nextClientSeq
Expand All @@ -30,12 +31,12 @@ contract ICS02Client is IICS02Client, IICS02ClientErrors, Ownable {
return string.concat(clientType, "-", Strings.toString(seq));
}

/// @inheritdoc IICS02Client
function getCounterparty(string calldata clientId) public view returns (CounterpartyInfo memory) {
CounterpartyInfo memory counterpartyInfo = counterpartyInfos[clientId];
require(bytes(counterpartyInfo.clientId).length != 0, IBCCounterpartyClientNotFound(clientId));
/// @inheritdoc IICS04Channel
function getChannel(string calldata channelId) public view returns (Channel memory) {
Channel memory channel = channels[channelId];
require(bytes(channel.counterpartyId).length != 0, IBCCounterpartyClientNotFound(channelId));

return counterpartyInfo;
return channel;
}

/// @inheritdoc IICS02Client
Expand All @@ -46,20 +47,22 @@ contract ICS02Client is IICS02Client, IICS02ClientErrors, Ownable {
return client;
}

/// @inheritdoc IICS02Client
function addClient(
/// @inheritdoc IICS04Channel
function addChannel(
string calldata clientType,
CounterpartyInfo calldata counterpartyInfo,
Channel calldata channel,
address client
)
external
returns (string memory)
{
string memory clientId = getNextClientId(clientType);
clients[clientId] = ILightClient(client);
counterpartyInfos[clientId] = counterpartyInfo;
// use the same identifier for channelId and clientId
// for Solidity implementation
channels[clientId] = channel;

emit ICS02ClientAdded(clientId, counterpartyInfo);
emit ICS04ChannelAdded(clientId, channel);

return clientId;
}
Expand All @@ -69,10 +72,10 @@ contract ICS02Client is IICS02Client, IICS02ClientErrors, Ownable {
getClient(subjectClientId); // Ensure subject client exists
ILightClient substituteClient = getClient(substituteClientId);

getCounterparty(subjectClientId); // Ensure subject client's counterparty exists
CounterpartyInfo memory substituteCounterpartyInfo = getCounterparty(substituteClientId);
getChannel(subjectClientId); // Ensure channel exists for this clientId
Channel memory substituteChannel = getChannel(substituteClientId);

counterpartyInfos[subjectClientId] = substituteCounterpartyInfo;
channels[subjectClientId] = substituteChannel;
clients[subjectClientId] = substituteClient;
}

Expand Down
23 changes: 0 additions & 23 deletions src/interfaces/IICS02Client.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,11 @@ import { ILightClient } from "./ILightClient.sol";
/// @title ICS02 Light Client Router Interface
/// @notice IICS02Client is an interface for the IBC Eureka client router
interface IICS02Client is IICS02ClientMsgs {
/// @notice Emitted when a new client is added to the client router.
/// @param clientId The newly created client identifier
/// @param counterpartyInfo The counterparty client information, if provided
event ICS02ClientAdded(string clientId, CounterpartyInfo counterpartyInfo);

/// @notice Returns the counterparty client information given the client identifier.
/// @param clientId The client identifier
/// @return The counterparty client information
function getCounterparty(string calldata clientId) external view returns (CounterpartyInfo memory);

/// @notice Returns the address of the client contract given the client identifier.
/// @param clientId The client identifier
/// @return The address of the client contract
function getClient(string calldata clientId) external view returns (ILightClient);

/// @notice Adds a client to the client router.
/// @param clientType The client type, e.g., "07-tendermint".
/// @param counterpartyInfo The counterparty client information
/// @param client The address of the client contract
/// @return The client identifier
function addClient(
string calldata clientType,
CounterpartyInfo calldata counterpartyInfo,
address client
)
external
returns (string memory);

/// @notice Migrate the underlying client of the subject client to the substitute client.
/// @dev This is a privilaged operation, only the owner of ICS02Client can call this function.
/// @param subjectClientId The client identifier of the subject client
Expand Down
31 changes: 31 additions & 0 deletions src/interfaces/IICS04Channel.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import { IICS04ChannelMsgs } from "../msgs/IICS04ChannelMsgs.sol";

/// @title ICS04 Channel Interface
/// @notice IICS04CHANNEL is an interface for the IBC Eureka channel router
interface IICS04Channel is IICS04ChannelMsgs {
/// @notice Emitted when a new channel is added to the channel router.
/// @param channelId The newly created channel identifier. NOTE: In this implementation, the channelId is the client identifier.
/// @param channel The counterparty client information, if provided
event ICS04ChannelAdded(string channelId, Channel channel);

/// @notice Returns the channel given the channel identifier.
/// @param channelId The channel identifier
/// @return channel
function getChannel(string calldata channelId) external view returns (Channel memory);

/// @notice Adds a channel to the channel router.
/// @param clientType The client type, e.g., "07-tendermint".
/// @param channel The channel information
/// @param client The address of the client contract
/// @return The channel identifier NOTE: This is the same as the client identifier
function addChannel(
string calldata clientType,
Channel calldata channel,
address client
)
external
returns (string memory);
}
10 changes: 0 additions & 10 deletions src/msgs/IICS02ClientMsgs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@
pragma solidity ^0.8.28;

interface IICS02ClientMsgs {
/// @notice Counterparty client information.
/// @custom:spec
/// https://github.com/cosmos/ibc/blob/67fe813f7e4ec603a7c5dec35bc654f3b012afda/spec/micro/README.md?plain=1#L91
/// @param clientId The client identifier from the counterparty chain.
/// @param merklePrefix The counterparty chain's merkle prefix.
struct CounterpartyInfo {
string clientId;
bytes[] merklePrefix;
}

/// @notice Height of the counterparty chain
/// @param revisionNumber The revision number of the counterparty chain
/// @param revisionHeight The height of the counterparty chain
Expand Down
14 changes: 14 additions & 0 deletions src/msgs/IICS04ChannelMsgs.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

interface IICS04ChannelMsgs {
/// @notice Channel information.
/// @custom:spec
/// https://github.com/cosmos/ibc/blob/67fe813f7e4ec603a7c5dec35bc654f3b012afda/spec/micro/README.md?plain=1#L91
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The link should be updated?

/// @param counterpartyId The counterparty channel identifier from the counterparty chain.
/// @param merklePrefix The counterparty chain's merkle prefix.
struct Channel {
string counterpartyId;
bytes[] merklePrefix;
}
}
43 changes: 0 additions & 43 deletions test/ICS02ClientTest.t.sol

This file was deleted.

9 changes: 5 additions & 4 deletions test/ICS26RouterTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity ^0.8.28;

import { Test } from "forge-std/Test.sol";
import { IICS02ClientMsgs } from "../src/msgs/IICS02ClientMsgs.sol";
import { IICS04ChannelMsgs } from "../src/msgs/IICS04ChannelMsgs.sol";
import { ICS26Router } from "../src/ICS26Router.sol";
import { IICS26Router } from "../src/interfaces/IICS26Router.sol";
import { IICS26RouterMsgs } from "../src/msgs/IICS26RouterMsgs.sol";
Expand Down Expand Up @@ -45,10 +46,10 @@ contract ICS26RouterTest is Test {
}

function test_RecvPacketWithFailedMembershipVerification() public {
string memory counterpartyClientID = "42-dummy-01";
string memory counterpartyID = "42-dummy-01";
DummyLightClient lightClient = new DummyLightClient(ILightClientMsgs.UpdateResult.Update, 0, true);
string memory clientIdentifier = ics26Router.ICS02_CLIENT().addClient(
"07-tendermint", IICS02ClientMsgs.CounterpartyInfo(counterpartyClientID, merklePrefix), address(lightClient)
string memory clientIdentifier = ics26Router.ICS04_CHANNEL().addChannel(
"07-tendermint", IICS04ChannelMsgs.Channel(counterpartyID, merklePrefix), address(lightClient)
);

ICS20Transfer ics20Transfer = new ICS20Transfer(address(ics26Router));
Expand All @@ -64,7 +65,7 @@ contract ICS26RouterTest is Test {
});
IICS26RouterMsgs.Packet memory packet = IICS26RouterMsgs.Packet({
sequence: 1,
sourceChannel: counterpartyClientID,
sourceChannel: counterpartyID,
destChannel: clientIdentifier,
timeoutTimestamp: uint64(block.timestamp + 1000),
payloads: payloads
Expand Down
Loading
Loading