chia_sdk_bindings/
program.rs

1use std::sync::{Arc, Mutex};
2
3use bindy::{Error, Result};
4use chia_protocol::{Bytes, Coin, Program as SerializedProgram};
5use chia_puzzle_types::{
6    nft::NftMetadata,
7    offer::{NotarizedPayment as ChiaNotarizedPayment, Payment as ChiaPayment},
8};
9use chia_sdk_driver::{OptionMetadata, RewardDistributor as SdkRewardDistributor, SpendContext};
10use chia_sdk_types::run_puzzle_with_cost;
11use chialisp::classic::clvm_tools::stages::run;
12use chialisp::classic::clvm_tools::stages::stage_0::TRunProgram;
13use chialisp::classic::clvm_tools::{
14    binutils::disassemble, stages::stage_2::operators::run_program_for_search_paths,
15};
16use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm};
17use clvm_utils::{TreeHash, tree_hash};
18use clvmr::{
19    NodePtr, SExp,
20    serde::{node_to_bytes, node_to_bytes_backrefs},
21};
22use num_bigint::BigInt;
23
24use crate::{
25    AsProgram, CurriedProgram, NotarizedPayment, Output, Pair, Payment, Puzzle,
26    RewardDistributorLauncherSolutionInfo,
27};
28
29#[derive(Clone)]
30pub struct Program(pub(crate) Arc<Mutex<SpendContext>>, pub(crate) NodePtr);
31
32impl Program {
33    pub fn compile(&self) -> Result<Output> {
34        let mut ctx = self.0.lock().unwrap();
35
36        let invoke = run(&mut ctx);
37        let input = ctx.new_pair(self.1, NodePtr::NIL)?;
38        let run_program = run_program_for_search_paths("program.clsp", &[], false);
39        let output = run_program.run_program(&mut ctx, invoke, input, None)?;
40
41        Ok(Output {
42            value: Program(self.0.clone(), output.1),
43            cost: output.0,
44        })
45    }
46
47    pub fn unparse(&self) -> Result<String> {
48        let ctx = self.0.lock().unwrap();
49        Ok(disassemble(&ctx, self.1, None))
50    }
51
52    pub fn serialize(&self) -> Result<SerializedProgram> {
53        let ctx = self.0.lock().unwrap();
54        Ok(node_to_bytes(&ctx, self.1)?.into())
55    }
56
57    pub fn serialize_with_backrefs(&self) -> Result<SerializedProgram> {
58        let ctx = self.0.lock().unwrap();
59        Ok(node_to_bytes_backrefs(&ctx, self.1)?.into())
60    }
61
62    pub fn run(&self, solution: Self, max_cost: u64, mempool_mode: bool) -> Result<Output> {
63        let mut ctx = self.0.lock().unwrap();
64
65        let reduction = run_puzzle_with_cost(&mut ctx, self.1, solution.1, max_cost, mempool_mode)?;
66
67        Ok(Output {
68            value: Program(self.0.clone(), reduction.1),
69            cost: reduction.0,
70        })
71    }
72
73    pub fn curry(&self, args: Vec<Program>) -> Result<Program> {
74        let mut ctx = self.0.lock().unwrap();
75
76        let mut args_ptr = ctx.one();
77
78        for arg in args.into_iter().rev() {
79            args_ptr = ctx.encode_curried_arg(arg.1, args_ptr)?;
80        }
81
82        let ptr = ctx.alloc(&clvm_utils::CurriedProgram {
83            program: self.1,
84            args: args_ptr,
85        })?;
86
87        Ok(Program(self.0.clone(), ptr))
88    }
89
90    pub fn uncurry(&self) -> Result<Option<CurriedProgram>> {
91        let ctx = self.0.lock().unwrap();
92
93        let Ok(value) = clvm_utils::CurriedProgram::<NodePtr, NodePtr>::from_clvm(&ctx, self.1)
94        else {
95            return Ok(None);
96        };
97
98        let mut args = Vec::new();
99        let mut args_ptr = value.args;
100
101        while let Ok((first, rest)) = ctx.decode_curried_arg(&args_ptr) {
102            args.push(first);
103            args_ptr = rest;
104        }
105
106        if ctx.small_number(args_ptr) != Some(1) {
107            return Ok(None);
108        }
109
110        Ok(Some(CurriedProgram {
111            program: Program(self.0.clone(), value.program),
112            args: args
113                .into_iter()
114                .map(|ptr| Program(self.0.clone(), ptr))
115                .collect(),
116        }))
117    }
118
119    pub fn tree_hash(&self) -> Result<TreeHash> {
120        let ctx = self.0.lock().unwrap();
121        Ok(tree_hash(&ctx, self.1))
122    }
123
124    pub fn is_atom(&self) -> Result<bool> {
125        let ctx = self.0.lock().unwrap();
126        Ok(matches!(ctx.sexp(self.1), SExp::Atom))
127    }
128
129    pub fn is_pair(&self) -> Result<bool> {
130        let ctx = self.0.lock().unwrap();
131        Ok(matches!(ctx.sexp(self.1), SExp::Pair(..)))
132    }
133
134    pub fn is_null(&self) -> Result<bool> {
135        let ctx = self.0.lock().unwrap();
136        Ok(matches!(ctx.sexp(self.1), SExp::Atom) && ctx.atom_len(self.1) == 0)
137    }
138
139    pub fn length(&self) -> Result<u32> {
140        let ctx = self.0.lock().unwrap();
141
142        let SExp::Atom = ctx.sexp(self.1) else {
143            return Err(Error::AtomExpected);
144        };
145
146        Ok(ctx.atom_len(self.1) as u32)
147    }
148
149    pub fn first(&self) -> Result<Program> {
150        let ctx = self.0.lock().unwrap();
151
152        let SExp::Pair(first, _) = ctx.sexp(self.1) else {
153            return Err(Error::PairExpected);
154        };
155
156        Ok(Program(self.0.clone(), first))
157    }
158
159    pub fn rest(&self) -> Result<Program> {
160        let ctx = self.0.lock().unwrap();
161
162        let SExp::Pair(_, rest) = ctx.sexp(self.1) else {
163            return Err(Error::PairExpected);
164        };
165
166        Ok(Program(self.0.clone(), rest))
167    }
168
169    pub fn to_int(&self) -> Result<Option<BigInt>> {
170        let ctx = self.0.lock().unwrap();
171
172        let SExp::Atom = ctx.sexp(self.1) else {
173            return Ok(None);
174        };
175
176        Ok(Some(ctx.number(self.1)))
177    }
178
179    pub fn to_bound_checked_number(&self) -> Result<Option<f64>> {
180        let ctx = self.0.lock().unwrap();
181
182        let SExp::Atom = ctx.sexp(self.1) else {
183            return Ok(None);
184        };
185
186        let number = ctx.number(self.1);
187
188        if number > BigInt::from(9_007_199_254_740_991i64) {
189            return Err(Error::TooLarge);
190        }
191
192        if number < BigInt::from(-9_007_199_254_740_991i64) {
193            return Err(Error::TooSmall);
194        }
195
196        let number: i64 = number.try_into().unwrap();
197
198        Ok(Some(number as f64))
199    }
200
201    pub fn to_string(&self) -> Result<Option<String>> {
202        let ctx = self.0.lock().unwrap();
203
204        let SExp::Atom = ctx.sexp(self.1) else {
205            return Ok(None);
206        };
207
208        let bytes = ctx.atom(self.1);
209
210        Ok(Some(String::from_utf8(bytes.to_vec())?))
211    }
212
213    pub fn to_bool(&self) -> Result<Option<bool>> {
214        let ctx = self.0.lock().unwrap();
215
216        let SExp::Atom = ctx.sexp(self.1) else {
217            return Ok(None);
218        };
219
220        let Some(number) = ctx.small_number(self.1) else {
221            return Ok(None);
222        };
223
224        if number != 0 && number != 1 {
225            return Ok(None);
226        }
227
228        Ok(Some(number != 0))
229    }
230
231    pub fn to_atom(&self) -> Result<Option<Bytes>> {
232        let ctx = self.0.lock().unwrap();
233
234        let SExp::Atom = ctx.sexp(self.1) else {
235            return Ok(None);
236        };
237
238        Ok(Some(ctx.atom(self.1).to_vec().into()))
239    }
240
241    pub fn to_list(&self) -> Result<Option<Vec<Program>>> {
242        let ctx = self.0.lock().unwrap();
243
244        let Some(value) = Vec::<NodePtr>::from_clvm(&ctx, self.1).ok() else {
245            return Ok(None);
246        };
247
248        Ok(Some(
249            value
250                .into_iter()
251                .map(|ptr| Program(self.0.clone(), ptr))
252                .collect(),
253        ))
254    }
255
256    pub fn to_arg_list(&self) -> Result<Option<Vec<Program>>> {
257        let ctx = self.0.lock().unwrap();
258
259        let mut args = Vec::new();
260        let mut ptr = self.1;
261
262        while let Ok((first, rest)) = ctx.decode_curried_arg(&ptr) {
263            args.push(Program(self.0.clone(), first));
264            ptr = rest;
265        }
266
267        Ok(Some(args))
268    }
269
270    pub fn to_pair(&self) -> Result<Option<Pair>> {
271        let ctx = self.0.lock().unwrap();
272
273        let SExp::Pair(first, rest) = ctx.sexp(self.1) else {
274            return Ok(None);
275        };
276
277        Ok(Some(Pair {
278            first: Program(self.0.clone(), first),
279            rest: Program(self.0.clone(), rest),
280        }))
281    }
282
283    pub fn puzzle(&self) -> Result<Puzzle> {
284        let ctx = self.0.lock().unwrap();
285        let value = chia_sdk_driver::Puzzle::parse(&ctx, self.1);
286        Ok(Puzzle::new(&self.0, value))
287    }
288
289    pub fn parse_nft_metadata(&self) -> Result<Option<NftMetadata>> {
290        let ctx = self.0.lock().unwrap();
291        let value = NftMetadata::from_clvm(&**ctx, self.1);
292        Ok(value.ok())
293    }
294
295    pub fn parse_option_metadata(&self) -> Result<Option<OptionMetadata>> {
296        let ctx = self.0.lock().unwrap();
297        let value = OptionMetadata::from_clvm(&**ctx, self.1);
298        Ok(value.ok())
299    }
300
301    pub fn parse_payment(&self) -> Result<Option<Payment>> {
302        let ctx = self.0.lock().unwrap();
303        let value = ChiaPayment::from_clvm(&**ctx, self.1);
304        Ok(value.ok().map(|p| p.as_program(&self.0)))
305    }
306
307    pub fn parse_notarized_payment(&self) -> Result<Option<NotarizedPayment>> {
308        let ctx = self.0.lock().unwrap();
309        let value = ChiaNotarizedPayment::from_clvm(&**ctx, self.1);
310        Ok(value.ok().map(|p| p.as_program(&self.0)))
311    }
312
313    pub fn parse_reward_distributor_launcher_solution(
314        &self,
315        launcher_coin: Coin,
316    ) -> Result<Option<RewardDistributorLauncherSolutionInfo>> {
317        let mut ctx = self.0.lock().unwrap();
318
319        if let Some((constants, initial_state, coin)) =
320            SdkRewardDistributor::from_launcher_solution(&mut ctx, launcher_coin, self.1)?
321        {
322            Ok(Some(RewardDistributorLauncherSolutionInfo {
323                constants,
324                initial_state,
325                coin,
326            }))
327        } else {
328            Ok(None)
329        }
330    }
331}