Skip to content

Commit

Permalink
Merge pull request #6 from datachainlab/update-ibc
Browse files Browse the repository at this point in the history
Update ibc-solidity to v0.3.23

Signed-off-by: Jun Kimura <[email protected]>
  • Loading branch information
bluele authored Jan 24, 2024
2 parents 8242dfc + 6c6be07 commit e8e86ef
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 180 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ An e2e demo is available [here](https://github.com/datachainlab/cosmos-ethereum-

## Gas cost

- registerEnclaveKey: 550k: first registration in the client or signing key changed(very rare)
- registerEnclaveKey: 210k
- updateState: 190k
- registerEnclaveKey: 500k: first registration in the client or signing key changed(very rare)
- registerEnclaveKey: 160k
- updateState: 100k
- verifyMembership: 15k
- verifyNonMembership: 14k

Expand Down
143 changes: 61 additions & 82 deletions contracts/LCPClient.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,45 +52,32 @@ contract LCPClient is ILightClient {
}

// @dev isDevelopmentMode returns true if the client allows the remote attestation of IAS in development.
function isDevelopmentMode() public view returns (bool) {
function isDevelopmentMode() external view returns (bool) {
return developmentMode;
}

/**
* @dev createClient creates a new client with the given state.
* If succeeded, it returns a commitment for the initial state.
* @dev initializeClient initializes a new client with the given state.
* If succeeded, it returns heights at which the consensus state are stored.
* The function must be only called by IBCHandler.
*/
function createClient(string calldata clientId, bytes calldata clientStateBytes, bytes calldata consensusStateBytes)
public
onlyIBC
returns (bytes32 clientStateCommitment, ConsensusStateUpdate memory update, bool ok)
{
ClientState.Data memory clientState;
ConsensusState.Data memory consensusState;
function initializeClient(
string calldata clientId,
bytes calldata protoClientState,
bytes calldata protoConsensusState
) external onlyIBC returns (Height.Data memory height) {
ClientState.Data memory clientState = LCPProtoMarshaler.unmarshalClientState(protoClientState);
ConsensusState.Data memory consensusState = LCPProtoMarshaler.unmarshalConsensusState(protoConsensusState);

(clientState, ok) = LCPProtoMarshaler.unmarshalClientState(clientStateBytes);
if (!ok) {
return (clientStateCommitment, update, false);
}
// NOTE consensusState is always default value
(consensusState, ok) = LCPProtoMarshaler.unmarshalConsensusState(consensusStateBytes);
if (!ok) {
return (clientStateCommitment, update, false);
}
// validate an initial state
require(
clientState.latest_height.revision_number == 0 && clientState.latest_height.revision_height == 0,
"invalid initial height"
);
require(clientState.key_expiration != 0, "key_expiration must be non-zero");
require(clientState.mrenclave.length == 32, "invalid mrenclave length");
require(consensusState.timestamp == 0 && consensusState.state_id.length == 0, "invalid consensus state");

// Validate an initial state
if (clientState.latest_height.revision_number != 0 || clientState.latest_height.revision_height != 0) {
return (clientStateCommitment, update, false);
}
if (clientState.key_expiration == 0) {
return (clientStateCommitment, update, false);
}
if (clientState.mrenclave.length != 32) {
return (clientStateCommitment, update, false);
}
if (consensusState.timestamp != 0 || consensusState.state_id.length != 0) {
return (clientStateCommitment, update, false);
}
// NOTE should we set only non-default values?
clientStates[clientId] = clientState;

Expand All @@ -102,34 +89,29 @@ contract LCPClient is ILightClient {
allowedAdvisories[clientId][clientState.allowed_advisory_ids[i]] = AVRValidator.FLAG_ALLOWED;
}

return (
keccak256(clientStateBytes),
ConsensusStateUpdate({
consensusStateCommitment: keccak256(consensusStateBytes),
height: clientState.latest_height
}),
true
);
return clientState.latest_height;
}

/**
* @dev getTimestampAtHeight returns the timestamp of the consensus state at the given height.
*/
function getTimestampAtHeight(string calldata clientId, Height.Data calldata height)
public
external
view
returns (uint64, bool)
returns (uint64)
{
ConsensusState.Data storage consensusState = consensusStates[clientId][height.toUint128()];
return (consensusState.timestamp, consensusState.timestamp != 0);
require(consensusState.timestamp != 0, "consensus state not found");
return consensusState.timestamp;
}

/**
* @dev getLatestHeight returns the latest height of the client state corresponding to `clientId`.
*/
function getLatestHeight(string calldata clientId) public view returns (Height.Data memory, bool) {
function getLatestHeight(string calldata clientId) external view returns (Height.Data memory) {
ClientState.Data storage clientState = clientStates[clientId];
return (clientState.latest_height, clientState.latest_height.revision_height != 0);
require(clientState.latest_height.revision_height != 0, "client state not found");
return clientState.latest_height;
}
/**
* @dev getStatus returns the status of the client corresponding to `clientId`.
Expand All @@ -141,21 +123,24 @@ contract LCPClient is ILightClient {
}

/**
* @dev updateClient updates the client corresponding to `clientId`.
* If succeeded, it returns a commitment for the updated state.
* If there are no updates for consensus state, this function should returns an empty array as `updates`.
* @dev routeUpdateClient returns the calldata to the receiving function of the client message.
* Light client contract may encode a client message as other encoding scheme(e.g. ethereum ABI)
* Check ADR-001 for details.
*/
function updateClient(string calldata clientId, bytes calldata clientMessageBytes)
public
onlyIBC
returns (bytes32 clientStateCommitment, ConsensusStateUpdate[] memory updates, bool ok)
function routeUpdateClient(string calldata clientId, bytes calldata protoClientMessage)
external
pure
returns (bytes4 selector, bytes memory args)
{
Any.Data memory anyClientMessage = Any.decode(clientMessageBytes);
Any.Data memory anyClientMessage = Any.decode(protoClientMessage);
bytes32 typeUrlHash = keccak256(abi.encodePacked(anyClientMessage.type_url));
if (typeUrlHash == LCPProtoMarshaler.UPDATE_CLIENT_MESSAGE_TYPE_URL_HASH) {
return updateState(clientId, UpdateClientMessage.decode(anyClientMessage.value));
return (this.updateState.selector, abi.encode(clientId, UpdateClientMessage.decode(anyClientMessage.value)));
} else if (typeUrlHash == LCPProtoMarshaler.REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL_HASH) {
return registerEnclaveKey(clientId, RegisterEnclaveKeyMessage.decode(anyClientMessage.value));
return (
this.registerEnclaveKey.selector,
abi.encode(clientId, RegisterEnclaveKeyMessage.decode(anyClientMessage.value))
);
} else {
revert("unknown type url");
}
Expand Down Expand Up @@ -238,7 +223,7 @@ contract LCPClient is ILightClient {
* @dev getClientState returns the clientState corresponding to `clientId`.
* If it's not found, the function returns false.
*/
function getClientState(string calldata clientId) public view returns (bytes memory clientStateBytes, bool) {
function getClientState(string calldata clientId) external view returns (bytes memory clientStateBytes, bool) {
ClientState.Data storage clientState = clientStates[clientId];
if (clientState.latest_height.revision_height == 0) {
return (clientStateBytes, false);
Expand All @@ -251,7 +236,7 @@ contract LCPClient is ILightClient {
* If it's not found, the function returns false.
*/
function getConsensusState(string calldata clientId, Height.Data calldata height)
public
external
view
returns (bytes memory consensusStateBytes, bool)
{
Expand All @@ -262,9 +247,9 @@ contract LCPClient is ILightClient {
return (LCPProtoMarshaler.marshal(consensusState), true);
}

function updateState(string calldata clientId, UpdateClientMessage.Data memory message)
internal
returns (bytes32 clientStateCommitment, ConsensusStateUpdate[] memory updates, bool ok)
function updateState(string calldata clientId, UpdateClientMessage.Data calldata message)
public
returns (Height.Data[] memory heights)
{
require(message.signer.length == 20, "invalid signer length");
require(message.signature.length == 65, "invalid signature length");
Expand Down Expand Up @@ -300,28 +285,14 @@ contract LCPClient is ILightClient {
consensusState.state_id = abi.encodePacked(commitment.postStateId);
consensusState.timestamp = uint64(commitment.timestamp);

/* Make updates message */

updates = new ConsensusStateUpdate[](1);
updates[0] = ConsensusStateUpdate({
consensusStateCommitment: keccak256(LCPProtoMarshaler.marshal(consensusState)),
height: commitment.postHeight
});

return (keccak256(LCPProtoMarshaler.marshal(clientState)), updates, true);
}

function isActiveKey(string calldata clientId, address signer) internal view returns (bool) {
uint256 expiredAt = enclaveKeys[clientId][signer];
if (expiredAt == 0) {
return false;
}
return expiredAt > block.timestamp;
heights = new Height.Data[](1);
heights[0] = commitment.postHeight;
return heights;
}

function registerEnclaveKey(string calldata clientId, RegisterEnclaveKeyMessage.Data memory message)
internal
returns (bytes32 clientStateCommitment, ConsensusStateUpdate[] memory updates, bool ok)
function registerEnclaveKey(string calldata clientId, RegisterEnclaveKeyMessage.Data calldata message)
public
returns (Height.Data[] memory heights)
{
{
AVRValidator.RSAParams storage params = verifiedSigningRSAParams[keccak256(message.signing_cert)];
Expand Down Expand Up @@ -363,14 +334,22 @@ contract LCPClient is ILightClient {
if (enclaveKeys[clientId][enclaveKey] != 0) {
require(enclaveKeys[clientId][enclaveKey] == expiredAt, "expiredAt mismatch");
// NOTE: if the key already exists, don't update any state
return (bytes32(0), updates, true);
return heights;
}

enclaveKeys[clientId][enclaveKey] = expiredAt;
emit RegisteredEnclaveKey(clientId, enclaveKey, expiredAt);

// Note: client and consensus state are not always updated in registerEnclaveKey
return (bytes32(0), updates, true);
return heights;
}

function isActiveKey(string calldata clientId, address signer) internal view returns (bool) {
uint256 expiredAt = enclaveKeys[clientId][signer];
if (expiredAt == 0) {
return false;
}
return expiredAt > block.timestamp;
}

function verifyCommitmentProof(bytes32 commitment, bytes memory signature, address signer)
Expand Down
2 changes: 1 addition & 1 deletion contracts/LCPCommitment.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ library LCPCommitment {
bytes state;
}

function parseUpdateClientMessage(bytes memory commitmentBytes)
function parseUpdateClientMessage(bytes calldata commitmentBytes)
internal
pure
returns (UpdateClientMessage memory commitment)
Expand Down
26 changes: 12 additions & 14 deletions contracts/LCPProtoMarshaler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,27 +49,25 @@ library LCPProtoMarshaler {
return Any.encode(anyConsensusState);
}

function unmarshalClientState(bytes calldata bz)
external
pure
returns (ClientState.Data memory clientState, bool ok)
{
function unmarshalClientState(bytes calldata bz) external pure returns (ClientState.Data memory clientState) {
Any.Data memory anyClientState = Any.decode(bz);
if (keccak256(abi.encodePacked(anyClientState.type_url)) != CLIENT_STATE_TYPE_URL_HASH) {
return (clientState, false);
}
return (ClientState.decode(anyClientState.value), true);
require(
keccak256(abi.encodePacked(anyClientState.type_url)) == CLIENT_STATE_TYPE_URL_HASH,
"invalid client state type url"
);
return ClientState.decode(anyClientState.value);
}

function unmarshalConsensusState(bytes calldata bz)
external
pure
returns (ConsensusState.Data memory consensusState, bool ok)
returns (ConsensusState.Data memory consensusState)
{
Any.Data memory anyConsensusState = Any.decode(bz);
if (keccak256(abi.encodePacked(anyConsensusState.type_url)) != CONSENSUS_STATE_TYPE_URL_HASH) {
return (consensusState, false);
}
return (ConsensusState.decode(anyConsensusState.value), true);
require(
keccak256(abi.encodePacked(anyConsensusState.type_url)) == CONSENSUS_STATE_TYPE_URL_HASH,
"invalid consensus state type url"
);
return ConsensusState.decode(anyConsensusState.value);
}
}
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "LCP Client in Solidity",
"dependencies": {
"@openzeppelin/contracts": "^4.8.0",
"@hyperledger-labs/yui-ibc-solidity": "git+https://github.com/hyperledger-labs/yui-ibc-solidity.git#v0.3.21",
"@hyperledger-labs/yui-ibc-solidity": "git+https://github.com/hyperledger-labs/yui-ibc-solidity.git#v0.3.23",
"base64": "git+https://github.com/datachainlab/base64.git#4d85607b18d981acff392d2e99ba654305552a97",
"solidity-datetime": "git+https://github.com/datachainlab/solidity-datetime.git#294fc244973cc5f9ec374713affde12c1403927c",
"@ensdomains/ens-contracts": "0.0.22"
Expand Down
Loading

0 comments on commit e8e86ef

Please sign in to comment.