Skip to content

Commit 5ac4e18

Browse files
authored
Merge pull request #17 from datachainlab/multiple-operators
Multiple operators support Signed-off-by: Jun Kimura <jun.kimura@datachain.jp>
2 parents ec447cd + c44ef6d commit 5ac4e18

File tree

85 files changed

+2369
-660
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+2369
-660
lines changed

contracts/AVRValidator.sol

Lines changed: 194 additions & 134 deletions
Large diffs are not rendered by default.

contracts/ILCPClientErrors.sol

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.12;
3+
4+
interface ILCPClientErrors {
5+
error LCPClientRootCACertAlreadyInitialized();
6+
error LCPClientClientStateInvalidLatestHeight();
7+
error LCPClientClientStateFrozen();
8+
error LCPClientClientStateInvalidKeyExpiration();
9+
error LCPClientClientStateInvalidMrenclaveLength();
10+
error LCPClientClientStateUnexpectedMrenclave();
11+
error LCPClientClientStateEmptyOperators();
12+
error LCPClientClientStateInvalidOperatorAddress();
13+
error LCPClientClientStateInvalidOperatorAddressLength();
14+
error LCPClientClientStateInvalidOperatorsNonce();
15+
error LCPClientClientStateUnexpectedOperatorsNonce(uint64 expectedNonce);
16+
17+
error LCPClientOperatorsInvalidOrder(address prevOperator, address nextOperator);
18+
error LCPClientClientStateInvalidOperatorsThreshold();
19+
20+
error LCPClientConsensusStateInvalidTimestamp();
21+
error LCPClientConsensusStateInvalidStateId();
22+
23+
error LCPClientClientStateNotFound();
24+
error LCPClientConsensusStateNotFound();
25+
error LCPClientUnknownProxyMessageHeader();
26+
error LCPClientUnknownProtoTypeUrl();
27+
28+
error LCPClientMembershipVerificationInvalidHeight();
29+
error LCPClientMembershipVerificationInvalidPrefix();
30+
error LCPClientMembershipVerificationInvalidPath();
31+
error LCPClientMembershipVerificationInvalidValue();
32+
error LCPClientMembershipVerificationInvalidStateId();
33+
34+
error LCPClientUpdateStateEmittedStatesMustNotEmpty();
35+
error LCPClientUpdateStatePrevStateIdMustNotEmpty();
36+
error LCPClientUpdateStateUnexpectedPrevStateId();
37+
38+
error LCPClientMisbehaviourPrevStatesMustNotEmpty();
39+
40+
error LCPClientEnclaveKeyNotExist();
41+
error LCPClientEnclaveKeyExpired();
42+
error LCPClientEnclaveKeyUnexpectedOperator(address expected, address actual);
43+
error LCPClientEnclaveKeyUnexpectedExpiredAt();
44+
45+
error LCPClientOperatorSignaturesInsufficient(uint256 success);
46+
47+
error LCPClientIASRootCertExpired();
48+
error LCPClientIASCertExpired();
49+
50+
error LCPClientAVRInvalidSignature();
51+
error LCPClientAVRAlreadyExpired();
52+
53+
error LCPClientInvalidSignaturesLength();
54+
55+
error LCPClientAVRUnexpectedOperator(address actual, address expected);
56+
57+
error LCPClientUpdateOperatorsPermissionless();
58+
error LCPClientUpdateOperatorsSignatureUnexpectedOperator(address actual, address expected);
59+
}

contracts/LCPClientBase.sol

Lines changed: 390 additions & 162 deletions
Large diffs are not rendered by default.

contracts/LCPCommitment.sol

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,26 @@ pragma solidity ^0.8.12;
44
import {Height} from "@hyperledger-labs/yui-ibc-solidity/contracts/proto/Client.sol";
55

66
library LCPCommitment {
7-
uint16 constant LCP_MESSAGE_VERSION = 1;
8-
uint16 constant LCP_MESSAGE_TYPE_UPDATE_STATE = 1;
9-
uint16 constant LCP_MESSAGE_TYPE_STATE = 2;
10-
uint16 constant LCP_MESSAGE_TYPE_MISBEHAVIOUR = 3;
11-
uint16 constant LCP_MESSAGE_CONTEXT_TYPE_EMPTY = 0;
12-
uint16 constant LCP_MESSAGE_CONTEXT_TYPE_TRUSTING_PERIOD = 1;
13-
14-
bytes32 constant LCP_MESSAGE_HEADER_UPDATE_STATE =
7+
uint16 internal constant LCP_MESSAGE_VERSION = 1;
8+
uint16 internal constant LCP_MESSAGE_TYPE_UPDATE_STATE = 1;
9+
uint16 internal constant LCP_MESSAGE_TYPE_STATE = 2;
10+
uint16 internal constant LCP_MESSAGE_TYPE_MISBEHAVIOUR = 3;
11+
uint16 internal constant LCP_MESSAGE_CONTEXT_TYPE_EMPTY = 0;
12+
uint16 internal constant LCP_MESSAGE_CONTEXT_TYPE_TRUSTING_PERIOD = 1;
13+
14+
bytes32 internal constant LCP_MESSAGE_HEADER_UPDATE_STATE =
1515
bytes32(uint256(LCP_MESSAGE_VERSION) << 240 | uint256(LCP_MESSAGE_TYPE_UPDATE_STATE) << 224);
16-
bytes32 constant LCP_MESSAGE_HEADER_STATE =
16+
bytes32 internal constant LCP_MESSAGE_HEADER_STATE =
1717
bytes32(uint256(LCP_MESSAGE_VERSION) << 240 | uint256(LCP_MESSAGE_TYPE_STATE) << 224);
18-
bytes32 constant LCP_MESSAGE_HEADER_MISBEHAVIOUR =
18+
bytes32 internal constant LCP_MESSAGE_HEADER_MISBEHAVIOUR =
1919
bytes32(uint256(LCP_MESSAGE_VERSION) << 240 | uint256(LCP_MESSAGE_TYPE_MISBEHAVIOUR) << 224);
2020

21+
error LCPCommitmentUnexpectedProxyMessageHeader();
22+
error LCPCommtimentInvalidTrustingPeriodContextLength();
23+
error LCPCommitmentUnknownValidationContextType();
24+
error LCPCommtimentTrustingPeriodContextOutOfTrustingPeriod();
25+
error LCPCommitmentTrustingPeriodHeaderFromFuture();
26+
2127
struct HeaderedProxyMessage {
2228
bytes32 header;
2329
bytes message;
@@ -48,7 +54,9 @@ library LCPCommitment {
4854
// 0-1: version
4955
// 2-3: message type
5056
// 4-31: reserved
51-
require(hm.header == LCP_MESSAGE_HEADER_UPDATE_STATE, "unexpected header");
57+
if (hm.header != LCP_MESSAGE_HEADER_UPDATE_STATE) {
58+
revert LCPCommitmentUnexpectedProxyMessageHeader();
59+
}
5260
return abi.decode(hm.message, (UpdateStateProxyMessage));
5361
}
5462

@@ -73,7 +81,9 @@ library LCPCommitment {
7381
// 0-1: version
7482
// 2-3: message type
7583
// 4-31: reserved
76-
require(hm.header == LCP_MESSAGE_HEADER_MISBEHAVIOUR, "unexpected header");
84+
if (hm.header != LCP_MESSAGE_HEADER_MISBEHAVIOUR) {
85+
revert LCPCommitmentUnexpectedProxyMessageHeader();
86+
}
7787
return abi.decode(hm.message, (MisbehaviourProxyMessage));
7888
}
7989

@@ -109,10 +119,12 @@ library LCPCommitment {
109119
if (contextType == LCP_MESSAGE_CONTEXT_TYPE_EMPTY) {
110120
return;
111121
} else if (contextType == LCP_MESSAGE_CONTEXT_TYPE_TRUSTING_PERIOD) {
112-
require(vc.context.length == 64, "invalid trusting period context length");
122+
if (vc.context.length != 64) {
123+
revert LCPCommtimentInvalidTrustingPeriodContextLength();
124+
}
113125
return trustingPeriodContextEval(parseTrustingPeriodContext(vc.context), currentTimestampNanos);
114126
} else {
115-
revert("unknown context type");
127+
revert LCPCommitmentUnknownValidationContextType();
116128
}
117129
}
118130

@@ -138,17 +150,16 @@ library LCPCommitment {
138150
pure
139151
{
140152
if (currentTimestampNanos >= context.trustedStateTimestamp + context.trustingPeriod) {
141-
require(false, "out of trusting period");
153+
revert LCPCommtimentTrustingPeriodContextOutOfTrustingPeriod();
142154
} else if (currentTimestampNanos + context.clockDrift <= context.untrustedHeaderTimestamp) {
143-
require(false, "header is from the future");
155+
revert LCPCommitmentTrustingPeriodHeaderFromFuture();
144156
}
145157
return;
146158
}
147159

148-
struct CommitmentProof {
160+
struct CommitmentProofs {
149161
bytes message;
150-
address signer;
151-
bytes signature;
162+
bytes[] signatures;
152163
}
153164

154165
struct VerifyMembershipProxyMessage {
@@ -169,16 +180,18 @@ library LCPCommitment {
169180
// 0-1: version
170181
// 2-3: message type
171182
// 4-31: reserved
172-
require(hm.header == LCP_MESSAGE_HEADER_STATE, "unexpected header");
183+
if (hm.header != LCP_MESSAGE_HEADER_STATE) {
184+
revert LCPCommitmentUnexpectedProxyMessageHeader();
185+
}
173186
return abi.decode(hm.message, (VerifyMembershipProxyMessage));
174187
}
175188

176-
function parseVerifyMembershipCommitmentProof(bytes calldata commitmentProofBytes)
189+
function parseVerifyMembershipCommitmentProofs(bytes calldata commitmentProofsBytes)
177190
internal
178191
pure
179-
returns (CommitmentProof memory, VerifyMembershipProxyMessage memory)
192+
returns (CommitmentProofs memory, VerifyMembershipProxyMessage memory)
180193
{
181-
CommitmentProof memory commitmentProof = abi.decode(commitmentProofBytes, (CommitmentProof));
182-
return (commitmentProof, parseVerifyMembershipProxyMessage(commitmentProof.message));
194+
CommitmentProofs memory commitmentProofs = abi.decode(commitmentProofsBytes, (CommitmentProofs));
195+
return (commitmentProofs, parseVerifyMembershipProxyMessage(commitmentProofs.message));
183196
}
184197
}

contracts/LCPOperator.sol

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.12;
3+
4+
library LCPOperator {
5+
type ChainType is uint16;
6+
7+
bytes32 internal constant TYPEHASH_DOMAIN_SEPARATOR =
8+
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)");
9+
bytes32 internal constant TYPEHASH_REGISTER_ENCLAVE_KEY = keccak256("RegisterEnclaveKey(string avr)");
10+
bytes32 internal constant TYPEHASH_UPDATE_OPERATORS = keccak256(
11+
"UpdateOperators(string clientId,uint64 nonce,address[] newOperators,uint64 thresholdNumerator,uint64 thresholdDenominator)"
12+
);
13+
14+
bytes32 internal constant DOMAIN_SEPARATOR_NAME = keccak256("LCPClient");
15+
bytes32 internal constant DOMAIN_SEPARATOR_VERSION = keccak256("1");
16+
17+
// domainSeparator(0, address(0))
18+
bytes32 internal constant DOMAIN_SEPARATOR_REGISTER_ENCLAVE_KEY =
19+
0xe33d217bff42bc015bf037be8386bf5055ec6019e58e8c5e89b5c74b8225fa6a;
20+
ChainType internal constant CHAIN_TYPE_EVM = ChainType.wrap(1);
21+
// chainTypeSalt(CHAIN_TYPE_EVM, hex"")
22+
bytes32 internal constant CHAIN_TYPE_EVM_SALT = keccak256(abi.encodePacked(CHAIN_TYPE_EVM, hex""));
23+
24+
function chainTypeSalt(ChainType chainType, bytes memory args) internal pure returns (bytes32) {
25+
return keccak256(abi.encodePacked(chainType, args));
26+
}
27+
28+
function domainSeparator(uint256 chainId, address verifyingContract) internal pure returns (bytes32) {
29+
return keccak256(
30+
abi.encode(
31+
TYPEHASH_DOMAIN_SEPARATOR,
32+
DOMAIN_SEPARATOR_NAME,
33+
DOMAIN_SEPARATOR_VERSION,
34+
chainId,
35+
verifyingContract,
36+
CHAIN_TYPE_EVM_SALT
37+
)
38+
);
39+
}
40+
41+
function computeEIP712RegisterEnclaveKey(bytes calldata avr) internal pure returns (bytes memory) {
42+
return abi.encodePacked(
43+
hex"1901",
44+
DOMAIN_SEPARATOR_REGISTER_ENCLAVE_KEY,
45+
keccak256(abi.encode(TYPEHASH_REGISTER_ENCLAVE_KEY, keccak256(avr)))
46+
);
47+
}
48+
49+
function computeEIP712UpdateOperators(
50+
string calldata clientId,
51+
uint64 nonce,
52+
address[] memory newOperators,
53+
uint64 thresholdNumerator,
54+
uint64 thresholdDenominator
55+
) internal view returns (bytes memory) {
56+
return computeEIP712UpdateOperators(
57+
block.chainid, address(this), clientId, nonce, newOperators, thresholdNumerator, thresholdDenominator
58+
);
59+
}
60+
61+
function computeEIP712UpdateOperators(
62+
uint256 chainId,
63+
address verifyingContract,
64+
string calldata clientId,
65+
uint64 nonce,
66+
address[] memory newOperators,
67+
uint64 thresholdNumerator,
68+
uint64 thresholdDenominator
69+
) internal pure returns (bytes memory) {
70+
return abi.encodePacked(
71+
hex"1901",
72+
domainSeparator(chainId, verifyingContract),
73+
keccak256(
74+
abi.encode(
75+
TYPEHASH_UPDATE_OPERATORS,
76+
keccak256(bytes(clientId)),
77+
nonce,
78+
keccak256(abi.encodePacked(newOperators)),
79+
thresholdNumerator,
80+
thresholdDenominator
81+
)
82+
)
83+
);
84+
}
85+
}

contracts/LCPProtoMarshaler.sol

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,64 @@ import {
55
IbcLightclientsLcpV1ClientState as ClientState,
66
IbcLightclientsLcpV1ConsensusState as ConsensusState,
77
IbcLightclientsLcpV1RegisterEnclaveKeyMessage as RegisterEnclaveKeyMessage,
8-
IbcLightclientsLcpV1UpdateClientMessage as UpdateClientMessage
8+
IbcLightclientsLcpV1UpdateClientMessage as UpdateClientMessage,
9+
IbcLightclientsLcpV1UpdateOperatorsMessage as UpdateOperatorsMessage
910
} from "./proto/ibc/lightclients/lcp/v1/LCP.sol";
1011
import {GoogleProtobufAny as Any} from "@hyperledger-labs/yui-ibc-solidity/contracts/proto/GoogleProtobufAny.sol";
1112

1213
library LCPProtoMarshaler {
1314
string constant UPDATE_CLIENT_MESSAGE_TYPE_URL = "/ibc.lightclients.lcp.v1.UpdateClientMessage";
1415
string constant REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL = "/ibc.lightclients.lcp.v1.RegisterEnclaveKeyMessage";
16+
string constant UPDATE_OPERATORS_MESSAGE_TYPE_URL = "/ibc.lightclients.lcp.v1.UpdateOperatorsMessage";
1517
string constant CLIENT_STATE_TYPE_URL = "/ibc.lightclients.lcp.v1.ClientState";
1618
string constant CONSENSUS_STATE_TYPE_URL = "/ibc.lightclients.lcp.v1.ConsensusState";
1719

1820
bytes32 constant UPDATE_CLIENT_MESSAGE_TYPE_URL_HASH = keccak256(abi.encodePacked(UPDATE_CLIENT_MESSAGE_TYPE_URL));
1921
bytes32 constant REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL_HASH =
2022
keccak256(abi.encodePacked(REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL));
23+
bytes32 constant UPDATE_OPERATORS_MESSAGE_TYPE_URL_HASH =
24+
keccak256(abi.encodePacked(UPDATE_OPERATORS_MESSAGE_TYPE_URL));
2125
bytes32 constant CLIENT_STATE_TYPE_URL_HASH = keccak256(abi.encodePacked(CLIENT_STATE_TYPE_URL));
2226
bytes32 constant CONSENSUS_STATE_TYPE_URL_HASH = keccak256(abi.encodePacked(CONSENSUS_STATE_TYPE_URL));
2327

24-
function marshal(UpdateClientMessage.Data calldata message) external pure returns (bytes memory) {
28+
function marshal(UpdateClientMessage.Data calldata message) public pure returns (bytes memory) {
2529
Any.Data memory any;
2630
any.type_url = UPDATE_CLIENT_MESSAGE_TYPE_URL;
2731
any.value = UpdateClientMessage.encode(message);
2832
return Any.encode(any);
2933
}
3034

31-
function marshal(RegisterEnclaveKeyMessage.Data calldata message) external pure returns (bytes memory) {
35+
function marshalConsensusState(bytes32 stateId, uint64 timestamp) public pure returns (bytes memory) {
36+
Any.Data memory anyConsensusState;
37+
anyConsensusState.type_url = CONSENSUS_STATE_TYPE_URL;
38+
anyConsensusState.value =
39+
ConsensusState.encode(ConsensusState.Data({state_id: abi.encodePacked(stateId), timestamp: timestamp}));
40+
return Any.encode(anyConsensusState);
41+
}
42+
43+
function marshal(RegisterEnclaveKeyMessage.Data calldata message) public pure returns (bytes memory) {
3244
Any.Data memory any;
3345
any.type_url = REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL;
3446
any.value = RegisterEnclaveKeyMessage.encode(message);
3547
return Any.encode(any);
3648
}
3749

38-
function marshal(ClientState.Data calldata clientState) external pure returns (bytes memory) {
50+
function marshal(ClientState.Data calldata clientState) public pure returns (bytes memory) {
3951
Any.Data memory anyClientState;
4052
anyClientState.type_url = CLIENT_STATE_TYPE_URL;
4153
anyClientState.value = ClientState.encode(clientState);
4254
return Any.encode(anyClientState);
4355
}
4456

45-
function marshal(ConsensusState.Data calldata consensusState) external pure returns (bytes memory) {
57+
function marshal(ConsensusState.Data calldata consensusState) public pure returns (bytes memory) {
4658
Any.Data memory anyConsensusState;
4759
anyConsensusState.type_url = CONSENSUS_STATE_TYPE_URL;
4860
anyConsensusState.value = ConsensusState.encode(consensusState);
4961
return Any.encode(anyConsensusState);
5062
}
5163

5264
function routeClientMessage(string calldata clientId, bytes calldata protoClientMessage)
53-
external
65+
public
5466
pure
5567
returns (bytes32 typeUrlHash, bytes memory args)
5668
{
@@ -62,12 +74,15 @@ library LCPProtoMarshaler {
6274
} else if (typeUrlHash == REGISTER_ENCLAVE_KEY_MESSAGE_TYPE_URL_HASH) {
6375
RegisterEnclaveKeyMessage.Data memory message = RegisterEnclaveKeyMessage.decode(anyClientMessage.value);
6476
return (typeUrlHash, abi.encode(clientId, message));
77+
} else if (typeUrlHash == UPDATE_OPERATORS_MESSAGE_TYPE_URL_HASH) {
78+
UpdateOperatorsMessage.Data memory message = UpdateOperatorsMessage.decode(anyClientMessage.value);
79+
return (typeUrlHash, abi.encode(clientId, message));
6580
} else {
6681
revert("unsupported client message type");
6782
}
6883
}
6984

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

7994
function unmarshalConsensusState(bytes calldata bz)
80-
external
95+
public
8196
pure
8297
returns (ConsensusState.Data memory consensusState)
8398
{

0 commit comments

Comments
 (0)