Skip to content

Commit

Permalink
channel handshake: split open-init and open-try
Browse files Browse the repository at this point in the history
  • Loading branch information
nicopernas committed Mar 7, 2024
1 parent 425a1c9 commit a9096c3
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 151 deletions.
65 changes: 35 additions & 30 deletions contracts/core/Dispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,35 @@ contract Dispatcher is IbcDispatcher, IbcEventsEmitter, Ownable, Ibc {
}

/**
* This func is called by a 'relayer' on behalf of a dApp. The dApp should be implements IbcChannelHandler.
* The dApp should implement the onOpenIbcChannel method to handle one of the first two channel handshake methods,
* ie. ChanOpenInit or ChanOpenTry.
* If callback succeeds, the dApp should return the selected version, and an emitted event will be relayed to the
* IBC/VIBC hub chain.
* This function is called by a 'relayer' on behalf of a dApp. The dApp should implement IbcChannelHandler's
* onChanOpenInit. If the callback succeeds, the dApp should return the selected version and the emitted event
* will be relayed to the IBC/VIBC hub chain.
*/
function openIbcChannel(
function channelOpenInit(
IbcChannelReceiver portAddress,
string calldata version,
ChannelOrder ordering,
bool feeEnabled,
string[] calldata connectionHops,
string calldata counterpartyPortId
) external {
if (bytes(counterpartyPortId).length == 0) {
revert IBCErrors.invalidCounterPartyPortId();
}

string memory selectedVersion = portAddress.onChanOpenInit(version);

emit ChannelOpenInit(
address(portAddress), selectedVersion, ordering, feeEnabled, connectionHops, counterpartyPortId
);
}

/**
* This function is called by a 'relayer' on behalf of a dApp. The dApp should implement IbcChannelHandler's
* onChanOpenTry. If the callback succeeds, the dApp should return the selected version and the emitted event
* will be relayed to the IBC/VIBC hub chain.
*/
function channelOpenTry(
IbcChannelReceiver portAddress,
CounterParty calldata local,
ChannelOrder ordering,
Expand All @@ -87,18 +109,15 @@ contract Dispatcher is IbcDispatcher, IbcEventsEmitter, Ownable, Ibc {
revert IBCErrors.invalidCounterPartyPortId();
}

if (_isChannelOpenTry(counterparty)) {
consensusStateManager.verifyMembership(
proof,
channelProofKey(local.portId, local.channelId),
channelProofValue(ChannelState.TRY_PENDING, ordering, local.version, connectionHops, counterparty)
);
}
consensusStateManager.verifyMembership(
proof,
channelProofKey(local.portId, local.channelId),
channelProofValue(ChannelState.TRY_PENDING, ordering, local.version, connectionHops, counterparty)
);

string memory selectedVersion =
portAddress.onOpenIbcChannel(local.version, ordering, feeEnabled, connectionHops, counterparty);
string memory selectedVersion = portAddress.onChanOpenTry(counterparty.version);

emit OpenIbcChannel(
emit ChannelOpenTry(
address(portAddress),
selectedVersion,
ordering,
Expand Down Expand Up @@ -515,18 +534,4 @@ contract Dispatcher is IbcDispatcher, IbcEventsEmitter, Ownable, Ibc {
addr := mload(add(addrBytes, 20))
}
}

// For XXXX => vIBC direction, SC needs to verify the proof of membership of TRY_PENDING
// For vIBC initiated channel, SC doesn't need to verify any proof, and these should be all empty
function _isChannelOpenTry(CounterParty calldata counterparty) internal pure returns (bool open) {
if (counterparty.channelId == bytes32(0) && bytes(counterparty.version).length == 0) {
open = false;
// ChanOpenInit with unknow conterparty
} else if (counterparty.channelId != bytes32(0) && bytes(counterparty.version).length != 0) {
// this is the ChanOpenTry; counterparty must not be zero-value
open = true;
} else {
revert IBCErrors.invalidCounterParty();
}
}
}
40 changes: 22 additions & 18 deletions contracts/core/UniversalChannelHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ contract UniversalChannelHandler is IbcReceiverBase, IbcUniversalChannelMW {
dispatcher.closeIbcChannel(channelId);
}

// IBC callback functions
function onConnectIbcChannel(bytes32 channelId, bytes32, string calldata counterpartyVersion)
external
onlyIbcDispatcher
Expand Down Expand Up @@ -149,23 +148,28 @@ contract UniversalChannelHandler is IbcReceiverBase, IbcUniversalChannelMW {
mwStackAddrs[mwBitmap] = mwAddrs;
}

function onOpenIbcChannel(
string calldata version,
ChannelOrder,
bool,
string[] calldata,
CounterParty calldata counterparty
) external view onlyIbcDispatcher returns (string memory selectedVersion) {
if (counterparty.channelId == bytes32(0)) {
// ChanOpenInit
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(VERSION))) {
revert UnsupportedVersion();
}
} else {
// ChanOpenTry
if (keccak256(abi.encodePacked(counterparty.version)) != keccak256(abi.encodePacked(VERSION))) {
revert UnsupportedVersion();
}
// IBC callback functions
function onChanOpenInit(string calldata version)
external
view
onlyIbcDispatcher
returns (string memory selectedVersion)
{
return _openChannel(version);
}

function onChanOpenTry(string calldata counterpartyVersion)
external
view
onlyIbcDispatcher
returns (string memory selectedVersion)
{
return _openChannel(counterpartyVersion);
}

function _openChannel(string calldata version) private pure returns (string memory selectedVersion) {
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(VERSION))) {
revert UnsupportedVersion();
}
return VERSION;
}
Expand Down
53 changes: 22 additions & 31 deletions contracts/examples/Mars.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,39 +81,30 @@ contract Mars is IbcReceiverBase, IbcReceiver {
dispatcher.sendPacket(channelId, bytes(message), timeoutTimestamp);
}

function onOpenIbcChannel(
string calldata version,
ChannelOrder,
bool,
string[] calldata,
CounterParty calldata counterparty
) external view onlyIbcDispatcher returns (string memory selectedVersion) {
if (bytes(counterparty.portId).length <= 8) {
revert IBCErrors.invalidCounterPartyPortId();
}
/**
* Version selection is determined by if the callback is invoked on behalf of ChanOpenInit or ChanOpenTry.
* ChanOpenInit: self version should be provided whereas the counterparty version is empty.
* ChanOpenTry: counterparty version should be provided whereas the self version is empty.
* In both cases, the selected version should be in the supported versions list.
*/
bool foundVersion = false;
selectedVersion =
keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked("")) ? counterparty.version : version;
function onChanOpenInit(string calldata version)
external
view
onlyIbcDispatcher
returns (string memory selectedVersion)
{
return _openChannel(version);
}

function onChanOpenTry(string calldata counterpartyVersion)
external
view
onlyIbcDispatcher
returns (string memory selectedVersion)
{
return _openChannel(counterpartyVersion);
}

function _openChannel(string calldata version) private view returns (string memory selectedVersion) {
for (uint256 i = 0; i < supportedVersions.length; i++) {
if (keccak256(abi.encodePacked(selectedVersion)) == keccak256(abi.encodePacked(supportedVersions[i]))) {
foundVersion = true;
break;
if (keccak256(abi.encodePacked(version)) == keccak256(abi.encodePacked(supportedVersions[i]))) {
return version;
}
}
if (!foundVersion) revert UnsupportedVersion();
// if counterpartyVersion is not empty, then it must be the same foundVersion
if (keccak256(abi.encodePacked(counterparty.version)) != keccak256(abi.encodePacked(""))) {
if (keccak256(abi.encodePacked(counterparty.version)) != keccak256(abi.encodePacked(selectedVersion))) {
revert VersionMismatch();
}
}

return selectedVersion;
revert UnsupportedVersion();
}
}
18 changes: 13 additions & 5 deletions contracts/interfaces/IbcDispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ interface IbcPacketSender {
* Other features are implemented as callback methods in the IbcReceiver interface.
*/
interface IbcDispatcher is IbcPacketSender {
function openIbcChannel(
function channelOpenInit(
IbcChannelReceiver portAddress,
CounterParty calldata self,
string calldata version,
ChannelOrder ordering,
bool feeEnabled,
string[] calldata connectionHops,
CounterParty calldata counterparty,
Ics23Proof calldata proof
string calldata counterpartyPortId
) external;

function closeIbcChannel(bytes32 channelId) external;
Expand All @@ -46,7 +45,16 @@ interface IbcEventsEmitter {
//
// channel events
//
event OpenIbcChannel(
event ChannelOpenInit(
address indexed portAddress,
string version,
ChannelOrder ordering,
bool feeEnabled,
string[] connectionHops,
string counterpartyPortId
);

event ChannelOpenTry(
address indexed portAddress,
string version,
ChannelOrder ordering,
Expand Down
11 changes: 3 additions & 8 deletions contracts/interfaces/IbcReceiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ import {ChannelOrder, CounterParty, IbcPacket, AckPacket} from "../libs/Ibc.sol"
* handshake callbacks.
*/
interface IbcChannelReceiver {
function onOpenIbcChannel(
string calldata version,
ChannelOrder ordering,
bool feeEnabled,
string[] calldata connectionHops,
CounterParty calldata counterparty
) external returns (string memory selectedVersion);
function onChanOpenInit(string calldata version) external returns (string memory selectedVersion);

function onChanOpenTry(string calldata counterpartyVersion) external returns (string memory selectedVersion);

function onConnectIbcChannel(bytes32 channelId, bytes32 counterpartyChannelId, string calldata counterpartyVersion)
external;
Expand Down Expand Up @@ -53,7 +49,6 @@ contract IbcReceiverBase is Ownable {

error notIbcDispatcher();
error UnsupportedVersion();
error VersionMismatch();
error ChannelNotFound();

/**
Expand Down
37 changes: 26 additions & 11 deletions test/Dispatcher.base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,35 +65,50 @@ contract Base is IbcEventsEmitter, ProofBase {
// ⬇️ IBC functions for testing

/**
* @dev Step-1/2 of the 4-step handshake to open an IBC channel.
* @dev Step-1 of the 4-step handshake to open an IBC channel.
* @param le Local end settings for the channel.
* @param re Remote end settings for the channel.
* @param s Channel handshake settings.
* @param expPass Expected pass status of the operation.
* If expPass is false, `vm.expectRevert` should be called before this function.
*/
function openChannel(LocalEnd memory le, CounterParty memory re, ChannelHandshakeSetting memory s, bool expPass)
function channelOpenInit(LocalEnd memory le, CounterParty memory re, ChannelHandshakeSetting memory s, bool expPass)
public
{
CounterParty memory cp;
cp.portId = re.portId;
if (!s.localInitiate) {
cp.channelId = re.channelId;
cp.version = re.version;
if (expPass) {
vm.expectEmit(true, true, true, true);
emit ChannelOpenInit(
address(le.receiver), le.versionExpected, s.ordering, s.feeEnabled, le.connectionHops, re.portId
);
}
dispatcher.channelOpenInit(le.receiver, le.versionCall, s.ordering, s.feeEnabled, le.connectionHops, re.portId);
}

/**
* @dev Step-2 of the 4-step handshake to open an IBC channel.
* @param le Local end settings for the channel.
* @param re Remote end settings for the channel.
* @param s Channel handshake settings.
* @param expPass Expected pass status of the operation.
* If expPass is false, `vm.expectRevert` should be called before this function.
*/
function channelOpenTry(LocalEnd memory le, CounterParty memory re, ChannelHandshakeSetting memory s, bool expPass)
public
{
if (expPass) {
vm.expectEmit(true, true, true, true);
emit OpenIbcChannel(
emit ChannelOpenTry(
address(le.receiver),
le.versionExpected,
s.ordering,
s.feeEnabled,
le.connectionHops,
cp.portId,
cp.channelId
re.portId,
re.channelId
);
}
dispatcher.openIbcChannel(
CounterParty memory cp = CounterParty(re.portId, re.channelId, re.version);
dispatcher.channelOpenTry(
le.receiver,
CounterParty(le.portId, le.channelId, le.versionCall),
s.ordering,
Expand Down
11 changes: 5 additions & 6 deletions test/Dispatcher.proof.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,20 @@ contract DispatcherIbcWithRealProofs is IbcEventsEmitter, ProofBase {
}

function test_ibc_channel_open_init() public {
CounterParty memory counterparty = CounterParty(ch1.portId, bytes32(0), "");

vm.expectEmit(true, true, true, true);
emit OpenIbcChannel(address(mars), "1.0", ChannelOrder.NONE, false, connectionHops1, ch1.portId, bytes32(0));
emit ChannelOpenInit(address(mars), "1.0", ChannelOrder.NONE, false, connectionHops1, ch1.portId);

// since this is open chann init, the proof is not used. so use an invalid one
dispatcher.openIbcChannel(mars, ch1, ChannelOrder.NONE, false, connectionHops1, counterparty, invalidProof);
dispatcher.channelOpenInit(mars, ch1.version, ChannelOrder.NONE, false, connectionHops1, ch1.portId);
}

function test_ibc_channel_open_try() public {
Ics23Proof memory proof = load_proof("/test/payload/channel_try_pending_proof.hex");

vm.expectEmit(true, true, true, true);
emit OpenIbcChannel(address(mars), "1.0", ChannelOrder.NONE, false, connectionHops1, ch0.portId, ch0.channelId);
emit ChannelOpenTry(address(mars), "1.0", ChannelOrder.NONE, false, connectionHops1, ch0.portId, ch0.channelId);

dispatcher.openIbcChannel(mars, ch1, ChannelOrder.NONE, false, connectionHops1, ch0, proof);
dispatcher.channelOpenTry(mars, ch1, ChannelOrder.NONE, false, connectionHops1, ch0, proof);
}

function test_ibc_channel_ack() public {
Expand Down
Loading

0 comments on commit a9096c3

Please sign in to comment.