Skip to content

Commit a4026d4

Browse files
authored
Merge 951c6be into 45a9871
2 parents 45a9871 + 951c6be commit a4026d4

File tree

4 files changed

+94
-11
lines changed

4 files changed

+94
-11
lines changed

crates/common/rlp/encode.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,25 @@ impl RLPEncode for [u8] {
139139
buf.put_slice(self);
140140
}
141141
}
142+
143+
fn length(&self) -> usize {
144+
const U8_MAX_PLUS_ONE: usize = u8::MAX as usize + 1;
145+
const U16_MAX_PLUS_ONE: usize = u16::MAX as usize + 1;
146+
147+
match self.len() {
148+
0 => 1, // encodes to RLP_NULL
149+
1 if self[0] < 0x80 => 1, // `self` is its own encoding
150+
1..56 => 1 + self.len(), // single byte prefix
151+
56..U8_MAX_PLUS_ONE => 1 + 1 + self.len(), // single byte prefix + payload len bytes
152+
U8_MAX_PLUS_ONE..U16_MAX_PLUS_ONE => 1 + 2 + self.len(), // single byte prefix + payload len bytes
153+
_ => {
154+
// fallback if `self` is longer than 2^16 - 1 bytes
155+
let payload_len_bytes =
156+
((usize::BITS - self.len().leading_zeros()) as usize).div_ceil(8);
157+
1 + payload_len_bytes + self.len()
158+
}
159+
}
160+
}
142161
}
143162

144163
impl<const N: usize> RLPEncode for [u8; N] {

crates/common/trie/node.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,22 @@ impl NodeRef {
135135
}
136136

137137
pub fn compute_hash(&self) -> NodeHash {
138+
*self.compute_hash_ref()
139+
}
140+
141+
pub fn compute_hash_ref(&self) -> &NodeHash {
138142
match self {
139-
NodeRef::Node(node, hash) => *hash.get_or_init(|| node.compute_hash()),
140-
NodeRef::Hash(hash) => *hash,
143+
NodeRef::Node(node, hash) => hash.get_or_init(|| node.compute_hash()),
144+
NodeRef::Hash(hash) => hash,
145+
}
146+
}
147+
148+
pub fn memoize_hashes(&self) {
149+
if let NodeRef::Node(node, hash) = &self
150+
&& hash.get().is_none()
151+
{
152+
node.memoize_hashes();
153+
let _ = hash.set(node.compute_hash());
141154
}
142155
}
143156

@@ -294,12 +307,27 @@ impl Node {
294307

295308
/// Computes the node's hash
296309
pub fn compute_hash(&self) -> NodeHash {
310+
self.memoize_hashes();
297311
match self {
298312
Node::Branch(n) => n.compute_hash(),
299313
Node::Extension(n) => n.compute_hash(),
300314
Node::Leaf(n) => n.compute_hash(),
301315
}
302316
}
317+
318+
/// Recursively memoizes the hashes of all nodes of the subtrie that has
319+
/// `self` as root (post-order traversal)
320+
pub fn memoize_hashes(&self) {
321+
match self {
322+
Node::Branch(n) => {
323+
for child in &n.choices {
324+
child.memoize_hashes();
325+
}
326+
}
327+
Node::Extension(n) => n.child.memoize_hashes(),
328+
_ => {}
329+
}
330+
}
303331
}
304332

305333
/// Used as return type for `Node` remove operations that may resolve into either:

crates/common/trie/node_hash.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ impl RLPEncode for NodeHash {
120120
fn encode(&self, buf: &mut dyn bytes::BufMut) {
121121
RLPEncode::encode(&Into::<Vec<u8>>::into(self), buf)
122122
}
123+
124+
fn length(&self) -> usize {
125+
match self {
126+
NodeHash::Hashed(_) => 33, // 1 byte prefix + 32 bytes
127+
NodeHash::Inline((_, 0)) => 1, // if empty then it's encoded to RLP_NULL
128+
NodeHash::Inline((_, len)) => *len as usize, // already encoded
129+
}
130+
}
123131
}
124132

125133
impl RLPDecode for NodeHash {

crates/common/trie/rlp.rs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use std::array;
33
// Contains RLP encoding and decoding implementations for Trie Nodes
44
// This encoding is only used to store the nodes in the DB, it is not the encoding used for hash computation
55
use ethrex_rlp::{
6+
constants::RLP_NULL,
67
decode::{RLPDecode, decode_bytes},
7-
encode::RLPEncode,
8+
encode::{RLPEncode, encode_length},
89
error::RLPDecodeError,
910
structs::{Decoder, Encoder},
1011
};
@@ -14,18 +15,45 @@ use crate::{Nibbles, NodeHash};
1415

1516
impl RLPEncode for BranchNode {
1617
fn encode(&self, buf: &mut dyn bytes::BufMut) {
17-
let mut encoder = Encoder::new(buf);
18+
let value_len = <[u8] as RLPEncode>::length(&self.value);
19+
let payload_len = self.choices.iter().fold(value_len, |acc, child| {
20+
acc + RLPEncode::length(child.compute_hash_ref())
21+
});
22+
23+
encode_length(payload_len, buf);
24+
for child in self.choices.iter() {
25+
match child.compute_hash_ref() {
26+
NodeHash::Hashed(hash) => hash.0.encode(buf),
27+
NodeHash::Inline((_, 0)) => buf.put_u8(RLP_NULL),
28+
NodeHash::Inline((encoded, len)) => buf.put_slice(&encoded[..*len as usize]),
29+
}
30+
}
31+
<[u8] as RLPEncode>::encode(&self.value, buf);
32+
}
33+
34+
// Duplicated to prealloc the buffer and avoid calculating the payload length twice
35+
fn encode_to_vec(&self) -> Vec<u8> {
36+
let value_len = <[u8] as RLPEncode>::length(&self.value);
37+
let choices_len = self.choices.iter().fold(0, |acc, child| {
38+
acc + RLPEncode::length(child.compute_hash_ref())
39+
});
40+
let payload_len = choices_len + value_len;
41+
42+
let mut buf: Vec<u8> = Vec::with_capacity(payload_len + 3); // 3 byte prefix headroom
43+
44+
encode_length(payload_len, &mut buf);
1845
for child in self.choices.iter() {
19-
match child.compute_hash() {
20-
NodeHash::Hashed(hash) => encoder = encoder.encode_bytes(&hash.0),
21-
child @ NodeHash::Inline(raw) if raw.1 != 0 => {
22-
encoder = encoder.encode_raw(child.as_ref())
46+
match child.compute_hash_ref() {
47+
NodeHash::Hashed(hash) => hash.0.encode(&mut buf),
48+
NodeHash::Inline((_, 0)) => buf.push(RLP_NULL),
49+
NodeHash::Inline((encoded, len)) => {
50+
buf.extend_from_slice(&encoded[..*len as usize])
2351
}
24-
_ => encoder = encoder.encode_bytes(&[]),
2552
}
2653
}
27-
encoder = encoder.encode_bytes(&self.value);
28-
encoder.finish();
54+
<[u8] as RLPEncode>::encode(&self.value, &mut buf);
55+
56+
buf
2957
}
3058
}
3159

0 commit comments

Comments
 (0)