Skip to content

Commit

Permalink
feat: implemented recv_packet
Browse files Browse the repository at this point in the history
  • Loading branch information
srdtrk committed Jul 23, 2024
1 parent 794605c commit 8955434
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 67 deletions.
17 changes: 15 additions & 2 deletions src/ICS02Client.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,27 @@ contract ICS02Client is IICS02Client, IICS02ClientErrors {
return clients[clientId];
}

function addClient(string calldata clientType, CounterpartyInfo calldata counterpartyInfo, address client) external returns (string memory) {
function addClient(
string calldata clientType,
CounterpartyInfo calldata counterpartyInfo,
address client
)
external
returns (string memory)
{
string memory clientId = getNextClientId(clientType);
clients[clientId] = ILightClient(client);
counterpartyInfos[clientId] = counterpartyInfo;
return clientId;
}

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

Expand Down
52 changes: 40 additions & 12 deletions src/ICS26Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { IBCIdentifiers } from "./utils/IBCIdentifiers.sol";
import { IIBCAppCallbacks } from "./msgs/IIBCAppCallbacks.sol";
import { ICS24Host } from "./utils/ICS24Host.sol";
import { ILightClientMsgs } from "./msgs/ILightClientMsgs.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

/// @title IBC Eureka Router
/// @notice ICS26Router is the router for the IBC Eureka protocol
contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors {
contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors, ReentrancyGuard {
mapping(string => IIBCApp) private apps;
IICS02Client private ics02Client;

Expand Down Expand Up @@ -55,14 +57,12 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors {
/// @notice Sends a packet
/// @param msg_ The message for sending packets
/// @return The sequence number of the packet
function sendPacket(MsgSendPacket calldata msg_) external returns (uint32) {
IIBCApp app = apps[msg_.sourcePort];

function sendPacket(MsgSendPacket calldata msg_) external nonReentrant returns (uint32) {
string memory counterpartyId = ics02Client.getCounterparty(msg_.sourcePort).clientId;

// TODO: validate all identifiers
if (msg_.timeoutTimestamp <= block.timestamp) {
revert IBCInvalidTimeoutTimestamp(msg_.timeoutTimestamp);
revert IBCInvalidTimeoutTimestamp(msg_.timeoutTimestamp, block.timestamp);
}

uint32 sequence = IBCStore.nextSequenceSend(msg_.sourcePort, msg_.sourceChannel);
Expand All @@ -81,7 +81,7 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors {
IIBCAppCallbacks.OnSendPacketCallback memory sendPacketCallback =
IIBCAppCallbacks.OnSendPacketCallback({ packet: packet, sender: msg.sender });

app.onSendPacket(sendPacketCallback); // TODO: do not allow reentrancy
apps[msg_.sourcePort].onSendPacket(sendPacketCallback);

IBCStore.commitPacket(packet);

Expand All @@ -91,7 +91,7 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors {

/// @notice Receives a packet
/// @param msg_ The message for receiving packets
function recvPacket(MsgRecvPacket calldata msg_) external {
function recvPacket(MsgRecvPacket calldata msg_) external nonReentrant {
// TODO: implement
IIBCApp app = apps[msg_.packet.destPort];

Expand All @@ -101,24 +101,52 @@ contract ICS26Router is IICS26Router, IBCStore, Ownable, IICS26RouterErrors {
}

bytes memory commitmentPath = ICS24Host.packetCommitmentPathCalldata(
msg_.packet.sourcePort,
msg_.packet.sourceChannel,
msg_.packet.sequence
msg_.packet.sourcePort, msg_.packet.sourceChannel, msg_.packet.sequence
);
bytes32 commitmentBz = ICS24Host.packetCommitmentBytes32(msg_.packet);

ILightClientMsgs.MsgMembership memory membershipMsg = ILightClientMsgs.MsgMembership({
proof: msg_.proofCommitment,
proofHeight: msg_.proofHeight,
kvPair: ILightClientMsgs.KVPair({ path: commitmentPath, value: abi.encodePacked(commitmentBz) })
});

uint32 proofTimestamp = ics02Client.getClient(msg_.packet.destChannel).verifyMembership(membershipMsg);
if (msg_.packet.timeoutTimestamp <= proofTimestamp) {
revert IBCInvalidTimeoutTimestamp(proofTimestamp, msg_.packet.timeoutTimestamp);
}
if (msg_.packet.timeoutTimestamp <= block.timestamp) {
revert IBCInvalidTimeoutTimestamp(msg_.packet.timeoutTimestamp, block.timestamp);
}

bytes memory ack =
app.onRecvPacket(IIBCAppCallbacks.OnRecvPacketCallback({ packet: msg_.packet, relayer: msg.sender }));
if (ack.length == 0) {
revert IBCAsyncAcknowledgementNotSupported();
}

writeAcknowledgement(msg_.packet, ack);

emit RecvPacket(msg_.packet);
}

/// @notice Acknowledges a packet
/// @param msg_ The message for acknowledging packets
function ackPacket(MsgAckPacket calldata msg_) external {
function ackPacket(MsgAckPacket calldata msg_) external nonReentrant {
// TODO: implement
// IIBCApp app = IIBCApp(apps[msg.packet.sourcePort]);
}

/// @notice Timeouts a packet
/// @param msg_ The message for timing out packets
function timeoutPacket(MsgTimeoutPacket calldata msg_) external {
function timeoutPacket(MsgTimeoutPacket calldata msg_) external nonReentrant {
// TODO: implement
// IIBCApp app = IIBCApp(apps[msg.packet.sourcePort]);
}

/// @notice Writes a packet acknowledgement and emits an event
function writeAcknowledgement(Packet calldata packet, bytes memory ack) private {
IBCStore.commitPacketAcknowledgement(packet, ack);
emit WriteAcknowledgement(packet, ack);
}
}
4 changes: 3 additions & 1 deletion src/errors/IICS26RouterErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ interface IICS26RouterErrors {
error IBCInvalidPortIdentifier(string portId);

/// @param timeoutTimestamp timeout timestamp in seconds
error IBCInvalidTimeoutTimestamp(uint32 timeoutTimestamp);
error IBCInvalidTimeoutTimestamp(uint256 timeoutTimestamp, uint256 comparedTimestamp);

/// @param expected expected counterparty identifier
/// @param actual actual counterparty identifier
error IBCInvalidCounterparty(string expected, string actual);

error IBCAsyncAcknowledgementNotSupported();
}
8 changes: 7 additions & 1 deletion src/interfaces/IICS02Client.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ interface IICS02Client is IICS02ClientMsgs {
/// @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);
function addClient(
string calldata clientType,
CounterpartyInfo calldata counterpartyInfo,
address client
)
external
returns (string memory);

/// @notice Updates the client given the client identifier.
/// @param clientId The client identifier
Expand Down
63 changes: 12 additions & 51 deletions test/IbcIdentifiers.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,68 +15,29 @@ contract IBCIdentifiersTest is Test {
// The following test cases are based on the test cases of ibc-go:
// https://github.com/cosmos/ibc-go/blob/e443a88e0f2c84c131c5a1de47945a5733ff9c91/modules/core/24-host/validate_test.go#L57
ValidatePortIdentifierTestCase[] memory testCases = new ValidatePortIdentifierTestCase[](12);
testCases[0] = ValidatePortIdentifierTestCase({
m: "valid lowercase",
id: "transfer",
expPass: true
});
testCases[1] = ValidatePortIdentifierTestCase({
m: "valid id special chars",
id: "._+-#[]<>._+-#[]<>",
expPass: true
});
testCases[0] = ValidatePortIdentifierTestCase({ m: "valid lowercase", id: "transfer", expPass: true });
testCases[1] =
ValidatePortIdentifierTestCase({ m: "valid id special chars", id: "._+-#[]<>._+-#[]<>", expPass: true });
testCases[2] = ValidatePortIdentifierTestCase({
m: "valid id lower and special chars",
id: "lower._+-#[]<>",
expPass: true
});
testCases[3] = ValidatePortIdentifierTestCase({
m: "numeric id",
id: "1234567890",
expPass: true
});
testCases[4] = ValidatePortIdentifierTestCase({
m: "uppercase id",
id: "NOTLOWERCASE",
expPass: true
});
testCases[5] = ValidatePortIdentifierTestCase({
m: "numeric id",
id: "1234567890",
expPass: true
});
testCases[6] = ValidatePortIdentifierTestCase({
m: "blank id",
id: " ",
expPass: false
});
testCases[7] = ValidatePortIdentifierTestCase({
m: "id length out of range",
id: "1",
expPass: false
});
testCases[3] = ValidatePortIdentifierTestCase({ m: "numeric id", id: "1234567890", expPass: true });
testCases[4] = ValidatePortIdentifierTestCase({ m: "uppercase id", id: "NOTLOWERCASE", expPass: true });
testCases[5] = ValidatePortIdentifierTestCase({ m: "numeric id", id: "1234567890", expPass: true });
testCases[6] = ValidatePortIdentifierTestCase({ m: "blank id", id: " ", expPass: false });
testCases[7] = ValidatePortIdentifierTestCase({ m: "id length out of range", id: "1", expPass: false });
testCases[8] = ValidatePortIdentifierTestCase({
m: "id is too long",
id: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis eros neque, ultricies vel ligula ac, convallis porttitor elit. Maecenas tincidunt turpis elit, vel faucibus nisl pellentesque sodales",
expPass: false
});
testCases[9] = ValidatePortIdentifierTestCase({
m: "path-like id",
id: "lower/case/id",
expPass: false
});
testCases[10] = ValidatePortIdentifierTestCase({
m: "invalid id",
id: "(clientid)",
expPass: false
});
testCases[11] = ValidatePortIdentifierTestCase({
m: "empty string",
id: "",
expPass: false
});
testCases[9] = ValidatePortIdentifierTestCase({ m: "path-like id", id: "lower/case/id", expPass: false });
testCases[10] = ValidatePortIdentifierTestCase({ m: "invalid id", id: "(clientid)", expPass: false });
testCases[11] = ValidatePortIdentifierTestCase({ m: "empty string", id: "", expPass: false });

for (uint i = 0; i < testCases.length; i++) {
for (uint256 i = 0; i < testCases.length; i++) {
ValidatePortIdentifierTestCase memory tc = testCases[i];
bool res = IBCIdentifiers.validatePortIdentifier(bytes(tc.id));
if (tc.expPass) {
Expand Down

0 comments on commit 8955434

Please sign in to comment.