datalayer_driver/
xch_server_coin.rs1use 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#[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}