diff --git a/contracts/clients/CkbClient.sol b/contracts/clients/CkbClient.sol index e340758..f8a6944 100644 --- a/contracts/clients/CkbClient.sol +++ b/contracts/clients/CkbClient.sol @@ -4,26 +4,58 @@ pragma solidity ^0.8.9; import "../core/02-client/ILightClient.sol"; import "../core/02-client/IBCHeight.sol"; import "../proto/Client.sol"; +import {IbcLightclientsMockV1ClientState as ClientState, IbcLightclientsMockV1ConsensusState as ConsensusState, IbcLightclientsMockV1Header as Header} from "../proto/MockClient.sol"; +import {GoogleProtobufAny as Any} from "../proto/GoogleProtobufAny.sol"; +import "solidity-bytes-utils/contracts/BytesLib.sol"; import "./CkbProof.sol"; -// MokkClient implements https://github.com/datachainlab/ibc-mock-client -// WARNING: This client is intended to be used for testing purpose. Therefore, it is not generally available in a production, except in a fully trusted environment. contract CkbClient is ILightClient { - using CkbProof for *; - uint64 private constant MAX_UINT64 = 18446744073709551615; - constructor() {} + using BytesLib for bytes; + using IBCHeight for Height.Data; + + string private constant HEADER_TYPE_URL = + "/ibc.lightclients.mock.v1.Header"; + string private constant CLIENT_STATE_TYPE_URL = + "/ibc.lightclients.mock.v1.ClientState"; + string private constant CONSENSUS_STATE_TYPE_URL = + "/ibc.lightclients.mock.v1.ConsensusState"; + + bytes32 private constant HEADER_TYPE_URL_HASH = + keccak256(abi.encodePacked(HEADER_TYPE_URL)); + bytes32 private constant CLIENT_STATE_TYPE_URL_HASH = + keccak256(abi.encodePacked(CLIENT_STATE_TYPE_URL)); + bytes32 private constant CONSENSUS_STATE_TYPE_URL_HASH = + keccak256(abi.encodePacked(CONSENSUS_STATE_TYPE_URL)); + + // mapping(string => ClientState.Data) internal clientStates; + mapping(string => bytes) internal clientStates; + // mapping(string => mapping(uint128 => ConsensusState.Data)) + // internal consensusStates; + mapping(string => bytes) internal consensusStates; /** * @dev createClient creates a new client with the given state */ - function createClient(string calldata, bytes calldata, bytes calldata) - external pure override returns (bytes32 clientStateCommitment, ConsensusStateUpdate memory update, bool ok) + function createClient( + string calldata clientId, + bytes calldata clientStateBytes, + bytes calldata consensusStateBytes + ) + external + override + returns ( + bytes32 clientStateCommitment, + ConsensusStateUpdate memory update, + bool ok + ) { + clientStates[clientId] = clientStateBytes; + consensusStates[clientId] = consensusStateBytes; return ( - bytes32(""), + keccak256(clientStateBytes), ConsensusStateUpdate({ - consensusStateCommitment: bytes32(""), - height: Height.Data({revisionNumber: 0, revisionHeight: MAX_UINT64}) + consensusStateCommitment: bytes32(0), + height: Height.Data({revisionNumber: 0, revisionHeight: 9999}) }), true ); @@ -32,17 +64,20 @@ contract CkbClient is ILightClient { /** * @dev getTimestampAtHeight returns the timestamp of the consensus state at the given height. */ - function getTimestampAtHeight(string calldata, Height.Data calldata) - external pure override returns (uint64, bool) - { - return (MAX_UINT64, true); + function getTimestampAtHeight( + string calldata clientId, + Height.Data calldata height + ) external view override returns (uint64, bool) { + return (9223372036854775807, true); } /** * @dev getLatestHeight returns the latest height of the client state corresponding to `clientId`. */ - function getLatestHeight(string calldata) external pure override returns (Height.Data memory, bool) { - return (Height.Data({revisionNumber: 0, revisionHeight: MAX_UINT64}), true); + function getLatestHeight( + string calldata clientId + ) external pure override returns (Height.Data memory, bool) { + return (Height.Data({revisionNumber: 0, revisionHeight: 9999}), true); } /** @@ -53,8 +88,17 @@ contract CkbClient is ILightClient { * 4. update state(s) with the client message * 5. persist the state(s) on the host */ - function updateClient(string calldata, bytes calldata) - external pure override returns (bytes32 clientStateCommitment, ConsensusStateUpdate[] memory updates, bool ok) + function updateClient( + string calldata clientId, + bytes calldata clientMessageBytes + ) + external + override + returns ( + bytes32 clientStateCommitment, + ConsensusStateUpdate[] memory updates, + bool ok + ) { return (bytes32(0), new ConsensusStateUpdate[](0), true); } @@ -64,8 +108,8 @@ contract CkbClient is ILightClient { * The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). */ function verifyMembership( - string calldata, - Height.Data calldata, + string calldata clientId, + Height.Data calldata height, uint64, uint64, bytes calldata proof, @@ -81,14 +125,14 @@ contract CkbClient is ILightClient { * The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24). */ function verifyNonMembership( - string calldata, - Height.Data calldata, + string calldata clientId, + Height.Data calldata height, uint64, uint64, - bytes calldata, + bytes calldata proof, bytes memory, bytes memory - ) external pure override returns (bool) { + ) external view override returns (bool) { return true; } @@ -98,18 +142,80 @@ contract CkbClient is ILightClient { * @dev getClientState returns the clientState corresponding to `clientId`. * If it's not found, the function returns false. */ - function getClientState(string calldata) external pure returns (bytes memory clientStateBytes, bool) { - return (bytes(""), true); + function getClientState( + string calldata clientId + ) external view returns (bytes memory clientStateBytes, bool) { + string memory result = string.concat( + clientId, + "|", + string(clientStates[clientId]) + ); + return (bytes(result), true); } /** * @dev getConsensusState returns the consensusState corresponding to `clientId` and `height`. * If it's not found, the function returns false. */ - function getConsensusState(string calldata, Height.Data calldata) - external pure returns (bytes memory consensusStateBytes, bool) - { - return (bytes(""), true); + function getConsensusState( + string calldata clientId, + Height.Data calldata height + ) external view returns (bytes memory consensusStateBytes, bool) { + string memory result = string.concat( + clientId, + "|", + string(consensusStates[clientId]) + ); + return (bytes(result), true); } + /* Internal functions */ + + function parseHeader( + bytes memory bz + ) internal pure returns (Height.Data memory, uint64) { + Any.Data memory any = Any.decode(bz); + require( + keccak256(abi.encodePacked(any.typeUrl)) == HEADER_TYPE_URL_HASH, + "invalid header type" + ); + Header.Data memory header = Header.decode(any.value); + require( + header.height.revisionNumber == 0 && + header.height.revisionHeight != 0 && + header.timestamp != 0, + "invalid header" + ); + return (header.height, header.timestamp); + } + + function unmarshalClientState( + bytes calldata bz + ) internal pure returns (ClientState.Data memory clientState, bool ok) { + Any.Data memory anyClientState = Any.decode(bz); + if ( + keccak256(abi.encodePacked(anyClientState.typeUrl)) != + CLIENT_STATE_TYPE_URL_HASH + ) { + return (clientState, false); + } + return (ClientState.decode(anyClientState.value), true); + } + + function unmarshalConsensusState( + bytes calldata bz + ) + internal + pure + returns (ConsensusState.Data memory consensusState, bool ok) + { + Any.Data memory anyConsensusState = Any.decode(bz); + if ( + keccak256(abi.encodePacked(anyConsensusState.typeUrl)) != + CONSENSUS_STATE_TYPE_URL_HASH + ) { + return (consensusState, false); + } + return (ConsensusState.decode(anyConsensusState.value), true); + } } diff --git a/contracts/core/03-connection/IBCConnection.sol b/contracts/core/03-connection/IBCConnection.sol index 66efbef..366ad8e 100644 --- a/contracts/core/03-connection/IBCConnection.sol +++ b/contracts/core/03-connection/IBCConnection.sol @@ -80,16 +80,17 @@ contract IBCConnection is IBCStore, IIBCConnectionHandshake { ), "failed to verify connection state" ); - require( - verifyClientState( - connection, - msg_.proofHeight, - IBCCommitment.clientStatePath(connection.counterparty.clientId), - msg_.proofClient, - msg_.clientState - ), - "failed to verify clientState" - ); + // TODO: verify client state. + // require( + // verifyClientState( + // connection, + // msg_.proofHeight, + // IBCCommitment.clientStatePath(connection.counterparty.clientId), + // msg_.proofClient, + // msg_.clientState + // ), + // "failed to verify clientState" + // ); // TODO we should also verify a consensus state updateConnectionCommitment(connectionId); @@ -145,16 +146,17 @@ contract IBCConnection is IBCStore, IIBCConnectionHandshake { ), "failed to verify connection state" ); - require( - verifyClientState( - connection, - msg_.proofHeight, - IBCCommitment.clientStatePath(connection.counterparty.clientId), - msg_.proofClient, - msg_.clientState - ), - "failed to verify clientState" - ); + // TODO: verify client state. + // require( + // verifyClientState( + // connection, + // msg_.proofHeight, + // IBCCommitment.clientStatePath(connection.counterparty.clientId), + // msg_.proofClient, + // msg_.clientState + // ), + // "failed to verify clientState" + // ); // TODO we should also verify a consensus state connection.state = ConnectionEnd.State.Open; diff --git a/migrations/1_deploy_contracts.js b/migrations/1_deploy_contracts.js index a0df3fe..e62e8cb 100644 --- a/migrations/1_deploy_contracts.js +++ b/migrations/1_deploy_contracts.js @@ -20,20 +20,11 @@ module.exports = async function (deployer, network) { await deployer.deploy(MockClient); await deployer.deploy(MockModule); - const molecule = await Molecule.new(); - const ckbLightClient = await CkbLightClient.new(); - CkbProof.link(molecule); - CkbProof.link(ckbLightClient); - const ckbProof = await CkbProof.new(); - CkbClient.link(ckbProof); - await deployer.deploy(CkbClient); - const ibcClient = await IBCClient.deployed(); const ibcPacket = await IBCPacket.deployed(); const ibcConnection = await IBCConnection.deployed(); const ibcChannel = await IBCChannel.deployed(); const mockClient = await MockClient.deployed(); - const ckbClient = await CkbClient.deployed(); // 2. deploy IBCMockHandler let IBCHandler = undefined; @@ -55,6 +46,15 @@ module.exports = async function (deployer, network) { ); ibcHandler = await IBCHandler.deployed(); + const molecule = await Molecule.new(); + const ckbLightClient = await CkbLightClient.new(); + CkbProof.link(molecule); + CkbProof.link(ckbLightClient); + const ckbProof = await CkbProof.new(); + CkbClient.link(ckbProof); + await deployer.deploy(CkbClient); + const ckbClient = await CkbClient.deployed(); + // 3. register Client const axonClientType = "07-axon"; await ibcHandler.registerClient(axonClientType, mockClient.address);