chia_sdk_bindings/
program.rs

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