1
1
//! This module provides [`verify_membership`] function to verify the membership of a key in the
2
2
//! storage trie.
3
3
4
- use alloy_primitives:: { keccak256, Bytes , Keccak256 , U256 } ;
5
- use alloy_rlp:: encode_fixed_size;
6
- use alloy_trie:: { proof:: verify_proof, Nibbles } ;
4
+ use alloy_primitives:: { keccak256, Keccak256 , U256 } ;
5
+ use ethereum_trie_db:: trie_db:: { verify_storage_exclusion_proof, verify_storage_inclusion_proof} ;
7
6
use ethereum_types:: execution:: storage_proof:: StorageProof ;
8
7
9
8
use crate :: { client_state:: ClientState , consensus_state:: ConsensusState , error:: EthereumIBCError } ;
@@ -17,67 +16,100 @@ pub fn verify_membership(
17
16
client_state : ClientState ,
18
17
proof : Vec < u8 > ,
19
18
path : Vec < Vec < u8 > > ,
20
- raw_value : Option < Vec < u8 > > ,
19
+ raw_value : Vec < u8 > ,
21
20
) -> Result < ( ) , EthereumIBCError > {
22
- let path = path. first ( ) . ok_or ( EthereumIBCError :: EmptyPath ) ?;
23
-
24
21
let storage_proof: StorageProof = serde_json:: from_slice ( proof. as_slice ( ) )
25
22
. map_err ( |_| EthereumIBCError :: StorageProofDecode ) ?;
26
23
27
- check_commitment_key (
28
- path. clone ( ) ,
24
+ check_commitment_path (
25
+ & path,
29
26
client_state. ibc_commitment_slot ,
30
27
storage_proof. key . into ( ) ,
31
28
) ?;
32
29
33
- let value = match raw_value {
34
- Some ( unwrapped_raw_value) => {
35
- let proof_value = storage_proof. value . to_be_bytes_vec ( ) ;
36
- if proof_value != unwrapped_raw_value {
37
- return Err ( EthereumIBCError :: StoredValueMistmatch {
38
- expected : unwrapped_raw_value,
39
- actual : proof_value,
40
- } ) ;
41
- }
42
- Some ( encode_fixed_size ( & storage_proof. value ) . to_vec ( ) )
30
+ ensure ! (
31
+ storage_proof. value. to_be_bytes_vec( ) == raw_value,
32
+ EthereumIBCError :: StoredValueMistmatch {
33
+ expected: raw_value,
34
+ actual: storage_proof. value. to_be_bytes_vec( ) ,
43
35
}
44
- None => None ,
45
- } ;
36
+ ) ;
37
+
38
+ let rlp_value = alloy_rlp:: encode_fixed_size ( & storage_proof. value ) ;
39
+ verify_storage_inclusion_proof (
40
+ & trusted_consensus_state. storage_root ,
41
+ & storage_proof. key ,
42
+ & rlp_value,
43
+ storage_proof. proof . iter ( ) ,
44
+ )
45
+ . map_err ( |err| EthereumIBCError :: VerifyStorageProof ( err. to_string ( ) ) )
46
+ }
47
+
48
+ /// Verifies the non-membership of a key in the storage trie.
49
+ /// # Errors
50
+ /// Returns an error if the proof cannot be verified.
51
+ #[ allow( clippy:: module_name_repetitions, clippy:: needless_pass_by_value) ]
52
+ pub fn verify_non_membership (
53
+ trusted_consensus_state : ConsensusState ,
54
+ client_state : ClientState ,
55
+ proof : Vec < u8 > ,
56
+ path : Vec < Vec < u8 > > ,
57
+ ) -> Result < ( ) , EthereumIBCError > {
58
+ let storage_proof: StorageProof = serde_json:: from_slice ( proof. as_slice ( ) )
59
+ . map_err ( |_| EthereumIBCError :: StorageProofDecode ) ?;
46
60
47
- let proof: Vec < & Bytes > = storage_proof. proof . iter ( ) . collect ( ) ;
61
+ check_commitment_path (
62
+ & path,
63
+ client_state. ibc_commitment_slot ,
64
+ storage_proof. key . into ( ) ,
65
+ ) ?;
66
+
67
+ ensure ! (
68
+ storage_proof. value. is_zero( ) ,
69
+ EthereumIBCError :: StoredValueMistmatch {
70
+ expected: vec![ 0 ] ,
71
+ actual: storage_proof. value. to_be_bytes_vec( ) ,
72
+ }
73
+ ) ;
48
74
49
- verify_proof :: < Vec < & Bytes > > (
50
- trusted_consensus_state. storage_root ,
51
- Nibbles :: unpack ( keccak256 ( storage_proof. key ) ) ,
52
- value,
53
- proof,
75
+ verify_storage_exclusion_proof (
76
+ & trusted_consensus_state. storage_root ,
77
+ & storage_proof. key ,
78
+ storage_proof. proof . iter ( ) ,
54
79
)
55
80
. map_err ( |err| EthereumIBCError :: VerifyStorageProof ( err. to_string ( ) ) )
56
81
}
57
82
58
- fn check_commitment_key (
59
- path : Vec < u8 > ,
83
+ fn check_commitment_path (
84
+ path : & [ Vec < u8 > ] ,
60
85
ibc_commitment_slot : U256 ,
61
86
key : U256 ,
62
87
) -> Result < ( ) , EthereumIBCError > {
63
- let expected_commitment_key = ibc_commitment_key_v2 ( path, ibc_commitment_slot) ;
64
-
65
- // Data MUST be stored to the commitment path that is defined in ICS23.
66
- if expected_commitment_key == key {
67
- Ok ( ( ) )
68
- } else {
69
- Err ( EthereumIBCError :: InvalidCommitmentKey (
70
- format ! ( "0x{expected_commitment_key:x}" ) ,
88
+ ensure ! (
89
+ path. len( ) == 1 ,
90
+ EthereumIBCError :: InvalidPathLength {
91
+ expected: 1 ,
92
+ found: path. len( )
93
+ }
94
+ ) ;
95
+
96
+ let expected_commitment_path = evm_ics26_commitment_path ( & path[ 0 ] , ibc_commitment_slot) ;
97
+ ensure ! (
98
+ expected_commitment_path == key,
99
+ EthereumIBCError :: InvalidCommitmentKey (
100
+ format!( "0x{expected_commitment_path:x}" ) ,
71
101
format!( "0x{key:x}" ) ,
72
- ) )
73
- }
102
+ )
103
+ ) ;
104
+
105
+ Ok ( ( ) )
74
106
}
75
107
76
108
// TODO: Unit test
77
109
/// Computes the commitment key for a given path and slot.
78
- #[ must_use = "calculating the commitment key has no effect" ]
79
- pub fn ibc_commitment_key_v2 ( path : Vec < u8 > , slot : U256 ) -> U256 {
80
- let path_hash = keccak256 ( path ) ;
110
+ #[ must_use = "calculating the commitment path has no effect" ]
111
+ pub fn evm_ics26_commitment_path ( ibc_path : & [ u8 ] , slot : U256 ) -> U256 {
112
+ let path_hash = keccak256 ( ibc_path ) ;
81
113
82
114
let mut hasher = Keccak256 :: new ( ) ;
83
115
hasher. update ( path_hash) ;
@@ -105,7 +137,7 @@ mod test {
105
137
106
138
use prost:: Message ;
107
139
108
- use super :: verify_membership;
140
+ use super :: { verify_membership, verify_non_membership } ;
109
141
110
142
#[ test]
111
143
fn test_with_fixture ( ) {
@@ -148,7 +180,7 @@ mod test {
148
180
client_state,
149
181
storage_proof,
150
182
vec ! [ path] ,
151
- Some ( value) ,
183
+ value,
152
184
)
153
185
. unwrap ( ) ;
154
186
}
@@ -199,7 +231,7 @@ mod test {
199
231
client_state. clone ( ) ,
200
232
storage_proof_bz,
201
233
path. clone ( ) ,
202
- Some ( value. to_be_bytes_vec ( ) ) ,
234
+ value. to_be_bytes_vec ( ) ,
203
235
)
204
236
. unwrap ( ) ;
205
237
@@ -208,7 +240,7 @@ mod test {
208
240
let storage_proof = StorageProof { key, value, proof } ;
209
241
let storage_proof_bz = serde_json:: to_vec ( & storage_proof) . unwrap ( ) ;
210
242
211
- verify_membership ( consensus_state, client_state, storage_proof_bz, path, None ) . unwrap_err ( ) ;
243
+ verify_non_membership ( consensus_state, client_state, storage_proof_bz, path) . unwrap_err ( ) ;
212
244
}
213
245
214
246
#[ test]
@@ -246,12 +278,11 @@ mod test {
246
278
let proof = StorageProof { key, value, proof } ;
247
279
let proof_bz = serde_json:: to_vec ( & proof) . unwrap ( ) ;
248
280
249
- verify_membership (
281
+ verify_non_membership (
250
282
consensus_state. clone ( ) ,
251
283
client_state. clone ( ) ,
252
284
proof_bz. clone ( ) ,
253
285
path. clone ( ) ,
254
- None ,
255
286
)
256
287
. unwrap ( ) ;
257
288
@@ -261,7 +292,7 @@ mod test {
261
292
client_state,
262
293
proof_bz,
263
294
path,
264
- Some ( value. to_be_bytes_vec ( ) ) ,
295
+ value. to_be_bytes_vec ( ) ,
265
296
)
266
297
. unwrap_err ( ) ;
267
298
}
0 commit comments