datalayer_driver/
xch_server_coin.rs

1use crate::CoinSpend;
2use chia_wallet_sdk::prelude::{
3    Allocator, Bytes, Bytes32, Coin, Condition, CreateCoin, CurriedProgram, FromClvm, Memos, Mod,
4    ToClvm, ToTreeHash, TreeHash,
5};
6use hex_literal::hex;
7use num_bigint::BigInt;
8use std::borrow::Cow;
9
10/// The new server coin and coin spends to create it.
11#[derive(Clone, Debug)]
12pub struct NewXchServerCoin {
13    pub server_coin: XchServerCoin,
14    pub coin_spends: Vec<CoinSpend>,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct XchServerCoin {
19    pub coin: Coin,
20    pub p2_puzzle_hash: Bytes32,
21    pub memo_urls: Vec<String>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
25#[clvm(curry)]
26pub struct MirrorArgs<M> {
27    pub morpher: M,
28}
29
30impl Default for MirrorArgs<i32> {
31    fn default() -> Self {
32        Self { morpher: 1 }
33    }
34}
35
36impl MirrorArgs<i32> {
37    pub fn curry_tree_hash() -> TreeHash {
38        CurriedProgram {
39            program: MIRROR_PUZZLE_HASH,
40            args: Self::default(),
41        }
42        .tree_hash()
43    }
44}
45
46impl<M> Mod for MirrorArgs<M> {
47    fn mod_hash() -> TreeHash {
48        MIRROR_PUZZLE_HASH
49    }
50
51    fn mod_reveal() -> Cow<'static, [u8]> {
52        Cow::Borrowed(&MIRROR_PUZZLE)
53    }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
57#[clvm(list)]
58pub struct MirrorSolution<I, S> {
59    pub parent_parent_id: Bytes32,
60    pub parent_inner_puzzle: I,
61    pub parent_amount: u64,
62    pub parent_solution: S,
63}
64
65pub const MIRROR_PUZZLE: [u8; 242] = hex!(
66    "
67    ff02ffff01ff04ffff04ff08ffff04ffff02ff0affff04ff02ffff04ff0bffff
68    04ffff02ff05ffff02ff0effff04ff02ffff04ff17ff8080808080ffff04ff2f
69    ff808080808080ff808080ffff02ff17ff5f8080ffff04ffff01ffff4720ffff
70    02ffff03ffff22ffff09ffff0dff0580ff0c80ffff09ffff0dff0b80ff0c80ff
71    ff15ff17ffff0181ff8080ffff01ff0bff05ff0bff1780ffff01ff088080ff01
72    80ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ff
73    ff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff
74    01ff0bffff0101ff058080ff0180ff018080
75    "
76);
77
78pub const MIRROR_PUZZLE_HASH: TreeHash = TreeHash::new(hex!(
79    "
80    b10ce2d0b18dcf8c21ddfaf55d9b9f0adcbf1e0beb55b1a8b9cad9bbff4e5f22
81    "
82));
83
84pub fn morph_launcher_id(launcher_id: Bytes32, offset: &BigInt) -> Bytes32 {
85    let launcher_id_int = BigInt::from_signed_bytes_be(&launcher_id);
86    let morphed_int = launcher_id_int + offset;
87
88    let mut bytes = morphed_int.to_signed_bytes_be();
89    if bytes.len() > 32 {
90        return Bytes32::default();
91    }
92
93    while bytes.len() < 32 {
94        bytes.insert(0, 0u8);
95    }
96
97    Bytes32::new(bytes.try_into().unwrap())
98}
99
100pub fn urls_from_conditions(
101    allocator: &Allocator,
102    server_coin: &Coin,
103    parent_conditions: &[Condition],
104) -> Option<Vec<String>> {
105    parent_conditions.iter().find_map(|condition| {
106        let Condition::CreateCoin(CreateCoin {
107            puzzle_hash,
108            amount,
109            memos: maybe_memos,
110        }) = condition
111        else {
112            return None;
113        };
114
115        if puzzle_hash != &server_coin.puzzle_hash || *amount != server_coin.amount {
116            return None;
117        }
118
119        let memos_vec = match maybe_memos {
120            Memos::Some(node) => Vec::<Bytes>::from_clvm(allocator, *node)
121                .ok()
122                .unwrap_or_default(),
123            Memos::None => Vec::new(),
124        };
125
126        memos_vec
127            .iter()
128            .skip(1)
129            .map(|memo| String::from_utf8(memo.as_ref().to_vec()).ok())
130            .collect()
131    })
132}
133
134#[cfg(test)]
135mod tests {
136    use chia::clvm_utils::tree_hash;
137    use clvmr::{serde::node_from_bytes, Allocator};
138
139    use super::*;
140
141    #[test]
142    fn test_puzzle_hash() {
143        let mut a = Allocator::new();
144        let ptr = node_from_bytes(&mut a, &MIRROR_PUZZLE).unwrap();
145        let hash = tree_hash(&a, ptr);
146        assert_eq!(MIRROR_PUZZLE_HASH, hash);
147    }
148
149    #[test]
150    fn test_morph() {
151        let mut id = [3u8; 32];
152        id[31] = 255;
153
154        let mut expected = id;
155        expected[31] = 0;
156        expected[30] = 4;
157
158        let actual = morph_launcher_id(id.into(), &1.into());
159
160        assert_eq!(hex::encode(actual), hex::encode(expected));
161    }
162}