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
ClientState
andConsensusState
. - 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
ClientState
andConsensusState
. - 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
ClientState
using Protobuf and let the resulting byte array beclient_bytes
. - Use the Protobuf Any type to encode an object with
value
set toclient_bytes
andtype_url
set 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)
version
indicates the version of the LCP message schema. Currently,0x0001
is used.message type
indicates the type of message.0x0001
representsUpdateStateProxyMessage
,0x0002
representsVerifyMembershipProxyMessage
, and0x0003
representsMisbehaviourProxyMessage
.reserved
is reserved for future extensions.message bytes
are 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_height
is the height of theConsensusState
referenced. Note that ininit_client
,prev_height
is set to (0,0).post_height
is the height after applying the Header.prev_state_id
is theState ID
indicating the referenced state. Ininit_client
,prev_state_id
is set to 0.post_state_id
is theState ID
indicating the state after the update.timestamp
is the timestamp of the applied Header.context
is theValidation Context
indicating the context of the environment during verification.emitted_states
indicates the newClientState
generated 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;
}
prefix
is the prefix of the upstream chain's state store, applied topath
during verification.path
is the path in the state tree corresponding to the value to be verified.value
is the value corresponding topath
. In the case ofverifyMembership
, the value must be non-zero. In the case ofverifyNonMembership
, it must be zero.height
is the height corresponding tostate_id
.state_id
is theState ID
of 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_height
andprev_state_id
of the 0-th message match the existing consensus height and itsState ID
. - The
post_height
of theindex
-th message matches theprev_height
of theindex+1
-th message. - The
post_state_id
of theindex
-th message matches theprev_state_id
of theindex+1
-th message.
Then, they are aggregated into one UpdateStateProxyMessage
according to the following rules:
prev_height
is the smallestprev_height
among the concatenated messages, andpost_height
is the largestpost_height
.prev_state_id
is theprev_state_id
of the first message, andpost_state_id
is thepost_state_id
of the last message.- The
timestamp
of the aggregated message is the most recenttimestamp
among the concatenated messages.