diff --git a/contracts/DCAPValidator.sol b/contracts/DCAPValidator.sol index ff4826e..5e67c7a 100644 --- a/contracts/DCAPValidator.sol +++ b/contracts/DCAPValidator.sol @@ -48,6 +48,7 @@ library DCAPValidator { */ struct Output { string tcbStatus; + uint32 minTcbEvaluationDataNumber; bytes32 sgxIntelRootCAHash; uint64 validityNotBeforeMax; uint64 validityNotAfterMin; @@ -70,6 +71,7 @@ library DCAPValidator { Output memory output; output.tcbStatus = tcbStatusToString(uint8(outputBytes[8])); + output.minTcbEvaluationDataNumber = uint32(bytes4(outputBytes[9:13])); output.sgxIntelRootCAHash = bytes32(outputBytes[19:51]); output.validityNotBeforeMax = uint64(bytes8(outputBytes[51:59])); output.validityNotAfterMin = uint64(bytes8(outputBytes[59:67])); diff --git a/contracts/ILCPClientErrors.sol b/contracts/ILCPClientErrors.sol index a7ba8a8..a9c625b 100644 --- a/contracts/ILCPClientErrors.sol +++ b/contracts/ILCPClientErrors.sol @@ -62,6 +62,8 @@ interface ILCPClientErrors { error LCPClientZKDCAPInvalidConstructorParams(); error LCPClientZKDCAPOutputNotValid(); error LCPClientZKDCAPUnrecognizedTCBStatus(); + error LCPClientZKDCAPCurrentTcbEvaluationDataNumberNotSet(); + error LCPClientZKDCAPInvalidNextTcbEvaluationDataNumberInfo(); error LCPClientZKDCAPInvalidVerifierInfos(); error LCPClientZKDCAPInvalidVerifierInfoLength(); error LCPClientZKDCAPInvalidVerifierInfoZKVMType(); @@ -72,4 +74,5 @@ interface ILCPClientErrors { error LCPClientZKDCAPDisallowedTCBStatus(); error LCPClientZKDCAPDisallowedAdvisoryID(); error LCPClientZKDCAPUnexpectedEnclaveDebugMode(); + error LCPClientZKDCAPUnexpectedTcbEvaluationDataNumber(uint64 currentTcbEvaluationDataNumber); } diff --git a/contracts/LCPClientZKDCAPBase.sol b/contracts/LCPClientZKDCAPBase.sol index bd47257..178a94d 100644 --- a/contracts/LCPClientZKDCAPBase.sol +++ b/contracts/LCPClientZKDCAPBase.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.12; import {IBCHeight} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/02-client/IBCHeight.sol"; import {Height} from "@hyperledger-labs/yui-ibc-solidity/contracts/proto/Client.sol"; +import {IIBCHandler} from "@hyperledger-labs/yui-ibc-solidity/contracts/core/25-handler/IIBCHandler.sol"; import {IRiscZeroVerifier} from "risc0-ethereum/contracts/src/IRiscZeroVerifier.sol"; import { IbcLightclientsLcpV1ClientState as ProtoClientState, @@ -22,7 +23,14 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { // --------------------- Events --------------------- - event ZKDCAPRegisteredEnclaveKey(string clientId, address enclaveKey, uint256 expiredAt, address operator); + /// @dev Emitted when an enclave key from zkDCAP quote is registered. + event LCPClientZKDCAPRegisteredEnclaveKey(string clientId, address enclaveKey, uint256 expiredAt, address operator); + + /// @dev Emitted when the current TCB evaluation data number is updated. + event LCPClientZKDCAPUpdateCurrentTcbEvaluationDataNumber(string clientId, uint32 tcbEvaluationDataNumber); + /// @dev Emitted when the next TCB evaluation data number is updated. + /// This event is emitted only when the new next TCB evaluation data number is set. + event LCPClientZKDCAPUpdateNextTcbEvaluationDataNumber(string clientId, uint32 tcbEvaluationDataNumber); // --------------------- Immutable fields --------------------- @@ -74,22 +82,27 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { ClientStorage storage clientStorage = clientStorages[clientId]; (ProtoClientState.Data memory clientState,) = _initializeClient(clientStorage, protoClientState, protoConsensusState); - if (clientState.zkdcap_verifier_infos.length != 1) { - revert LCPClientZKDCAPInvalidVerifierInfos(); + if (clientState.current_tcb_evaluation_data_number == 0) { + revert LCPClientZKDCAPCurrentTcbEvaluationDataNumberNotSet(); } - bytes memory verifierInfo = clientState.zkdcap_verifier_infos[0]; - if (verifierInfo.length != 64) { - revert LCPClientZKDCAPInvalidVerifierInfoLength(); + // check if both next_tcb_evaluation_data_number and next_tcb_evaluation_data_number_update_time are zero or non-zero + if ( + (clientState.next_tcb_evaluation_data_number == 0) + != (clientState.next_tcb_evaluation_data_number_update_time == 0) + ) { + revert LCPClientZKDCAPInvalidNextTcbEvaluationDataNumberInfo(); } - if (uint8(verifierInfo[0]) != ZKVM_TYPE_RISC_ZERO) { - revert LCPClientZKDCAPInvalidVerifierInfoZKVMType(); + if ( + clientState.next_tcb_evaluation_data_number != 0 + && clientState.current_tcb_evaluation_data_number >= clientState.next_tcb_evaluation_data_number + ) { + revert LCPClientZKDCAPInvalidNextTcbEvaluationDataNumberInfo(); } - // 32..64 bytes: image ID - bytes32 imageId; - assembly { - imageId := mload(add(add(verifierInfo, 32), 32)) + if (clientState.zkdcap_verifier_infos.length != 1) { + revert LCPClientZKDCAPInvalidVerifierInfos(); } - clientStorage.zkDCAPRisc0ImageId = imageId; + // Currently, the client only supports RISC Zero zkVM + clientStorage.zkDCAPRisc0ImageId = parseRiscZeroVerifierInfo(clientState.zkdcap_verifier_infos[0]); return clientState.latest_height; } @@ -131,6 +144,7 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { if (clientStorage.zkDCAPRisc0ImageId == bytes32(0)) { revert LCPClientZKDCAPRisc0ImageIdNotSet(); } + ProtoClientState.Data storage clientState = clientStorage.clientState; // NOTE: the client must revert if the proof is invalid riscZeroVerifier.verify( message.proof, clientStorage.zkDCAPRisc0ImageId, sha256(message.quote_verification_output) @@ -139,7 +153,7 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { if (output.sgxIntelRootCAHash != intelRootCAHash) { revert LCPClientZKDCAPUnexpectedIntelRootCAHash(); } - if (output.mrenclave != bytes32(clientStorage.clientState.mrenclave)) { + if (output.mrenclave != bytes32(clientState.mrenclave)) { revert LCPClientClientStateUnexpectedMrenclave(); } @@ -162,14 +176,30 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { } } + // check if the validity period of the output is valid at the current block timestamp + if (block.timestamp < output.validityNotBeforeMax || block.timestamp > output.validityNotAfterMin) { + revert LCPClientZKDCAPOutputNotValid(); + } + // check if the `output.enclaveDebugEnabled` and `developmentMode` are consistent if (output.enclaveDebugEnabled != developmentMode) { revert LCPClientZKDCAPUnexpectedEnclaveDebugMode(); } - // check if the validity period of the output is valid at the current block timestamp - if (block.timestamp < output.validityNotBeforeMax || block.timestamp > output.validityNotAfterMin) { - revert LCPClientZKDCAPOutputNotValid(); + (bool currentUpdated, bool nextUpdated) = + checkAndUpdateTcbEvaluationDataNumber(clientId, output.minTcbEvaluationDataNumber); + if (currentUpdated) { + emit LCPClientZKDCAPUpdateCurrentTcbEvaluationDataNumber( + clientId, clientState.current_tcb_evaluation_data_number + ); + } + if (nextUpdated) { + emit LCPClientZKDCAPUpdateNextTcbEvaluationDataNumber(clientId, clientState.next_tcb_evaluation_data_number); + } + if (currentUpdated || nextUpdated) { + // update the commitment of the client state in the IBC handler + // `heights` is always empty because the consensus state is never updated in this function + IIBCHandler(ibcHandler).updateClientCommitments(clientId, heights); } // if `operator_signature` is empty, the operator address is zero @@ -178,7 +208,7 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { operator = verifyECDSASignature( keccak256( LCPOperator.computeEIP712ZKDCAPRegisterEnclaveKey( - clientStorage.clientState.zkdcap_verifier_infos[0], keccak256(message.quote_verification_output) + clientState.zkdcap_verifier_infos[0], keccak256(message.quote_verification_output) ) ), message.operator_signature @@ -197,15 +227,135 @@ abstract contract LCPClientZKDCAPBase is LCPClientBase { if (ekInfo.expiredAt != expiredAt) { revert LCPClientEnclaveKeyUnexpectedExpiredAt(); } - // NOTE: if the key already exists, don't update any state return heights; } ekInfo.expiredAt = expiredAt; ekInfo.operator = operator; - emit ZKDCAPRegisteredEnclaveKey(clientId, output.enclaveKey, expiredAt, operator); + emit LCPClientZKDCAPRegisteredEnclaveKey(clientId, output.enclaveKey, expiredAt, operator); - // Note: client and consensus state are not always updated in registerEnclaveKey return heights; } + + // --------------------- Internal methods --------------------- // + + function parseRiscZeroVerifierInfo(bytes memory verifierInfo) internal pure returns (bytes32) { + // The verifier information for the zkDCAP + // + // The format is as follows: + // 0: zkVM type + // 1-N: arbitrary data for each zkVM type + // + // The format of the risc0 zkVM is as follows: + // | 0 | 1 - 31 | 32 - 64 | + // |---|----------|-----------| + // | 1 | reserved | image id | + uint256 vlen = verifierInfo.length; + if (vlen == 0) { + revert LCPClientZKDCAPInvalidVerifierInfoLength(); + } + // Currently, the client only supports RISC Zero zkVM + if (uint8(verifierInfo[0]) != ZKVM_TYPE_RISC_ZERO) { + revert LCPClientZKDCAPInvalidVerifierInfoZKVMType(); + } + if (vlen < 64) { + revert LCPClientZKDCAPInvalidVerifierInfoLength(); + } + // 32..64 bytes: image ID + bytes32 imageId; + assembly { + imageId := mload(add(add(verifierInfo, 32), 32)) + } + return imageId; + } + + /// @dev checkAndUpdateTcbEvaluationDataNumber checks if the current or next TCB evaluation data number update is required. + /// @param clientId the client identifier + /// @param outputTcbEvaluationDataNumber the minimum TCB evaluation data number of the zkDCAP output + /// @return currentUpdated true if the current TCB evaluation data number is updated + /// @return nextUpdated true if the next TCB evaluation data number is updated + function checkAndUpdateTcbEvaluationDataNumber(string calldata clientId, uint32 outputTcbEvaluationDataNumber) + internal + returns (bool currentUpdated, bool nextUpdated) + { + ProtoClientState.Data storage clientState = clientStorages[clientId].clientState; + + // check if the current or next TCB evaluation data number update is required + if ( + clientState.next_tcb_evaluation_data_number != 0 + && block.timestamp >= clientState.next_tcb_evaluation_data_number_update_time + ) { + clientState.current_tcb_evaluation_data_number = clientState.next_tcb_evaluation_data_number; + clientState.next_tcb_evaluation_data_number = 0; + clientState.next_tcb_evaluation_data_number_update_time = 0; + currentUpdated = true; + // NOTE: + // - If the current number is updated again in a subsequent process, only one event is emitted + // - A new next TCB evaluation data number is not set, so the `next` is false here + } + + if (outputTcbEvaluationDataNumber > clientState.current_tcb_evaluation_data_number) { + if (clientState.tcb_evaluation_data_number_update_grace_period == 0) { + // If the grace period is zero, the client immediately updates the current TCB evaluation data number + clientState.current_tcb_evaluation_data_number = outputTcbEvaluationDataNumber; + // If the grace period is zero, the `next_tcb_evaluation_data_number` and `next_tcb_evaluation_data_number_update_time` must always be zero + // Otherwise, there is an internal error in the client + require( + clientState.next_tcb_evaluation_data_number == 0 + && clientState.next_tcb_evaluation_data_number_update_time == 0 + ); + return (true, false); + } else { + // If the grace period is not zero, there may be a next TCB evaluation data number update in the client state + + uint64 nextUpdateTime = + uint64(block.timestamp) + clientState.tcb_evaluation_data_number_update_grace_period; + + // If the next TCB evaluation data number is not set, the client sets the next TCB evaluation data number to the output's TCB evaluation data number + if (clientState.next_tcb_evaluation_data_number == 0) { + clientState.next_tcb_evaluation_data_number = outputTcbEvaluationDataNumber; + clientState.next_tcb_evaluation_data_number_update_time = nextUpdateTime; + return (currentUpdated, true); + } + + // If the next TCB evaluation data number is set, the client updates the next TCB evaluation data number + + if (outputTcbEvaluationDataNumber > clientState.next_tcb_evaluation_data_number) { + // Edge case 1. clientState.current_tcb_evaluation_data_number < clientState.next_tcb_evaluation_data_number < outputTcbEvaluationDataNumber + // + // In this case, the client immediately updates the current TCB evaluation data number with the `clientState.next_tcb_evaluation_data_number` + // and updates the next TCB evaluation data number with the `outputTcbEvaluationDataNumber` + // + // This case can be caused by too long grace period values or multiple TCB Recovery Events with very short intervals. + // Note that in this case the current number is updated ignoring the grace period setting. + // However, the current number is still a non-latest number, so there should be no problem for the operator operating as expected. + clientState.current_tcb_evaluation_data_number = clientState.next_tcb_evaluation_data_number; + clientState.next_tcb_evaluation_data_number = outputTcbEvaluationDataNumber; + clientState.next_tcb_evaluation_data_number_update_time = nextUpdateTime; + return (true, true); + } else if (outputTcbEvaluationDataNumber < clientState.next_tcb_evaluation_data_number) { + // Edge case 2. clientState.current_tcb_evaluation_data_number < outputTcbEvaluationDataNumber < clientState.next_tcb_evaluation_data_number + // + // In this case, the client immediately updates the current TCB evaluation data number with the `outputTcbEvaluationDataNumber` + // and does not update the next TCB evaluation data number. + // + // This case can be caused by too long grace period values or multiple TCB Recovery Events with very short intervals. + // Note that in this case the current number is updated ignoring the grace period setting. + // However, the current number is still a non-latest number, so there should be no problem for the operator operating as expected. + clientState.current_tcb_evaluation_data_number = outputTcbEvaluationDataNumber; + return (true, false); + } else { + // General case. outputTcbEvaluationDataNumber == clientState.next_tcb_evaluation_data_number + // In this case, the client already has the next TCB evaluation data number, so it does not need to be updated + return (currentUpdated, false); + } + } + } else if (outputTcbEvaluationDataNumber < clientState.current_tcb_evaluation_data_number) { + // The client must revert if the output's TCB evaluation data number is less than the current TCB evaluation data number + revert LCPClientZKDCAPUnexpectedTcbEvaluationDataNumber(clientState.current_tcb_evaluation_data_number); + } else { + // nop: case outputTcbEvaluationDataNumber == clientState.current_tcb_evaluation_data_number + return (currentUpdated, false); + } + } } diff --git a/contracts/LCPOperator.sol b/contracts/LCPOperator.sol index c5c085b..58351be 100644 --- a/contracts/LCPOperator.sol +++ b/contracts/LCPOperator.sol @@ -8,7 +8,7 @@ library LCPOperator { keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"); bytes32 internal constant TYPEHASH_REGISTER_ENCLAVE_KEY = keccak256("RegisterEnclaveKey(string avr)"); bytes32 internal constant TYPEHASH_ZKDCAP_REGISTER_ENCLAVE_KEY = - keccak256("ZKDCAPRegisterEnclaveKey(bytes zkDCAPVerifierInfo,bytes32 commitHash)"); + keccak256("ZKDCAPRegisterEnclaveKey(bytes zkDCAPVerifierInfo,bytes32 outputHash)"); bytes32 internal constant TYPEHASH_UPDATE_OPERATORS = keccak256( "UpdateOperators(string clientId,uint64 nonce,address[] newOperators,uint64 thresholdNumerator,uint64 thresholdDenominator)" ); @@ -56,7 +56,7 @@ library LCPOperator { ); } - function computeEIP712ZKDCAPRegisterEnclaveKey(bytes memory zkdcapVerifierInfo, bytes32 commitHash) + function computeEIP712ZKDCAPRegisterEnclaveKey(bytes memory zkdcapVerifierInfo, bytes32 outputHash) internal pure returns (bytes memory) @@ -64,7 +64,7 @@ library LCPOperator { return abi.encodePacked( hex"1901", DOMAIN_SEPARATOR_LCP_CLIENT, - keccak256(abi.encode(TYPEHASH_ZKDCAP_REGISTER_ENCLAVE_KEY, keccak256(zkdcapVerifierInfo), commitHash)) + keccak256(abi.encode(TYPEHASH_ZKDCAP_REGISTER_ENCLAVE_KEY, keccak256(zkdcapVerifierInfo), outputHash)) ); } diff --git a/contracts/proto/ibc/lightclients/lcp/v1/LCP.sol b/contracts/proto/ibc/lightclients/lcp/v1/LCP.sol index 227e678..f663873 100644 --- a/contracts/proto/ibc/lightclients/lcp/v1/LCP.sol +++ b/contracts/proto/ibc/lightclients/lcp/v1/LCP.sol @@ -1440,6 +1440,10 @@ library IbcLightclientsLcpV1ClientState { uint64 operators_nonce; uint64 operators_threshold_numerator; uint64 operators_threshold_denominator; + uint32 current_tcb_evaluation_data_number; + uint32 tcb_evaluation_data_number_update_grace_period; + uint32 next_tcb_evaluation_data_number; + uint64 next_tcb_evaluation_data_number_update_time; bytes[] zkdcap_verifier_infos; } @@ -1480,7 +1484,7 @@ library IbcLightclientsLcpV1ClientState { returns (Data memory, uint) { Data memory r; - uint[12] memory counters; + uint[16] memory counters; uint256 fieldId; ProtoBufRuntime.WireType wireType; uint256 bytesRead; @@ -1520,6 +1524,18 @@ library IbcLightclientsLcpV1ClientState { pointer += _read_operators_threshold_denominator(pointer, bs, r); } else if (fieldId == 11) { + pointer += _read_current_tcb_evaluation_data_number(pointer, bs, r); + } else + if (fieldId == 12) { + pointer += _read_tcb_evaluation_data_number_update_grace_period(pointer, bs, r); + } else + if (fieldId == 13) { + pointer += _read_next_tcb_evaluation_data_number(pointer, bs, r); + } else + if (fieldId == 14) { + pointer += _read_next_tcb_evaluation_data_number_update_time(pointer, bs, r); + } else + if (fieldId == 15) { pointer += _read_unpacked_repeated_zkdcap_verifier_infos(pointer, bs, nil(), counters); } else { @@ -1540,9 +1556,9 @@ library IbcLightclientsLcpV1ClientState { require(r.operators.length == 0); r.operators = new bytes[](counters[7]); } - if (counters[11] > 0) { + if (counters[15] > 0) { require(r.zkdcap_verifier_infos.length == 0); - r.zkdcap_verifier_infos = new bytes[](counters[11]); + r.zkdcap_verifier_infos = new bytes[](counters[15]); } while (pointer < offset + sz) { @@ -1557,7 +1573,7 @@ library IbcLightclientsLcpV1ClientState { if (fieldId == 7) { pointer += _read_unpacked_repeated_operators(pointer, bs, r, counters); } else - if (fieldId == 11) { + if (fieldId == 15) { pointer += _read_unpacked_repeated_zkdcap_verifier_infos(pointer, bs, r, counters); } else { @@ -1649,7 +1665,7 @@ library IbcLightclientsLcpV1ClientState { uint256 p, bytes memory bs, Data memory r, - uint[12] memory counters + uint[16] memory counters ) internal pure returns (uint) { /** * if `r` is NULL, then only counting the number of fields. @@ -1676,7 +1692,7 @@ library IbcLightclientsLcpV1ClientState { uint256 p, bytes memory bs, Data memory r, - uint[12] memory counters + uint[16] memory counters ) internal pure returns (uint) { /** * if `r` is NULL, then only counting the number of fields. @@ -1703,7 +1719,7 @@ library IbcLightclientsLcpV1ClientState { uint256 p, bytes memory bs, Data memory r, - uint[12] memory counters + uint[16] memory counters ) internal pure returns (uint) { /** * if `r` is NULL, then only counting the number of fields. @@ -1769,6 +1785,74 @@ library IbcLightclientsLcpV1ClientState { return sz; } + /** + * @dev The decoder for reading a field + * @param p The offset of bytes array to start decode + * @param bs The bytes array to be decoded + * @param r The in-memory struct + * @return The number of bytes decoded + */ + function _read_current_tcb_evaluation_data_number( + uint256 p, + bytes memory bs, + Data memory r + ) internal pure returns (uint) { + (uint32 x, uint256 sz) = ProtoBufRuntime._decode_uint32(p, bs); + r.current_tcb_evaluation_data_number = x; + return sz; + } + + /** + * @dev The decoder for reading a field + * @param p The offset of bytes array to start decode + * @param bs The bytes array to be decoded + * @param r The in-memory struct + * @return The number of bytes decoded + */ + function _read_tcb_evaluation_data_number_update_grace_period( + uint256 p, + bytes memory bs, + Data memory r + ) internal pure returns (uint) { + (uint32 x, uint256 sz) = ProtoBufRuntime._decode_uint32(p, bs); + r.tcb_evaluation_data_number_update_grace_period = x; + return sz; + } + + /** + * @dev The decoder for reading a field + * @param p The offset of bytes array to start decode + * @param bs The bytes array to be decoded + * @param r The in-memory struct + * @return The number of bytes decoded + */ + function _read_next_tcb_evaluation_data_number( + uint256 p, + bytes memory bs, + Data memory r + ) internal pure returns (uint) { + (uint32 x, uint256 sz) = ProtoBufRuntime._decode_uint32(p, bs); + r.next_tcb_evaluation_data_number = x; + return sz; + } + + /** + * @dev The decoder for reading a field + * @param p The offset of bytes array to start decode + * @param bs The bytes array to be decoded + * @param r The in-memory struct + * @return The number of bytes decoded + */ + function _read_next_tcb_evaluation_data_number_update_time( + uint256 p, + bytes memory bs, + Data memory r + ) internal pure returns (uint) { + (uint64 x, uint256 sz) = ProtoBufRuntime._decode_uint64(p, bs); + r.next_tcb_evaluation_data_number_update_time = x; + return sz; + } + /** * @dev The decoder for reading a field * @param p The offset of bytes array to start decode @@ -1781,17 +1865,17 @@ library IbcLightclientsLcpV1ClientState { uint256 p, bytes memory bs, Data memory r, - uint[12] memory counters + uint[16] memory counters ) internal pure returns (uint) { /** * if `r` is NULL, then only counting the number of fields. */ (bytes memory x, uint256 sz) = ProtoBufRuntime._decode_bytes(p, bs); if (isNil(r)) { - counters[11] += 1; + counters[15] += 1; } else { - r.zkdcap_verifier_infos[r.zkdcap_verifier_infos.length - counters[11]] = x; - counters[11] -= 1; + r.zkdcap_verifier_infos[r.zkdcap_verifier_infos.length - counters[15]] = x; + counters[15] -= 1; } return sz; } @@ -1945,10 +2029,46 @@ library IbcLightclientsLcpV1ClientState { ); pointer += ProtoBufRuntime._encode_uint64(r.operators_threshold_denominator, pointer, bs); } + if (r.current_tcb_evaluation_data_number != 0) { + pointer += ProtoBufRuntime._encode_key( + 11, + ProtoBufRuntime.WireType.Varint, + pointer, + bs + ); + pointer += ProtoBufRuntime._encode_uint32(r.current_tcb_evaluation_data_number, pointer, bs); + } + if (r.tcb_evaluation_data_number_update_grace_period != 0) { + pointer += ProtoBufRuntime._encode_key( + 12, + ProtoBufRuntime.WireType.Varint, + pointer, + bs + ); + pointer += ProtoBufRuntime._encode_uint32(r.tcb_evaluation_data_number_update_grace_period, pointer, bs); + } + if (r.next_tcb_evaluation_data_number != 0) { + pointer += ProtoBufRuntime._encode_key( + 13, + ProtoBufRuntime.WireType.Varint, + pointer, + bs + ); + pointer += ProtoBufRuntime._encode_uint32(r.next_tcb_evaluation_data_number, pointer, bs); + } + if (r.next_tcb_evaluation_data_number_update_time != 0) { + pointer += ProtoBufRuntime._encode_key( + 14, + ProtoBufRuntime.WireType.Varint, + pointer, + bs + ); + pointer += ProtoBufRuntime._encode_uint64(r.next_tcb_evaluation_data_number_update_time, pointer, bs); + } if (r.zkdcap_verifier_infos.length != 0) { for(i = 0; i < r.zkdcap_verifier_infos.length; i++) { pointer += ProtoBufRuntime._encode_key( - 11, + 15, ProtoBufRuntime.WireType.LengthDelim, pointer, bs) @@ -2015,6 +2135,10 @@ library IbcLightclientsLcpV1ClientState { e += 1 + ProtoBufRuntime._sz_uint64(r.operators_nonce); e += 1 + ProtoBufRuntime._sz_uint64(r.operators_threshold_numerator); e += 1 + ProtoBufRuntime._sz_uint64(r.operators_threshold_denominator); + e += 1 + ProtoBufRuntime._sz_uint32(r.current_tcb_evaluation_data_number); + e += 1 + ProtoBufRuntime._sz_uint32(r.tcb_evaluation_data_number_update_grace_period); + e += 1 + ProtoBufRuntime._sz_uint32(r.next_tcb_evaluation_data_number); + e += 1 + ProtoBufRuntime._sz_uint64(r.next_tcb_evaluation_data_number_update_time); for(i = 0; i < r.zkdcap_verifier_infos.length; i++) { e += 1 + ProtoBufRuntime._sz_lendelim(r.zkdcap_verifier_infos[i].length); } @@ -2062,6 +2186,22 @@ library IbcLightclientsLcpV1ClientState { return false; } + if (r.current_tcb_evaluation_data_number != 0) { + return false; + } + + if (r.tcb_evaluation_data_number_update_grace_period != 0) { + return false; + } + + if (r.next_tcb_evaluation_data_number != 0) { + return false; + } + + if (r.next_tcb_evaluation_data_number_update_time != 0) { + return false; + } + if (r.zkdcap_verifier_infos.length != 0) { return false; } @@ -2087,6 +2227,10 @@ library IbcLightclientsLcpV1ClientState { output.operators_nonce = input.operators_nonce; output.operators_threshold_numerator = input.operators_threshold_numerator; output.operators_threshold_denominator = input.operators_threshold_denominator; + output.current_tcb_evaluation_data_number = input.current_tcb_evaluation_data_number; + output.tcb_evaluation_data_number_update_grace_period = input.tcb_evaluation_data_number_update_grace_period; + output.next_tcb_evaluation_data_number = input.next_tcb_evaluation_data_number; + output.next_tcb_evaluation_data_number_update_time = input.next_tcb_evaluation_data_number_update_time; output.zkdcap_verifier_infos = input.zkdcap_verifier_infos; } diff --git a/foundry.toml b/foundry.toml index c360c4b..af81449 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,7 +3,7 @@ src = 'contracts' out = 'out' libs = ['lib', 'node_modules'] optimizer = true -optimizer_runs = 9_999_999 +optimizer_runs = 8000 via-ir = false ffi = true ast = true diff --git a/proto/ibc/lightclients/lcp/v1/LCP.proto b/proto/ibc/lightclients/lcp/v1/LCP.proto index ff785c5..d5a6907 100644 --- a/proto/ibc/lightclients/lcp/v1/LCP.proto +++ b/proto/ibc/lightclients/lcp/v1/LCP.proto @@ -3,64 +3,114 @@ package ibc.lightclients.lcp.v1; import "@hyperledger-labs/yui-ibc-solidity/proto/core/02-client/Client.proto"; -option go_package = "github.com/datachainlab/lcp/go/light-clients/lcp/types"; - +// A message containing information required to update the client. message UpdateClientMessage { + // A proxy message generated by the LCP node running on the target platform bytes proxy_message = 1; + // Signatures of the proxy message by the LCP node repeated bytes signatures = 2; } +// A message to verify IAS report and signature for the enclave key registration message RegisterEnclaveKeyMessage { + // IAS report bytes report = 1; + // A signature of the IAS report by the IAS signing key bytes signature = 2; + // A certificate of the IAS signing key bytes signing_cert = 3; + // An operator's signature of the EIP-712 message `RegisterEnclaveKey` bytes operator_signature = 4; } +// A message to verify zkDCAP's output and proof for the enclave key registration message ZKDCAPRegisterEnclaveKeyMessage { + // A type of zkVM generated the `quote_verification_output` and `proof` uint32 zkvm_type = 1; + // An output of the zkDCAP program that verifies the quote bytes quote_verification_output = 2; + // A proof of the zkVM generated the `quote_verification_output` bytes proof = 3; + // An operator's signature of the EIP-712 message `ZKDCAPRegisterEnclaveKey` bytes operator_signature = 4; } message UpdateOperatorsMessage { + // A nonce for this operators update uint64 nonce = 1; + // A list of new operators repeated bytes new_operators = 2; + // A numerator of the threshold of signatures required for new operators uint64 new_operators_threshold_numerator = 3; + // A denominator of the threshold of signatures required for new operators uint64 new_operators_threshold_denominator = 4; + // Signatures of the EIP-712 message `UpdateOperators` by the current operators repeated bytes signatures = 5; } message ClientState { + // The MRENCLAVE of the enclave in running on the target platform bytes mrenclave = 1; - // enclave key's expiration from the remote attestation time in seconds + // The expiration period (in seconds) of the enclave key from the time of remote attestation. // - // If the key is got from the DCAP/zkDCAP's quote, it should be the collateral's expiration time instead. + // This is used only for IAS. uint64 key_expiration = 2; + // Indicates whether the client is frozen. bool frozen = 3; + // The height of the latest consensus state that the client has tracked Height latest_height = 4; - // e.g. SW_HARDENING_NEEDED, CONFIGURATION_AND_SW_HARDENING_NEEDED (except "OK") + // The set of quote statuses that the client accepts for the target enclave. + // + // e.g. IAS: SW_HARDENING_NEEDED, CONFIGURATION_AND_SW_HARDENING_NEEDED + // DCAP: SWHardeningNeeded, ConfigurationAndSWHardeningNeeded repeated string allowed_quote_statuses = 5; - // e.g. INTEL-SA-XXXXX + // The set of Security Advisory IDs that the client allows. + // + // e.g. INTEL-SA-00001, INTEL-SA-00002 repeated string allowed_advisory_ids = 6; + // A list of LCP operator addresses associated with this client. + // + // Please check LCP operator details: repeated bytes operators = 7; + // The current nonce used in operator updates. uint64 operators_nonce = 8; + // The numerator of the signature threshold for operator updates. uint64 operators_threshold_numerator = 9; + // The denominator of the signature threshold for operator updates. uint64 operators_threshold_denominator = 10; - // An optional field to store the verifier info for zkDCAP + // The current TCB evaluation data number + // + // The client only accepts the zkDCAP output generated using collateral with a TCB evaluation data number equal to or greater than this number. + uint32 current_tcb_evaluation_data_number = 11; + // The grace period for updating to the latest TCB evaluation data number (in seconds) // - // if empty, the zkDCAP is not enabled - // otherwise, the zkDCAP is enabled - // The layout of each element is as follows: - // 0: zkVM type (e.g. 1 for risc0) - // 1-31: reserved - // 32-64: guest program identifier - repeated bytes zkdcap_verifier_infos = 11; + // Note that this grace period is not affected for updates to non-latest numbers. + uint32 tcb_evaluation_data_number_update_grace_period = 12; + // The next TCB evaluation data number to be updated + // + // If this number is non-zero, `next_tcb_evaluation_data_number` must be greater than `current_tcb_evaluation_data_number`. + uint32 next_tcb_evaluation_data_number = 13; + // The update time of the next TCB evaluation data number (in UNIX time seconds) + uint64 next_tcb_evaluation_data_number_update_time = 14; + // The verifier information for the zkDCAP + // + // The format is as follows: + // 0: zkVM type + // 1-N: arbitrary data for each zkVM type + // + // The format of the risc0 zkVM is as follows: + // | 0 | 1 - 31 | 32 - 64 | + // |---|----------|-----------| + // | 1 | reserved | image id | + repeated bytes zkdcap_verifier_infos = 15; } message ConsensusState { + // An identifier that uniquely indicates the ELC state at a specific height + // + // Please check the state ID details: bytes state_id = 1; - // unix timestamp in seconds + // The timestamp of the target chain's block corresponding to the consensus height, + // expressed in UNIX time (seconds). uint64 timestamp = 2; }