Skip to content

Commit

Permalink
feat(evm) sibling cosmos lc (#2672)
Browse files Browse the repository at this point in the history
Towards #2659 (we need to implement the second half on cosmwasm)

In this PR:

- we implements a Cosmos in Cosmos light client in EVM that will allow
any cosmos chain connected to union to have it's root hash committed on
EVM. This will allow direct connections/channels to EVM through Union.
- we extend the `ILightClient` interface to expose `isFrozen` for light
clients.
- we fix a Cometbls light client bug where we weren't checking whether
the client was frozen or not before checking membership proofs.
- we simplify the UCS01 contract and improve some tests (fuzz edge case
I found that were wrong as we were missing some `vm.assume`)
  • Loading branch information
hussein-aitlahcen authored Aug 6, 2024
2 parents ae802ba + 8b6ecd4 commit 515f5b7
Show file tree
Hide file tree
Showing 13 changed files with 1,465 additions and 48 deletions.
5 changes: 5 additions & 0 deletions evm/contracts/Glue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ import "./proto/cosmos/ics23/v1/proofs.sol";
import "./proto/tendermint/types/types.sol";
import "./proto/tendermint/types/canonical.sol";
import "./proto/union/ibc/lightclients/cometbls/v1/cometbls.sol";
import "./proto/union/ibc/lightclients/cosmosincosmos/v1/cosmosincosmos.sol";
import "./proto/ibc/lightclients/wasm/v1/wasm.sol";
import "./lib/CometblsHelp.sol";
import "./clients/CosmosInCosmosClient.sol";

contract Glue {
function typesTelescope(
UnionIbcLightclientsCometblsV1ClientState.Data memory,
UnionIbcLightclientsCometblsV1ConsensusState.Data memory,
UnionIbcLightclientsCometblsV1Header.Data memory,
UnionIbcLightclientsCosmosincosmosV1ClientState.Data memory,
UnionIbcLightclientsCosmosincosmosV1Header.Data memory,
OptimizedCosmosInCosmosConsensusState memory,
TendermintTypesHeader.Data memory,
TendermintTypesCommit.Data memory,
IbcCoreClientV1Height.Data memory,
Expand Down
116 changes: 77 additions & 39 deletions evm/contracts/apps/ucs/01-relay/Relay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,60 @@ contract UCS01Relay is
}
}

function calculateFee(
uint256 amount,
uint128 fee
) internal pure returns (uint256, uint256) {
uint256 feeAmount = Math.mulDiv(amount, fee, type(uint128).max);
uint256 actualAmount = amount - feeAmount;
return (actualAmount, feeAmount);
}

function onRecvLocalTransfer(
string memory destinationChannel,
string memory denom,
address receiver,
uint256 amount,
address relayer,
uint256 feeAmount
) internal returns (address) {
// It's an ERC20 string 0x prefixed hex address
address denomAddress = Hex.hexToAddress(denom);
// The token must be outstanding.
decreaseOutstanding(destinationChannel, denomAddress, amount);
IERC20(denomAddress).transfer(receiver, amount);
if (feeAmount > 0) {
IERC20(denomAddress).transfer(relayer, feeAmount);
}
return denomAddress;
}

function onRecvRemoteTransfer(
uint64 sequence,
string memory destinationChannel,
string memory denom,
address receiver,
uint256 amount,
address relayer,
uint256 feeAmount
) internal returns (address) {
address denomAddress = denomToAddress[destinationChannel][denom];
if (denomAddress == address(0)) {
denomAddress =
address(new ERC20Denom{salt: keccak256(bytes(denom))}(denom));
denomToAddress[destinationChannel][denom] = denomAddress;
addressToDenom[destinationChannel][denomAddress] = denom;
emit RelayLib.DenomCreated(
sequence, destinationChannel, denom, denomAddress
);
}
IERC20Denom(denomAddress).mint(receiver, amount);
if (feeAmount > 0) {
IERC20Denom(denomAddress).mint(relayer, feeAmount);
}
return denomAddress;
}

function onRecvPacketProcessing(
IbcCoreChannelV1Packet.Data calldata ibcPacket,
address relayer
Expand All @@ -391,65 +445,49 @@ contract UCS01Relay is
}
RelayPacket calldata packet = RelayPacketLib.decode(ibcPacket.data);
string memory prefix = RelayLib.makeDenomPrefix(
ibcPacket.source_port, ibcPacket.source_channel
ibcPacket.destination_port, ibcPacket.destination_channel
);
uint256 packetTokensLength = packet.tokens.length;
for (uint256 i; i < packetTokensLength; i++) {
Token memory token = packet.tokens[i];
if (token.amount == 0) {
revert RelayLib.ErrInvalidAmount();
}
uint256 feeAmount =
Math.mulDiv(token.amount, token.fee, type(uint128).max);
uint256 actualAmount = token.amount - feeAmount;
(uint256 actualAmount, uint256 feeAmount) =
calculateFee(token.amount, token.fee);
address receiver = RelayLib.bytesToAddress(packet.receiver);
address denomAddress;
string memory denom;
if (token.denom.startsWith(prefix)) {
// In this branch the token was originating from
// this chain as it was prefixed by the local channel/port.
// We need to unescrow the amount.
// This will trim the denom in-place IFF it is prefixed
denom = token.denom.slice(bytes(prefix).length);
// It's an ERC20 string 0x prefixed hex address
denomAddress = Hex.hexToAddress(denom);
// The token must be outstanding.
decreaseOutstanding(
ibcPacket.destination_channel, denomAddress, token.amount
denomAddress = onRecvLocalTransfer(
ibcPacket.destination_channel,
denom,
receiver,
actualAmount,
relayer,
feeAmount
);
IERC20(denomAddress).transfer(receiver, actualAmount);
if (feeAmount > 0) {
IERC20(denomAddress).transfer(relayer, feeAmount);
}
} else {
// In this branch the token was originating from the
// counterparty chain. We need to mint the amount.
// counterparty chain. We need to prefix the denom and mint the amount.
denom = RelayLib.makeForeignDenom(
ibcPacket.destination_port,
ibcPacket.destination_channel,
token.denom
);
denomAddress =
denomToAddress[ibcPacket.destination_channel][denom];
if (denomAddress == address(0)) {
denomAddress = address(
new ERC20Denom{salt: keccak256(bytes(denom))}(denom)
);
denomToAddress[ibcPacket.destination_channel][denom] =
denomAddress;
addressToDenom[ibcPacket.destination_channel][denomAddress]
= denom;
emit RelayLib.DenomCreated(
ibcPacket.sequence,
ibcPacket.source_channel,
denom,
denomAddress
);
}
IERC20Denom(denomAddress).mint(receiver, actualAmount);
if (feeAmount > 0) {
IERC20Denom(denomAddress).mint(relayer, feeAmount);
}
denomAddress = onRecvRemoteTransfer(
ibcPacket.sequence,
ibcPacket.destination_channel,
denom,
receiver,
actualAmount,
relayer,
feeAmount
);
}
string memory senderAddress = packet.sender.toHexString();
emit RelayLib.Received(
Expand Down Expand Up @@ -541,7 +579,7 @@ contract UCS01Relay is
string calldata,
IbcCoreChannelV1Counterparty.Data calldata,
string calldata version,
address relayer
address
) external view override(IBCAppBase, IIBCModule) onlyIBC {
if (!RelayLib.isValidVersion(version)) {
revert RelayLib.ErrInvalidProtocolVersion();
Expand All @@ -559,7 +597,7 @@ contract UCS01Relay is
IbcCoreChannelV1Counterparty.Data calldata,
string calldata version,
string calldata counterpartyVersion,
address relayer
address
) external view override(IBCAppBase, IIBCModule) onlyIBC {
if (!RelayLib.isValidVersion(version)) {
revert RelayLib.ErrInvalidProtocolVersion();
Expand All @@ -577,7 +615,7 @@ contract UCS01Relay is
string calldata,
string calldata,
string calldata counterpartyVersion,
address relayer
address
) external view override(IBCAppBase, IIBCModule) onlyIBC {
if (!RelayLib.isValidVersion(counterpartyVersion)) {
revert RelayLib.ErrInvalidCounterpartyProtocolVersion();
Expand Down
23 changes: 23 additions & 0 deletions evm/contracts/clients/CometblsClientV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ contract CometblsClient is
bytes calldata path,
bytes calldata value
) external virtual returns (bool) {
if (isFrozenImpl(clientId)) {
revert CometblsClientLib.ErrClientFrozen();
}
bytes32 appHash = validateDelayPeriod(
clientId, height, delayPeriodTime, delayPeriodBlocks
);
Expand All @@ -326,6 +329,9 @@ contract CometblsClient is
bytes calldata prefix,
bytes calldata path
) external virtual returns (bool) {
if (isFrozenImpl(clientId)) {
revert CometblsClientLib.ErrClientFrozen();
}
bytes32 appHash = validateDelayPeriod(
clientId, height, delayPeriodTime, delayPeriodBlocks
);
Expand Down Expand Up @@ -391,6 +397,23 @@ contract CometblsClient is
return clientStates[clientId].latest_height;
}

function isFrozen(string calldata clientId)
external
view
virtual
returns (bool)
{
return isFrozenImpl(clientId);
}

function isFrozenImpl(string calldata clientId)
internal
view
returns (bool)
{
return !clientStates[clientId].frozen_height.isZero();
}

// ZKP VERIFICATION
uint256 constant PRIME_R =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
Expand Down
Loading

0 comments on commit 515f5b7

Please sign in to comment.