datalayer-driver 6.0.0

Native Chia DataLayer Driver for storing and retrieving data in Chia blockchain
Documentation
use crate::CoinSpend;
use chia_wallet_sdk::prelude::{
    Allocator, Bytes, Bytes32, Coin, Condition, CreateCoin, CurriedProgram, FromClvm, Memos, Mod,
    ToClvm, ToTreeHash, TreeHash,
};
use hex_literal::hex;
use num_bigint::BigInt;
use std::borrow::Cow;

/// The new server coin and coin spends to create it.
#[derive(Clone, Debug)]
pub struct NewXchServerCoin {
    pub server_coin: XchServerCoin,
    pub coin_spends: Vec<CoinSpend>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct XchServerCoin {
    pub coin: Coin,
    pub p2_puzzle_hash: Bytes32,
    pub memo_urls: Vec<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
#[clvm(curry)]
pub struct MirrorArgs<M> {
    pub morpher: M,
}

impl Default for MirrorArgs<i32> {
    fn default() -> Self {
        Self { morpher: 1 }
    }
}

impl MirrorArgs<i32> {
    pub fn curry_tree_hash() -> TreeHash {
        CurriedProgram {
            program: MIRROR_PUZZLE_HASH,
            args: Self::default(),
        }
        .tree_hash()
    }
}

impl<M> Mod for MirrorArgs<M> {
    fn mod_hash() -> TreeHash {
        MIRROR_PUZZLE_HASH
    }

    fn mod_reveal() -> Cow<'static, [u8]> {
        Cow::Borrowed(&MIRROR_PUZZLE)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
#[clvm(list)]
pub struct MirrorSolution<I, S> {
    pub parent_parent_id: Bytes32,
    pub parent_inner_puzzle: I,
    pub parent_amount: u64,
    pub parent_solution: S,
}

pub const MIRROR_PUZZLE: [u8; 242] = hex!(
    "
    ff02ffff01ff04ffff04ff08ffff04ffff02ff0affff04ff02ffff04ff0bffff
    04ffff02ff05ffff02ff0effff04ff02ffff04ff17ff8080808080ffff04ff2f
    ff808080808080ff808080ffff02ff17ff5f8080ffff04ffff01ffff4720ffff
    02ffff03ffff22ffff09ffff0dff0580ff0c80ffff09ffff0dff0b80ff0c80ff
    ff15ff17ffff0181ff8080ffff01ff0bff05ff0bff1780ffff01ff088080ff01
    80ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ff
    ff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff
    01ff0bffff0101ff058080ff0180ff018080
    "
);

pub const MIRROR_PUZZLE_HASH: TreeHash = TreeHash::new(hex!(
    "
    b10ce2d0b18dcf8c21ddfaf55d9b9f0adcbf1e0beb55b1a8b9cad9bbff4e5f22
    "
));

pub fn morph_launcher_id(launcher_id: Bytes32, offset: &BigInt) -> Bytes32 {
    let launcher_id_int = BigInt::from_signed_bytes_be(&launcher_id);
    let morphed_int = launcher_id_int + offset;

    let mut bytes = morphed_int.to_signed_bytes_be();
    if bytes.len() > 32 {
        return Bytes32::default();
    }

    while bytes.len() < 32 {
        bytes.insert(0, 0u8);
    }

    Bytes32::new(bytes.try_into().unwrap())
}

pub fn urls_from_conditions(
    allocator: &Allocator,
    server_coin: &Coin,
    parent_conditions: &[Condition],
) -> Option<Vec<String>> {
    parent_conditions.iter().find_map(|condition| {
        let Condition::CreateCoin(CreateCoin {
            puzzle_hash,
            amount,
            memos: maybe_memos,
        }) = condition
        else {
            return None;
        };

        if puzzle_hash != &server_coin.puzzle_hash || *amount != server_coin.amount {
            return None;
        }

        let memos_vec = match maybe_memos {
            Memos::Some(node) => Vec::<Bytes>::from_clvm(allocator, *node)
                .ok()
                .unwrap_or_default(),
            Memos::None => Vec::new(),
        };

        memos_vec
            .iter()
            .skip(1)
            .map(|memo| String::from_utf8(memo.as_ref().to_vec()).ok())
            .collect()
    })
}

#[cfg(test)]
mod tests {
    use clvm_utils::tree_hash;
    use clvmr::{serde::node_from_bytes, Allocator};

    use super::*;

    #[test]
    fn test_puzzle_hash() {
        let mut a = Allocator::new();
        let ptr = node_from_bytes(&mut a, &MIRROR_PUZZLE).unwrap();
        let hash = tree_hash(&a, ptr);
        assert_eq!(MIRROR_PUZZLE_HASH, hash);
    }

    #[test]
    fn test_morph() {
        let mut id = [3u8; 32];
        id[31] = 255;

        let mut expected = id;
        expected[31] = 0;
        expected[30] = 4;

        let actual = morph_launcher_id(id.into(), &1.into());

        assert_eq!(hex::encode(actual), hex::encode(expected));
    }
}