Skip to content

Commit 74c4f47

Browse files
authored
Merge e701c6c into 5984f49
2 parents 5984f49 + e701c6c commit 74c4f47

4 files changed

Lines changed: 94 additions & 12 deletions

File tree

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
@@ -115,9 +115,22 @@ impl NodeRef {
115115
}
116116

117117
pub fn compute_hash(&self) -> NodeHash {
118+
*self.compute_hash_ref()
119+
}
120+
121+
pub fn compute_hash_ref(&self) -> &NodeHash {
118122
match self {
119-
NodeRef::Node(node, hash) => *hash.get_or_init(|| node.compute_hash()),
120-
NodeRef::Hash(hash) => *hash,
123+
NodeRef::Node(node, hash) => hash.get_or_init(|| node.compute_hash()),
124+
NodeRef::Hash(hash) => hash,
125+
}
126+
}
127+
128+
pub fn memoize_hashes(&self) {
129+
if let NodeRef::Node(node, hash) = &self
130+
&& hash.get().is_none()
131+
{
132+
node.memoize_hashes();
133+
let _ = hash.set(node.compute_hash());
121134
}
122135
}
123136

@@ -274,12 +287,27 @@ impl Node {
274287

275288
/// Computes the node's hash
276289
pub fn compute_hash(&self) -> NodeHash {
290+
self.memoize_hashes();
277291
match self {
278292
Node::Branch(n) => n.compute_hash(),
279293
Node::Extension(n) => n.compute_hash(),
280294
Node::Leaf(n) => n.compute_hash(),
281295
}
282296
}
297+
298+
/// Recursively memoizes the hashes of all nodes of the subtrie that has
299+
/// `self` as root (post-order traversal)
300+
pub fn memoize_hashes(&self) {
301+
match self {
302+
Node::Branch(n) => {
303+
for child in &n.choices {
304+
child.memoize_hashes();
305+
}
306+
}
307+
Node::Extension(n) => n.child.memoize_hashes(),
308+
_ => {}
309+
}
310+
}
283311
}
284312

285313
/// 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 & 10 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,44 @@ 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 choices_len = self.choices.iter().fold(0, |acc, child| {
20+
acc + RLPEncode::length(child.compute_hash_ref())
21+
});
22+
let payload_len = choices_len + value_len;
23+
24+
encode_length(payload_len, buf);
1825
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())
23-
}
24-
_ => encoder = encoder.encode_bytes(&[]),
26+
match child.compute_hash_ref() {
27+
NodeHash::Hashed(hash) => hash.0.encode(buf),
28+
NodeHash::Inline((_, 0)) => buf.put_u8(RLP_NULL),
29+
NodeHash::Inline((encoded, len)) => buf.put_slice(&encoded[..*len as usize]),
2530
}
2631
}
27-
encoder = encoder.encode_bytes(&self.value);
28-
encoder.finish();
32+
<[u8] as RLPEncode>::encode(&self.value, buf);
33+
}
34+
35+
// Duplicated to prealloc the buffer and avoid calculating the payload length twice
36+
fn encode_to_vec(&self) -> Vec<u8> {
37+
let value_len = <[u8] as RLPEncode>::length(&self.value);
38+
let choices_len = self.choices.iter().fold(0, |acc, child| {
39+
acc + RLPEncode::length(child.compute_hash_ref())
40+
});
41+
let payload_len = choices_len + value_len;
42+
43+
let mut buf: Vec<u8> = Vec::with_capacity(payload_len + 3); // 3 byte prefix headroom
44+
45+
encode_length(payload_len, &mut buf);
46+
for child in self.choices.iter() {
47+
match child.compute_hash_ref() {
48+
NodeHash::Hashed(hash) => hash.0.encode(&mut buf),
49+
NodeHash::Inline((_, 0)) => buf.push(RLP_NULL),
50+
NodeHash::Inline((encoded, len)) => buf.extend_from_slice(&encoded[..*len as usize]),
51+
}
52+
}
53+
<[u8] as RLPEncode>::encode(&self.value, &mut buf);
54+
55+
buf
2956
}
3057
}
3158

0 commit comments

Comments
 (0)