Skip to content

Commit e6cbd87

Browse files
committed
perf(proof): reduce size of ProofVerificationError
1 parent a6c87b8 commit e6cbd87

4 files changed

Lines changed: 64 additions & 32 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ all = { level = "warn", priority = -1 }
3535
missing-const-for-fn = "warn"
3636
use-self = "warn"
3737
redundant-clone = "warn"
38-
result_large_err = "allow"
38+
result_large_err = "warn"
3939

4040
# Use the `--profile profiling` flag to show symbols in release mode.
4141
# e.g. `cargo build --profile profiling`

src/proof/error.rs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,56 @@
1+
use alloc::boxed::Box;
12
use alloy_primitives::{B256, Bytes};
23
use nybbles::Nibbles;
34

45
/// Error during proof verification.
56
#[derive(PartialEq, Eq, Debug, thiserror::Error)]
67
pub enum ProofVerificationError {
78
/// State root does not match the expected.
8-
#[error("root mismatch. got: {got}. expected: {expected}")]
9-
RootMismatch {
10-
/// Computed state root.
11-
got: B256,
12-
/// State root provided to verify function.
13-
expected: B256,
14-
},
9+
#[error(transparent)]
10+
RootMismatch(Box<RootMismatchError>),
1511
/// The node value does not match at specified path.
16-
#[error("value mismatch at path {path:?}. got: {got:?}. expected: {expected:?}")]
17-
ValueMismatch {
18-
/// Path at which error occurred.
19-
path: Nibbles,
20-
/// Value in the proof.
21-
got: Option<Bytes>,
22-
/// Expected value.
23-
expected: Option<Bytes>,
24-
},
12+
#[error(transparent)]
13+
ValueMismatch(Box<ValueMismatchError>),
2514
/// Encountered unexpected empty root node.
2615
#[error("unexpected empty root node")]
2716
UnexpectedEmptyRoot,
2817
/// Error during RLP decoding of trie node.
2918
#[error(transparent)]
3019
Rlp(#[from] alloy_rlp::Error),
3120
}
21+
22+
/// State root does not match the expected.
23+
#[derive(Clone, Copy, PartialEq, Eq, Debug, thiserror::Error)]
24+
#[error("root mismatch. got: {got}. expected: {expected}")]
25+
pub struct RootMismatchError {
26+
/// Computed state root.
27+
pub got: B256,
28+
/// State root provided to verify function.
29+
pub expected: B256,
30+
}
31+
32+
/// The node value does not match at specified path.
33+
#[derive(PartialEq, Eq, Debug, thiserror::Error)]
34+
#[error("value mismatch at path {path:?}. got: {got:?}. expected: {expected:?}")]
35+
pub struct ValueMismatchError {
36+
/// Path at which error occurred.
37+
pub path: Nibbles,
38+
/// Value in the proof.
39+
pub got: Option<Bytes>,
40+
/// Expected value.
41+
pub expected: Option<Bytes>,
42+
}
43+
44+
#[cfg(test)]
45+
mod tests {
46+
use super::*;
47+
48+
#[test]
49+
fn test_error_size() {
50+
let size = core::mem::size_of::<ProofVerificationError>();
51+
eprintln!("ProofVerificationError size: {size} bytes");
52+
// Down from 112 bytes to 24 after boxing both large variants. (The error was
53+
// 144 bytes when #89 was filed; nybbles 0.4 had already shrunk `Nibbles` since.)
54+
assert!(size <= 24, "ProofVerificationError is {size} bytes, should be <= 24");
55+
}
56+
}

src/proof/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ mod verify;
77
pub use verify::verify_proof;
88

99
mod error;
10-
pub use error::ProofVerificationError;
10+
pub use error::{ProofVerificationError, RootMismatchError, ValueMismatchError};
1111

1212
mod decoded_proof_nodes;
1313
pub use decoded_proof_nodes::DecodedProofNodes;

src/proof/verify.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
use crate::{
44
EMPTY_ROOT_HASH,
55
nodes::{BranchNode, CHILD_INDEX_RANGE, RlpNode, TrieNode},
6-
proof::ProofVerificationError,
6+
proof::{ProofVerificationError, RootMismatchError, ValueMismatchError},
77
};
8-
use alloc::vec::Vec;
8+
use alloc::{boxed::Box, vec::Vec};
99
use alloy_primitives::{B256, Bytes};
1010
use alloy_rlp::{Decodable, EMPTY_STRING_CODE};
1111
use core::ops::Deref;
@@ -32,14 +32,17 @@ where
3232
if expected_value.is_none() {
3333
Ok(())
3434
} else {
35-
Err(ProofVerificationError::ValueMismatch {
35+
Err(ProofVerificationError::ValueMismatch(Box::new(ValueMismatchError {
3636
path: key,
3737
got: None,
3838
expected: expected_value.map(Bytes::from),
39-
})
39+
})))
4040
}
4141
} else {
42-
Err(ProofVerificationError::RootMismatch { got: EMPTY_ROOT_HASH, expected: root })
42+
Err(ProofVerificationError::RootMismatch(Box::new(RootMismatchError {
43+
got: EMPTY_ROOT_HASH,
44+
expected: root,
45+
})))
4346
};
4447
}
4548

@@ -51,7 +54,11 @@ where
5154
if Some(RlpNode::from_rlp(node).as_slice()) != last_decoded_node.as_deref() {
5255
let got = Some(Bytes::copy_from_slice(node));
5356
let expected = last_decoded_node.as_deref().map(Bytes::copy_from_slice);
54-
return Err(ProofVerificationError::ValueMismatch { path: walked_path, got, expected });
57+
return Err(ProofVerificationError::ValueMismatch(Box::new(ValueMismatchError {
58+
path: walked_path,
59+
got,
60+
expected,
61+
})));
5562
}
5663

5764
// Decode the next node from the proof.
@@ -64,11 +71,11 @@ where
6471
if last_decoded_node.as_deref() == expected_value.as_deref() {
6572
Ok(())
6673
} else {
67-
Err(ProofVerificationError::ValueMismatch {
74+
Err(ProofVerificationError::ValueMismatch(Box::new(ValueMismatchError {
6875
path: key,
6976
got: last_decoded_node.as_deref().map(Bytes::copy_from_slice),
7077
expected: expected_value.map(Bytes::from),
71-
})
78+
})))
7279
}
7380
}
7481

@@ -223,11 +230,11 @@ mod tests {
223230
BranchNode::default().encode(&mut dummy_proof);
224231
assert_eq!(
225232
verify_proof(root, key, None, [&Bytes::from(dummy_proof.clone())]),
226-
Err(ProofVerificationError::ValueMismatch {
233+
Err(ProofVerificationError::ValueMismatch(Box::new(ValueMismatchError {
227234
path: Nibbles::default(),
228235
got: Some(Bytes::from(dummy_proof)),
229236
expected: Some(Bytes::from(RlpNode::word_rlp(&EMPTY_ROOT_HASH)[..].to_vec()))
230-
})
237+
})))
231238
);
232239
}
233240

@@ -254,21 +261,21 @@ mod tests {
254261
assert_eq!(verify_proof(root, first_key, Some(first_value.clone()), &proof), Ok(()));
255262
assert_eq!(
256263
verify_proof(root, first_key, None, &proof),
257-
Err(ProofVerificationError::ValueMismatch {
264+
Err(ProofVerificationError::ValueMismatch(Box::new(ValueMismatchError {
258265
path: first_key,
259266
got: Some(first_value.into()),
260267
expected: None,
261-
})
268+
})))
262269
);
263270

264271
assert_eq!(verify_proof(root, second_key, Some(second_value.clone()), &proof), Ok(()));
265272
assert_eq!(
266273
verify_proof(root, second_key, None, &proof),
267-
Err(ProofVerificationError::ValueMismatch {
274+
Err(ProofVerificationError::ValueMismatch(Box::new(ValueMismatchError {
268275
path: second_key,
269276
got: Some(second_value.into()),
270277
expected: None,
271-
})
278+
})))
272279
);
273280
}
274281

0 commit comments

Comments
 (0)