1
1
package ibc .ics08 .tendermint ;
2
2
3
3
import icon .ibc .interfaces .ILightClient ;
4
- import ibc .icon .score .util .ByteUtil ;
5
4
import ibc .icon .score .util .NullChecker ;
6
5
import ibc .icon .score .util .StringUtil ;
7
6
import ibc .ics23 .commitment .types .Merkle ;
8
7
import ibc .ics24 .host .IBCCommitment ;
9
- import cosmos .ics23 .v1 .*;
10
8
import google .protobuf .*;
11
9
import tendermint .types .*;
12
10
import ibc .core .commitment .v1 .*;
17
15
import score .Context ;
18
16
import score .DictDB ;
19
17
import score .annotation .External ;
20
- import score .annotation .Optional ;
21
18
22
19
import java .math .BigInteger ;
23
20
import java .util .Arrays ;
@@ -54,7 +51,7 @@ private void onlyHandler() {
54
51
55
52
/**
56
53
* @dev getTimestampAtHeight returns the timestamp of the consensus state at the
57
- * given height.
54
+ * given height.
58
55
*/
59
56
@ External (readonly = true )
60
57
public BigInteger getTimestampAtHeight (
@@ -118,79 +115,29 @@ public Map<String, byte[]> createClient(String clientId, byte[] clientStateBytes
118
115
@ External
119
116
public Map <String , byte []> updateClient (String clientId , byte [] clientMessageBytes ) {
120
117
onlyHandler ();
121
- ibc .lightclients .tendermint .v1 .Header tmHeader = ibc .lightclients .tendermint .v1 .Header .decode (clientMessageBytes );
122
- boolean conflictingHeader = false ;
123
- // Check if the Client store already has a consensus state for the header's
124
- // height
125
- // If the consensus state exists, and it matches the header then we return early
126
- // since header has already been submitted in a previous UpdateClient.
127
- byte [] prevConsState = consensusStates .at (clientId )
128
- .get (tmHeader .getSignedHeader ().getHeader ().getHeight ());
129
- if (prevConsState != null ) {
130
- // This header has already been submitted and the necessary state is already
131
- // stored
132
- Context .require (!Arrays .equals (prevConsState , toConsensusState (tmHeader ).encode ()),
133
- "LC: This header has already been submitted" );
134
-
135
- // A consensus state already exists for this height, but it does not match the
136
- // provided header.
137
- // Thus, we must check that this header is valid, and if so we will freeze the
138
- // client.
139
- conflictingHeader = true ;
140
- }
118
+ ibc .lightclients .tendermint .v1 .Header tmHeader = ibc .lightclients .tendermint .v1 .Header
119
+ .decode (clientMessageBytes );
120
+ boolean conflictingHeader = checkForDuplicateHeader (clientId , tmHeader );
141
121
142
122
byte [] encodedClientState = clientStates .get (clientId );
143
123
require (encodedClientState != null , "LC: client state is invalid" );
144
124
ClientState clientState = ClientState .decode (encodedClientState );
145
- byte [] encodedTrustedonsensusState = consensusStates .at (clientId ).get (tmHeader .getTrustedHeight ().getRevisionHeight ());
146
- require (encodedTrustedonsensusState != null , "LC: consensusState not found at trusted height" );
147
- ConsensusState trustedConsensusState = ConsensusState .decode (encodedTrustedonsensusState );
148
125
149
- Timestamp currentTime = getCurrentTime ();
150
- checkValidity (clientState , trustedConsensusState , tmHeader , currentTime );
126
+ byte [] encodedTrustedConsensusState = consensusStates .at (clientId )
127
+ .get (tmHeader .getTrustedHeight ().getRevisionHeight ());
128
+ require (encodedTrustedConsensusState != null , "LC: consensusState not found at trusted height" );
129
+ ConsensusState trustedConsensusState = ConsensusState .decode (encodedTrustedConsensusState );
130
+
131
+ checkValidity (clientState , trustedConsensusState , tmHeader );
151
132
152
133
// Header is different from existing consensus state and also valid, so freeze
153
134
// the client and return
154
135
if (conflictingHeader ) {
155
- BigInteger revision = getRevisionNumber (tmHeader .getSignedHeader ().getHeader ().getChainId ());
156
- clientState .setFrozenHeight (newHeight (tmHeader .getSignedHeader ().getHeader ().getHeight (), revision ));
157
- encodedClientState = clientState .encode ();
158
- clientStates .set (clientId , encodedClientState );
159
-
160
- byte [] encodedConsensusState = toConsensusState (tmHeader ).encode ();
161
- consensusStates .at (clientId ).set (clientState .getLatestHeight ().getRevisionHeight (), encodedConsensusState );
162
- processedHeights .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
163
- BigInteger .valueOf (Context .getBlockHeight ()));
164
- processedTimes .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
165
- BigInteger .valueOf (Context .getBlockTimestamp ()));
166
-
167
- return Map .of (
168
- "clientStateCommitment" , IBCCommitment .keccak256 (encodedClientState ),
169
- "consensusStateCommitment" , IBCCommitment .keccak256 (encodedConsensusState ),
170
- "height" ,
171
- newHeight (tmHeader .getSignedHeader ().getHeader ().getHeight (), revision ).encode ());
136
+ return handleConflict (clientId , tmHeader , clientState );
172
137
}
173
-
174
138
// update the consensus state from a new header and set processed time metadata
175
- if (tmHeader .getSignedHeader ().getHeader ().getHeight ().compareTo (clientState .getLatestHeight ().getRevisionHeight ()) > 0 ) {
176
- BigInteger revision = getRevisionNumber (tmHeader .getSignedHeader ().getHeader ().getChainId ());
177
- clientState .setLatestHeight (newHeight (tmHeader .getSignedHeader ().getHeader ().getHeight (), revision ));
178
- encodedClientState = clientState .encode ();
179
- clientStates .set (clientId , encodedClientState );
180
- }
139
+ return updateHeader (clientId , tmHeader , clientState , encodedClientState );
181
140
182
- byte [] encodedConsensusState = toConsensusState (tmHeader ).encode ();
183
- consensusStates .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
184
- encodedConsensusState );
185
- processedHeights .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
186
- BigInteger .valueOf (Context .getBlockHeight ()));
187
- processedTimes .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
188
- BigInteger .valueOf (Context .getBlockTimestamp ()));
189
-
190
- return Map .of (
191
- "clientStateCommitment" , IBCCommitment .keccak256 (encodedClientState ),
192
- "consensusStateCommitment" , IBCCommitment .keccak256 (encodedConsensusState ),
193
- "height" , clientState .getLatestHeight ().encode ());
194
141
}
195
142
196
143
@ External (readonly = true )
@@ -242,12 +189,79 @@ public void verifyNonMembership(
242
189
Merkle .verifyNonMembership (merkleProof , Merkle .SDK_SPEC , root , merklePath );
243
190
}
244
191
192
+ private Map <String , byte []> updateHeader (String clientId , ibc .lightclients .tendermint .v1 .Header tmHeader ,
193
+ ClientState clientState , byte [] encodedClientState ) {
194
+ if (tmHeader .getSignedHeader ().getHeader ().getHeight ()
195
+ .compareTo (clientState .getLatestHeight ().getRevisionHeight ()) > 0 ) {
196
+ BigInteger revision = getRevisionNumber (tmHeader .getSignedHeader ().getHeader ().getChainId ());
197
+ clientState .setLatestHeight (newHeight (tmHeader .getSignedHeader ().getHeader ().getHeight (), revision ));
198
+ encodedClientState = clientState .encode ();
199
+ clientStates .set (clientId , encodedClientState );
200
+ }
201
+
202
+ byte [] encodedConsensusState = toConsensusState (tmHeader ).encode ();
203
+ consensusStates .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
204
+ encodedConsensusState );
205
+ processedHeights .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
206
+ BigInteger .valueOf (Context .getBlockHeight ()));
207
+ processedTimes .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
208
+ BigInteger .valueOf (Context .getBlockTimestamp ()));
209
+
210
+ return Map .of (
211
+ "clientStateCommitment" , IBCCommitment .keccak256 (encodedClientState ),
212
+ "consensusStateCommitment" , IBCCommitment .keccak256 (encodedConsensusState ),
213
+ "height" , clientState .getLatestHeight ().encode ());
214
+ }
215
+
216
+ private Map <String , byte []> handleConflict (String clientId , ibc .lightclients .tendermint .v1 .Header tmHeader ,
217
+ ClientState clientState ) {
218
+ BigInteger revision = getRevisionNumber (tmHeader .getSignedHeader ().getHeader ().getChainId ());
219
+ clientState .setFrozenHeight (newHeight (tmHeader .getSignedHeader ().getHeader ().getHeight (), revision ));
220
+ byte [] encodedClientState = clientState .encode ();
221
+ clientStates .set (clientId , encodedClientState );
222
+
223
+ byte [] encodedConsensusState = toConsensusState (tmHeader ).encode ();
224
+ consensusStates .at (clientId ).set (clientState .getLatestHeight ().getRevisionHeight (), encodedConsensusState );
225
+ processedHeights .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
226
+ BigInteger .valueOf (Context .getBlockHeight ()));
227
+ processedTimes .at (clientId ).set (tmHeader .getSignedHeader ().getHeader ().getHeight (),
228
+ BigInteger .valueOf (Context .getBlockTimestamp ()));
229
+
230
+ return Map .of (
231
+ "clientStateCommitment" , IBCCommitment .keccak256 (encodedClientState ),
232
+ "consensusStateCommitment" , IBCCommitment .keccak256 (encodedConsensusState ),
233
+ "height" ,
234
+ newHeight (tmHeader .getSignedHeader ().getHeader ().getHeight (), revision ).encode ());
235
+ }
236
+
237
+ private boolean checkForDuplicateHeader (String clientId , ibc .lightclients .tendermint .v1 .Header tmHeader ) {
238
+ // Check if the Client store already has a consensus state for the header's
239
+ // height
240
+ // If the consensus state exists, and it matches the header then we return early
241
+ // since header has already been submitted in a previous UpdateClient.
242
+ byte [] prevConsState = consensusStates .at (clientId )
243
+ .get (tmHeader .getSignedHeader ().getHeader ().getHeight ());
244
+ if (prevConsState == null ) {
245
+ return false ;
246
+ }
247
+
248
+ // This header has already been submitted and the necessary state is already
249
+ // stored
250
+ Context .require (!Arrays .equals (prevConsState , toConsensusState (tmHeader ).encode ()),
251
+ "LC: This header has already been submitted" );
252
+
253
+ // A consensus state already exists for this height, but it does not match the
254
+ // provided header.
255
+ // Thus, we must check that this header is valid, and if so we will freeze the
256
+ // client.
257
+ return true ;
258
+ };
259
+
245
260
// checkValidity checks if the Tendermint header is valid.
246
261
public void checkValidity (
247
262
ClientState clientState ,
248
263
ConsensusState trustedConsensusState ,
249
- ibc .lightclients .tendermint .v1 .Header tmHeader ,
250
- Timestamp currentTime ) {
264
+ ibc .lightclients .tendermint .v1 .Header tmHeader ) {
251
265
// assert header height is newer than consensus state
252
266
require (
253
267
tmHeader .getSignedHeader ().getHeader ().getHeight ()
@@ -267,11 +281,11 @@ public void checkValidity(
267
281
SignedHeader untrustedHeader = tmHeader .getSignedHeader ();
268
282
ValidatorSet untrustedVals = tmHeader .getValidatorSet ();
269
283
284
+ Timestamp currentTime = getCurrentTime ();
270
285
Context .require (!isExpired (trustedHeader , clientState .getTrustingPeriod (), currentTime ),
271
286
"header can't be expired" );
272
287
273
288
boolean ok = verify (
274
- clientState .getTrustingPeriod (),
275
289
clientState .getMaxClockDrift (),
276
290
clientState .getTrustLevel (),
277
291
trustedHeader ,
@@ -287,15 +301,15 @@ private void validateArgs(ClientState cs, BigInteger height, byte[] prefix, byte
287
301
Context .require (cs .getLatestHeight ().getRevisionHeight ().compareTo (height ) >= 0 ,
288
302
"Latest height must be greater or equal to proof height" );
289
303
Context .require (cs .getFrozenHeight ().getRevisionHeight ().equals (BigInteger .ZERO ) ||
290
- cs .getFrozenHeight ().getRevisionHeight ().compareTo (height ) >= 0 ,
304
+ cs .getFrozenHeight ().getRevisionHeight ().compareTo (height ) >= 0 ,
291
305
"Client is Frozen" );
292
306
Context .require (prefix .length > 0 , "Prefix cant be empty" );
293
307
Context .require (proof .length > 0 , "Proof cant be empty" );
294
308
}
295
309
296
310
private void validateDelayPeriod (String clientId , Height height ,
297
- BigInteger delayPeriodTime ,
298
- BigInteger delayPeriodBlocks ) {
311
+ BigInteger delayPeriodTime ,
312
+ BigInteger delayPeriodBlocks ) {
299
313
BigInteger currentTime = BigInteger .valueOf (Context .getBlockTimestamp ());
300
314
BigInteger validTime = mustGetProcessedTime (clientId ,
301
315
height .getRevisionHeight ()).add (delayPeriodTime );
0 commit comments