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

imp!: improved client interfaces #3

Merged
merged 3 commits into from
Jul 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions src/ICS02Client.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import { Strings } from "@openzeppelin/contracts/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/contracts/access/Ownable.sol";

contract ICS02Client is IICS02Client, IICS02ClientErrors {
contract ICS02Client is IICS02Client, IICS02ClientErrors, Ownable {
/// @dev clientId => counterpartyInfo
mapping(string => CounterpartyInfo) private counterpartyInfos;
/// @dev clientId => client
mapping(string => ILightClient) private clients;
/// @dev clientType => nextClientSeq
mapping(string => uint32) private nextClientSeq;

/// @param owner_ The owner of the contract
constructor(address owner_) Ownable(owner_) { }

/// @notice Generates the next client identifier
/// @param clientType The client type
/// @return The next client identifier
Expand All @@ -28,12 +32,22 @@ contract ICS02Client is IICS02Client, IICS02ClientErrors {
return string.concat(clientType, "-", Strings.toString(seq));
}

function getCounterparty(string calldata clientId) external view returns (CounterpartyInfo memory) {
return counterpartyInfos[clientId];
function getCounterparty(string calldata clientId) public view returns (CounterpartyInfo memory) {
CounterpartyInfo memory counterpartyInfo = counterpartyInfos[clientId];
if (bytes(counterpartyInfo.clientId).length == 0) {
revert IBCClientNotFound(clientId);
}

return counterpartyInfo;
}

function getClient(string calldata clientId) external view returns (ILightClient) {
return clients[clientId];
function getClient(string calldata clientId) public view returns (ILightClient) {
ILightClient client = clients[clientId];
if (client == ILightClient(address(0))) {
revert IBCClientNotFound(clientId);
}

return client;
}

function addClient(
Expand All @@ -50,17 +64,32 @@ contract ICS02Client is IICS02Client, IICS02ClientErrors {
return clientId;
}

function migrateClient(string calldata subjectClientId, string calldata substituteClientId) external onlyOwner {
getClient(subjectClientId); // Ensure subject client exists
ILightClient substituteClient = getClient(substituteClientId);

getCounterparty(subjectClientId); // Ensure subject client's counterparty exists
CounterpartyInfo memory substituteCounterpartyInfo = getCounterparty(substituteClientId);

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

function updateClient(
string calldata clientId,
bytes calldata updateMsg
)
external
returns (ILightClient.UpdateResult)
{
return clients[clientId].updateClient(updateMsg);
return getClient(clientId).updateClient(updateMsg);
}

function submitMisbehaviour(string calldata clientId, bytes calldata misbehaviourMsg) external {
clients[clientId].misbehaviour(misbehaviourMsg);
getClient(clientId).misbehaviour(misbehaviourMsg);
}

function upgradeClient(string calldata clientId, bytes calldata upgradeMsg) external {
getClient(clientId).upgradeClient(upgradeMsg);
}
}
7 changes: 3 additions & 4 deletions src/ICS26Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors, Ree
kvPair: ILightClientMsgs.KVPair({ path: commitmentPath, value: abi.encodePacked(commitmentBz) })
});

ics02Client.getClient(msg_.packet.destChannel).verifyMembership(membershipMsg);
ics02Client.getClient(msg_.packet.destChannel).membership(membershipMsg);
if (msg_.packet.timeoutTimestamp <= block.timestamp) {
revert IBCInvalidTimeoutTimestamp(msg_.packet.timeoutTimestamp, block.timestamp);
}
Expand Down Expand Up @@ -156,7 +156,7 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors, Ree
kvPair: ILightClientMsgs.KVPair({ path: commitmentPath, value: abi.encodePacked(commitmentBz) })
});

ics02Client.getClient(msg_.packet.sourceChannel).verifyMembership(membershipMsg);
ics02Client.getClient(msg_.packet.sourceChannel).membership(membershipMsg);

app.onAcknowledgementPacket(
IIBCAppCallbacks.OnAcknowledgementPacketCallback({
Expand Down Expand Up @@ -194,8 +194,7 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors, Ree
kvPair: ILightClientMsgs.KVPair({ path: receiptPath, value: bytes("") })
});

uint32 counterpartyTimestamp =
ics02Client.getClient(msg_.packet.sourceChannel).verifyMembership(nonMembershipMsg);
uint32 counterpartyTimestamp = ics02Client.getClient(msg_.packet.sourceChannel).membership(nonMembershipMsg);
if (counterpartyTimestamp < msg_.packet.timeoutTimestamp) {
revert IBCInvalidTimeoutTimestamp(msg_.packet.timeoutTimestamp, counterpartyTimestamp);
}
Expand Down
3 changes: 3 additions & 0 deletions src/errors/IICS02ClientErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ pragma solidity >=0.8.25;
interface IICS02ClientErrors {
/// @param clientType client type
error IBCInvalidClientType(string clientType);

/// @param clientId client identifier
error IBCClientNotFound(string clientId);
}
11 changes: 11 additions & 0 deletions src/interfaces/IICS02Client.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ interface IICS02Client is IICS02ClientMsgs {
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
/// @param substituteClientId The client identifier of the substitute client
function migrateClient(string calldata subjectClientId, string calldata substituteClientId) external;

/// @notice Updates the client given the client identifier.
/// @param clientId The client identifier
/// @param updateMsg The update message
Expand All @@ -45,4 +51,9 @@ interface IICS02Client is IICS02ClientMsgs {
/// @param clientId The client identifier
/// @param misbehaviourMsg The misbehaviour message
function submitMisbehaviour(string calldata clientId, bytes calldata misbehaviourMsg) external;

/// @notice Upgrades the client with the given client identifier.
/// @param clientId The client identifier
/// @param upgradeMsg The upgrade message
function upgradeClient(string calldata clientId, bytes calldata upgradeMsg) external;
}
6 changes: 5 additions & 1 deletion src/interfaces/ILightClient.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ interface ILightClient is ILightClientMsgs {

/// @notice Querying the (non)membership of the key-value pairs
/// @return The unix timestamp of the verification height in the counterparty chain in seconds.
function verifyMembership(MsgMembership calldata msg_) external view returns (uint32);
function membership(MsgMembership calldata msg_) external view returns (uint32);

/// @notice Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected
/// @param misbehaviourMsg The misbehaviour message
function misbehaviour(bytes calldata misbehaviourMsg) external;

/// @notice Upgrading the client
/// @param upgradeMsg The upgrade message
function upgradeClient(bytes calldata upgradeMsg) external;
}