diff --git a/contracts/core/IBFT2Client.sol b/contracts/core/IBFT2Client.sol index 46705751..d121293d 100644 --- a/contracts/core/IBFT2Client.sol +++ b/contracts/core/IBFT2Client.sol @@ -6,11 +6,7 @@ import "./IBCHost.sol"; import "./IBCMsgs.sol"; import "./IBCHeight.sol"; import "./types/Client.sol"; -import { - IbcLightclientsIbft2V1ClientState as ClientState, - IbcLightclientsIbft2V1ConsensusState as ConsensusState, - IbcLightclientsIbft2V1Header as Header -} from "./types/IBFT2.sol"; +import {IbcLightclientsIbft2V1ClientState as ClientState, IbcLightclientsIbft2V1ConsensusState as ConsensusState, IbcLightclientsIbft2V1Header as Header} from "./types/IBFT2.sol"; import {GoogleProtobufAny as Any} from "./types/GoogleProtobufAny.sol"; import "../lib/ECRecovery.sol"; import "../lib/Bytes.sol"; @@ -38,9 +34,15 @@ contract IBFT2Client is IClient { // TODO The typeUrl should be defined in types/IBFT2Client.sol // The schema of typeUrl follows cosmos/cosmos-sdk/codec/types/any.go pts = protoTypes({ - clientState: keccak256(abi.encodePacked("/ibc.lightclients.ibft2.v1.ClientState")), - consensusState: keccak256(abi.encodePacked("/ibc.lightclients.ibft2.v1.ConsensusState")), - header: keccak256(abi.encodePacked("/ibc.lightclients.ibft2.v1.Header")) + clientState: keccak256( + abi.encodePacked("/ibc.lightclients.ibft2.v1.ClientState") + ), + consensusState: keccak256( + abi.encodePacked("/ibc.lightclients.ibft2.v1.ConsensusState") + ), + header: keccak256( + abi.encodePacked("/ibc.lightclients.ibft2.v1.Header") + ) }); } @@ -66,19 +68,27 @@ contract IBFT2Client is IClient { IBCHost host, string memory clientId, Height.Data memory height - ) public override view returns (uint64, bool) { - (ConsensusState.Data memory consensusState, bool found) = getConsensusState(host, clientId, height); + ) public view override returns (uint64, bool) { + ( + ConsensusState.Data memory consensusState, + bool found + ) = getConsensusState(host, clientId, height); if (!found) { return (0, false); } return (consensusState.timestamp, true); } - function getLatestHeight( - IBCHost host, - string memory clientId - ) public override view returns (Height.Data memory, bool) { - (ClientState.Data memory clientState, bool found) = getClientState(host, clientId); + function getLatestHeight(IBCHost host, string memory clientId) + public + view + override + returns (Height.Data memory, bool) + { + (ClientState.Data memory clientState, bool found) = getClientState( + host, + clientId + ); if (!found) { return (Height.Data(0, 0), false); } @@ -86,14 +96,14 @@ contract IBFT2Client is IClient { } /** - * @dev checkHeaderAndUpdateState checks if the provided header is valid + * @dev verifyHeader checks if the provided header is valid */ - function checkHeaderAndUpdateState( + function verifyHeader( IBCHost host, - string memory clientId, + string memory clientId, bytes memory clientStateBytes, bytes memory headerBytes - ) public override view returns (bytes memory newClientStateBytes, bytes memory newConsensusStateBytes, Height.Data memory height) { + ) public view override returns (bool) { Header.Data memory header; ClientState.Data memory clientState; ConsensusState.Data memory consensusState; @@ -106,43 +116,110 @@ contract IBFT2Client is IClient { (header, ok) = unmarshalHeader(headerBytes); require(ok, "header is invalid"); - (consensusState, ok) = getConsensusState(host, clientId, header.trusted_height); + (consensusState, ok) = getConsensusState( + host, + clientId, + header.trusted_height + ); require(ok, "consensusState not found"); //// check validity //// ParsedBesuHeader memory parsedHeader = parseBesuHeader(header); - require(parsedHeader.height.gt(header.trusted_height), "header height <= consensus state height"); + require( + parsedHeader.height.gt(header.trusted_height), + "header height <= consensus state height" + ); (validators, ok) = verify(consensusState, parsedHeader); require(ok, "failed to verify the header"); + return true; + } + + /** + * @dev updateState after header is verified + */ + function updateState( + IBCHost host, + string memory clientId, + bytes memory clientStateBytes, + bytes memory headerBytes + ) + public + view + override + returns ( + bytes memory newClientStateBytes, + ConsensusUpdate.Result[] memory results + ) + { + Header.Data memory header = Header.decode( + Any.decode(headerBytes).value + ); + ClientState.Data memory clientState = ClientState.decode( + Any.decode(clientStateBytes).value + ); + ConsensusState.Data memory consensusState; + bytes[] memory validators; + + (bytes memory consensusStateBytes, bool ok) = host.getConsensusState( + clientId, + header.trusted_height + ); + + consensusState = ConsensusState.decode( + Any.decode(consensusStateBytes).value + ); + + ParsedBesuHeader memory parsedHeader = parseBesuHeader(header); + (validators, ok) = verify(consensusState, parsedHeader); - //// update //// consensusState.timestamp = parsedHeader.time; consensusState.root = abi.encodePacked( - verifyStorageProof(Bytes.toAddress(clientState.ibc_store_address), parsedHeader.stateRoot, header.account_state_proof) + verifyStorageProof( + Bytes.toAddress(clientState.ibc_store_address), + parsedHeader.stateRoot, + header.account_state_proof + ) ); consensusState.validators = validators; if (parsedHeader.height.gt(clientState.latest_height)) { clientState.latest_height = parsedHeader.height; } - return (marshalClientState(clientState), marshalConsensusState(consensusState), parsedHeader.height); + results[0] = ConsensusUpdate.Result( + parsedHeader.height, + marshalConsensusState(consensusState) + ); + return (marshalClientState(clientState), results); } - function marshalClientState(ClientState.Data memory clientState) internal pure returns (bytes memory) { + function marshalClientState(ClientState.Data memory clientState) + internal + pure + returns (bytes memory) + { Any.Data memory anyClientState; anyClientState.type_url = "/ibc.lightclients.ibft2.v1.ClientState"; anyClientState.value = ClientState.encode(clientState); return Any.encode(anyClientState); } - function marshalConsensusState(ConsensusState.Data memory consensusState) internal pure returns (bytes memory) { + function marshalConsensusState(ConsensusState.Data memory consensusState) + internal + pure + returns (bytes memory) + { Any.Data memory anyConsensusState; - anyConsensusState.type_url = "/ibc.lightclients.ibft2.v1.ConsensusState"; + anyConsensusState + .type_url = "/ibc.lightclients.ibft2.v1.ConsensusState"; anyConsensusState.value = ConsensusState.encode(consensusState); return Any.encode(anyConsensusState); } - function unmarshalHeader(bytes memory bz) internal view returns (Header.Data memory header, bool ok) { + function unmarshalHeader(bytes memory bz) + internal + view + returns (Header.Data memory header, bool ok) + { Any.Data memory anyHeader = Any.decode(bz); if (keccak256(abi.encodePacked(anyHeader.type_url)) != pts.header) { return (header, false); @@ -150,17 +227,31 @@ contract IBFT2Client is IClient { return (Header.decode(anyHeader.value), true); } - function unmarshalClientState(bytes memory bz) internal view returns (ClientState.Data memory clientState, bool ok) { + function unmarshalClientState(bytes memory bz) + internal + view + returns (ClientState.Data memory clientState, bool ok) + { Any.Data memory anyClientState = Any.decode(bz); - if (keccak256(abi.encodePacked(anyClientState.type_url)) != pts.clientState) { + if ( + keccak256(abi.encodePacked(anyClientState.type_url)) != + pts.clientState + ) { return (clientState, false); } return (ClientState.decode(anyClientState.value), true); } - function unmarshalConsensusState(bytes memory bz) internal view returns (ConsensusState.Data memory consensusState, bool ok) { + function unmarshalConsensusState(bytes memory bz) + internal + view + returns (ConsensusState.Data memory consensusState, bool ok) + { Any.Data memory anyConsensusState = Any.decode(bz); - if (keccak256(abi.encodePacked(anyConsensusState.type_url)) != pts.consensusState) { + if ( + keccak256(abi.encodePacked(anyConsensusState.type_url)) != + pts.consensusState + ) { return (consensusState, false); } return (ConsensusState.decode(anyConsensusState.value), true); @@ -173,14 +264,29 @@ contract IBFT2Client is IClient { * @param consensusState consensusState corresponding to trusted height * @param untrustedHeader untrusted header */ - function verify(ConsensusState.Data memory consensusState, ParsedBesuHeader memory untrustedHeader) internal pure returns (bytes[] memory validators, bool ok) { + function verify( + ConsensusState.Data memory consensusState, + ParsedBesuHeader memory untrustedHeader + ) internal pure returns (bytes[] memory validators, bool ok) { bytes32 blkHash = keccak256(untrustedHeader.base.besu_header_rlp); - if (!verifyCommitSealsTrusting(consensusState.validators, untrustedHeader.base.seals, blkHash, Fraction({numerator: 1, denominator: 3}))) { + if ( + !verifyCommitSealsTrusting( + consensusState.validators, + untrustedHeader.base.seals, + blkHash, + Fraction({numerator: 1, denominator: 3}) + ) + ) { return (validators, false); } - return verifyCommitSeals(untrustedHeader.validators, untrustedHeader.base.seals, blkHash); + return + verifyCommitSeals( + untrustedHeader.validators, + untrustedHeader.base.seals, + blkHash + ); } /** @@ -190,22 +296,30 @@ contract IBFT2Client is IClient { * @param blkHash the hash of untrusted block * @param trustLevel new header can be trusted if at least one correct validator signed it */ - function verifyCommitSealsTrusting(bytes[] memory trustedVals, bytes[] memory seals, bytes32 blkHash, Fraction memory trustLevel) internal pure returns (bool) { + function verifyCommitSealsTrusting( + bytes[] memory trustedVals, + bytes[] memory seals, + bytes32 blkHash, + Fraction memory trustLevel + ) internal pure returns (bool) { uint8 success = 0; bool[] memory marked = new bool[](trustedVals.length); - for (uint i = 0; i < seals.length; i++) { + for (uint256 i = 0; i < seals.length; i++) { if (seals[i].length == 0) { continue; } address signer = ECRecovery.recover(blkHash, seals[i]); - for (uint j = 0; j < trustedVals.length; j++) { + for (uint256 j = 0; j < trustedVals.length; j++) { if (!marked[j] && trustedVals[j].toAddress() == signer) { success++; marked[j] = true; } } } - return success >= trustedVals.length * trustLevel.numerator / trustLevel.denominator; + return + success >= + (trustedVals.length * trustLevel.numerator) / + trustLevel.denominator; } /** @@ -214,18 +328,25 @@ contract IBFT2Client is IClient { * @param seals commit seals for untrusted block header * @param blkHash the hash of untrusted block */ - function verifyCommitSeals(RLP.RLPItem[] memory untrustedVals, bytes[] memory seals, bytes32 blkHash) internal pure returns (bytes[] memory, bool) { + function verifyCommitSeals( + RLP.RLPItem[] memory untrustedVals, + bytes[] memory seals, + bytes32 blkHash + ) internal pure returns (bytes[] memory, bool) { bytes[] memory validators = new bytes[](untrustedVals.length); uint8 success = 0; - for (uint i = 0; i < seals.length; i++) { + for (uint256 i = 0; i < seals.length; i++) { validators[i] = untrustedVals[i].toBytes(); if (seals[i].length == 0) { continue; - } else if (validators[i].toAddress() == ECRecovery.recover(blkHash, seals[i])) { + } else if ( + validators[i].toAddress() == + ECRecovery.recover(blkHash, seals[i]) + ) { success++; } } - return (validators, success > untrustedVals.length * 2 / 3); + return (validators, success > (untrustedVals.length * 2) / 3); } /// State verification functions /// @@ -238,7 +359,7 @@ contract IBFT2Client is IClient { string memory counterpartyClientIdentifier, bytes memory proof, bytes memory clientStateBytes - ) public override view returns (bool) { + ) public view override returns (bool) { ClientState.Data memory clientState; ConsensusState.Data memory consensusState; bool found; @@ -254,7 +375,16 @@ contract IBFT2Client is IClient { if (!found) { return false; } - return verifyMembership(proof, consensusState.root.toBytes32(), prefix, IBCIdentifier.clientStateCommitmentSlot(counterpartyClientIdentifier), keccak256(clientStateBytes)); + return + verifyMembership( + proof, + consensusState.root.toBytes32(), + prefix, + IBCIdentifier.clientStateCommitmentSlot( + counterpartyClientIdentifier + ), + keccak256(clientStateBytes) + ); } function verifyClientConsensusState( @@ -266,7 +396,7 @@ contract IBFT2Client is IClient { bytes memory prefix, bytes memory proof, bytes memory consensusStateBytes // serialized with pb - ) public override view returns (bool) { + ) public view override returns (bool) { ClientState.Data memory clientState; ConsensusState.Data memory consensusState; bool found; @@ -282,7 +412,18 @@ contract IBFT2Client is IClient { if (!found) { return false; } - return verifyMembership(proof, consensusState.root.toBytes32(), prefix, IBCIdentifier.consensusStateCommitmentSlot(counterpartyClientIdentifier, consensusHeight.revision_number, consensusHeight.revision_height), keccak256(consensusStateBytes)); + return + verifyMembership( + proof, + consensusState.root.toBytes32(), + prefix, + IBCIdentifier.consensusStateCommitmentSlot( + counterpartyClientIdentifier, + consensusHeight.revision_number, + consensusHeight.revision_height + ), + keccak256(consensusStateBytes) + ); } function verifyConnectionState( @@ -293,7 +434,7 @@ contract IBFT2Client is IClient { bytes memory proof, string memory connectionId, bytes memory connectionBytes // serialized with pb - ) public override view returns (bool) { + ) public view override returns (bool) { ClientState.Data memory clientState; ConsensusState.Data memory consensusState; bool found; @@ -309,7 +450,14 @@ contract IBFT2Client is IClient { if (!found) { return false; } - return verifyMembership(proof, consensusState.root.toBytes32(), prefix, IBCIdentifier.connectionCommitmentSlot(connectionId), keccak256(connectionBytes)); + return + verifyMembership( + proof, + consensusState.root.toBytes32(), + prefix, + IBCIdentifier.connectionCommitmentSlot(connectionId), + keccak256(connectionBytes) + ); } function verifyChannelState( @@ -321,7 +469,7 @@ contract IBFT2Client is IClient { string memory portId, string memory channelId, bytes memory channelBytes // serialized with pb - ) public override view returns (bool) { + ) public view override returns (bool) { ClientState.Data memory clientState; ConsensusState.Data memory consensusState; bool found; @@ -337,7 +485,14 @@ contract IBFT2Client is IClient { if (!found) { return false; } - return verifyMembership(proof, consensusState.root.toBytes32(), prefix, IBCIdentifier.channelCommitmentSlot(portId, channelId), keccak256(channelBytes)); + return + verifyMembership( + proof, + consensusState.root.toBytes32(), + prefix, + IBCIdentifier.channelCommitmentSlot(portId, channelId), + keccak256(channelBytes) + ); } function verifyPacketCommitment( @@ -352,7 +507,7 @@ contract IBFT2Client is IClient { string memory channelId, uint64 sequence, bytes32 commitmentBytes - ) public override view returns (bool) { + ) public view override returns (bool) { ClientState.Data memory clientState; ConsensusState.Data memory consensusState; bool found; @@ -364,14 +519,29 @@ contract IBFT2Client is IClient { if (!validateArgs(clientState, height, prefix, proof)) { return false; } - if (!validateDelayPeriod(host, clientId, height, delayPeriodTime, delayPeriodBlocks)) { + if ( + !validateDelayPeriod( + host, + clientId, + height, + delayPeriodTime, + delayPeriodBlocks + ) + ) { return false; } (consensusState, found) = getConsensusState(host, clientId, height); if (!found) { return false; } - return verifyMembership(proof, consensusState.root.toBytes32(), prefix, IBCIdentifier.packetCommitmentSlot(portId, channelId, sequence), commitmentBytes); + return + verifyMembership( + proof, + consensusState.root.toBytes32(), + prefix, + IBCIdentifier.packetCommitmentSlot(portId, channelId, sequence), + commitmentBytes + ); } function verifyPacketAcknowledgement( @@ -386,23 +556,50 @@ contract IBFT2Client is IClient { string memory channelId, uint64 sequence, bytes memory acknowledgement - ) public override view returns (bool) { - ClientState.Data memory clientState = mustGetClientState(host, clientId); + ) public view override returns (bool) { + ClientState.Data memory clientState = mustGetClientState( + host, + clientId + ); if (!validateArgs(clientState, height, prefix, proof)) { return false; } - if (!validateDelayPeriod(host, clientId, height, delayPeriodTime, delayPeriodBlocks)) { + if ( + !validateDelayPeriod( + host, + clientId, + height, + delayPeriodTime, + delayPeriodBlocks + ) + ) { return false; } - bytes32 stateRoot = mustGetConsensusState(host, clientId, height).root.toBytes32(); - bytes32 ackCommitmentSlot = IBCIdentifier.packetAcknowledgementCommitmentSlot(portId, channelId, sequence); - bytes32 ackCommitment = host.makePacketAcknowledgementCommitment(acknowledgement); - return verifyMembership(proof, stateRoot, prefix, ackCommitmentSlot, ackCommitment); + bytes32 stateRoot = mustGetConsensusState(host, clientId, height) + .root + .toBytes32(); + bytes32 ackCommitmentSlot = IBCIdentifier + .packetAcknowledgementCommitmentSlot(portId, channelId, sequence); + bytes32 ackCommitment = host.makePacketAcknowledgementCommitment( + acknowledgement + ); + return + verifyMembership( + proof, + stateRoot, + prefix, + ackCommitmentSlot, + ackCommitment + ); } /// helper functions /// - function getClientState(IBCHost host, string memory clientId) public view returns (ClientState.Data memory clientState, bool found) { + function getClientState(IBCHost host, string memory clientId) + public + view + returns (ClientState.Data memory clientState, bool found) + { bytes memory clientStateBytes; (clientStateBytes, found) = host.getClientState(clientId); if (!found) { @@ -411,16 +608,32 @@ contract IBFT2Client is IClient { return (ClientState.decode(Any.decode(clientStateBytes).value), true); } - function getConsensusState(IBCHost host, string memory clientId, Height.Data memory height) public view returns (ConsensusState.Data memory consensusState, bool found) { + function getConsensusState( + IBCHost host, + string memory clientId, + Height.Data memory height + ) + public + view + returns (ConsensusState.Data memory consensusState, bool found) + { bytes memory consensusStateBytes; (consensusStateBytes, found) = host.getConsensusState(clientId, height); if (!found) { return (consensusState, false); } - return (ConsensusState.decode(Any.decode(consensusStateBytes).value), true); + return ( + ConsensusState.decode(Any.decode(consensusStateBytes).value), + true + ); } - function validateArgs(ClientState.Data memory cs, Height.Data memory height, bytes memory prefix, bytes memory proof) internal pure returns (bool) { + function validateArgs( + ClientState.Data memory cs, + Height.Data memory height, + bytes memory prefix, + bytes memory proof + ) internal pure returns (bool) { if (cs.latest_height.lt(height)) { return false; } else if (prefix.length == 0) { @@ -431,14 +644,22 @@ contract IBFT2Client is IClient { return true; } - function validateDelayPeriod(IBCHost host, string memory clientId, Height.Data memory height, uint64 delayPeriodTime, uint64 delayPeriodBlocks) private view returns (bool) { + function validateDelayPeriod( + IBCHost host, + string memory clientId, + Height.Data memory height, + uint64 delayPeriodTime, + uint64 delayPeriodBlocks + ) private view returns (bool) { uint64 currentTime = uint64(block.timestamp * 1000 * 1000 * 1000); - uint64 validTime = mustGetProcessedTime(host, clientId, height) + delayPeriodTime; + uint64 validTime = mustGetProcessedTime(host, clientId, height) + + delayPeriodTime; if (currentTime < validTime) { return false; } uint64 currentHeight = uint64(block.number); - uint64 validHeight = mustGetProcessedHeight(host, clientId, height) + delayPeriodBlocks; + uint64 validHeight = mustGetProcessedHeight(host, clientId, height) + + delayPeriodBlocks; if (currentHeight < validHeight) { return false; } @@ -446,27 +667,55 @@ contract IBFT2Client is IClient { } // NOTE: this is a workaround to avoid the error `Stack too deep` in caller side - function mustGetClientState(IBCHost host, string memory clientId) internal view returns (ClientState.Data memory) { - (ClientState.Data memory clientState, bool found) = getClientState(host, clientId); + function mustGetClientState(IBCHost host, string memory clientId) + internal + view + returns (ClientState.Data memory) + { + (ClientState.Data memory clientState, bool found) = getClientState( + host, + clientId + ); require(found, "client state not found"); return clientState; } // NOTE: this is a workaround to avoid the error `Stack too deep` in caller side - function mustGetConsensusState(IBCHost host, string memory clientId, Height.Data memory height) internal view returns (ConsensusState.Data memory) { - (ConsensusState.Data memory consensusState, bool found) = getConsensusState(host, clientId, height); + function mustGetConsensusState( + IBCHost host, + string memory clientId, + Height.Data memory height + ) internal view returns (ConsensusState.Data memory) { + ( + ConsensusState.Data memory consensusState, + bool found + ) = getConsensusState(host, clientId, height); require(found, "consensus state not found"); return consensusState; } - function mustGetProcessedTime(IBCHost host, string memory clientId, Height.Data memory height) internal view returns (uint64) { - (uint256 processedTime, bool found) = host.getProcessedTime(clientId, height); + function mustGetProcessedTime( + IBCHost host, + string memory clientId, + Height.Data memory height + ) internal view returns (uint64) { + (uint256 processedTime, bool found) = host.getProcessedTime( + clientId, + height + ); require(found, "processed time not found"); return uint64(processedTime) * 1000 * 1000 * 1000; } - function mustGetProcessedHeight(IBCHost host, string memory clientId, Height.Data memory height) internal view returns (uint64) { - (uint256 processedHeight, bool found) = host.getProcessedHeight(clientId, height); + function mustGetProcessedHeight( + IBCHost host, + string memory clientId, + Height.Data memory height + ) internal view returns (uint64) { + (uint256 processedHeight, bool found) = host.getProcessedHeight( + clientId, + height + ); require(found, "processed height not found"); return uint64(processedHeight); } @@ -483,13 +732,23 @@ contract IBFT2Client is IClient { return expectedValue == dataHash.toRLPItem().toBytes().toBytes32(); } - function parseBesuHeader(Header.Data memory header) internal pure returns (ParsedBesuHeader memory) { + function parseBesuHeader(Header.Data memory header) + internal + pure + returns (ParsedBesuHeader memory) + { ParsedBesuHeader memory parsedHeader; parsedHeader.base = header; - RLP.RLPItem[] memory items = header.besu_header_rlp.toRLPItem().toList(); + RLP.RLPItem[] memory items = header + .besu_header_rlp + .toRLPItem() + .toList(); parsedHeader.stateRoot = items[3].toBytes().toBytes32(); - parsedHeader.height = Height.Data({ revision_number: 0, revision_height: uint64(items[8].toUint()) }); + parsedHeader.height = Height.Data({ + revision_number: 0, + revision_height: uint64(items[8].toUint()) + }); require(items.length == 15, "items length must be 15"); parsedHeader.time = uint64(items[11].toUint()); @@ -500,9 +759,21 @@ contract IBFT2Client is IClient { return parsedHeader; } - function verifyStorageProof(address account, bytes32 stateRoot, bytes memory accountStateProof) internal pure returns (bytes32) { + function verifyStorageProof( + address account, + bytes32 stateRoot, + bytes memory accountStateProof + ) internal pure returns (bytes32) { bytes32 proofPath = keccak256(abi.encodePacked(account)); - bytes memory accountRLP = accountStateProof.verify(stateRoot, proofPath); // reverts if proof is invalid - return bytes32(accountRLP.toRLPItem().toList()[ACCOUNT_STORAGE_ROOT_INDEX].toUint()); + bytes memory accountRLP = accountStateProof.verify( + stateRoot, + proofPath + ); // reverts if proof is invalid + return + bytes32( + accountRLP + .toRLPItem() + .toList()[ACCOUNT_STORAGE_ROOT_INDEX].toUint() + ); } } diff --git a/contracts/core/IClient.sol b/contracts/core/IClient.sol index 9d4e59bb..02929f67 100644 --- a/contracts/core/IClient.sol +++ b/contracts/core/IClient.sol @@ -38,17 +38,17 @@ interface IClient { ConsensusUpdate.Result[] memory results ); - function updateStateOnMisbehaviour( - bytes calldata clientStateBytes, - bytes calldata headerBytes - ) external returns (bytes memory newClientStateBytes); + // function updateStateOnMisbehaviour( + // bytes calldata clientStateBytes, + // bytes calldata headerBytes + // ) external returns (bytes memory newClientStateBytes); - function checkForMisbehaviour( - IBCHost host, - string calldata clientId, - bytes calldata clientStateBytes, - bytes calldata headerBytes - ) external returns (bool); + // function checkForMisbehaviour( + // IBCHost host, + // string calldata clientId, + // bytes calldata clientStateBytes, + // bytes calldata headerBytes + // ) external returns (bool); function verifyClientState( IBCHost host, diff --git a/contracts/core/MockClient.sol b/contracts/core/MockClient.sol index 02b69a1a..feca655b 100644 --- a/contracts/core/MockClient.sol +++ b/contracts/core/MockClient.sol @@ -6,11 +6,7 @@ import "./IBCHost.sol"; import "./IBCMsgs.sol"; import "./IBCHeight.sol"; import "./types/Client.sol"; -import { - IbcLightclientsMockV1ClientState as ClientState, - IbcLightclientsMockV1ConsensusState as ConsensusState, - IbcLightclientsMockV1Header as Header -} from "./types/MockClient.sol"; +import {IbcLightclientsMockV1ClientState as ClientState, IbcLightclientsMockV1ConsensusState as ConsensusState, IbcLightclientsMockV1Header as Header} from "./types/MockClient.sol"; import {GoogleProtobufAny as Any} from "./types/GoogleProtobufAny.sol"; import "../lib/Bytes.sol"; @@ -32,9 +28,15 @@ contract MockClient is IClient { // TODO The typeUrl should be defined in types/MockClient.sol // The schema of typeUrl follows cosmos/cosmos-sdk/codec/types/any.go pts = protoTypes({ - clientState: keccak256(abi.encodePacked("/ibc.lightclients.mock.v1.ClientState")), - consensusState: keccak256(abi.encodePacked("/ibc.lightclients.mock.v1.ConsensusState")), - header: keccak256(abi.encodePacked("/ibc.lightclients.mock.v1.Header")) + clientState: keccak256( + abi.encodePacked("/ibc.lightclients.mock.v1.ClientState") + ), + consensusState: keccak256( + abi.encodePacked("/ibc.lightclients.mock.v1.ConsensusState") + ), + header: keccak256( + abi.encodePacked("/ibc.lightclients.mock.v1.Header") + ) }); } @@ -45,19 +47,27 @@ contract MockClient is IClient { IBCHost host, string memory clientId, Height.Data memory height - ) public override view returns (uint64, bool) { - (ConsensusState.Data memory consensusState, bool found) = getConsensusState(host, clientId, height); + ) public view override returns (uint64, bool) { + ( + ConsensusState.Data memory consensusState, + bool found + ) = getConsensusState(host, clientId, height); if (!found) { return (0, false); } return (consensusState.timestamp, true); } - function getLatestHeight( - IBCHost host, - string memory clientId - ) public override view returns (Height.Data memory, bool) { - (ClientState.Data memory clientState, bool found) = getClientState(host, clientId); + function getLatestHeight(IBCHost host, string memory clientId) + public + view + override + returns (Height.Data memory, bool) + { + (ClientState.Data memory clientState, bool found) = getClientState( + host, + clientId + ); if (!found) { return (Height.Data(0, 0), false); } @@ -65,30 +75,65 @@ contract MockClient is IClient { } /** - * @dev checkHeaderAndUpdateState checks if the provided header is valid + * @dev verifyHeader checks if the provided header is valid + */ + function verifyHeader( + IBCHost, + string memory, + bytes memory clientStateBytes, + bytes memory headerBytes + ) public view override returns (bool) { + Any.Data memory anyClientState = Any.decode(clientStateBytes); + Any.Data memory anyHeader = Any.decode(headerBytes); + require( + keccak256(abi.encodePacked(anyClientState.type_url)) == + pts.clientState, + "invalid client type" + ); + return + keccak256(abi.encodePacked(anyHeader.type_url)) != pts.header + ? false + : true; + } + + /** + * @dev updateState after header is verified */ - function checkHeaderAndUpdateState( + + function updateState( IBCHost, string memory, bytes memory clientStateBytes, bytes memory headerBytes - ) public override view returns (bytes memory newClientStateBytes, bytes memory newConsensusStateBytes, Height.Data memory height) { + ) + public + pure + override + returns ( + bytes memory newClientStateBytes, + ConsensusUpdate.Result[] memory results + ) + { uint64 timestamp; - Any.Data memory anyClientState; Any.Data memory anyConsensusState; - anyClientState = Any.decode(clientStateBytes); - require(keccak256(abi.encodePacked(anyClientState.type_url)) == pts.clientState, "invalid client type"); - ClientState.Data memory clientState = ClientState.decode(anyClientState.value); - (height, timestamp) = parseHeader(headerBytes); - if (height.gt(clientState.latest_height)) { - clientState.latest_height = height; + Any.Data memory anyClientState = Any.decode(clientStateBytes); + ClientState.Data memory clientState = ClientState.decode( + anyClientState.value + ); + + (results[0].height, timestamp) = parseHeader(headerBytes); + if (results[0].height.gt(clientState.latest_height)) { + clientState.latest_height = results[0].height; } anyClientState.value = ClientState.encode(clientState); anyConsensusState.type_url = "/ibc.lightclients.mock.v1.ConsensusState"; - anyConsensusState.value = ConsensusState.encode(ConsensusState.Data({timestamp: timestamp})); - return (Any.encode(anyClientState), Any.encode(anyConsensusState), height); + anyConsensusState.value = ConsensusState.encode( + ConsensusState.Data({timestamp: timestamp}) + ); + results[0].newConsensusStateBytes = Any.encode(anyConsensusState); + return (Any.encode(anyClientState), results); } function verifyClientState( @@ -99,7 +144,7 @@ contract MockClient is IClient { string memory, bytes memory proof, bytes memory clientStateBytes // serialized with pb - ) public override view returns (bool) { + ) public view override returns (bool) { (, bool found) = host.getConsensusState(clientId, height); require(found, "consensus state not found"); return sha256(clientStateBytes) == proof.toBytes32(); @@ -114,7 +159,7 @@ contract MockClient is IClient { bytes memory, bytes memory proof, bytes memory consensusStateBytes // serialized with pb - ) public override view returns (bool) { + ) public view override returns (bool) { (, bool found) = host.getConsensusState(clientId, height); require(found, "consensus state not found"); return sha256(consensusStateBytes) == proof.toBytes32(); @@ -128,7 +173,7 @@ contract MockClient is IClient { bytes memory proof, string memory, bytes memory connectionBytes // serialized with pb - ) public override view returns (bool) { + ) public view override returns (bool) { (, bool found) = host.getConsensusState(clientId, height); require(found, "consensus state not found"); return sha256(connectionBytes) == proof.toBytes32(); @@ -143,7 +188,7 @@ contract MockClient is IClient { string memory, string memory, bytes memory channelBytes // serialized with pb - ) public override view returns (bool) { + ) public view override returns (bool) { (, bool found) = host.getConsensusState(clientId, height); require(found, "consensus state not found"); return sha256(channelBytes) == proof.toBytes32(); @@ -161,7 +206,7 @@ contract MockClient is IClient { string memory, uint64, bytes32 commitmentBytes - ) public override view returns (bool) { + ) public view override returns (bool) { (, bool found) = host.getConsensusState(clientId, height); require(found, "consensus state not found"); return commitmentBytes == proof.toBytes32(); @@ -179,13 +224,19 @@ contract MockClient is IClient { string memory, uint64, bytes memory acknowledgement - ) public override view returns (bool) { + ) public view override returns (bool) { (, bool found) = host.getConsensusState(clientId, height); require(found, "consensus state not found"); - return host.makePacketAcknowledgementCommitment(acknowledgement) == proof.toBytes32(); + return + host.makePacketAcknowledgementCommitment(acknowledgement) == + proof.toBytes32(); } - function getClientState(IBCHost host, string memory clientId) public view returns (ClientState.Data memory clientState, bool found) { + function getClientState(IBCHost host, string memory clientId) + public + view + returns (ClientState.Data memory clientState, bool found) + { bytes memory clientStateBytes; (clientStateBytes, found) = host.getClientState(clientId); if (!found) { @@ -194,19 +245,34 @@ contract MockClient is IClient { return (ClientState.decode(Any.decode(clientStateBytes).value), true); } - function getConsensusState(IBCHost host, string memory clientId, Height.Data memory height) public view returns (ConsensusState.Data memory consensusState, bool found) { + function getConsensusState( + IBCHost host, + string memory clientId, + Height.Data memory height + ) + public + view + returns (ConsensusState.Data memory consensusState, bool found) + { bytes memory consensusStateBytes; (consensusStateBytes, found) = host.getConsensusState(clientId, height); if (!found) { return (consensusState, false); } - return (ConsensusState.decode(Any.decode(consensusStateBytes).value), true); + return ( + ConsensusState.decode(Any.decode(consensusStateBytes).value), + true + ); } - function parseHeader(bytes memory headerBytes) internal view returns (Height.Data memory, uint64) { - Any.Data memory any = Any.decode(headerBytes); - require(keccak256(abi.encodePacked(any.type_url)) == pts.header, "invalid header type"); - Header.Data memory header = Header.decode(any.value); + function parseHeader(bytes memory headerBytes) + internal + pure + returns (Height.Data memory, uint64) + { + Header.Data memory header = Header.decode( + (Any.decode(headerBytes)).value + ); return (header.height, header.timestamp); } }