diff --git a/sealable-trie/src/nodes.rs b/sealable-trie/src/nodes.rs index 117b5703..fffd3d45 100644 --- a/sealable-trie/src/nodes.rs +++ b/sealable-trie/src/nodes.rs @@ -56,11 +56,21 @@ pub enum Node<'a, R: AsReference<'a> = RawRef<'a>> { child: R, }, Value { + is_sealed: IsSealed, value_hash: &'a CryptoHash, child: Option, }, } +/// Flag indicating whether value or node is sealed or not. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum IsSealed { + Unsealed, + Sealed, +} + +pub use IsSealed::*; + /// Binary representation of the node as kept in the persistent storage. /// /// This representation is compact and includes internal details needed to @@ -79,7 +89,7 @@ pub enum Node<'a, R: AsReference<'a> = RawRef<'a>> { // 36-byte array which holds the key extension. Only `o..o+k` bits in it // are the actual key; others are set to zero. // -// Value: 1100_0000 0000_0000 0000_0000 0000_0000 +// Value: 11s0_0000 0000_0000 0000_0000 0000_0000 // is the hash of the stored value. `s` is zero if the value hasn’t // been sealed or one otherwise. // @@ -87,24 +97,25 @@ pub enum Node<'a, R: AsReference<'a> = RawRef<'a>> { // a prefix of another key) the is a references the child (as in // Branch). Otherwise, the first byte of is 0x40 (which normally // indicates that the reference is to a value) and rest are set to zero. -// -// TODO(mina86): Implement handling of sealed values. // ``` // // A Reference is a 36-byte sequence consisting of a 4-byte pointer and -// a 32-byte hash. The most significant bit of the pointer is always set to -// zero (this is so that Branch nodes can be distinguished from other nodes). -// The second most significant bit is zero if the reference is to a node and one -// if it’s a hash of a value. In the latter case, the other bits of the pointer -// are always zero if the value isn’t sealed or one if it has been sealed: +// a 32-byte hash. The most significant bit of the pointer is always zero (this +// is so that Branch nodes can be distinguished from other nodes). The second +// most significant bit is zero if the reference is a node reference and one if +// it’s a value reference. // // ```ignore -// Node Ref: 0b0cpp_pppp pppp_pppp pppp_pppp pppp_pppp +// Node Ref: 0b00pp_pppp pppp_pppp pppp_pppp pppp_pppp +// `ppp` is the pointer to the node. If it’s zero than the node is sealed +// the it’s not stored anywhere. +// +// Value Ref: 0b01s0_0000 0000_0000 0000_0000 0000_0000 +// `s` determines whether the value is sealed or not. If it is, it cannot be +// changed. // ``` // // The actual pointer value is therefore 30-bit long. -// -// TODO(mina86): Implement handling of sealed values. #[derive(Clone, Copy, PartialEq, derive_more::Deref)] #[repr(transparent)] pub struct RawNode(pub(crate) [u8; 72]); @@ -134,7 +145,7 @@ pub struct ProofNode(Box<[u8]>); #[derive(Clone, Copy, Debug)] pub enum RawRef<'a> { Node { ptr: Option, hash: &'a CryptoHash }, - Value { hash: &'a CryptoHash }, + Value { is_sealed: IsSealed, hash: &'a CryptoHash }, } /// Reference to a node with given pointer and hash as parse from the raw node @@ -193,10 +204,11 @@ impl<'a, R: AsReference<'a>> Node<'a, R> { /// Constructs a Value node with given value hash and child. pub fn value( + is_sealed: IsSealed, value_hash: &'a CryptoHash, child: Option, ) -> Self { - Self::Value { value_hash, child } + Self::Value { is_sealed, value_hash, child } } /// Returns a hash of the node. @@ -225,13 +237,23 @@ impl<'a, R: AsReference<'a>> Node<'a, R> { Node::Extension { key, child } => { Node::Extension { key, child: ref_map(child) } } - Node::Value { value_hash, child } => { - Node::Value { value_hash, child: child.map(node_map) } + Node::Value { is_sealed, value_hash, child } => { + let child = child.map(node_map); + Node::Value { is_sealed, value_hash, child } } } } } +impl IsSealed { + pub fn new(is_sealed: bool) -> Self { + match is_sealed { + false => Self::Unsealed, + true => Self::Sealed, + } + } +} + impl RawNode { /// Constructs a Branch node with specified children. pub fn branch(left: RawRef, right: RawRef) -> Self { @@ -257,16 +279,22 @@ impl RawNode { } /// Constructs a Value node with given value hash and child. - pub fn value(value_hash: &CryptoHash, child: Option) -> Self { + pub fn value( + is_sealed: IsSealed, + value_hash: &CryptoHash, + child: Option, + ) -> Self { + const NO_CHILD: [u8; 36] = { + let mut res = [0; 36]; + res[0] = 0x40; + res + }; + let mut res = Self([0; 72]); let (lft, rht) = res.halfs_mut(); - let (tag, value) = stdx::split_array_mut::<4, 32, 36>(lft); - *tag = 0xC000_0000_u32.to_be_bytes(); - *value = value_hash.into(); - *rht = child - .map(RawRef::from) - .unwrap_or(RawRef::Value { hash: &CryptoHash::DEFAULT }) - .encode_raw(); + *lft = RawRef::value(is_sealed, value_hash).encode_raw(); + lft[0] |= 0x80; + *rht = child.map_or(NO_CHILD, |nref| RawRef::from(nref).encode_raw()); res } @@ -309,7 +337,9 @@ impl<'a> RawRef<'a> { /// Creates a new reference pointing at value with given hash. #[inline] - pub fn value(hash: &'a CryptoHash) -> Self { Self::Value { hash } } + pub fn value(is_sealed: IsSealed, hash: &'a CryptoHash) -> Self { + Self::Value { is_sealed, hash } + } /// Parses bytes to form a raw node reference representation. /// @@ -317,9 +347,12 @@ impl<'a> RawRef<'a> { /// significant bit is zero or that if second bit is one than pointer value /// must be zero. /// - /// In debug builds panics if `bytes` is an invalid raw node representation, - /// i.e. if any unused bits (which must be cleared) are set. - fn from_raw(bytes: &'a [u8; 36]) -> Self { + /// In debug builds, panics if `bytes` has non-canonical representation, + /// i.e. any unused bits are set. `value_high_bit` in this case determines + /// whether for value reference the most significant bit should be set or + /// not. This is to facilitate decoding Value nodes. The argument is + /// ignored in builds with debug assertions disabled. + fn from_raw(bytes: &'a [u8; 36], value_high_bit: bool) -> Self { let (ptr, hash) = stdx::split_array_ref::<4, 32, 36>(bytes); let ptr = u32::from_be_bytes(*ptr); let hash = hash.into(); @@ -333,10 +366,12 @@ impl<'a> RawRef<'a> { Self::Node { ptr, hash } } else { debug_assert_eq!( - 0x4000_0000, ptr, + 0x4000_0000 | (u32::from(value_high_bit) << 31), + ptr & !0x2000_0000, "Failed decoding RawRef: {bytes:?}" ); - Self::Value { hash } + let is_sealed = IsSealed::new(ptr & 0x2000_0000 != 0); + Self::Value { is_sealed, hash } } } @@ -344,7 +379,13 @@ impl<'a> RawRef<'a> { fn encode_raw(&self) -> [u8; 36] { let (ptr, hash) = match self { Self::Node { ptr, hash } => (ptr.map_or(0, |ptr| ptr.get()), *hash), - Self::Value { hash } => (0x4000_0000, *hash), + Self::Value { is_sealed, hash } => { + let ptr = match is_sealed { + Unsealed => 0x4000_0000, + Sealed => 0x6000_0000, + }; + (ptr, *hash) + } }; let mut buf = [0; 36]; let (left, right) = stdx::split_array_mut::<4, 32, 36>(&mut buf); @@ -366,8 +407,7 @@ impl<'a> Ref<'a> { /// Creates a new node reference. #[inline] pub fn new>(is_value: bool, hash: T) -> Self { - let hash = hash.into(); - Self { is_value, hash } + Self { is_value, hash: hash.into() } } } @@ -392,8 +432,8 @@ impl<'a> AsReference<'a> for RawRef<'a> { #[inline] fn as_reference(&self) -> Ref<'a> { let (is_value, hash) = match self { - Self::Node { hash, .. } => (false, hash), - Self::Value { hash } => (true, hash), + Self::Node { hash, .. } => (false, *hash), + Self::Value { hash, .. } => (true, *hash), }; Ref { is_value, hash } } @@ -536,15 +576,15 @@ impl<'a> TryFrom> for NodeRef<'a> { } impl<'a> TryFrom> for RawNodeRef<'a> { - type Error = &'a CryptoHash; + type Error = (IsSealed, &'a CryptoHash); /// If reference is to a node, returns it as node reference. Otherwise - /// returns hash of the value as `Err`. + /// returns is_sealed flag hash of the value as `Err`. #[inline] fn try_from(rf: RawRef<'a>) -> Result, Self::Error> { match rf { RawRef::Node { ptr, hash } => Ok(Self { ptr, hash }), - RawRef::Value { hash } => Err(hash), + RawRef::Value { is_sealed, hash } => Err((is_sealed, hash)), } } } @@ -601,9 +641,9 @@ impl<'a, 'b> core::cmp::PartialEq> for RawRef<'a> { RawRef::Node { ptr: rhs_ptr, hash: rhs_hash }, ) => lhs_ptr == rhs_ptr && lhs_hash == rhs_hash, ( - RawRef::Value { hash: lhs_hash }, - RawRef::Value { hash: rhs_hash }, - ) => lhs_hash == rhs_hash, + RawRef::Value { is_sealed: lhs_sealed, hash: lhs_hash }, + RawRef::Value { is_sealed: rhs_sealed, hash: rhs_hash }, + ) => lhs_sealed == rhs_sealed && lhs_hash == rhs_hash, _ => false, } } @@ -635,12 +675,11 @@ impl<'a, 'b> core::cmp::PartialEq> for NodeRef<'a> { /// unused bits (which must be cleared) are set. fn decode_raw<'a>(node: &'a RawNode) -> Node<'a, RawRef<'a>> { let (left, right) = node.halfs(); + let right = RawRef::from_raw(right, false); let tag = node.first() >> 6; if tag == 0 || tag == 1 { // Branch - Node::Branch { - children: [RawRef::from_raw(left), RawRef::from_raw(right)], - } + Node::Branch { children: [RawRef::from_raw(left, false), right] } } else if tag == 2 { // Extension let (num, key) = @@ -649,23 +688,31 @@ fn decode_raw<'a>(node: &'a RawNode) -> Node<'a, RawRef<'a>> { debug_assert_eq!(0x8000, num & 0xF000, "Failed decoding raw: {node:?}"); Node::Extension { key: Slice::from_raw(num & 0x0FFF, key), - child: RawRef::from_raw(right), + child: right, } } else { // Value - let (_, value) = stdx::split_array_ref::<4, 32, 36>(left); + let (num, value) = stdx::split_array_ref::<4, 32, 36>(left); + let num = u32::from_be_bytes(*num); + debug_assert_eq!( + 0xC000_0000, + num & !0x2000_0000, + "Failed decoding raw node: {node:?}", + ); + let is_sealed = IsSealed::new(num & 0x2000_0000 != 0); let value_hash = value.into(); - let child = RawNodeRef::try_from(RawRef::from_raw(right)) - .map_err(|hash| { + let child = match right { + RawRef::Node { ptr, hash } => Some(RawNodeRef::new(ptr, hash)), + RawRef::Value { is_sealed, hash } => { debug_assert_eq!( - CryptoHash::default(), - *hash, - "Failed decoding raw node: {:?}", - node.0 - ) - }) - .ok(); - Node::Value { value_hash, child } + (Unsealed, &CryptoHash::default()), + (is_sealed, hash), + "Failed decoding raw node: {node:?}", + ); + None + } + }; + Node::Value { is_sealed, value_hash, child } } } @@ -700,7 +747,7 @@ fn decode_proof<'a>(bytes: &'a [u8]) -> Option>> { } else { return None; }; - Some(Node::Value { value_hash, child }) + Some(Node::Value { is_sealed: Unsealed, value_hash, child }) } else { None } @@ -718,8 +765,8 @@ fn raw_from_node<'a>(node: &Node<'a, RawRef<'a>>) -> Option { Some(RawNode::branch(*left, *right)) } Node::Extension { key, child } => RawNode::extension(*key, *child), - Node::Value { value_hash, child } => { - Some(RawNode::value(value_hash, *child)) + Node::Value { is_sealed, value_hash, child } => { + Some(RawNode::value(*is_sealed, value_hash, *child)) } } } @@ -753,7 +800,7 @@ fn proof_from_node<'a, 'b, R: AsReference<'a>>( Node::Extension { key, child } => { build_proof_extension(dest, *key, child.as_reference())? } - Node::Value { value_hash, child } => { + Node::Value { is_sealed: _, value_hash, child } => { dest[0] = 0xC0; dest[1..33].copy_from_slice(value_hash.as_slice()); if let Some(child) = child { @@ -788,7 +835,30 @@ fn build_proof_extension( } // ============================================================================= -// Debug +// Formatting + +impl core::fmt::Debug for IsSealed { + #[inline] + fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { + fmtr.write_str(match self { + Unsealed => "unsealed", + Sealed => "sealed", + }) + } +} + +impl core::fmt::Display for IsSealed { + #[inline] + fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { + let val = match (fmtr.alternate(), self) { + (true, Unsealed) => return Ok(()), + (true, Sealed) => " (sealed)", + (_, Unsealed) => "unsealed", + (_, Sealed) => "sealed", + }; + fmtr.write_str(val) + } +} impl core::fmt::Debug for RawNode { fn fmt(&self, fmtr: &mut core::fmt::Formatter) -> core::fmt::Result { diff --git a/sealable-trie/src/nodes/tests.rs b/sealable-trie/src/nodes/tests.rs index 42ed162a..593b2987 100644 --- a/sealable-trie/src/nodes/tests.rs +++ b/sealable-trie/src/nodes/tests.rs @@ -6,7 +6,8 @@ use crate::bits; use crate::hash::CryptoHash; use crate::memory::Ptr; use crate::nodes::{ - Node, NodeRef, ProofNode, RawNode, RawNodeRef, RawRef, Ref, + Node, NodeRef, ProofNode, RawNode, RawNodeRef, RawRef, Ref, Sealed, + Unsealed, }; const DEAD: Ptr = match Ptr::new(0xDEAD) { @@ -72,7 +73,7 @@ fn check_node_encoding(node: Node, want_raw: [u8; 72], want_proof: &[u8]) { let proof = proof_from_node(&node); assert_eq!(want_proof, &proof[..], "Unexpected proof representation"); - assert_eq!(proof, ProofNode::from(raw), "Bad Raw → Proof conversion"); + assert_eq!(node, Node::from(&RawNode(want_raw)), "Bad Raw→Node conversion"); let mut bad_proof = want_proof.to_vec(); bad_proof.push(0); @@ -97,12 +98,12 @@ fn test_branch_encoding() { check_node_encoding(Node::Branch { children: [ RawRef::node(Some(DEAD), &ONE), - RawRef::node(Some(BEEF), &TWO), + RawRef::node(None, &TWO), ], }, [ /* ptr1: */ 0, 0, 0xDE, 0xAD, /* hash1: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - /* ptr2: */ 0, 0, 0xBE, 0xEF, + /* ptr2: */ 0, 0, 0, 0, /* hash2: */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ], &[ /* tag: */ 0, @@ -114,7 +115,7 @@ fn test_branch_encoding() { check_node_encoding(Node::Branch { children: [ RawRef::node(Some(DEAD), &ONE), - RawRef::value(&TWO), + RawRef::value(Unsealed, &TWO), ], }, [ /* ptr1: */ 0, 0, 0xDE, 0xAD, @@ -130,11 +131,11 @@ fn test_branch_encoding() { // Branch with first child being a value and second being a node. check_node_encoding(Node::Branch { children: [ - RawRef::value(&ONE), + RawRef::value(Sealed, &ONE), RawRef::node(Some(BEEF), &TWO), ], }, [ - /* ptr1: */ 0x40, 0, 0, 0, + /* ptr1: */ 0x60, 0, 0, 0, /* hash1: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ptr2: */ 0, 0, 0xBE, 0xEF, /* hash2: */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 @@ -147,13 +148,13 @@ fn test_branch_encoding() { // Branch with both children being values. check_node_encoding(Node::Branch { children: [ - RawRef::value(&ONE), - RawRef::value(&TWO), + RawRef::value(Unsealed, &ONE), + RawRef::value(Sealed, &TWO), ], }, [ /* ptr1: */ 0x40, 0, 0, 0, /* hash1: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - /* ptr2: */ 0x40, 0, 0, 0, + /* ptr2: */ 0x60, 0, 0, 0, /* hash2: */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ], &[ /* tag: */ 3, @@ -180,12 +181,25 @@ fn test_extension_encoding() { /* hash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]); + // Extension pointing at a sealed node + check_node_encoding(Node::Extension { + key: bits::Slice::new(&[0xFF; 34], 5, 25).unwrap(), + child: RawRef::node(None, &ONE), + }, [ + /* tag: */ 0x80, 0xCD, + /* key: */ 0x07, 0xFF, 0xFF, 0xFC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* ptr: */ 0, 0, 0, 0, + /* hash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ], &[ + /* tag: */ 0x80, 0xCD, + /* key: */ 0x07, 0xFF, 0xFF, 0xFC, + /* hash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ]); + // Extension pointing at a value check_node_encoding(Node::Extension { key: bits::Slice::new(&[0xFF; 34], 4, 248).unwrap(), - child: RawRef::Value { - hash: &ONE, - }, + child: RawRef::value(Unsealed, &ONE), }, [ /* tag: */ 0x87, 0xC4, /* key: */ 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -199,12 +213,30 @@ fn test_extension_encoding() { /* */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, /* hash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]); + + check_node_encoding(Node::Extension { + key: bits::Slice::new(&[0xFF; 34], 4, 248).unwrap(), + child: RawRef::value(Sealed, &ONE), + }, [ + /* tag: */ 0x87, 0xC4, + /* key: */ 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + /* */ 0x00, 0x00, + /* ptr: */ 0x60, 0, 0, 0, + /* hash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ], &[ + /* tag: */ 0x97, 0xC4, + /* key: */ 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, + /* hash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ]); } #[test] #[rustfmt::skip] fn test_value_encoding() { check_node_encoding(Node::Value { + is_sealed: Unsealed, value_hash: &ONE, child: None, }, [ @@ -218,6 +250,7 @@ fn test_value_encoding() { ]); check_node_encoding(Node::Value { + is_sealed: Unsealed, value_hash: &ONE, child: Some(RawNodeRef::new(Some(BEEF), &TWO)), }, [ @@ -230,6 +263,20 @@ fn test_value_encoding() { /* hash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* chash: */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ]); + + check_node_encoding(Node::Value { + is_sealed: Sealed, + value_hash: &ONE, + child: None, + }, [ + /* tag: */ 0xE0, 0, 0, 0, + /* vhash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* ptr: */ 0x40, 0, 0, 0, + /* chash: */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], &[ + /* tag: */ 0xC0, + /* hash: */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + ]); } #[test] diff --git a/sealable-trie/src/nodes/tests_rand.rs b/sealable-trie/src/nodes/tests_rand.rs index 01460c55..062ce1a8 100644 --- a/sealable-trie/src/nodes/tests_rand.rs +++ b/sealable-trie/src/nodes/tests_rand.rs @@ -9,7 +9,8 @@ use pretty_assertions::assert_eq; use crate::memory::Ptr; use crate::nodes::{ - self, Node, NodeRef, ProofNode, RawNode, RawNodeRef, RawRef, Ref, + self, IsSealed, Node, NodeRef, ProofNode, RawNode, RawNodeRef, RawRef, Ref, + Unsealed, }; use crate::{bits, stdx}; @@ -37,7 +38,7 @@ fn test_raw_encoding_round_trip() { let node = Node::from(&raw); // Test RawNode→Node→RawNode round trip conversion. - assert_eq!(Ok(raw), RawNode::try_from(node), "{raw:?}"); + assert_eq!(Ok(raw), RawNode::try_from(node), "node: {node:?}"); // Test RawNode→Proof→Node gives the same result as RawNode→Node. let proof = ProofNode::from(raw); @@ -53,8 +54,9 @@ fn gen_random_raw_node(rng: &mut impl rand::Rng, bytes: &mut [u8; 72]) { // Node reference. Pointer can be non-zero. bytes[0] &= !0x80; } else { - // Value reference. Pointer must be zero. - bytes[..4].copy_from_slice(&0x4000_0000_u32.to_be_bytes()); + // Value reference. Pointer must be zero but key is_sealed flag: + // 0b01s0_0000 + bytes[..4].copy_from_slice(&0x6000_0000_u32.to_be_bytes()); } } @@ -89,7 +91,7 @@ fn gen_random_raw_node(rng: &mut impl rand::Rng, bytes: &mut [u8; 72]) { } else { // Value. Most bits in the first four bytes must be zero and child must // be a node reference. - bytes[0] &= 0xC0; + bytes[0] &= 0xE0; bytes[1] = 0; bytes[2] = 0; bytes[3] = 0; @@ -128,7 +130,7 @@ fn do_test_proof_encoding_round_trip(want: &[u8]) { let node = node.map_refs( |child| match child.is_value { false => RawRef::node(None, child.hash), - true => RawRef::value(child.hash), + true => RawRef::value(Unsealed, child.hash), }, |nref| RawNodeRef::new(None, nref.hash), ); @@ -213,7 +215,7 @@ fn gen_random_node<'a>( if num < 0x8000_0000 { RawRef::node(Ptr::new(num).ok().flatten(), hash.into()) } else { - RawRef::value(hash.into()) + RawRef::value(IsSealed::new(num & 1 != 0), hash.into()) } } @@ -230,11 +232,12 @@ fn gen_random_node<'a>( Node::extension(key, rand_ref(rng, &right)) } 2 => { + let is_sealed = IsSealed::new(rng.gen::() & 1 == 1); let num = rng.gen::(); let child = (num < 0x8000_0000).then(|| { RawNodeRef::new(Ptr::new(num).ok().flatten(), right.into()) }); - Node::value(left.into(), child) + Node::value(is_sealed, left.into(), child) } _ => unreachable!(), } diff --git a/sealable-trie/src/proof.rs b/sealable-trie/src/proof.rs index d9b9bca2..b3fecedb 100644 --- a/sealable-trie/src/proof.rs +++ b/sealable-trie/src/proof.rs @@ -45,7 +45,7 @@ pub fn verify( } node_hash = child.hash; } - Ok(Node::Value { value_hash, child }) => { + Ok(Node::Value { is_sealed: _, value_hash, child }) => { if our_key.is_empty() { return our_hash == Some(value_hash); } else if let Some(child) = child { diff --git a/sealable-trie/src/trie.rs b/sealable-trie/src/trie.rs index 919684e9..ed1e0a51 100644 --- a/sealable-trie/src/trie.rs +++ b/sealable-trie/src/trie.rs @@ -2,7 +2,9 @@ use alloc::vec::Vec; use crate::hash::CryptoHash; use crate::memory::Ptr; -use crate::nodes::{Node, ProofNode, RawNode, RawNodeRef, RawRef}; +use crate::nodes::{ + Node, ProofNode, RawNode, RawNodeRef, RawRef, Sealed, Unsealed, +}; use crate::{bits, memory}; #[cfg(test)] @@ -196,7 +198,9 @@ impl Trie { use std::{print, println}; let print_ref = |nref, depth| match nref { - RawRef::Value { hash } => println!("{:depth$}value {hash}", ""), + RawRef::Value { is_sealed, hash } => { + println!("{:depth$}value {hash}{is_sealed:#?}", "") + } RawRef::Node { ptr, hash } => self.print_impl(ptr, hash, depth), }; @@ -218,10 +222,10 @@ impl Trie { println!(" Extension {key}"); print_ref(child, depth + 2); } - Node::Value { value_hash, child } => { + Node::Value { is_sealed, value_hash, child } => { println!( - " Value {value_hash} {}", - if child.is_none() { '∅' } else { ' ' }, + " Value {value_hash}{is_sealed:#?}{}", + if child.is_none() { " ∅" } else { "" }, ); if let Some(child) = child { print_ref(RawRef::from(child), depth + 2); @@ -270,7 +274,7 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { } else { // Trie is empty but key is empty as well so we need to insert Value // node. - self.alloc_node(RawNode::value(self.value_hash, None)) + self.alloc_node(RawNode::value(Unsealed, self.value_hash, None)) } } @@ -283,7 +287,8 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { Node::Extension { key, child } => { self.handle_extension(nref, key, child) } - Node::Value { value_hash, child } => { + Node::Value { is_sealed: Sealed, .. } => Err(Error::Sealed), + Node::Value { is_sealed: Unsealed, value_hash, child } => { self.handle_value(nref, value_hash, child) } } @@ -382,7 +387,7 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { Ok(self.set_node(nref.0, node)) } - /// Inserts value assuming current node is an Extension. + /// Inserts value assuming current node is an unsealed Value. fn handle_value( &mut self, nref: (Ptr, &CryptoHash), @@ -390,7 +395,7 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { child: Option, ) -> Result<(Ptr, CryptoHash)> { let node = if self.key.is_empty() { - RawNode::value(self.value_hash, child) + RawNode::value(Unsealed, self.value_hash, child) } else { let (ptr, hash) = if let Some(child) = child { self.handle(child)? @@ -402,7 +407,7 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { unreachable!() }; let child = RawNodeRef::new(Some(ptr), &hash); - RawNode::value(existing_value, Some(child)) + RawNode::value(Unsealed, existing_value, Some(child)) }; Ok(self.set_node(nref.0, node)) } @@ -420,7 +425,8 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { self.handle(RawNodeRef::new(ptr, hash)) .map(|(p, h)| OwnedRef::Node(p, h)) } - RawRef::Value { hash } => { + RawRef::Value { is_sealed: Sealed, .. } => Err(Error::Sealed), + RawRef::Value { is_sealed: Unsealed, hash } => { // It’s a value reference so we just need to update it // accordingly. One tricky thing is that we need to insert // Value node with the old hash if our key isn’t empty. @@ -428,7 +434,7 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { owned_ref @ OwnedRef::Value(_) => Ok(owned_ref), OwnedRef::Node(p, h) => { let child = RawNodeRef::new(Some(p), &h); - let node = RawNode::value(hash, Some(child)); + let node = RawNode::value(Unsealed, hash, Some(child)); self.alloc_node(node).map(|(p, h)| OwnedRef::Node(p, h)) } } @@ -448,7 +454,7 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { let mut hash = self.value_hash.clone(); for chunk in self.key.chunks().rev() { let child = match ptr { - None => RawRef::value(&hash), + None => RawRef::value(Unsealed, &hash), Some(_) => RawRef::node(ptr, &hash), }; let (p, h) = self.alloc_extension_node(chunk, child)?; @@ -488,7 +494,7 @@ impl<'a, A: memory::Allocator> SetContext<'a, A> { hash: &CryptoHash, ) -> Result<(Ptr, CryptoHash)> { let child = RawNodeRef::new(Some(ptr), hash); - self.alloc_node(RawNode::value(value_hash, Some(child))) + self.alloc_node(RawNode::value(Unsealed, value_hash, Some(child))) } /// Sets value of a node cell at given address and returns its hash. @@ -528,7 +534,7 @@ impl OwnedRef { fn to_raw_ref<'a>(&'a self) -> RawRef { match self { Self::Node(ptr, hash) => RawRef::node(Some(*ptr), &hash), - Self::Value(hash) => RawRef::value(&hash), + Self::Value(hash) => RawRef::value(Unsealed, &hash), } } }