Enclave Light Client (ELC)
Overview
ELC is a Light Client implemented within the LCP Enclave. The ELC must be implemented for each consensus algorithm of the target chain to be verified.
Each ELC must implement the functions defined in this section. Each function returns messages that indicate state transitions or verification results. LCP generates commitments and signatures using the EK for these messages to make them verifiable by external verifiers. These signatures and messages are verified by the on-chain LCP Client.
Desired Properties
The ELC must satisfy properties similar to those in ICS-02.
In addition, it must satisfy the following properties:
- State Transition Commitments: Generate commitments to state transitions by verifying the consensus of the target chain based on the
ClientStateandConsensusState. - State Verification Commitments: Generate commitments to the state and the results of its verification by verifying the state of the target chain based on the
ClientStateandConsensusState. - Misbehavior Handling: When misbehavior is detected during consensus verification, the ELC is frozen and generates a commitment to that misbehavior.
The commitments must uniquely indicate the states referenced by the ELC and the states after updates. Unlike on-chain light clients that perform verification on-chain, such as in IBC, the ELC does not have trust assumptions regarding the consistency of the storage it references, so it must uniquely represent all referenced states in the commitments.
Data Structures
Client ID
Client ID is an identifier that uniquely indicates an instance of the ELC. It is specified when a new instance is created with init_client. In functions such as update_client and verify_client, this identifier is used to specify the target ELC instance.
State ID
State ID is an identifier that uniquely indicates the state of the ELC corresponding to a certain height. The State ID is the keccak256 hash value of the concatenation of the encoded ClientState and ConsensusState.
The encoding method is as follows:
- Encode the
ClientStateusing Protobuf and let the resulting byte array beclient_bytes. - Use the Protobuf Any type to encode an object with
valueset toclient_bytesandtype_urlset to the type name of theClientState.
Here, the type_url of Any must be unique among ELCs within a single LCP Enclave. This ensures that different ELCs' ClientState/ConsensusState do not have the same State ID.
Furthermore, the State ID at a certain height in an ELC instance initialized with the same ClientState must always be unique.
In general, in ICS-02 compliant clients, the ConsensusState includes the state root and timestamp of the chain, and the ClientState includes parameters related to verification. By including the ClientState as well as the ConsensusState in the calculation of the State ID, it ensures that the state is consistently tracked with specific verification parameters. Therefore, note that in light clients like 07-tendermint that have state such as latest_height in the ClientState, this value must be excluded when calculating the State ID.
Commitment Hash
A commitment hash indicates the state transitions or verification results of the ELC. It is defined as follows:
commitment = keccak256(abi.encode(| version (2 bytes) | message type (2 bytes) | reserved (28 bytes) |), message bytes)
versionindicates the version of the LCP message schema. Currently,0x0001is used.message typeindicates the type of message.0x0001representsUpdateStateProxyMessage,0x0002representsVerifyMembershipProxyMessage, and0x0003representsMisbehaviourProxyMessage.reservedis reserved for future extensions.message bytesare the message encoded with the Ethereum ABI.
Also, | version (2 bytes) | message type (2 bytes) | reserved (28 bytes) | is referred to as the Header, and the concatenation with message bytes is referred to as the HeaderedMessage.
Commitment Proof
A commitment proof (simply called proof) is a proof that indicates the state transitions or verification results of the ELC. It is the Commitment Hash signed by the EK generated in the Enclave. The proof is used to enable external verifiers to verify the state transitions or verification results of the ELC.
Validation Context
Validation Context consists of environmental information such as the current time used during Header verification by the ELC's client.
Typically, during Header verification, a light client checks the following two things:
- Signature Verification: Verification that sufficient signatures are present on the given Header based on the validator set corresponding to a certain height.
- State Validity Confirmation: Confirmation that the state of the client being verified is currently valid.
Regarding 1, this is the verification of signatures such as ECDSA. This is performed by the ELC within the Enclave.
Regarding 2, it is defined as a function that returns a boolean value indicating validity, taking parameters such as the current time, the timestamp of the block to be accepted, and the timestamp of the state referenced during verification. For example, in 07-tendermint, there is validation of the validity of the state using the trusting period. However, since there is generally no reliable timestamp in TEE, verification referencing the current time within the TEE is unreliable. Therefore, the ELC generates a Validation Context that includes information about the verification function used and its parameters and includes it in the commitment so that the verifier can reproduce the verification. The verifier can confirm that the state transition of the ELC is valid by performing verification using information such as the latest block's timestamp and the Validation Context.
Message
Message or Proxy Message is a message that indicates the state transitions or verification results of the ELC.
UpdateStateProxyMessage
UpdateStateProxyMessage is a message that indicates the state transition of the ELC's client. The downstream chain tracks the state transitions of the upstream chain by verifying this message using the LCP Client.
The message is defined as follows:
struct UpdateStateProxyMessage {
Height prev_height;
bytes32 prev_state_id;
Height post_height;
bytes32 post_state_id;
uint128 timestamp;
bytes context;
EmittedState[] emitted_states;
}
struct Height {
uint64 revision_number;
uint64 revision_height;
}
struct EmittedState {
Height height;
bytes state;
}
prev_heightis the height of theConsensusStatereferenced. Note that ininit_client,prev_heightis set to (0,0).post_heightis the height after applying the Header.prev_state_idis theState IDindicating the referenced state. Ininit_client,prev_state_idis set to 0.post_state_idis theState IDindicating the state after the update.timestampis the timestamp of the applied Header.contextis theValidation Contextindicating the context of the environment during verification.emitted_statesindicates the newClientStategenerated during the update. It is mainly used when setting a new state throughinit_client.
The keccak256 hash of the UpdateStateProxyMessage encoded with the Ethereum ABI is used as the commitment and is signed by the EK to generate a proof of the state transition.
This message includes both the pre-update and post-update State ID, allowing the LCP Client to confirm that the message corresponds to a specific state transition. Specifically, the initial post_state_id obtained from the message is trusted during initialization, and thereafter, by verifying proofs and accepting messages where the prev_state_id is verified or accepting the post_state_id of the ConsensusState at initialization, it is ensured that accurate state transitions are being tracked. (This process is repeated subsequently.)
The verifier of the message can confirm the correctness of the state transition by confirming that the prev_state_id is trustworthy and verifying the signature by the EK and the context. The UpdateState function of the LCP Client implements this verification.
VerifyMembershipProxyMessage
VerifyMembershipProxyMessage is a message that indicates the proof of existence of a value based on the ConsensusState at a certain height of the ELC. The downstream chain verifies a specific state of the upstream chain by verifying this message and proof using the LCP Client.
The message is defined as follows:
struct VerifyMembershipProxyMessage {
bytes prefix;
bytes path;
bytes32 value;
Height height;
bytes32 state_id;
}
prefixis the prefix of the upstream chain's state store, applied topathduring verification.pathis the path in the state tree corresponding to the value to be verified.valueis the value corresponding topath. In the case ofverifyMembership, the value must be non-zero. In the case ofverifyNonMembership, it must be zero.heightis the height corresponding tostate_id.state_idis theState IDof the referenced state.
The keccak256 hash of the VerifyMembershipProxyMessage encoded according to the Ethereum ABI is used as the commitment and is signed by the EK to serve as proof of the existence of the state.
The verifier can confirm the existence/non-existence of the state to be verified by confirming that the state_id of the message is trustworthy and verifying the signature by the EK. The VerifyMembership function of the LCP Client implements this verification.
MisbehaviourProxyMessage
MisbehaviourProxyMessage is a message that indicates misbehavior of the chain detected by the ELC based on the ConsensusState at a certain height. An example of misbehavior is the existence of two valid Headers corresponding to the same height.
struct MisbehaviourProxyMessage {
PrevState[] prev_states;
bytes context;
bytes client_message;
}
struct PrevState {
Height height;
bytes32 state_id;
}
The keccak256 hash of the MisbehaviourProxyMessage encoded according to the Ethereum ABI is used as the commitment and is signed by the EK to serve as proof of misbehavior.
The verifier can confirm the correctness of the misbehavior by confirming that each state_id in prev_states of the message is trustworthy and verifying the signature by the EK and the context. The Misbehaviour function of the LCP Client implements this verification.
Functions
ELC
init_client
init_client is a function that creates a new instance of the ELC based on the given Client ID, ClientState, and ConsensusState. This function returns an UpdateStateProxyMessage and proof.
If an ELC instance corresponding to a certain Client ID already exists or invalid ClientState or ConsensusState is given, it must return an error.
This function corresponds to the initialise function in ICS-02.
update_client
update_client is a function that updates the state of the ELC instance corresponding to the specified Client ID with the given Header. This function returns an UpdateStateProxyMessage or a MisbehaviourProxyMessage and proof.
This function corresponds to the update function in ICS-02.
verify_membership
verify_membership is a function that verifies the proof of existence based on the ConsensusState corresponding to the given height. This function returns a VerifyMembershipProxyMessage and proof.
This function corresponds to the verifyMembership function in ICS-02.
verify_non_membership
verify_non_membership is a function that verifies the proof of non-existence based on the ConsensusState corresponding to the given height. This function returns a VerifyMembershipProxyMessage and proof.
This function corresponds to the verifyNonMembership function in ICS-02.
Message Aggregation
UpdateStateProxyMessage usually indicates state transitions of the ELC concerning two heights. For some light clients, it is often impossible to update to an arbitrary height based on the ConsensusState at a certain height. This means that if the interval between two heights is large, the verifier may need to verify many messages. This can result in very high gas costs in on-chain LCP Clients. Therefore, LCP provides a message aggregation function aggregate_message that aggregates multiple messages into one. This allows for gas-efficient updates by verifying multiple messages through a single message.
The array of messages is verified according to the following rules:
- Confirm that all signatures and verifications of each message are valid.
- The
prev_heightandprev_state_idof the 0-th message match the existing consensus height and itsState ID. - The
post_heightof theindex-th message matches theprev_heightof theindex+1-th message. - The
post_state_idof theindex-th message matches theprev_state_idof theindex+1-th message.
Then, they are aggregated into one UpdateStateProxyMessage according to the following rules:
prev_heightis the smallestprev_heightamong the concatenated messages, andpost_heightis the largestpost_height.prev_state_idis theprev_state_idof the first message, andpost_state_idis thepost_state_idof the last message.- The
timestampof the aggregated message is the most recenttimestampamong the concatenated messages.