Skip to content

Commit 88df572

Browse files
author
Kristoffer Ström
committed
Initial model testing using quickcheck
1 parent 5eaa4ee commit 88df572

File tree

6 files changed

+161
-4
lines changed

6 files changed

+161
-4
lines changed

Cargo.lock

Lines changed: 25 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ warp = "0.3.7"
160160
flate2 = "1.0.35"
161161
prometheus = "0.13.4"
162162
ctor = "0.2"
163+
quickcheck = "1.0.3"
164+
rand = "0.8"
163165

164166
libc = { version = "0.2.161" }
165167
lazy_static = "1.4.0"

crates/eth-sparse-mpt/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ hash-db = { version = "0.15.2", optional = true }
4545
triehash = { version = "0.8.4", optional = true }
4646
flate2 = { workspace = true, optional = true }
4747

48+
quickcheck = { workspace = true }
49+
rand = { workspace = true }
50+
4851
[features]
4952
benchmark-utils = ["dep:hash-db", "dep:triehash", "dep:flate2"]
5053

@@ -69,4 +72,3 @@ harness = false
6972
[[bench]]
7073
name = "trie_do_bench"
7174
harness = false
72-

crates/eth-sparse-mpt/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use reth_provider::{
1515
StateCommitmentProvider,
1616
};
1717

18+
pub mod model_test;
19+
1820
#[cfg(any(test, feature = "benchmark-utils"))]
1921
pub mod test_utils;
2022
pub mod utils;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use crate::v2::trie::Trie;
2+
use quickcheck::{quickcheck, Arbitrary, Gen};
3+
use std::collections::HashMap;
4+
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+
#[derive(Clone, Debug)]
12+
enum Op {
13+
Insert(Vec<u8>, Vec<u8>),
14+
Get(Vec<u8>),
15+
}
16+
17+
trait ChooseNonempty {
18+
fn one_of<'a, T>(&'a mut self, entries: &'a [T]) -> &'a T;
19+
}
20+
21+
impl ChooseNonempty for Gen {
22+
fn one_of<'a, T>(&'a mut self, entries: &'a [T]) -> &'a T {
23+
self.choose(entries).expect("empty list in choose nonempty")
24+
}
25+
}
26+
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.
32+
impl Arbitrary for Op {
33+
fn arbitrary(g: &mut Gen) -> Self {
34+
// 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();
39+
40+
if *g.one_of(&[true, false]) {
41+
Op::Insert(key, "value".into())
42+
} else {
43+
Op::Get(key)
44+
}
45+
}
46+
}
47+
48+
quickcheck! {
49+
fn model_test_v2(ops: Vec<Op>) -> bool {
50+
let mut model = HashMap::new();
51+
let mut implementation = Trie::new_empty();
52+
53+
for op in ops {
54+
match op {
55+
Op::Insert(k, v) => {
56+
implementation.insert(k.as_slice(), v.as_slice());
57+
model.insert(k, v);
58+
}
59+
Op::Get(k) => {
60+
// if implementation.get(&k) != model.get(&k).map(AsRef::as_ref) {
61+
// return false;
62+
// }
63+
}
64+
}
65+
}
66+
67+
true
68+
}
69+
}

crates/eth-sparse-mpt/src/v2/trie/mod.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,66 @@ impl Trie {
361361
self.delete_nibbles_key(&n)
362362
}
363363

364+
pub fn get(&mut self, key: &[u8]) -> Option<&[u8]> {
365+
let n = Nibbles::unpack(key);
366+
self.get_nibbles_key(&n)
367+
}
368+
369+
pub fn get_nibbles_key(&mut self, nibbles_key: &Nibbles) -> Option<&[u8]> {
370+
let get_key = nibbles_key.as_slice();
371+
self.walk_path.clear();
372+
373+
let mut current_node = 0;
374+
let mut path_walked = 0;
375+
376+
loop {
377+
let node = self.nodes.get(current_node)?;
378+
379+
match node {
380+
DiffTrieNode::Branch { children } => {
381+
// deleting from branch, key not found
382+
if get_key.len() == path_walked {
383+
return None;
384+
}
385+
386+
let children = *children;
387+
388+
let n = get_key[path_walked];
389+
self.walk_path.push((current_node, n));
390+
path_walked += 1;
391+
392+
if let Some(child_ptr) = self.branch_node_children[children][n as usize] {
393+
current_node = child_ptr.local_ptr()?;
394+
continue;
395+
} else {
396+
return None;
397+
}
398+
}
399+
DiffTrieNode::Extension { key, next_node } => {
400+
let key = key.clone();
401+
let next_node = *next_node;
402+
403+
if get_key[path_walked..].starts_with(&self.keys[key.clone()]) {
404+
self.walk_path.push((current_node, 0));
405+
path_walked += key.len();
406+
current_node = next_node.local_ptr()?;
407+
continue;
408+
}
409+
410+
return None;
411+
}
412+
DiffTrieNode::Leaf { key, value } => {
413+
if self.keys[key.clone()] == get_key[path_walked..] {
414+
self.walk_path.push((current_node, 0));
415+
return Some(b"test");
416+
}
417+
return None;
418+
}
419+
DiffTrieNode::Null => return None,
420+
}
421+
}
422+
}
423+
364424
pub fn delete_nibbles_key(
365425
&mut self,
366426
nibbles_key: &Nibbles,

0 commit comments

Comments
 (0)