chia_sdk_bindings/
program.rs1use 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 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}