Skip to content

Commit f01e5c8

Browse files
committed
Start to move the prefix out of the Branch, replacing prior word and vec
This will allow bytemuck cast to be used to serialize Snapshots. This was a goal of the original design, but was not possible due to switch to prefixes from extension nodes.
1 parent d1aa8f6 commit f01e5c8

File tree

2 files changed

+62
-9
lines changed

2 files changed

+62
-9
lines changed

src/transaction.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ use crate::{
1515
};
1616

1717
use self::nodes::{
18-
Branch, KeyPosition, KeyPositionAdjacent, Leaf, Node, NodeRef, StoredLeafRef, TrieRoot,
18+
Branch, KeyPosition, KeyPositionAdjacent, Leaf, Node, NodeRef, PrefixesBuffer, StoredLeafRef,
19+
TrieRoot,
1920
};
2021

2122
pub struct Transaction<S, V> {
2223
pub data_store: S,
2324
current_root: TrieRoot<NodeRef<V>>,
25+
prefixes_buffer: PrefixesBuffer,
2426
}
2527

2628
impl<Db: DatabaseSet<V>, V: Clone + PortableHash> Transaction<SnapshotBuilder<Db, V>, V> {

src/transaction/nodes.rs

+59-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use alloc::boxed::Box;
2-
use core::{fmt, iter, mem};
2+
use core::{fmt, iter, mem, slice, usize};
33

44
use crate::{hash::PortableHasher, stored, KeyHash, NodeHash, PortableHash, PortableUpdate};
55

@@ -263,19 +263,68 @@ mod tests {
263263
}
264264
}
265265

266+
pub struct Prefix {
267+
/// This value will be 0 if the branch occurs in the first word of the hash key.
268+
/// The value is the prior word if the branches parent's word index no more than 1 less.
269+
/// If the parent's word index is more than 1 word prior,
270+
/// we must store the multiword prefix outside of the branch, so the value is the index of the prefix.
271+
/// The length of the prefix is the difference between the parent's word index and the branch's word index.
272+
prior_word_or_prefix_idx: u32,
273+
}
274+
275+
impl Prefix {
276+
pub fn get_prefix<'s, 'txn: 's>(
277+
&'s self,
278+
prefixies: &'txn PrefixesBuffer,
279+
word_idx: usize,
280+
parent_word_idx: usize,
281+
) -> Option<&'s [u32]> {
282+
if word_idx - parent_word_idx <= 1 {
283+
if cfg!(debug_assertions) && word_idx == 0 {
284+
debug_assert_eq!(self.prior_word_or_prefix_idx, 0);
285+
}
286+
287+
Some(slice::from_ref(&self.prior_word_or_prefix_idx))
288+
} else {
289+
let prefix_len = word_idx - parent_word_idx;
290+
debug_assert!(prefix_len > 1);
291+
prefixies.get_prefix(
292+
self.prior_word_or_prefix_idx as usize,
293+
word_idx - parent_word_idx,
294+
)
295+
}
296+
}
297+
}
298+
299+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
300+
pub struct PrefixesBuffer {
301+
buffer: Vec<u32>,
302+
}
303+
304+
impl PrefixesBuffer {
305+
pub fn new() -> Self {
306+
Self { buffer: Vec::new() }
307+
}
308+
309+
pub fn push_prefix(&mut self, prefix: &[u32]) -> u32 {
310+
let idx = self.buffer.len() as u32;
311+
self.buffer.extend_from_slice(prefix);
312+
idx
313+
}
314+
315+
pub fn get_prefix(&self, idx: usize, len: usize) -> Option<&[u32]> {
316+
let end = idx + len;
317+
self.buffer.get(idx..end)
318+
}
319+
}
320+
266321
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
267322
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
268323
pub struct Branch<NR> {
269324
pub left: NR,
270325
pub right: NR,
271326
pub mask: BranchMask,
272-
/// The word at the `(bit_idx / 32) - 1`.
273-
/// Common to both children.
274-
/// Will be 0 if this node is the root.
275-
pub prior_word: u32,
276-
/// The the segment of the hash key from the parent branch to `prior_word`.
277-
/// Will be empty if the parent_branch.mask.bit_idx / 32 == self.mask.bit_idx / 32.
278-
pub prefix: Box<[u32]>,
327+
pub prefix: Prefix,
279328
}
280329

281330
impl<NR> fmt::Debug for Branch<NR> {
@@ -352,6 +401,7 @@ impl<NR> Branch<NR> {
352401
/// Hash a branch node with known child hashes.
353402
///
354403
/// Caller must ensure that the hasher is reset before calling this function.
404+
///
355405
#[inline]
356406
pub fn hash_branch<H: PortableHasher<32>>(
357407
&self,
@@ -361,6 +411,7 @@ impl<NR> Branch<NR> {
361411
) -> NodeHash {
362412
hasher.portable_update(left);
363413
hasher.portable_update(right);
414+
// Security: it's important to hash the metadata to avoid a potential trie corruption attack.
364415
hasher.portable_update(self.mask.bit_idx.to_le_bytes());
365416
hasher.portable_update(self.mask.left_prefix.to_le_bytes());
366417
hasher.portable_update(self.prior_word.to_le_bytes());

0 commit comments

Comments
 (0)