chia_sdk_driver/
hashed_ptr.rs1use std::{cmp::Ordering, fmt};
2
3use clvm_traits::{FromClvm, FromClvmError, ToClvm, ToClvmError};
4use clvm_utils::{tree_hash, ToTreeHash, TreeHash};
5use clvmr::{Allocator, NodePtr};
6use hex_literal::hex;
7
8#[derive(Clone, Copy, Eq)]
9pub struct HashedPtr {
10 ptr: NodePtr,
11 tree_hash: TreeHash,
12}
13
14impl HashedPtr {
15 pub const NIL: Self = Self {
16 ptr: NodePtr::NIL,
17 tree_hash: TreeHash::new(hex!(
18 "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"
19 )),
20 };
21
22 pub fn new(ptr: NodePtr, tree_hash: TreeHash) -> Self {
23 Self { ptr, tree_hash }
24 }
25
26 pub fn from_ptr(allocator: &Allocator, ptr: NodePtr) -> Self {
27 Self::new(ptr, tree_hash(allocator, ptr))
28 }
29
30 pub fn ptr(&self) -> NodePtr {
31 self.ptr
32 }
33
34 pub fn tree_hash(&self) -> TreeHash {
35 self.tree_hash
36 }
37}
38
39impl fmt::Debug for HashedPtr {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 write!(f, "HashedPtr({})", self.tree_hash)
42 }
43}
44
45impl fmt::Display for HashedPtr {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 write!(f, "{}", self.tree_hash)
48 }
49}
50
51impl PartialEq for HashedPtr {
52 fn eq(&self, other: &Self) -> bool {
53 self.tree_hash == other.tree_hash
54 }
55}
56
57#[allow(clippy::non_canonical_partial_ord_impl)] impl PartialOrd for HashedPtr {
59 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
60 Some(self.tree_hash.cmp(&other.tree_hash))
61 }
62}
63
64impl Ord for HashedPtr {
65 fn cmp(&self, other: &Self) -> Ordering {
66 self.tree_hash.cmp(&other.tree_hash)
67 }
68}
69
70impl ToClvm<Allocator> for HashedPtr {
71 fn to_clvm(&self, _encoder: &mut Allocator) -> Result<NodePtr, ToClvmError> {
72 Ok(self.ptr)
73 }
74}
75
76impl FromClvm<Allocator> for HashedPtr {
77 fn from_clvm(decoder: &Allocator, node: NodePtr) -> Result<Self, FromClvmError> {
78 Ok(Self::from_ptr(decoder, node))
79 }
80}
81
82impl ToTreeHash for HashedPtr {
83 fn tree_hash(&self) -> TreeHash {
84 self.tree_hash
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_nil_hashed_ptr() {
94 let allocator = Allocator::new();
95 let atom = allocator.atom(HashedPtr::NIL.ptr);
96 assert!(atom.as_ref().is_empty());
97
98 assert_eq!(HashedPtr::NIL, HashedPtr::NIL);
99 assert_eq!(HashedPtr::NIL.ptr(), NodePtr::NIL);
100 assert_eq!(
101 HashedPtr::NIL.tree_hash(),
102 tree_hash(&allocator, NodePtr::NIL)
103 );
104 }
105
106 #[test]
107 fn test_hashed_ptr() -> anyhow::Result<()> {
108 let mut allocator = Allocator::new();
109
110 let ptr = ["Hello", " ", "world", "!"].to_clvm(&mut allocator)?;
111 let hashed_ptr = HashedPtr::from_ptr(&allocator, ptr);
112 assert_eq!(hashed_ptr.ptr(), ptr);
113 assert_eq!(hashed_ptr.tree_hash(), tree_hash(&allocator, ptr));
114 assert_eq!(hashed_ptr, hashed_ptr);
115 assert_eq!(hashed_ptr, HashedPtr::new(ptr, hashed_ptr.tree_hash()));
116
117 Ok(())
118 }
119
120 #[test]
121 fn test_hashed_ptr_roundtrip() -> anyhow::Result<()> {
122 let mut allocator = Allocator::new();
123
124 let ptr = "hello".to_clvm(&mut allocator)?;
125 let hashed_ptr = HashedPtr::from_ptr(&allocator, ptr);
126
127 let new_ptr = hashed_ptr.to_clvm(&mut allocator)?;
128 assert_eq!(ptr, new_ptr);
129
130 let new_hashed_ptr = HashedPtr::from_clvm(&allocator, new_ptr)?;
131 assert_eq!(hashed_ptr, new_hashed_ptr);
132
133 Ok(())
134 }
135
136 #[test]
137 fn test_hashed_ptr_to_treehash() -> anyhow::Result<()> {
138 let mut allocator = Allocator::new();
139
140 let ptr = "hello".to_clvm(&mut allocator)?;
141 let hashed_ptr = HashedPtr::from_ptr(&allocator, ptr);
142 let tree_hash = ToTreeHash::tree_hash(&hashed_ptr);
143 assert_eq!(tree_hash, hashed_ptr.tree_hash());
144
145 Ok(())
146 }
147
148 #[test]
149 fn test_hashed_ptr_order() -> anyhow::Result<()> {
150 let mut allocator = Allocator::new();
151
152 let mut ptrs = Vec::new();
153
154 for i in 0..5 {
155 let ptr = i.to_clvm(&mut allocator)?;
156 ptrs.push(HashedPtr::from_ptr(&allocator, ptr));
157 }
158
159 ptrs.sort();
160
161 let hashes: Vec<TreeHash> = ptrs.into_iter().map(|ptr| ptr.tree_hash()).collect();
162
163 assert_eq!(
164 hashes,
165 [
166 TreeHash::new(hex!(
167 "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"
168 )),
169 TreeHash::new(hex!(
170 "9dcf97a184f32623d11a73124ceb99a5709b083721e878a16d78f596718ba7b2"
171 )),
172 TreeHash::new(hex!(
173 "a12871fee210fb8619291eaea194581cbd2531e4b23759d225f6806923f63222"
174 )),
175 TreeHash::new(hex!(
176 "a8d5dd63fba471ebcb1f3e8f7c1e1879b7152a6e7298a91ce119a63400ade7c5"
177 )),
178 TreeHash::new(hex!(
179 "c79b932e1e1da3c0e098e5ad2c422937eb904a76cf61d83975a74a68fbb04b99"
180 ))
181 ]
182 );
183
184 Ok(())
185 }
186}