cml_chain/builders/
input_builder.rs1use crate::builders::witness_builder::{InputAggregateWitnessData, PartialPlutusWitness};
2
3use super::{
4 tx_builder::TransactionUnspentOutput,
5 utils::required_wits_from_required_signers,
6 witness_builder::{NativeScriptWitnessInfo, RequiredWitnessSet},
7};
8
9use crate::{
10 address::Address,
11 certs::StakeCredential,
12 crypto::hash::hash_plutus_data,
13 plutus::PlutusData,
14 transaction::{TransactionInput, TransactionOutput},
15 NativeScript, RequiredSigners,
16};
17
18#[derive(Debug, thiserror::Error)]
19pub enum InputBuilderError {
20 #[error("UTXO address was not a payment key: {0:?}")]
21 UTXOAddressNotPayment(Box<Address>),
22 #[error("Missing the following witnesses for the input: {0:?}")]
23 MissingWitnesses(Box<RequiredWitnessSet>),
24}
25
26pub fn input_required_wits(
27 utxo_info: &TransactionOutput,
28 required_witnesses: &mut RequiredWitnessSet,
29) {
30 if let Some(cred) = utxo_info.address().payment_cred() {
31 match cred {
32 StakeCredential::PubKey { hash, .. } => {
33 required_witnesses.add_vkey_key_hash(*hash);
34 }
35 StakeCredential::Script { hash, .. } => {
36 required_witnesses.add_script_hash(*hash);
37 if let Some(data_hash) = utxo_info.datum_hash() {
38 required_witnesses.add_plutus_datum_hash(*data_hash);
39 }
42 }
43 }
44 };
45 if let Address::Byron(byron) = utxo_info.address() {
46 required_witnesses.add_bootstrap(byron.clone());
47 }
48}
49
50#[derive(Clone, Debug)]
51pub struct InputBuilderResult {
52 pub input: TransactionInput,
53 pub utxo_info: TransactionOutput,
54 pub aggregate_witness: Option<InputAggregateWitnessData>,
55 pub required_wits: RequiredWitnessSet,
56}
57
58#[derive(Clone)]
59pub struct SingleInputBuilder {
60 input: TransactionInput,
61 utxo_info: TransactionOutput,
62}
63
64impl SingleInputBuilder {
65 pub fn new(input: TransactionInput, utxo_info: TransactionOutput) -> Self {
66 Self { input, utxo_info }
67 }
68
69 pub fn payment_key(self) -> Result<InputBuilderResult, InputBuilderError> {
70 let mut required_wits = RequiredWitnessSet::default();
71 input_required_wits(&self.utxo_info, &mut required_wits);
72 let required_wits_left = required_wits.clone();
73
74 if !required_wits_left.scripts.is_empty() {
75 return Err(InputBuilderError::UTXOAddressNotPayment(Box::new(
76 self.utxo_info.address().clone(),
77 )));
78 }
79
80 Ok(InputBuilderResult {
81 input: self.input,
82 utxo_info: self.utxo_info,
83 aggregate_witness: None,
84 required_wits,
85 })
86 }
87
88 pub fn native_script(
89 self,
90 native_script: NativeScript,
91 witness_info: NativeScriptWitnessInfo,
92 ) -> Result<InputBuilderResult, InputBuilderError> {
93 let mut required_wits = RequiredWitnessSet::default();
94 input_required_wits(&self.utxo_info, &mut required_wits);
95 let mut required_wits_left = required_wits.clone();
96
97 let script_hash = &native_script.hash();
98
99 required_wits_left.scripts.remove(script_hash);
101
102 if !required_wits_left.scripts.is_empty() {
103 return Err(InputBuilderError::MissingWitnesses(Box::new(
104 required_wits_left,
105 )));
106 }
107
108 Ok(InputBuilderResult {
109 input: self.input,
110 utxo_info: self.utxo_info,
111 aggregate_witness: Some(InputAggregateWitnessData::NativeScript(
112 native_script,
113 witness_info,
114 )),
115 required_wits,
116 })
117 }
118
119 pub fn plutus_script(
120 self,
121 partial_witness: PartialPlutusWitness,
122 required_signers: RequiredSigners,
123 datum: PlutusData,
124 ) -> Result<InputBuilderResult, InputBuilderError> {
125 self.plutus_script_inner(partial_witness, required_signers, Some(datum))
126 }
127
128 pub fn plutus_script_inline_datum(
129 self,
130 partial_witness: PartialPlutusWitness,
131 required_signers: RequiredSigners,
132 ) -> Result<InputBuilderResult, InputBuilderError> {
133 self.plutus_script_inner(partial_witness, required_signers, None)
134 }
135
136 fn plutus_script_inner(
137 self,
138 partial_witness: PartialPlutusWitness,
139 required_signers: RequiredSigners,
140 datum: Option<PlutusData>,
141 ) -> Result<InputBuilderResult, InputBuilderError> {
142 let mut required_wits = required_wits_from_required_signers(&required_signers);
143 input_required_wits(&self.utxo_info, &mut required_wits);
144 let mut required_wits_left = required_wits.clone();
145
146 required_wits_left.vkeys.clear();
148
149 let script_hash = partial_witness.script.hash();
150
151 required_wits_left.scripts.remove(&script_hash);
153 if let Some(datum) = &datum {
154 required_wits_left
155 .plutus_data
156 .remove(&hash_plutus_data(datum));
157 }
158
159 if required_wits_left.len() > 0 {
160 return Err(InputBuilderError::MissingWitnesses(Box::new(
161 required_wits_left,
162 )));
163 }
164
165 Ok(InputBuilderResult {
166 input: self.input,
167 utxo_info: self.utxo_info,
168 aggregate_witness: Some(InputAggregateWitnessData::PlutusScript(
169 partial_witness,
170 required_signers,
171 datum,
172 )),
173 required_wits,
174 })
175 }
176}
177
178impl From<TransactionUnspentOutput> for SingleInputBuilder {
179 fn from(utxo: TransactionUnspentOutput) -> Self {
180 Self {
181 input: utxo.input,
182 utxo_info: utxo.output,
183 }
184 }
185}