chik-sdk-driver 0.25.0

Driver code for interacting with standard puzzles on the Chik blockchain.
Documentation
use std::{cmp::Ordering, fmt};

use hex_literal::hex;
use klvm_traits::{FromKlvm, FromKlvmError, ToKlvm, ToKlvmError};
use klvm_utils::{tree_hash, ToTreeHash, TreeHash};
use klvmr::{Allocator, NodePtr};

#[derive(Clone, Copy, Eq)]
pub struct HashedPtr {
    ptr: NodePtr,
    tree_hash: TreeHash,
}

impl HashedPtr {
    pub const NIL: Self = Self {
        ptr: NodePtr::NIL,
        tree_hash: TreeHash::new(hex!(
            "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"
        )),
    };

    pub fn new(ptr: NodePtr, tree_hash: TreeHash) -> Self {
        Self { ptr, tree_hash }
    }

    pub fn from_ptr(allocator: &Allocator, ptr: NodePtr) -> Self {
        Self::new(ptr, tree_hash(allocator, ptr))
    }

    pub fn ptr(&self) -> NodePtr {
        self.ptr
    }

    pub fn tree_hash(&self) -> TreeHash {
        self.tree_hash
    }
}

impl fmt::Debug for HashedPtr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "HashedPtr({})", self.tree_hash)
    }
}

impl fmt::Display for HashedPtr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.tree_hash)
    }
}

impl PartialEq for HashedPtr {
    fn eq(&self, other: &Self) -> bool {
        self.tree_hash == other.tree_hash
    }
}

impl PartialOrd for HashedPtr {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.tree_hash.cmp(&other.tree_hash))
    }
}

impl Ord for HashedPtr {
    fn cmp(&self, other: &Self) -> Ordering {
        self.tree_hash.cmp(&other.tree_hash)
    }
}

impl ToKlvm<Allocator> for HashedPtr {
    fn to_klvm(&self, _encoder: &mut Allocator) -> Result<NodePtr, ToKlvmError> {
        Ok(self.ptr)
    }
}

impl FromKlvm<Allocator> for HashedPtr {
    fn from_klvm(decoder: &Allocator, node: NodePtr) -> Result<Self, FromKlvmError> {
        Ok(Self::from_ptr(decoder, node))
    }
}

impl ToTreeHash for HashedPtr {
    fn tree_hash(&self) -> TreeHash {
        self.tree_hash
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_nil_hashed_ptr() {
        let allocator = Allocator::new();
        let atom = allocator.atom(HashedPtr::NIL.ptr);
        assert!(atom.as_ref().is_empty());

        assert_eq!(HashedPtr::NIL, HashedPtr::NIL);
        assert_eq!(HashedPtr::NIL.ptr(), NodePtr::NIL);
        assert_eq!(
            HashedPtr::NIL.tree_hash(),
            tree_hash(&allocator, NodePtr::NIL)
        );
    }

    #[test]
    fn test_hashed_ptr() -> anyhow::Result<()> {
        let mut allocator = Allocator::new();

        let ptr = ["Hello", " ", "world", "!"].to_klvm(&mut allocator)?;
        let hashed_ptr = HashedPtr::from_ptr(&allocator, ptr);
        assert_eq!(hashed_ptr.ptr(), ptr);
        assert_eq!(hashed_ptr.tree_hash(), tree_hash(&allocator, ptr));
        assert_eq!(hashed_ptr, hashed_ptr);
        assert_eq!(hashed_ptr, HashedPtr::new(ptr, hashed_ptr.tree_hash()));

        Ok(())
    }

    #[test]
    fn test_hashed_ptr_roundtrip() -> anyhow::Result<()> {
        let mut allocator = Allocator::new();

        let ptr = "hello".to_klvm(&mut allocator)?;
        let hashed_ptr = HashedPtr::from_ptr(&allocator, ptr);

        let new_ptr = hashed_ptr.to_klvm(&mut allocator)?;
        assert_eq!(ptr, new_ptr);

        let new_hashed_ptr = HashedPtr::from_klvm(&allocator, new_ptr)?;
        assert_eq!(hashed_ptr, new_hashed_ptr);

        Ok(())
    }

    #[test]
    fn test_hashed_ptr_to_treehash() -> anyhow::Result<()> {
        let mut allocator = Allocator::new();

        let ptr = "hello".to_klvm(&mut allocator)?;
        let hashed_ptr = HashedPtr::from_ptr(&allocator, ptr);
        let tree_hash = ToTreeHash::tree_hash(&hashed_ptr);
        assert_eq!(tree_hash, hashed_ptr.tree_hash());

        Ok(())
    }

    #[test]
    fn test_hashed_ptr_order() -> anyhow::Result<()> {
        let mut allocator = Allocator::new();

        let mut ptrs = Vec::new();

        for i in 0..5 {
            let ptr = i.to_klvm(&mut allocator)?;
            ptrs.push(HashedPtr::from_ptr(&allocator, ptr));
        }

        ptrs.sort();

        let hashes: Vec<TreeHash> = ptrs.into_iter().map(|ptr| ptr.tree_hash()).collect();

        assert_eq!(
            hashes,
            [
                TreeHash::new(hex!(
                    "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"
                )),
                TreeHash::new(hex!(
                    "9dcf97a184f32623d11a73124ceb99a5709b083721e878a16d78f596718ba7b2"
                )),
                TreeHash::new(hex!(
                    "a12871fee210fb8619291eaea194581cbd2531e4b23759d225f6806923f63222"
                )),
                TreeHash::new(hex!(
                    "a8d5dd63fba471ebcb1f3e8f7c1e1879b7152a6e7298a91ce119a63400ade7c5"
                )),
                TreeHash::new(hex!(
                    "c79b932e1e1da3c0e098e5ad2c422937eb904a76cf61d83975a74a68fbb04b99"
                ))
            ]
        );

        Ok(())
    }
}