1
- use crate :: v2:: trie:: Trie ;
1
+ use crate :: v1:: sparse_mpt:: { DeletionError as DeletionErrorV1 , DiffTrie } ;
2
+ use crate :: v2:: trie:: { DeletionError as DeletionErrorV2 , Trie } ;
3
+
4
+ use alloy_primitives:: { hex, keccak256, Bytes , FixedBytes } ;
2
5
use quickcheck:: { quickcheck, Arbitrary , Gen } ;
3
6
use std:: collections:: HashMap ;
4
7
5
- // The maximum key size. keeping it relatively
6
- // small increases the chance of multiple
7
- // operations being executed against the same
8
- // key, which will tease out more bugs.
9
- const KEY_SPACE : u8 = 16 ;
10
-
11
8
#[ derive( Clone , Debug ) ]
12
9
enum Op {
13
- Insert ( Vec < u8 > , Vec < u8 > ) ,
14
- Get ( Vec < u8 > ) ,
10
+ Insert ( FixedKey , Vec < u8 > ) ,
11
+ Delete ( FixedKey ) ,
15
12
}
16
13
14
+ // helper trait to extend `choose` with exception handling
17
15
trait ChooseNonempty {
18
16
fn one_of < ' a , T > ( & ' a mut self , entries : & ' a [ T ] ) -> & ' a T ;
19
17
}
@@ -24,46 +22,129 @@ impl ChooseNonempty for Gen {
24
22
}
25
23
}
26
24
27
- // Arbitrary lets you create randomized instances
28
- // of types that you're interested in testing
29
- // properties with. QuickCheck will look for
30
- // this trait for things that are the arguments
31
- // to properties that it is testing.
25
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq , Hash ) ]
26
+ struct FixedKey ( FixedBytes < 32 > ) ;
27
+
28
+ impl FixedKey {
29
+ fn as_slice ( & self ) -> & [ u8 ; 32 ] {
30
+ & self . 0
31
+ }
32
+
33
+ fn from_string ( s : & str ) -> Self {
34
+ Self ( keccak256 ( "a" ) )
35
+ }
36
+
37
+ fn from_bytes ( bytes : [ u8 ; 32 ] ) -> Self {
38
+ Self ( FixedBytes :: new ( bytes) )
39
+ }
40
+
41
+ fn into_bytes ( self ) -> Bytes {
42
+ Bytes :: from ( self . 0 )
43
+ }
44
+ }
45
+
46
+ impl From < Bytes > for FixedKey {
47
+ fn from ( bytes : Bytes ) -> FixedKey {
48
+ let fbytes = FixedBytes :: from_slice ( bytes. as_ref ( ) ) ;
49
+ FixedKey ( fbytes)
50
+ }
51
+ }
52
+
53
+ // We chose a small number of keys, to make sure our error cases handle key collisions,
54
+ // as well as shared prefixes, properties that would be very unlikely for random keys
55
+ impl Arbitrary for FixedKey {
56
+ fn arbitrary ( g : & mut Gen ) -> Self {
57
+ let keys = [
58
+ FixedKey :: from_bytes ( hex ! (
59
+ "0000000000000000000000000000000000000000000000000000000000000000"
60
+ ) ) ,
61
+ FixedKey :: from_bytes ( hex ! (
62
+ "0000000000000000000000000000000000000000000000000000000000000001"
63
+ ) ) ,
64
+ FixedKey :: from_bytes ( hex ! (
65
+ "0000000000000000000000000000001000000000000000000000000000000001"
66
+ ) ) ,
67
+ FixedKey :: from_string ( "0" ) ,
68
+ FixedKey :: from_string ( "1" ) ,
69
+ FixedKey :: from_string ( "2" ) ,
70
+ FixedKey :: from_string ( "3" ) ,
71
+ FixedKey :: from_string ( "4" ) ,
72
+ FixedKey :: from_string ( "5" ) ,
73
+ FixedKey :: from_string ( "6" ) ,
74
+ FixedKey :: from_string ( "7" ) ,
75
+ ] ;
76
+ * g. one_of ( & keys)
77
+ }
78
+ }
79
+
32
80
impl Arbitrary for Op {
33
81
fn arbitrary ( g : & mut Gen ) -> Self {
34
82
// pick a random key to perform an operation on
35
- let key = g
36
- . one_of ( & [ "key00" , "key01" , "odd" , "key010" ] )
37
- . as_bytes ( )
38
- . to_owned ( ) ;
83
+ let key = FixedKey :: arbitrary ( g) ;
39
84
40
85
if * g. one_of ( & [ true , false ] ) {
41
86
Op :: Insert ( key, "value" . into ( ) )
42
87
} else {
43
- Op :: Get ( key)
88
+ Op :: Delete ( key)
44
89
}
45
90
}
46
91
}
47
92
93
+ /// This test fails, since the Trie is designed for fixed key sizes
94
+ #[ ignore]
95
+ #[ test]
96
+ fn crash_example_v2 ( ) {
97
+ let mut trie = Trie :: new_empty ( ) ;
98
+ trie. insert ( b"00aeee" , b"ok" ) . unwrap ( ) ;
99
+ trie. insert ( b"00ae" , b"ok" ) . unwrap ( ) ;
100
+ }
101
+
48
102
quickcheck ! {
49
- fn model_test_v2( ops: Vec <Op >) -> bool {
103
+ fn model_test_v1_map( ops: Vec <Op >) -> bool {
104
+ let mut model = HashMap :: new( ) ;
105
+ let mut implementation = DiffTrie :: new_empty( ) ;
106
+
107
+ for op in ops {
108
+ match op {
109
+ Op :: Insert ( key, value) => {
110
+ implementation. insert( key. into_bytes( ) , Bytes :: from( value. clone( ) ) ) . unwrap( ) ;
111
+ model. insert( key, value) ;
112
+ }
113
+ Op :: Delete ( key) => {
114
+ match ( implementation. delete( key. into_bytes( ) ) , model. remove( & key) ) {
115
+ ( Err ( DeletionErrorV1 :: KeyNotFound ) , None ) => ( ) ,
116
+ ( Err ( err) , _) => panic!( "Implementation error {err:?}" ) ,
117
+ ( Ok ( _) , Some ( _) ) => ( ) ,
118
+ ( Ok ( returned) , None ) => panic!( "Implementation returned {returned:?} on delete" ) ,
119
+ }
120
+ }
121
+ }
122
+ }
123
+ true
124
+ }
125
+ }
126
+
127
+ quickcheck ! {
128
+ fn model_test_v2_map( ops: Vec <Op >) -> bool {
50
129
let mut model = HashMap :: new( ) ;
51
130
let mut implementation = Trie :: new_empty( ) ;
52
131
53
132
for op in ops {
54
133
match op {
55
134
Op :: Insert ( k, v) => {
56
- implementation. insert( k. as_slice( ) , v. as_slice( ) ) ;
135
+ implementation. insert( k. as_slice( ) , v. as_slice( ) ) . unwrap ( ) ;
57
136
model. insert( k, v) ;
58
137
}
59
- Op :: Get ( k) => {
60
- // if implementation.get(&k) != model.get(&k).map(AsRef::as_ref) {
61
- // return false;
62
- // }
138
+ Op :: Delete ( k) => {
139
+ match ( implementation. delete( k. as_slice( ) ) , model. remove( & k) ) {
140
+ ( Err ( DeletionErrorV2 :: KeyNotFound ) , None ) => ( ) ,
141
+ ( Err ( e) , _) => panic!( "Implementation error {e:?}" ) ,
142
+ ( Ok ( _) , Some ( _) ) => ( ) ,
143
+ ( Ok ( a) , None ) => panic!( "Implementation returned {a:?} on delete" ) ,
144
+ }
63
145
}
64
146
}
65
147
}
66
-
67
148
true
68
149
}
69
150
}
0 commit comments