chia_sdk_driver/
hashed_ptr.rs

1use 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)] // TODO: Fix this
58impl 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}