whisky_csl/tx_builder/
core_csl.rs

1use crate::*;
2use cardano_serialization_lib as csl;
3use whisky_common::*;
4
5#[derive(Clone, Debug)]
6pub struct CoreCSL {
7    pub tx_hex: String,
8    pub tx_builder: csl::TransactionBuilder,
9    pub tx_inputs_builder: csl::TxInputsBuilder,
10    pub collateral_builder: csl::TxInputsBuilder,
11    pub mint_builder: csl::MintBuilder,
12    pub certificates_builder: csl::CertificatesBuilder,
13    pub vote_builder: csl::VotingBuilder,
14    pub tx_withdrawals_builder: csl::WithdrawalsBuilder,
15    pub protocol_params: Protocol,
16}
17
18impl CoreCSL {
19    pub fn new(params: Option<Protocol>) -> Result<CoreCSL, WError> {
20        Ok(CoreCSL {
21            tx_hex: String::new(),
22            tx_builder: build_tx_builder(params.clone())
23                .map_err(WError::add_err_trace("CoreCSL - new - build_tx_builder"))?,
24            tx_inputs_builder: csl::TxInputsBuilder::new(),
25            collateral_builder: csl::TxInputsBuilder::new(),
26            mint_builder: csl::MintBuilder::new(),
27            certificates_builder: csl::CertificatesBuilder::new(),
28            vote_builder: csl::VotingBuilder::new(),
29            tx_withdrawals_builder: csl::WithdrawalsBuilder::new(),
30            protocol_params: params.unwrap_or_default(),
31        })
32    }
33
34    pub fn reset_after_build(&mut self) {
35        self.tx_builder = build_tx_builder(Some(self.protocol_params.clone())).unwrap();
36        self.tx_inputs_builder = csl::TxInputsBuilder::new();
37        self.collateral_builder = csl::TxInputsBuilder::new();
38        self.mint_builder = csl::MintBuilder::new();
39        self.certificates_builder = csl::CertificatesBuilder::new();
40        self.vote_builder = csl::VotingBuilder::new();
41        self.tx_withdrawals_builder = csl::WithdrawalsBuilder::new();
42    }
43}
44
45impl CoreCSL {
46    pub fn add_tx_in(&mut self, input: PubKeyTxIn) -> Result<(), WError> {
47        self.tx_inputs_builder
48            .add_regular_input(
49                &csl::Address::from_bech32(&input.tx_in.address.unwrap())
50                    .map_err(WError::from_err("CoreCSL - add_tx_in - invalid address"))?,
51                &csl::TransactionInput::new(
52                    &csl::TransactionHash::from_hex(&input.tx_in.tx_hash)
53                        .map_err(WError::from_err("CoreCSL - add_tx_in - invalid tx_hash"))?,
54                    input.tx_in.tx_index,
55                ),
56                &to_value(&input.tx_in.amount.unwrap())?,
57            )
58            .map_err(WError::from_err(
59                "CoreCSL - add_tx_in - invalid regular input",
60            ))?;
61        Ok(())
62    }
63
64    pub fn add_simple_script_tx_in(&mut self, input: SimpleScriptTxIn) -> Result<(), WError> {
65        match input.simple_script_tx_in {
66            SimpleScriptTxInParameter::ProvidedSimpleScriptSource(script) => {
67                self.tx_inputs_builder.add_native_script_input(
68                    &csl::NativeScriptSource::new(
69                        &csl::NativeScript::from_hex(&script.script_cbor).map_err(
70                            WError::from_err(
71                                "CoreCSL - add_simple_script_tx_in - invalid script_cbor",
72                            ),
73                        )?,
74                    ),
75                    &csl::TransactionInput::new(
76                        &csl::TransactionHash::from_hex(&input.tx_in.tx_hash).map_err(
77                            WError::from_err(
78                                "CoreCSL - add_simple_script_tx_in - invalid tx_hash (1)",
79                            ),
80                        )?,
81                        input.tx_in.tx_index,
82                    ),
83                    &to_value(&input.tx_in.amount.unwrap())?,
84                );
85                Ok(())
86            }
87            SimpleScriptTxInParameter::InlineSimpleScriptSource(script) => {
88                self.tx_inputs_builder.add_native_script_input(
89                    &csl::NativeScriptSource::new_ref_input(
90                        &csl::ScriptHash::from_hex(&script.simple_script_hash).map_err(
91                            WError::from_err(
92                                "CoreCSL - add_simple_script_tx_in - invalid simple_script_hash",
93                            ),
94                        )?,
95                        &csl::TransactionInput::new(
96                            &csl::TransactionHash::from_hex(&script.ref_tx_in.tx_hash).map_err(
97                                WError::from_err(
98                                    "CoreCSL - add_simple_script_tx_in - invalid tx_hash (2)",
99                                ),
100                            )?,
101                            script.ref_tx_in.tx_index,
102                        ),
103                        script.script_size,
104                    ),
105                    &csl::TransactionInput::new(
106                        &csl::TransactionHash::from_hex(&input.tx_in.tx_hash).map_err(
107                            WError::from_err(
108                                "CoreCSL - add_simple_script_tx_in - invalid tx_hash (3)",
109                            ),
110                        )?,
111                        input.tx_in.tx_index,
112                    ),
113                    &to_value(&input.tx_in.amount.unwrap())?,
114                );
115                Ok(())
116            }
117        }
118    }
119
120    pub fn add_script_tx_in(&mut self, input: ScriptTxIn) -> Result<(), WError> {
121        let datum_source = input.script_tx_in.datum_source.unwrap();
122        let script_source = input.script_tx_in.script_source.unwrap();
123        let redeemer = input.script_tx_in.redeemer.unwrap();
124        let csl_datum: Option<csl::DatumSource> = match datum_source {
125            DatumSource::ProvidedDatumSource(datum) => Some(csl::DatumSource::new(
126                &csl::PlutusData::from_hex(&datum.data).map_err(WError::from_err(
127                    "CoreCSL - add_script_tx_in - invalid datum.data",
128                ))?,
129            )),
130            DatumSource::InlineDatumSource(datum) => {
131                let ref_input = csl::TransactionInput::new(
132                    &csl::TransactionHash::from_hex(&datum.tx_hash).map_err(WError::from_err(
133                        "CoreCSL - add_script_tx_in - invalid tx_hash",
134                    ))?,
135                    datum.tx_index,
136                );
137                if input.tx_in.tx_hash == datum.tx_hash && input.tx_in.tx_index == datum.tx_index {
138                    None
139                } else {
140                    Some(csl::DatumSource::new_ref_input(&ref_input))
141                }
142            }
143        };
144
145        let csl_script: csl::PlutusScriptSource = to_csl_script_source(script_source)?;
146
147        let csl_redeemer: csl::Redeemer = csl::Redeemer::new(
148            &csl::RedeemerTag::new_spend(),
149            &to_bignum(0).unwrap(),
150            &csl::PlutusData::from_hex(&redeemer.data).map_err(WError::from_err(
151                "CoreCSL - add_script_tx_in - invalid redeemer.data",
152            ))?,
153            &csl::ExUnits::new(
154                &to_bignum(redeemer.ex_units.mem).map_err(WError::add_err_trace(
155                    "CoreCSL - add_script_tx_in - invalid redeemer memory",
156                ))?,
157                &to_bignum(redeemer.ex_units.steps).map_err(WError::add_err_trace(
158                    "CoreCSL - add_script_tx_in - invalid redeemer steps",
159                ))?,
160            ),
161        );
162
163        let csl_plutus_witness = match csl_datum {
164            Some(datum) => csl::PlutusWitness::new_with_ref(&csl_script, &datum, &csl_redeemer),
165            None => csl::PlutusWitness::new_with_ref_without_datum(&csl_script, &csl_redeemer),
166        };
167
168        self.tx_inputs_builder.add_plutus_script_input(
169            &csl_plutus_witness,
170            &csl::TransactionInput::new(
171                &csl::TransactionHash::from_hex(&input.tx_in.tx_hash).map_err(WError::from_err(
172                    "CoreCSL - add_script_tx_in - invalid tx_hash (2)",
173                ))?,
174                input.tx_in.tx_index,
175            ),
176            &to_value(&input.tx_in.amount.unwrap())?,
177        );
178        Ok(())
179    }
180
181    pub fn add_output(&mut self, output: Output) -> Result<(), WError> {
182        let mut output_address = csl::Address::from_bech32(&output.address);
183        // If the address is not in bech32 format, it might be a Byron address
184        match output_address {
185            Ok(_) => {}
186            Err(_) => {
187                output_address = csl::ByronAddress::from_base58(&output.address)
188                    .map(|byron_addr| byron_addr.to_address());
189            }
190        };
191        let mut output_builder =
192            csl::TransactionOutputBuilder::new().with_address(&output_address.map_err(
193                WError::from_err("CoreCSL - add_output - invalid output_address"),
194            )?);
195        if output.datum.is_some() {
196            let datum = output.datum.unwrap();
197
198            match datum {
199                Datum::Hash(data) => {
200                    output_builder = output_builder.with_data_hash(&csl::hash_plutus_data(
201                        &csl::PlutusData::from_hex(&data).map_err(WError::from_err(
202                            "CoreCSL - add_output - invalid datum hash",
203                        ))?,
204                    ));
205                }
206                Datum::Inline(data) => {
207                    output_builder = output_builder.with_plutus_data(
208                        &csl::PlutusData::from_hex(&data).map_err(WError::from_err(
209                            "CoreCSL - add_output - invalid inline datum",
210                        ))?,
211                    );
212                }
213                Datum::Embedded(data) => {
214                    let datum = &csl::PlutusData::from_hex(&data).map_err(WError::from_err(
215                        "CoreCSL - add_output - invalid embedded datum",
216                    ))?;
217                    output_builder = output_builder.with_data_hash(&csl::hash_plutus_data(datum));
218                    self.tx_builder.add_extra_witness_datum(datum);
219                }
220            };
221        }
222
223        if output.reference_script.is_some() {
224            let output_script = output.reference_script.unwrap();
225            match output_script {
226                OutputScriptSource::ProvidedScriptSource(script) => {
227                    let language_version: csl::Language = match script.language_version {
228                        LanguageVersion::V1 => csl::Language::new_plutus_v1(),
229                        LanguageVersion::V2 => csl::Language::new_plutus_v2(),
230                        LanguageVersion::V3 => csl::Language::new_plutus_v3(),
231                    };
232                    output_builder =
233                        output_builder.with_script_ref(&csl::ScriptRef::new_plutus_script(
234                            &csl::PlutusScript::from_hex_with_version(
235                                &script.script_cbor,
236                                &language_version,
237                            )
238                            .map_err(WError::from_err(
239                                "CoreCSL - add_output - invalid script_cbor",
240                            ))?,
241                        ))
242                }
243                OutputScriptSource::ProvidedSimpleScriptSource(script) => {
244                    output_builder =
245                        output_builder.with_script_ref(&csl::ScriptRef::new_native_script(
246                            &csl::NativeScript::from_hex(&script.script_cbor).map_err(
247                                WError::from_err(
248                                    "CoreCSL - add_output - invalid simple script_cbor",
249                                ),
250                            )?,
251                        ))
252                }
253            }
254        }
255
256        let tx_value = to_value(&output.amount)?;
257        let amount_builder = output_builder
258            .next()
259            .map_err(WError::from_err("CoreCSL - add_output - next output"))?;
260        let built_output: csl::TransactionOutput = if tx_value.multiasset().is_some() {
261            if tx_value.coin().is_zero() {
262                amount_builder
263                    .with_asset_and_min_required_coin_by_utxo_cost(
264                        &tx_value.multiasset().unwrap(),
265                        &csl::DataCost::new_coins_per_byte(
266                            &to_bignum(self.protocol_params.coins_per_utxo_size).map_err(
267                                WError::add_err_trace(
268                                    "CoreCSL - add_output - invalid coins_per_utxo_size",
269                                ),
270                            )?,
271                        ),
272                    )
273                    .map_err(WError::from_err(
274                        "CoreCSL - add_output - with_asset_and_min_required_coin_by_utxo_cost",
275                    ))?
276                    .build()
277                    .map_err(WError::from_err("CoreCSL - add_output - build() (1)"))?
278            } else {
279                amount_builder
280                    .with_coin_and_asset(&tx_value.coin(), &tx_value.multiasset().unwrap())
281                    .build()
282                    .map_err(WError::from_err("CoreCSL - add_output - build() (2)"))?
283            }
284        } else {
285            amount_builder
286                .with_coin(&tx_value.coin())
287                .build()
288                .map_err(WError::from_err("CoreCSL - add_output - build() (3)"))?
289        };
290        self.tx_builder
291            .add_output(&built_output)
292            .map_err(WError::from_err("CoreCSL - add_output - add_output"))?;
293        Ok(())
294    }
295
296    pub fn add_collateral(&mut self, collateral: PubKeyTxIn) -> Result<(), WError> {
297        self.collateral_builder
298            .add_regular_input(
299                &csl::Address::from_bech32(&collateral.tx_in.address.unwrap()).map_err(
300                    WError::from_err("CoreCSL - add_collateral - invalid address"),
301                )?,
302                &csl::TransactionInput::new(
303                    &csl::TransactionHash::from_hex(&collateral.tx_in.tx_hash).map_err(
304                        WError::from_err("CoreCSL - add_collateral - invalid tx_hash"),
305                    )?,
306                    collateral.tx_in.tx_index,
307                ),
308                &to_value(&collateral.tx_in.amount.unwrap())?,
309            )
310            .map_err(WError::from_err(
311                "CoreCSL - add_collateral - add_regular_input",
312            ))?;
313        Ok(())
314    }
315
316    pub fn add_reference_input(&mut self, ref_input: RefTxIn) -> Result<(), WError> {
317        let csl_ref_input = csl::TransactionInput::new(
318            &csl::TransactionHash::from_hex(&ref_input.tx_hash).map_err(WError::from_err(
319                "CoreCSL - add_reference_input - invalid_tx_hash",
320            ))?,
321            ref_input.tx_index,
322        );
323        if ref_input.script_size.is_some() {
324            self.tx_builder
325                .add_script_reference_input(&csl_ref_input, ref_input.script_size.unwrap());
326        } else {
327            self.tx_builder.add_reference_input(&csl_ref_input);
328        }
329        Ok(())
330    }
331
332    pub fn add_pub_key_withdrawal(&mut self, withdrawal: PubKeyWithdrawal) -> Result<(), WError> {
333        self.tx_withdrawals_builder
334            .add(
335                &csl::RewardAddress::from_address(
336                    &csl::Address::from_bech32(&withdrawal.address).map_err(WError::from_err(
337                        "CoreCSL - add_pub_key_withdrawal - invalid address",
338                    ))?,
339                )
340                .unwrap(),
341                &csl::BigNum::from_str(&withdrawal.coin.to_string()).map_err(WError::from_err(
342                    "CoreCSL - add_collateral - invalid coin as BigNum",
343                ))?,
344            )
345            .map_err(WError::from_err("CoreCSL - add_pub_key_withdrawal - add()"))?;
346        Ok(())
347    }
348
349    pub fn add_plutus_withdrawal(
350        &mut self,
351        withdrawal: PlutusScriptWithdrawal,
352    ) -> Result<(), WError> {
353        let script_source = withdrawal.script_source.unwrap();
354        let redeemer = withdrawal.redeemer.unwrap();
355
356        let csl_script: csl::PlutusScriptSource = to_csl_script_source(script_source)?;
357
358        let csl_redeemer: csl::Redeemer = csl::Redeemer::new(
359            &csl::RedeemerTag::new_spend(),
360            &to_bignum(0).unwrap(),
361            &csl::PlutusData::from_hex(&redeemer.data).map_err(WError::from_err(
362                "CoreCSL - add_plutus_withdrawal - invalid redeemer.data",
363            ))?,
364            &csl::ExUnits::new(
365                &to_bignum(redeemer.ex_units.mem).map_err(WError::add_err_trace(
366                    "CoreCSL - add_plutus_withdrawal - invalid redeemer memory",
367                ))?,
368                &to_bignum(redeemer.ex_units.steps).map_err(WError::add_err_trace(
369                    "CoreCSL - add_plutus_withdrawal - invalid redeemer steps",
370                ))?,
371            ),
372        );
373
374        self.tx_withdrawals_builder
375            .add_with_plutus_witness(
376                &csl::RewardAddress::from_address(
377                    &csl::Address::from_bech32(&withdrawal.address).map_err(WError::from_err(
378                        "CoreCSL - add_plutus_withdrawal - invalid address",
379                    ))?,
380                )
381                .unwrap(),
382                &csl::BigNum::from_str(&withdrawal.coin.to_string()).map_err(WError::from_err(
383                    "CoreCSL - add_plutus_withdrawal - invalid coin as BigNum",
384                ))?,
385                &csl::PlutusWitness::new_with_ref_without_datum(&csl_script, &csl_redeemer),
386            )
387            .map_err(WError::from_err(
388                "CoreCSL - add_plutus_withdrawal - add_with_plutus_witness",
389            ))?;
390        Ok(())
391    }
392
393    pub fn add_simple_script_withdrawal(
394        &mut self,
395        withdrawal: SimpleScriptWithdrawal,
396    ) -> Result<(), WError> {
397        let csl_native_script_source = match withdrawal.script_source {
398            Some(script_source) => match script_source {
399                SimpleScriptSource::ProvidedSimpleScriptSource(ProvidedSimpleScriptSource {
400                    script_cbor: provided_script,
401                }) => csl::NativeScriptSource::new(
402                    &csl::NativeScript::from_hex(&provided_script).unwrap(),
403                ),
404                SimpleScriptSource::InlineSimpleScriptSource(InlineSimpleScriptSource {
405                    ref_tx_in,
406                    simple_script_hash,
407                    script_size,
408                }) => csl::NativeScriptSource::new_ref_input(
409                    &csl::ScriptHash::from_hex(&simple_script_hash).unwrap(),
410                    &csl::TransactionInput::new(
411                        &csl::TransactionHash::from_hex(&ref_tx_in.tx_hash).unwrap(),
412                        ref_tx_in.tx_index,
413                    ),
414                    script_size,
415                ),
416            },
417            None => {
418                return Err(WError::new(
419                    "add_simple_script_withdrawal",
420                    "Missing script source for native script withdrawal",
421                ))
422            }
423        };
424
425        self.tx_withdrawals_builder
426            .add_with_native_script(
427                &csl::RewardAddress::from_address(
428                    &csl::Address::from_bech32(&withdrawal.address).map_err(WError::from_err(
429                        "CoreCSL - add_simple_script_withdrawal - invalid reward address",
430                    ))?,
431                )
432                .unwrap(),
433                &csl::BigNum::from_str(&withdrawal.coin.to_string()).map_err(WError::from_err(
434                    "CoreCSL - add_simple_script_withdrawal - invalid coin as BigNum",
435                ))?,
436                &csl_native_script_source,
437            )
438            .map_err(WError::from_err(
439                "CoreCSL - add_simple_script_withdrawal - add_with_native_script",
440            ))
441    }
442
443    pub fn add_plutus_mint(&mut self, script_mint: ScriptMint, index: u64) -> Result<(), WError> {
444        let redeemer_info = script_mint.redeemer.unwrap();
445        let mint_redeemer = csl::Redeemer::new(
446            &csl::RedeemerTag::new_mint(),
447            &to_bignum(index).map_err(WError::from_err(
448                "CoreCSL - add_plutus_mint - invalid redeemer index",
449            ))?,
450            &csl::PlutusData::from_hex(&redeemer_info.data).map_err(WError::from_err(
451                "CoreCSL - add_plutus_mint - invalid redeemer_info.data",
452            ))?,
453            &csl::ExUnits::new(
454                &to_bignum(redeemer_info.ex_units.mem).map_err(WError::from_err(
455                    "CoreCSL - add_plutus_mint - invalid redeemer_info memory",
456                ))?,
457                &to_bignum(redeemer_info.ex_units.steps).map_err(WError::from_err(
458                    "CoreCSL - add_plutus_mint - invalid redeemer_info steps",
459                ))?,
460            ),
461        );
462        let script_source_info = script_mint.script_source.unwrap();
463        let mint_script = to_csl_script_source(script_source_info)?;
464        self.mint_builder
465            .add_asset(
466                &csl::MintWitness::new_plutus_script(&mint_script, &mint_redeemer),
467                &csl::AssetName::new(hex::decode(script_mint.mint.asset_name).map_err(
468                    WError::from_err("CoreCSL - add_plutus_mint - Invalid asset name found"),
469                )?)
470                .map_err(WError::from_err(
471                    "CoreCSL - add_plutus_mint - invalid asset name",
472                ))?,
473                &csl::Int::from_str(&script_mint.mint.amount.to_string()).map_err(
474                    WError::from_err("CoreCSL - add_plutus_mint - invalid mint amount "),
475                )?,
476            )
477            .map_err(WError::from_err("CoreCSL - add_plutus_mint - add_asset"))?;
478        Ok(())
479    }
480
481    pub fn add_native_mint(&mut self, native_mint: SimpleScriptMint) -> Result<(), WError> {
482        let script_info = native_mint.script_source.unwrap();
483        match script_info {
484            SimpleScriptSource::ProvidedSimpleScriptSource(script) => self
485                .mint_builder
486                .add_asset(
487                    &csl::MintWitness::new_native_script(&csl::NativeScriptSource::new(
488                        &csl::NativeScript::from_hex(&script.script_cbor).map_err(
489                            WError::from_err("CoreCSL - add_native_mint - invalid script_cbor"),
490                        )?,
491                    )),
492                    &csl::AssetName::new(hex::decode(native_mint.mint.asset_name).map_err(
493                        WError::from_err(
494                            "CoreCSL - add_native_mint - Invalid asset name found (1)",
495                        ),
496                    )?)
497                    .map_err(WError::from_err(
498                        "CoreCSL - add_native_mint - invalid asset name (1)",
499                    ))?,
500                    &csl::Int::from_str(&native_mint.mint.amount.to_string()).unwrap(),
501                )
502                .map_err(WError::from_err(
503                    "CoreCSL - add_native_mint - add_asset (1)",
504                ))?,
505            SimpleScriptSource::InlineSimpleScriptSource(script) => self
506                .mint_builder
507                .add_asset(
508                    &csl::MintWitness::new_native_script(&csl::NativeScriptSource::new_ref_input(
509                        &csl::ScriptHash::from_hex(&script.simple_script_hash).map_err(
510                            WError::from_err(
511                                "CoreCSL - add_native_mint - invalid simple_script_hash",
512                            ),
513                        )?,
514                        &csl::TransactionInput::new(
515                            &csl::TransactionHash::from_hex(&script.ref_tx_in.tx_hash).map_err(
516                                WError::from_err("CoreCSL - add_native_mint - invalid tx_hash"),
517                            )?,
518                            script.ref_tx_in.tx_index,
519                        ),
520                        script.script_size,
521                    )),
522                    &csl::AssetName::new(hex::decode(native_mint.mint.asset_name).map_err(
523                        WError::from_err(
524                            "CoreCSL - add_native_mint - Invalid asset name found (2)",
525                        ),
526                    )?)
527                    .map_err(WError::from_err(
528                        "CoreCSL - add_native_mint - invalid asset name (2)",
529                    ))?,
530                    &csl::Int::from_str(&native_mint.mint.amount.to_string()).unwrap(),
531                )
532                .map_err(WError::from_err(
533                    "CoreCSL - add_native_mint - add_asset (2)",
534                ))?,
535        };
536        Ok(())
537    }
538
539    pub fn add_cert(&mut self, cert: Certificate, index: u64) -> Result<(), WError> {
540        match cert {
541            Certificate::BasicCertificate(basic_cert) => self
542                .certificates_builder
543                .add(&to_csl_cert(basic_cert)?)
544                .map_err(WError::from_err("CoreCSL - add_cert - add (1)"))?,
545            Certificate::ScriptCertificate(script_cert) => {
546                let cert_script_source: csl::PlutusScriptSource = match script_cert.script_source {
547                    Some(script_source) => to_csl_script_source(script_source)?,
548                    None => {
549                        return Err(WError::new(
550                            "CoreCSL - add_cert",
551                            "Missing Plutus Script Source in Plutus Cert",
552                        ))
553                    }
554                };
555                let cert_redeemer = match script_cert.redeemer {
556                    Some(redeemer) => to_csl_redeemer(RedeemerTag::Cert, redeemer, index)?,
557                    None => {
558                        return Err(WError::new(
559                            "CoreCSL - add_cert",
560                            "Missing Redeemer in Plutus Cert",
561                        ))
562                    }
563                };
564                let csl_plutus_witness: csl::PlutusWitness =
565                    csl::PlutusWitness::new_with_ref_without_datum(
566                        &cert_script_source,
567                        &cert_redeemer,
568                    );
569                self.certificates_builder
570                    .add_with_plutus_witness(&to_csl_cert(script_cert.cert)?, &csl_plutus_witness)
571                    .map_err(WError::from_err(
572                        "CoreCSL - add_cert - add_with_plutus_witness",
573                    ))?
574            }
575            Certificate::SimpleScriptCertificate(simple_script_cert) => {
576                let script_info = simple_script_cert.simple_script_source;
577                let script_source: csl::NativeScriptSource = match script_info {
578                    Some(simple_script_source) => {
579                        to_csl_simple_script_source(simple_script_source)?
580                    }
581                    None => {
582                        return Err(WError::new(
583                            "CoreCSL - add_cert",
584                            "Missing Native Script Source in Native Cert",
585                        ))
586                    }
587                };
588                self.certificates_builder
589                    .add_with_native_script(&to_csl_cert(simple_script_cert.cert)?, &script_source)
590                    .map_err(WError::from_err(
591                        "CoreCSL - add_cert - add_with_native_script",
592                    ))?
593            }
594        };
595        Ok(())
596    }
597
598    pub fn add_vote(&mut self, vote: Vote, index: u64) -> Result<(), WError> {
599        match vote {
600            Vote::BasicVote(vote_type) => {
601                let voter = to_csl_voter(vote_type.voter)
602                    .map_err(WError::from_err("CoreCSL - add_vote - invalid voter"))?;
603                let vote_kind = to_csl_vote_kind(vote_type.voting_procedure.vote_kind);
604                let voting_procedure = match vote_type.voting_procedure.anchor {
605                    Some(anchor) => {
606                        csl::VotingProcedure::new_with_anchor(vote_kind, &to_csl_anchor(&anchor)?)
607                    }
608                    None => csl::VotingProcedure::new(vote_kind),
609                };
610                self.vote_builder
611                    .add(
612                        &voter,
613                        &csl::GovernanceActionId::new(
614                            &csl::TransactionHash::from_hex(&vote_type.gov_action_id.tx_hash)
615                                .map_err(WError::from_err(
616                                    "CoreCSL - add_vote - invalid tx_hash (1)",
617                                ))?,
618                            vote_type.gov_action_id.tx_index,
619                        ),
620                        &voting_procedure,
621                    )
622                    .map_err(WError::from_err("CoreCSL - add_vote - add"))?;
623            }
624            Vote::ScriptVote(script_vote) => {
625                let voter = to_csl_voter(script_vote.vote.voter)?;
626                let vote_kind = to_csl_vote_kind(script_vote.vote.voting_procedure.vote_kind);
627                let voting_procedure = match script_vote.vote.voting_procedure.anchor {
628                    Some(anchor) => {
629                        csl::VotingProcedure::new_with_anchor(vote_kind, &to_csl_anchor(&anchor)?)
630                    }
631                    None => csl::VotingProcedure::new(vote_kind),
632                };
633                let vote_script_source: csl::PlutusScriptSource = match script_vote.script_source {
634                    Some(script_source) => to_csl_script_source(script_source)?,
635                    None => {
636                        return Err(WError::new(
637                            "CoreCSL - add_vote",
638                            "Missing Plutus Script Source in Plutus Vote",
639                        ))
640                    }
641                };
642                let vote_redeemer = match script_vote.redeemer {
643                    Some(redeemer) => to_csl_redeemer(RedeemerTag::Vote, redeemer, index)?,
644                    None => {
645                        return Err(WError::new(
646                            "CoreCSL - add_vote",
647                            "Missing Redeemer in Plutus Vote",
648                        ))
649                    }
650                };
651                let csl_plutus_witness: csl::PlutusWitness =
652                    csl::PlutusWitness::new_with_ref_without_datum(
653                        &vote_script_source,
654                        &vote_redeemer,
655                    );
656                self.vote_builder
657                    .add_with_plutus_witness(
658                        &voter,
659                        &csl::GovernanceActionId::new(
660                            &csl::TransactionHash::from_hex(
661                                &script_vote.vote.gov_action_id.tx_hash,
662                            )
663                            .map_err(WError::from_err(
664                                "CoreCSL - add_vote - invalid tx_hash (2)",
665                            ))?,
666                            script_vote.vote.gov_action_id.tx_index,
667                        ),
668                        &voting_procedure,
669                        &csl_plutus_witness,
670                    )
671                    .map_err(WError::from_err(
672                        "CoreCSL - add_vote - add_with_plutus_witness",
673                    ))?;
674            }
675            Vote::SimpleScriptVote(simple_script_vote) => {
676                let voter = to_csl_voter(simple_script_vote.vote.voter)?;
677                let vote_kind =
678                    to_csl_vote_kind(simple_script_vote.vote.voting_procedure.vote_kind);
679                let voting_procedure = match simple_script_vote.vote.voting_procedure.anchor {
680                    Some(anchor) => {
681                        csl::VotingProcedure::new_with_anchor(vote_kind, &to_csl_anchor(&anchor)?)
682                    }
683                    None => csl::VotingProcedure::new(vote_kind),
684                };
685                let csl_simple_script_source = match simple_script_vote.simple_script_source {
686                    Some(simple_script_source) => {
687                        to_csl_simple_script_source(simple_script_source)?
688                    }
689                    None => {
690                        return Err(WError::new(
691                            "CoreCSL - add_vote",
692                            "Missing Native Script Source in Native Vote",
693                        ))
694                    }
695                };
696                self.vote_builder
697                    .add_with_native_script(
698                        &voter,
699                        &csl::GovernanceActionId::new(
700                            &csl::TransactionHash::from_hex(
701                                &simple_script_vote.vote.gov_action_id.tx_hash,
702                            )
703                            .map_err(WError::from_err(
704                                "CoreCSL - add_vote - invalid tx_hash (3)",
705                            ))?,
706                            simple_script_vote.vote.gov_action_id.tx_index,
707                        ),
708                        &voting_procedure,
709                        &csl_simple_script_source,
710                    )
711                    .map_err(WError::from_err(
712                        "CoreCSL - add_vote - add_with_native_script",
713                    ))?;
714            }
715        };
716        Ok(())
717    }
718
719    pub fn add_invalid_before(&mut self, invalid_before: u64) -> Result<(), WError> {
720        self.tx_builder
721            .set_validity_start_interval_bignum(to_bignum(invalid_before).map_err(
722                WError::from_err("CoreCSL - add_invalid_before - invalid invalid_before"),
723            )?);
724        Ok(())
725    }
726
727    pub fn add_invalid_hereafter(&mut self, invalid_hereafter: u64) -> Result<(), WError> {
728        self.tx_builder
729            .set_ttl_bignum(&to_bignum(invalid_hereafter).map_err(WError::from_err(
730                "CoreCSL - add_invalid_hereafter - invalid invalid_hereafter",
731            ))?);
732        Ok(())
733    }
734
735    pub fn set_fee(&mut self, fee: String) {
736        self.tx_builder
737            .set_fee(&csl::BigNum::from_str(&fee).expect("Error parsing fee amount"));
738    }
739
740    pub fn add_change(
741        &mut self,
742        change_address: String,
743        change_datum: Option<Datum>,
744    ) -> Result<(), WError> {
745        let mut output_address = csl::Address::from_bech32(&change_address);
746        // If the address is not in bech32 format, it might be a Byron address
747        match output_address {
748            Ok(_) => {}
749            Err(_) => {
750                output_address = csl::ByronAddress::from_base58(&change_address)
751                    .map(|byron_addr| byron_addr.to_address());
752            }
753        };
754        if let Some(change_datum) = change_datum {
755            self.tx_builder
756                .add_change_if_needed_with_datum(
757                    &output_address.map_err(WError::from_err(
758                        "CoreCSL - add_change - invalid change_address (1)",
759                    ))?,
760                    &csl::OutputDatum::new_data(
761                        &csl::PlutusData::from_hex(change_datum.get_inner()).map_err(
762                            WError::from_err("CoreCSL - add_change - invalid change_datum"),
763                        )?,
764                    ),
765                )
766                .map_err(WError::from_err(
767                    "CoreCSL - add_change - add_change_if_needed_with_datum",
768                ))?;
769        } else {
770            self.tx_builder
771                .add_change_if_needed(&output_address.map_err(WError::from_err(
772                    "CoreCSL - add_change - invalid change_address (2)",
773                ))?)
774                .map_err(WError::from_err(
775                    "CoreCSL - add_change - add_change_if_needed",
776                ))?;
777        }
778        Ok(())
779    }
780
781    pub fn add_signing_keys(&mut self, signing_keys: &[&str]) -> Result<(), WError> {
782        self.tx_hex = sign_transaction(&self.tx_hex, signing_keys)?;
783        Ok(())
784    }
785
786    pub fn add_required_signature(&mut self, pub_key_hash: &str) -> Result<(), WError> {
787        self.tx_builder
788            .add_required_signer(&csl::Ed25519KeyHash::from_hex(pub_key_hash).map_err(
789                WError::from_err("CoreCSL - add_required_signature - invalid pub_key_hash"),
790            )?);
791        Ok(())
792    }
793
794    pub fn add_metadata(&mut self, metadata: Metadata) -> Result<(), WError> {
795        self.tx_builder
796            .add_json_metadatum(
797                &csl::BigNum::from_str(&metadata.tag).map_err(WError::from_err(
798                    "CoreCSL - add_metadata - invalid metadata tag",
799                ))?,
800                metadata.metadata,
801            )
802            .map_err(WError::from_err(
803                "CoreCSL - add_metadata - add_json_metadatum",
804            ))?;
805        Ok(())
806    }
807
808    pub fn add_script_hash(&mut self, network: Network) -> Result<(), WError> {
809        self.tx_builder
810            .calc_script_data_hash(&build_csl_cost_models(&network))
811            .map_err(WError::from_err(
812                "CoreCSL - add_script_hash - calc_script_data_hash",
813            ))?;
814        Ok(())
815    }
816
817    pub fn build_tx(&mut self, safe_build: bool) -> Result<String, WError> {
818        let tx = if safe_build {
819            self.tx_builder.build_tx()
820        } else {
821            self.tx_builder.build_tx_unsafe()
822        }
823        .map_err(WError::from_err("CoreCSL - build_tx - build_tx"))?;
824
825        self.tx_hex = tx.to_hex();
826        Ok(self.tx_hex.to_string())
827    }
828}
829
830// impl Default for CoreCSL {
831//     pub fn default() -> Self {
832//         Self::new(None).unwrap()
833//     }
834// }