hal_simplicity/actions/simplicity/pset/
run.rs1use serde::Serialize;
5
6use crate::hal_simplicity::Program;
7use crate::simplicity::bit_machine::{BitMachine, ExecTracker, FrameIter, NodeOutput};
8use crate::simplicity::Value;
9use crate::simplicity::{jet, node};
10
11use super::{execution_environment, PsetError};
12
13#[derive(Debug, thiserror::Error)]
14pub enum PsetRunError {
15 #[error(transparent)]
16 SharedError(#[from] PsetError),
17
18 #[error("invalid PSET: {0}")]
19 PsetDecode(elements::pset::ParseError),
20
21 #[error("invalid input index: {0}")]
22 InputIndexParse(std::num::ParseIntError),
23
24 #[error("invalid program: {0}")]
25 ProgramParse(simplicity::ParseError),
26
27 #[error("program does not have a redeem node")]
28 NoRedeemNode,
29
30 #[error("failed to construct bit machine: {0}")]
31 BitMachineConstruction(simplicity::bit_machine::LimitError),
32}
33
34#[derive(Serialize)]
35pub struct JetCall {
36 pub jet: String,
37 pub source_ty: String,
38 pub target_ty: String,
39 pub success: bool,
40 pub input_value: String,
41 pub output_value: String,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub equality_check: Option<(String, String)>,
44}
45
46#[derive(Serialize)]
47pub struct RunResponse {
48 pub success: bool,
49 pub jets: Vec<JetCall>,
50}
51
52struct JetTracker(Vec<JetCall>);
53
54impl<J: jet::Jet> ExecTracker<J> for JetTracker {
55 fn visit_node(
56 &mut self,
57 node: &simplicity::RedeemNode<J>,
58 mut input: FrameIter,
59 output: NodeOutput,
60 ) {
61 if let node::Inner::Jet(jet) = node.inner() {
62 let input_value = Value::from_padded_bits(&mut input, &node.arrow().source)
63 .expect("valid value from bit machine");
64
65 let (success, output_value) = match output {
66 NodeOutput::NonTerminal => unreachable!(),
67 NodeOutput::JetFailed => (false, Value::unit()),
68 NodeOutput::Success(mut iter) => (
69 true,
70 Value::from_padded_bits(&mut iter, &node.arrow().target)
71 .expect("valid value from bit machine"),
72 ),
73 };
74
75 let jet_name = jet.to_string();
76 let equality_check = if jet_name.strip_prefix("eq_").is_some() {
77 let (left, right) = input_value.as_product().unwrap();
78 Some((left.to_value().to_string(), right.to_value().to_string()))
79 } else {
80 None
81 };
82
83 self.0.push(JetCall {
84 jet: jet_name,
85 source_ty: jet.source_ty().to_final().to_string(),
86 target_ty: jet.target_ty().to_final().to_string(),
87 success,
88 input_value: input_value.to_string(),
89 output_value: output_value.to_string(),
90 equality_check,
91 });
92 }
93 }
94}
95
96pub fn pset_run(
98 pset_b64: &str,
99 input_idx: &str,
100 program: &str,
101 witness: &str,
102 genesis_hash: Option<&str>,
103) -> Result<RunResponse, PsetRunError> {
104 let pset: elements::pset::PartiallySignedTransaction =
106 pset_b64.parse().map_err(PsetRunError::PsetDecode)?;
107 let input_idx: u32 = input_idx.parse().map_err(PsetRunError::InputIndexParse)?;
108 let input_idx_usize = input_idx as usize; let program = Program::<jet::Elements>::from_str(program, Some(witness))
111 .map_err(PsetRunError::ProgramParse)?;
112
113 let (tx_env, _control_block, _tap_leaf) =
115 execution_environment(&pset, input_idx_usize, program.cmr(), genesis_hash)?;
116
117 let redeem_node = program.redeem_node().ok_or(PsetRunError::NoRedeemNode)?;
119
120 let mut mac =
121 BitMachine::for_program(redeem_node).map_err(PsetRunError::BitMachineConstruction)?;
122 let mut tracker = JetTracker(vec![]);
123 let success = mac.exec_with_tracker(redeem_node, &tx_env, &mut tracker).is_ok();
125 Ok(RunResponse {
126 success,
127 jets: tracker.0,
128 })
129}