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

nico/split channel handshake api again #66

Merged
merged 2 commits into from
Mar 21, 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
193 changes: 116 additions & 77 deletions contracts/core/Dispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,42 @@ contract Dispatcher is IbcDispatcher, IbcEventsEmitter, Ownable {
}

/**
* 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(
IbcChannelReceiver portAddress,
function channelOpenInit(
IbcChannelReceiver receiver,
string calldata version,
ChannelOrder ordering,
bool feeEnabled,
string[] calldata connectionHops,
string calldata counterpartyPortId
) external {
if (bytes(counterpartyPortId).length == 0) {
revert IBCErrors.invalidCounterPartyPortId();
}

(bool success, bytes memory data) = _callIfContract(
address(receiver), abi.encodeWithSelector(IbcChannelReceiver.onChanOpenInit.selector, version)
);

if (success) {
emit ChannelOpenInit(
address(receiver), abi.decode(data, (string)), ordering, feeEnabled, connectionHops, counterpartyPortId
);
} else {
emit ChannelOpenInitError(address(receiver), data);
}
}

/**
* 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 receiver,
CounterParty calldata local,
ChannelOrder ordering,
bool feeEnabled,
Expand All @@ -88,28 +116,19 @@ contract Dispatcher is IbcDispatcher, IbcEventsEmitter, Ownable {
revert IBCErrors.invalidCounterPartyPortId();
}

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

(bool success, bytes memory data) = _callIfContract(
address(portAddress),
abi.encodeWithSelector(
IbcChannelReceiver.onOpenIbcChannel.selector,
local.version,
ordering,
feeEnabled,
connectionHops,
counterparty
)
address(receiver), abi.encodeWithSelector(IbcChannelReceiver.onChanOpenTry.selector, counterparty.version)
);

if (success) {
emit OpenIbcChannel(
address(portAddress),
emit ChannelOpenTry(
address(receiver),
abi.decode(data, (string)),
ordering,
feeEnabled,
Expand All @@ -118,58 +137,72 @@ contract Dispatcher is IbcDispatcher, IbcEventsEmitter, Ownable {
counterparty.channelId
);
} else {
emit OpenIbcChannelError(address(portAddress), data);
emit ChannelOpenTryError(address(receiver), data);
}
}

/**
* This func is called by a 'relayer' after the IBC/VIBC hub chain has processed the onOpenIbcChannel event.
* The dApp should implement the onConnectIbcChannel method to handle the last two channel handshake methods, ie.
* ChanOpenAck or ChanOpenConfirm.
* This func is called by a 'relayer' after the IBC/VIBC hub chain has processed the ChannelOpenTry event.
* The dApp should implement the onChannelConnect method to handle the third channel handshake method: ChanOpenAck
*/
function connectIbcChannel(
IbcChannelReceiver portAddress,
function channelOpenAck(
IbcChannelReceiver receiver,
CounterParty calldata local,
string[] calldata connectionHops,
ChannelOrder ordering,
bool feeEnabled,
bool isChanConfirm,
CounterParty calldata counterparty,
Ics23Proof calldata proof
) external {
_verifyConnectIbcChannelProof(local, connectionHops, ordering, isChanConfirm, counterparty, proof);
consensusStateManager.verifyMembership(
proof,
Ibc.channelProofKey(local.portId, local.channelId),
Ibc.channelProofValue(ChannelState.ACK_PENDING, ordering, local.version, connectionHops, counterparty)
);

(bool success, bytes memory data) = _callIfContract(
address(portAddress),
abi.encodeWithSelector(
IbcChannelReceiver.onConnectIbcChannel.selector,
local.channelId,
counterparty.channelId,
counterparty.version
)
address(receiver),
abi.encodeWithSelector(IbcChannelReceiver.onChanOpenAck.selector, local.channelId, counterparty.version)
);

if (success) {
emit ConnectIbcChannel(address(portAddress), local.channelId);

// Register port and channel mapping
// TODO: check duplicated channel registration?
// TODO: The call to `Channel` constructor MUST be move to `openIbcChannel` phase
// Then `connectIbcChannel` phase can use the `version` as part of `require` condition.
portChannelMap[address(portAddress)][local.channelId] = Channel(
counterparty.version, // TODO: this should be self version instead of counterparty version
ordering,
feeEnabled,
connectionHops,
counterparty.portId,
counterparty.channelId
);
_connectChannel(receiver, local, connectionHops, ordering, feeEnabled, counterparty);
emit ChannelOpenAck(address(receiver), local.channelId);
} else {
emit ChannelOpenAckError(address(receiver), data);
}
}

// initialize channel sequences
nextSequenceSend[address(portAddress)][local.channelId] = 1;
nextSequenceRecv[address(portAddress)][local.channelId] = 1;
nextSequenceAck[address(portAddress)][local.channelId] = 1;
/**
* This func is called by a 'relayer' after the IBC/VIBC hub chain has processed the ChannelOpenTry event.
* The dApp should implement the onChannelConnect method to handle the last channel handshake method:
* ChannelOpenConfirm
*/
function channelOpenConfirm(
IbcChannelReceiver receiver,
CounterParty calldata local,
string[] calldata connectionHops,
ChannelOrder ordering,
bool feeEnabled,
CounterParty calldata counterparty,
Ics23Proof calldata proof
) external {
consensusStateManager.verifyMembership(
proof,
Ibc.channelProofKey(local.portId, local.channelId),
Ibc.channelProofValue(ChannelState.CONFIRM_PENDING, ordering, local.version, connectionHops, counterparty)
);

(bool success, bytes memory data) = _callIfContract(
address(receiver),
abi.encodeWithSelector(IbcChannelReceiver.onChanOpenConfirm.selector, local.channelId, counterparty.version)
);

if (success) {
_connectChannel(receiver, local, connectionHops, ordering, feeEnabled, counterparty);
emit ChannelOpenConfirm(address(receiver), local.channelId);
} else {
emit ConnectIbcChannelError(address(portAddress), data);
emit ChannelOpenConfirmError(address(receiver), data);
}
}

Expand Down Expand Up @@ -504,39 +537,45 @@ contract Dispatcher is IbcDispatcher, IbcEventsEmitter, Ownable {
emit SendPacket(sender, channelId, packet, sequence, timeoutTimestamp);
}

function _verifyConnectIbcChannelProof(
function _connectChannel(
IbcChannelReceiver portAddress,
CounterParty calldata local,
string[] calldata connectionHops,
ChannelOrder ordering,
bool isChanConfirm,
CounterParty calldata counterparty,
Ics23Proof calldata proof
bool feeEnabled,
CounterParty calldata counterparty
) internal {
consensusStateManager.verifyMembership(
proof,
Ibc.channelProofKey(local.portId, local.channelId),
Ibc.channelProofValue(
isChanConfirm ? ChannelState.CONFIRM_PENDING : ChannelState.ACK_PENDING,
ordering,
local.version,
connectionHops,
counterparty
)
// Register port and channel mapping
// TODO: check duplicated channel registration?
// TODO: The call to `Channel` constructor MUST be move to `openIbcChannel` phase
// Then `connectIbcChannel` phase can use the `version` as part of `require` condition.
portChannelMap[address(portAddress)][local.channelId] = Channel(
counterparty.version, // TODO: this should be self version instead of counterparty version
ordering,
feeEnabled,
connectionHops,
counterparty.portId,
counterparty.channelId
);

// initialize channel sequences
nextSequenceSend[address(portAddress)][local.channelId] = 1;
nextSequenceRecv[address(portAddress)][local.channelId] = 1;
nextSequenceAck[address(portAddress)][local.channelId] = 1;
RnkSngh marked this conversation as resolved.
Show resolved Hide resolved
}

// Returns the result of the call if no revert, otherwise returns the error if thrown.
function _callIfContract(address portAddress, bytes memory args)
function _callIfContract(address receiver, bytes memory args)
internal
returns (bool success, bytes memory message)
{
if (!Address.isContract(portAddress)) {
if (!Address.isContract(receiver)) {
return (false, bytes("call to non-contract"));
}
// Only call if we are sure portAddress is a contract
// Only call if we are sure receiver is a contract
// Note: This tx won't revert if the low-level call fails, see
// https://docs.soliditylang.org/en/latest/cheatsheet.html#members-of-address
(success, message) = portAddress.call(args);
(success, message) = receiver.call(args);
}

// _isPacketTimeout returns true if the given packet has timed out acoording to host chain's block height and
Expand Down
65 changes: 37 additions & 28 deletions contracts/core/UniversalChannelHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,6 @@ contract UniversalChannelHandler is IbcReceiverBase, IbcUniversalChannelMW {
dispatcher.closeIbcChannel(channelId);
}

// IBC callback functions
function onConnectIbcChannel(bytes32 channelId, bytes32, string calldata counterpartyVersion)
external
onlyIbcDispatcher
{
if (keccak256(abi.encodePacked(counterpartyVersion)) != keccak256(abi.encodePacked(VERSION))) {
revert UnsupportedVersion();
}
connectedChannels.push(channelId);
}

function onCloseIbcChannel(bytes32 channelId, string calldata, bytes32) external onlyIbcDispatcher {
// logic to determin if the channel should be closed
bool channelFound = false;
Expand Down Expand Up @@ -149,23 +138,43 @@ 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 onChanOpenAck(bytes32 channelId, string calldata counterpartyVersion) external onlyIbcDispatcher {
_connectChannel(channelId, counterpartyVersion);
}

function onChanOpenConfirm(bytes32 channelId, string calldata counterpartyVersion) external onlyIbcDispatcher {
_connectChannel(channelId, counterpartyVersion);
}

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 _connectChannel(bytes32 channelId, string calldata version) private {
if (keccak256(abi.encodePacked(version)) != keccak256(abi.encodePacked(VERSION))) {
revert UnsupportedVersion();
}
connectedChannels.push(channelId);
}

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
Loading
Loading