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}