use std::sync::{Arc, Mutex};
use bindy::{Error, Result};
use chia_protocol::{Bytes, Coin, Program as SerializedProgram};
use chia_puzzle_types::{
nft::NftMetadata,
offer::{NotarizedPayment as ChiaNotarizedPayment, Payment as ChiaPayment},
};
use chia_sdk_driver::{OptionMetadata, RewardDistributor as SdkRewardDistributor, SpendContext};
use chia_sdk_types::run_puzzle_with_cost;
use chialisp::classic::clvm_tools::stages::run;
use chialisp::classic::clvm_tools::stages::stage_0::TRunProgram;
use chialisp::classic::clvm_tools::{
binutils::disassemble, stages::stage_2::operators::run_program_for_search_paths,
};
use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm};
use clvm_utils::{TreeHash, tree_hash};
use clvmr::{
NodePtr, SExp,
serde::{node_to_bytes, node_to_bytes_backrefs},
};
use num_bigint::BigInt;
use crate::{
AsProgram, CurriedProgram, NotarizedPayment, Output, Pair, Payment, Puzzle,
RewardDistributorLauncherSolutionInfo,
};
#[derive(Clone)]
pub struct Program(pub(crate) Arc<Mutex<SpendContext>>, pub(crate) NodePtr);
impl Program {
pub fn compile(&self) -> Result<Output> {
let mut ctx = self.0.lock().unwrap();
let invoke = run(&mut ctx);
let input = ctx.new_pair(self.1, NodePtr::NIL)?;
let run_program = run_program_for_search_paths("program.clsp", &[], false);
let output = run_program.run_program(&mut ctx, invoke, input, None)?;
Ok(Output {
value: Program(self.0.clone(), output.1),
cost: output.0,
})
}
pub fn unparse(&self) -> Result<String> {
let ctx = self.0.lock().unwrap();
Ok(disassemble(&ctx, self.1, None))
}
pub fn serialize(&self) -> Result<SerializedProgram> {
let ctx = self.0.lock().unwrap();
Ok(node_to_bytes(&ctx, self.1)?.into())
}
pub fn serialize_with_backrefs(&self) -> Result<SerializedProgram> {
let ctx = self.0.lock().unwrap();
Ok(node_to_bytes_backrefs(&ctx, self.1)?.into())
}
pub fn run(&self, solution: Self, max_cost: u64, mempool_mode: bool) -> Result<Output> {
let mut ctx = self.0.lock().unwrap();
let reduction = run_puzzle_with_cost(&mut ctx, self.1, solution.1, max_cost, mempool_mode)?;
Ok(Output {
value: Program(self.0.clone(), reduction.1),
cost: reduction.0,
})
}
pub fn curry(&self, args: Vec<Program>) -> Result<Program> {
let mut ctx = self.0.lock().unwrap();
let mut args_ptr = ctx.one();
for arg in args.into_iter().rev() {
args_ptr = ctx.encode_curried_arg(arg.1, args_ptr)?;
}
let ptr = ctx.alloc(&clvm_utils::CurriedProgram {
program: self.1,
args: args_ptr,
})?;
Ok(Program(self.0.clone(), ptr))
}
pub fn uncurry(&self) -> Result<Option<CurriedProgram>> {
let ctx = self.0.lock().unwrap();
let Ok(value) = clvm_utils::CurriedProgram::<NodePtr, NodePtr>::from_clvm(&ctx, self.1)
else {
return Ok(None);
};
let mut args = Vec::new();
let mut args_ptr = value.args;
while let Ok((first, rest)) = ctx.decode_curried_arg(&args_ptr) {
args.push(first);
args_ptr = rest;
}
if ctx.small_number(args_ptr) != Some(1) {
return Ok(None);
}
Ok(Some(CurriedProgram {
program: Program(self.0.clone(), value.program),
args: args
.into_iter()
.map(|ptr| Program(self.0.clone(), ptr))
.collect(),
}))
}
pub fn tree_hash(&self) -> Result<TreeHash> {
let ctx = self.0.lock().unwrap();
Ok(tree_hash(&ctx, self.1))
}
pub fn is_atom(&self) -> Result<bool> {
let ctx = self.0.lock().unwrap();
Ok(matches!(ctx.sexp(self.1), SExp::Atom))
}
pub fn is_pair(&self) -> Result<bool> {
let ctx = self.0.lock().unwrap();
Ok(matches!(ctx.sexp(self.1), SExp::Pair(..)))
}
pub fn is_null(&self) -> Result<bool> {
let ctx = self.0.lock().unwrap();
Ok(matches!(ctx.sexp(self.1), SExp::Atom) && ctx.atom_len(self.1) == 0)
}
pub fn length(&self) -> Result<u32> {
let ctx = self.0.lock().unwrap();
let SExp::Atom = ctx.sexp(self.1) else {
return Err(Error::AtomExpected);
};
Ok(ctx.atom_len(self.1) as u32)
}
pub fn first(&self) -> Result<Program> {
let ctx = self.0.lock().unwrap();
let SExp::Pair(first, _) = ctx.sexp(self.1) else {
return Err(Error::PairExpected);
};
Ok(Program(self.0.clone(), first))
}
pub fn rest(&self) -> Result<Program> {
let ctx = self.0.lock().unwrap();
let SExp::Pair(_, rest) = ctx.sexp(self.1) else {
return Err(Error::PairExpected);
};
Ok(Program(self.0.clone(), rest))
}
pub fn to_int(&self) -> Result<Option<BigInt>> {
let ctx = self.0.lock().unwrap();
let SExp::Atom = ctx.sexp(self.1) else {
return Ok(None);
};
Ok(Some(ctx.number(self.1)))
}
pub fn to_bound_checked_number(&self) -> Result<Option<f64>> {
let ctx = self.0.lock().unwrap();
let SExp::Atom = ctx.sexp(self.1) else {
return Ok(None);
};
let number = ctx.number(self.1);
if number > BigInt::from(9_007_199_254_740_991i64) {
return Err(Error::TooLarge);
}
if number < BigInt::from(-9_007_199_254_740_991i64) {
return Err(Error::TooSmall);
}
let number: i64 = number.try_into().unwrap();
Ok(Some(number as f64))
}
pub fn to_string(&self) -> Result<Option<String>> {
let ctx = self.0.lock().unwrap();
let SExp::Atom = ctx.sexp(self.1) else {
return Ok(None);
};
let bytes = ctx.atom(self.1);
Ok(Some(String::from_utf8(bytes.to_vec())?))
}
pub fn to_bool(&self) -> Result<Option<bool>> {
let ctx = self.0.lock().unwrap();
let SExp::Atom = ctx.sexp(self.1) else {
return Ok(None);
};
let Some(number) = ctx.small_number(self.1) else {
return Ok(None);
};
if number != 0 && number != 1 {
return Ok(None);
}
Ok(Some(number != 0))
}
pub fn to_atom(&self) -> Result<Option<Bytes>> {
let ctx = self.0.lock().unwrap();
let SExp::Atom = ctx.sexp(self.1) else {
return Ok(None);
};
Ok(Some(ctx.atom(self.1).to_vec().into()))
}
pub fn to_list(&self) -> Result<Option<Vec<Program>>> {
let ctx = self.0.lock().unwrap();
let Some(value) = Vec::<NodePtr>::from_clvm(&ctx, self.1).ok() else {
return Ok(None);
};
Ok(Some(
value
.into_iter()
.map(|ptr| Program(self.0.clone(), ptr))
.collect(),
))
}
pub fn to_arg_list(&self) -> Result<Option<Vec<Program>>> {
let ctx = self.0.lock().unwrap();
let mut args = Vec::new();
let mut ptr = self.1;
while let Ok((first, rest)) = ctx.decode_curried_arg(&ptr) {
args.push(Program(self.0.clone(), first));
ptr = rest;
}
Ok(Some(args))
}
pub fn to_pair(&self) -> Result<Option<Pair>> {
let ctx = self.0.lock().unwrap();
let SExp::Pair(first, rest) = ctx.sexp(self.1) else {
return Ok(None);
};
Ok(Some(Pair {
first: Program(self.0.clone(), first),
rest: Program(self.0.clone(), rest),
}))
}
pub fn puzzle(&self) -> Result<Puzzle> {
let ctx = self.0.lock().unwrap();
let value = chia_sdk_driver::Puzzle::parse(&ctx, self.1);
Ok(Puzzle::new(&self.0, value))
}
pub fn parse_nft_metadata(&self) -> Result<Option<NftMetadata>> {
let ctx = self.0.lock().unwrap();
let value = NftMetadata::from_clvm(&**ctx, self.1);
Ok(value.ok())
}
pub fn parse_option_metadata(&self) -> Result<Option<OptionMetadata>> {
let ctx = self.0.lock().unwrap();
let value = OptionMetadata::from_clvm(&**ctx, self.1);
Ok(value.ok())
}
pub fn parse_payment(&self) -> Result<Option<Payment>> {
let ctx = self.0.lock().unwrap();
let value = ChiaPayment::from_clvm(&**ctx, self.1);
Ok(value.ok().map(|p| p.as_program(&self.0)))
}
pub fn parse_notarized_payment(&self) -> Result<Option<NotarizedPayment>> {
let ctx = self.0.lock().unwrap();
let value = ChiaNotarizedPayment::from_clvm(&**ctx, self.1);
Ok(value.ok().map(|p| p.as_program(&self.0)))
}
pub fn parse_reward_distributor_launcher_solution(
&self,
launcher_coin: Coin,
) -> Result<Option<RewardDistributorLauncherSolutionInfo>> {
let mut ctx = self.0.lock().unwrap();
if let Some((constants, initial_state, coin)) =
SdkRewardDistributor::from_launcher_solution(&mut ctx, launcher_coin, self.1)?
{
Ok(Some(RewardDistributorLauncherSolutionInfo {
constants,
initial_state,
coin,
}))
} else {
Ok(None)
}
}
}