Skip to content

Commit

Permalink
Merge pull request #17 from datachainlab/multiple-operators
Browse files Browse the repository at this point in the history
Multiple operators support

Signed-off-by: Jun Kimura <jun.kimura@datachain.jp>
  • Loading branch information
bluele authored Jun 18, 2024
2 parents ec447cd + c44ef6d commit 5ac4e18
Show file tree
Hide file tree
Showing 85 changed files with 2,369 additions and 660 deletions.
328 changes: 194 additions & 134 deletions contracts/AVRValidator.sol

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions contracts/ILCPClientErrors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.12;

interface ILCPClientErrors {
error LCPClientRootCACertAlreadyInitialized();
error LCPClientClientStateInvalidLatestHeight();
error LCPClientClientStateFrozen();
error LCPClientClientStateInvalidKeyExpiration();
error LCPClientClientStateInvalidMrenclaveLength();
error LCPClientClientStateUnexpectedMrenclave();
error LCPClientClientStateEmptyOperators();
error LCPClientClientStateInvalidOperatorAddress();
error LCPClientClientStateInvalidOperatorAddressLength();
error LCPClientClientStateInvalidOperatorsNonce();
error LCPClientClientStateUnexpectedOperatorsNonce(uint64 expectedNonce);

error LCPClientOperatorsInvalidOrder(address prevOperator, address nextOperator);
error LCPClientClientStateInvalidOperatorsThreshold();

error LCPClientConsensusStateInvalidTimestamp();
error LCPClientConsensusStateInvalidStateId();

error LCPClientClientStateNotFound();
error LCPClientConsensusStateNotFound();
error LCPClientUnknownProxyMessageHeader();
error LCPClientUnknownProtoTypeUrl();

error LCPClientMembershipVerificationInvalidHeight();
error LCPClientMembershipVerificationInvalidPrefix();
error LCPClientMembershipVerificationInvalidPath();
error LCPClientMembershipVerificationInvalidValue();
error LCPClientMembershipVerificationInvalidStateId();

error LCPClientUpdateStateEmittedStatesMustNotEmpty();
error LCPClientUpdateStatePrevStateIdMustNotEmpty();
error LCPClientUpdateStateUnexpectedPrevStateId();

error LCPClientMisbehaviourPrevStatesMustNotEmpty();

error LCPClientEnclaveKeyNotExist();
error LCPClientEnclaveKeyExpired();
error LCPClientEnclaveKeyUnexpectedOperator(address expected, address actual);
error LCPClientEnclaveKeyUnexpectedExpiredAt();

error LCPClientOperatorSignaturesInsufficient(uint256 success);

error LCPClientIASRootCertExpired();
error LCPClientIASCertExpired();

error LCPClientAVRInvalidSignature();
error LCPClientAVRAlreadyExpired();

error LCPClientInvalidSignaturesLength();

error LCPClientAVRUnexpectedOperator(address actual, address expected);

error LCPClientUpdateOperatorsPermissionless();
error LCPClientUpdateOperatorsSignatureUnexpectedOperator(address actual, address expected);
}
552 changes: 390 additions & 162 deletions contracts/LCPClientBase.sol

Large diffs are not rendered by default.

61 changes: 37 additions & 24 deletions contracts/LCPCommitment.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ pragma solidity ^0.8.12;
import {Height} from "@hyperledger-labs/yui-ibc-solidity/contracts/proto/Client.sol";

library LCPCommitment {
uint16 constant LCP_MESSAGE_VERSION = 1;
uint16 constant LCP_MESSAGE_TYPE_UPDATE_STATE = 1;
uint16 constant LCP_MESSAGE_TYPE_STATE = 2;
uint16 constant LCP_MESSAGE_TYPE_MISBEHAVIOUR = 3;
uint16 constant LCP_MESSAGE_CONTEXT_TYPE_EMPTY = 0;
uint16 constant LCP_MESSAGE_CONTEXT_TYPE_TRUSTING_PERIOD = 1;

bytes32 constant LCP_MESSAGE_HEADER_UPDATE_STATE =
uint16 internal constant LCP_MESSAGE_VERSION = 1;
uint16 internal constant LCP_MESSAGE_TYPE_UPDATE_STATE = 1;
uint16 internal constant LCP_MESSAGE_TYPE_STATE = 2;
uint16 internal constant LCP_MESSAGE_TYPE_MISBEHAVIOUR = 3;
uint16 internal constant LCP_MESSAGE_CONTEXT_TYPE_EMPTY = 0;
uint16 internal constant LCP_MESSAGE_CONTEXT_TYPE_TRUSTING_PERIOD = 1;

bytes32 internal constant LCP_MESSAGE_HEADER_UPDATE_STATE =
bytes32(uint256(LCP_MESSAGE_VERSION) << 240 | uint256(LCP_MESSAGE_TYPE_UPDATE_STATE) << 224);
bytes32 constant LCP_MESSAGE_HEADER_STATE =
bytes32 internal constant LCP_MESSAGE_HEADER_STATE =
bytes32(uint256(LCP_MESSAGE_VERSION) << 240 | uint256(LCP_MESSAGE_TYPE_STATE) << 224);
bytes32 constant LCP_MESSAGE_HEADER_MISBEHAVIOUR =
bytes32 internal constant LCP_MESSAGE_HEADER_MISBEHAVIOUR =
bytes32(uint256(LCP_MESSAGE_VERSION) << 240 | uint256(LCP_MESSAGE_TYPE_MISBEHAVIOUR) << 224);

error LCPCommitmentUnexpectedProxyMessageHeader();
error LCPCommtimentInvalidTrustingPeriodContextLength();
error LCPCommitmentUnknownValidationContextType();
error LCPCommtimentTrustingPeriodContextOutOfTrustingPeriod();
error LCPCommitmentTrustingPeriodHeaderFromFuture();

struct HeaderedProxyMessage {
bytes32 header;
bytes message;
Expand Down Expand Up @@ -48,7 +54,9 @@ library LCPCommitment {
// 0-1: version
// 2-3: message type
// 4-31: reserved
require(hm.header == LCP_MESSAGE_HEADER_UPDATE_STATE, "unexpected header");
if (hm.header != LCP_MESSAGE_HEADER_UPDATE_STATE) {
revert LCPCommitmentUnexpectedProxyMessageHeader();
}
return abi.decode(hm.message, (UpdateStateProxyMessage));
}

Expand All @@ -73,7 +81,9 @@ library LCPCommitment {
// 0-1: version
// 2-3: message type
// 4-31: reserved
require(hm.header == LCP_MESSAGE_HEADER_MISBEHAVIOUR, "unexpected header");
if (hm.header != LCP_MESSAGE_HEADER_MISBEHAVIOUR) {
revert LCPCommitmentUnexpectedProxyMessageHeader();
}
return abi.decode(hm.message, (MisbehaviourProxyMessage));
}

Expand Down Expand Up @@ -109,10 +119,12 @@ library LCPCommitment {
if (contextType == LCP_MESSAGE_CONTEXT_TYPE_EMPTY) {
return;
} else if (contextType == LCP_MESSAGE_CONTEXT_TYPE_TRUSTING_PERIOD) {
require(vc.context.length == 64, "invalid trusting period context length");
if (vc.context.length != 64) {
revert LCPCommtimentInvalidTrustingPeriodContextLength();
}
return trustingPeriodContextEval(parseTrustingPeriodContext(vc.context), currentTimestampNanos);
} else {
revert("unknown context type");
revert LCPCommitmentUnknownValidationContextType();
}
}

Expand All @@ -138,17 +150,16 @@ library LCPCommitment {
pure
{
if (currentTimestampNanos >= context.trustedStateTimestamp + context.trustingPeriod) {
require(false, "out of trusting period");
revert LCPCommtimentTrustingPeriodContextOutOfTrustingPeriod();
} else if (currentTimestampNanos + context.clockDrift <= context.untrustedHeaderTimestamp) {
require(false, "header is from the future");
revert LCPCommitmentTrustingPeriodHeaderFromFuture();
}
return;
}

struct CommitmentProof {
struct CommitmentProofs {
bytes message;
address signer;
bytes signature;
bytes[] signatures;
}

struct VerifyMembershipProxyMessage {
Expand All @@ -169,16 +180,18 @@ library LCPCommitment {
// 0-1: version
// 2-3: message type
// 4-31: reserved
require(hm.header == LCP_MESSAGE_HEADER_STATE, "unexpected header");
if (hm.header != LCP_MESSAGE_HEADER_STATE) {
revert LCPCommitmentUnexpectedProxyMessageHeader();
}
return abi.decode(hm.message, (VerifyMembershipProxyMessage));
}

function parseVerifyMembershipCommitmentProof(bytes calldata commitmentProofBytes)
function parseVerifyMembershipCommitmentProofs(bytes calldata commitmentProofsBytes)
internal
pure
returns (CommitmentProof memory, VerifyMembershipProxyMessage memory)
returns (CommitmentProofs memory, VerifyMembershipProxyMessage memory)
{
CommitmentProof memory commitmentProof = abi.decode(commitmentProofBytes, (CommitmentProof));
return (commitmentProof, parseVerifyMembershipProxyMessage(commitmentProof.message));
CommitmentProofs memory commitmentProofs = abi.decode(commitmentProofsBytes, (CommitmentProofs));
return (commitmentProofs, parseVerifyMembershipProxyMessage(commitmentProofs.message));
}
}
85 changes: 85 additions & 0 deletions contracts/LCPOperator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.12;

library LCPOperator {
type ChainType is uint16;

bytes32 internal constant TYPEHASH_DOMAIN_SEPARATOR =
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_UPDATE_OPERATORS = keccak256(
"UpdateOperators(string clientId,uint64 nonce,address[] newOperators,uint64 thresholdNumerator,uint64 thresholdDenominator)"
);

bytes32 internal constant DOMAIN_SEPARATOR_NAME = keccak256("LCPClient");
bytes32 internal constant DOMAIN_SEPARATOR_VERSION = keccak256("1");

// domainSeparator(0, address(0))
bytes32 internal constant DOMAIN_SEPARATOR_REGISTER_ENCLAVE_KEY =
0xe33d217bff42bc015bf037be8386bf5055ec6019e58e8c5e89b5c74b8225fa6a;
ChainType internal constant CHAIN_TYPE_EVM = ChainType.wrap(1);
// chainTypeSalt(CHAIN_TYPE_EVM, hex"")
bytes32 internal constant CHAIN_TYPE_EVM_SALT = keccak256(abi.encodePacked(CHAIN_TYPE_EVM, hex""));

function chainTypeSalt(ChainType chainType, bytes memory args) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(chainType, args));
}

function domainSeparator(uint256 chainId, address verifyingContract) internal pure returns (bytes32) {
return keccak256(
abi.encode(
TYPEHASH_DOMAIN_SEPARATOR,
DOMAIN_SEPARATOR_NAME,
DOMAIN_SEPARATOR_VERSION,
chainId,
verifyingContract,
CHAIN_TYPE_EVM_SALT
)
);
}

function computeEIP712RegisterEnclaveKey(bytes calldata avr) internal pure returns (bytes memory) {
return abi.encodePacked(
hex"1901",
DOMAIN_SEPARATOR_REGISTER_ENCLAVE_KEY,
keccak256(abi.encode(TYPEHASH_REGISTER_ENCLAVE_KEY, keccak256(avr)))
);
}

function computeEIP712UpdateOperators(
string calldata clientId,
uint64 nonce,
address[] memory newOperators,
uint64 thresholdNumerator,
uint64 thresholdDenominator
) internal view returns (bytes memory) {
return computeEIP712UpdateOperators(
block.chainid, address(this), clientId, nonce, newOperators, thresholdNumerator, thresholdDenominator
);
}

function computeEIP712UpdateOperators(
uint256 chainId,
address verifyingContract,
string calldata clientId,
uint64 nonce,
address[] memory newOperators,
uint64 thresholdNumerator,
uint64 thresholdDenominator
) internal pure returns (bytes memory) {
return abi.encodePacked(
hex"1901",
domainSeparator(chainId, verifyingContract),
keccak256(
abi.encode(
TYPEHASH_UPDATE_OPERATORS,
keccak256(bytes(clientId)),
nonce,
keccak256(abi.encodePacked(newOperators)),
thresholdNumerator,
thresholdDenominator
)
)
);
}
}
31 changes: 23 additions & 8 deletions contracts/LCPProtoMarshaler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,64 @@ import {
IbcLightclientsLcpV1ClientState as ClientState,
IbcLightclientsLcpV1ConsensusState as ConsensusState,
IbcLightclientsLcpV1RegisterEnclaveKeyMessage as RegisterEnclaveKeyMessage,
IbcLightclientsLcpV1UpdateClientMessage as UpdateClientMessage
IbcLightclientsLcpV1UpdateClientMessage as UpdateClientMessage,
IbcLightclientsLcpV1UpdateOperatorsMessage as UpdateOperatorsMessage
} from "./proto/ibc/lightclients/lcp/v1/LCP.sol";
import {GoogleProtobufAny as Any} from "@hyperledger-labs/yui-ibc-solidity/contracts/proto/GoogleProtobufAny.sol";

library LCPProtoMarshaler {
string constant UPDATE_CLIENT_MESSAGE_TYPE_URL = "/ibc.lightclients.lcp.v1.UpdateClientMessage";
string constant REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL = "/ibc.lightclients.lcp.v1.RegisterEnclaveKeyMessage";
string constant UPDATE_OPERATORS_MESSAGE_TYPE_URL = "/ibc.lightclients.lcp.v1.UpdateOperatorsMessage";
string constant CLIENT_STATE_TYPE_URL = "/ibc.lightclients.lcp.v1.ClientState";
string constant CONSENSUS_STATE_TYPE_URL = "/ibc.lightclients.lcp.v1.ConsensusState";

bytes32 constant UPDATE_CLIENT_MESSAGE_TYPE_URL_HASH = keccak256(abi.encodePacked(UPDATE_CLIENT_MESSAGE_TYPE_URL));
bytes32 constant REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL_HASH =
keccak256(abi.encodePacked(REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL));
bytes32 constant UPDATE_OPERATORS_MESSAGE_TYPE_URL_HASH =
keccak256(abi.encodePacked(UPDATE_OPERATORS_MESSAGE_TYPE_URL));
bytes32 constant CLIENT_STATE_TYPE_URL_HASH = keccak256(abi.encodePacked(CLIENT_STATE_TYPE_URL));
bytes32 constant CONSENSUS_STATE_TYPE_URL_HASH = keccak256(abi.encodePacked(CONSENSUS_STATE_TYPE_URL));

function marshal(UpdateClientMessage.Data calldata message) external pure returns (bytes memory) {
function marshal(UpdateClientMessage.Data calldata message) public pure returns (bytes memory) {
Any.Data memory any;
any.type_url = UPDATE_CLIENT_MESSAGE_TYPE_URL;
any.value = UpdateClientMessage.encode(message);
return Any.encode(any);
}

function marshal(RegisterEnclaveKeyMessage.Data calldata message) external pure returns (bytes memory) {
function marshalConsensusState(bytes32 stateId, uint64 timestamp) public pure returns (bytes memory) {
Any.Data memory anyConsensusState;
anyConsensusState.type_url = CONSENSUS_STATE_TYPE_URL;
anyConsensusState.value =
ConsensusState.encode(ConsensusState.Data({state_id: abi.encodePacked(stateId), timestamp: timestamp}));
return Any.encode(anyConsensusState);
}

function marshal(RegisterEnclaveKeyMessage.Data calldata message) public pure returns (bytes memory) {
Any.Data memory any;
any.type_url = REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL;
any.value = RegisterEnclaveKeyMessage.encode(message);
return Any.encode(any);
}

function marshal(ClientState.Data calldata clientState) external pure returns (bytes memory) {
function marshal(ClientState.Data calldata clientState) public pure returns (bytes memory) {
Any.Data memory anyClientState;
anyClientState.type_url = CLIENT_STATE_TYPE_URL;
anyClientState.value = ClientState.encode(clientState);
return Any.encode(anyClientState);
}

function marshal(ConsensusState.Data calldata consensusState) external pure returns (bytes memory) {
function marshal(ConsensusState.Data calldata consensusState) public pure returns (bytes memory) {
Any.Data memory anyConsensusState;
anyConsensusState.type_url = CONSENSUS_STATE_TYPE_URL;
anyConsensusState.value = ConsensusState.encode(consensusState);
return Any.encode(anyConsensusState);
}

function routeClientMessage(string calldata clientId, bytes calldata protoClientMessage)
external
public
pure
returns (bytes32 typeUrlHash, bytes memory args)
{
Expand All @@ -62,12 +74,15 @@ library LCPProtoMarshaler {
} else if (typeUrlHash == REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL_HASH) {
RegisterEnclaveKeyMessage.Data memory message = RegisterEnclaveKeyMessage.decode(anyClientMessage.value);
return (typeUrlHash, abi.encode(clientId, message));
} else if (typeUrlHash == UPDATE_OPERATORS_MESSAGE_TYPE_URL_HASH) {
UpdateOperatorsMessage.Data memory message = UpdateOperatorsMessage.decode(anyClientMessage.value);
return (typeUrlHash, abi.encode(clientId, message));
} else {
revert("unsupported client message type");
}
}

function unmarshalClientState(bytes calldata bz) external pure returns (ClientState.Data memory clientState) {
function unmarshalClientState(bytes calldata bz) public pure returns (ClientState.Data memory clientState) {
Any.Data memory anyClientState = Any.decode(bz);
require(
keccak256(abi.encodePacked(anyClientState.type_url)) == CLIENT_STATE_TYPE_URL_HASH,
Expand All @@ -77,7 +92,7 @@ library LCPProtoMarshaler {
}

function unmarshalConsensusState(bytes calldata bz)
external
public
pure
returns (ConsensusState.Data memory consensusState)
{
Expand Down
Loading

0 comments on commit 5ac4e18

Please sign in to comment.