use super::certificate_builder::*;
use super::input_builder::InputBuilderResult;
use super::mint_builder::MintBuilderResult;
use super::output_builder::{OutputBuilderError, SingleOutputBuilderResult};
use super::redeemer_builder::RedeemerBuilderError;
use super::redeemer_builder::RedeemerSetBuilder;
use super::redeemer_builder::RedeemerWitnessKey;
use super::withdrawal_builder::WithdrawalBuilderResult;
use super::witness_builder::merge_fake_witness;
use super::witness_builder::PlutusScriptWitness;
use super::witness_builder::RequiredWitnessSet;
use super::witness_builder::TransactionWitnessSetBuilder;
use super::witness_builder::{InputAggregateWitnessData, WitnessBuilderError};
use crate::address::Address;
use crate::assets::MultiAsset;
use crate::assets::{AssetArithmeticError, Mint};
use crate::auxdata::AuxiliaryData;
use crate::builders::output_builder::TransactionOutputBuilder;
use crate::certs::Certificate;
use crate::crypto::hash::{calc_script_data_hash, hash_auxiliary_data, ScriptDataHashError};
use crate::crypto::{BootstrapWitness, Vkeywitness};
use crate::deposit::{internal_get_deposit, internal_get_implicit_input};
use crate::fees::LinearFee;
use crate::min_ada::min_ada_required;
use crate::plutus::PlutusData;
use crate::plutus::{CostModels, ExUnits, Language, Redeemer};
use crate::transaction::{
DatumOption, ScriptRef, Transaction, TransactionBody, TransactionInput, TransactionOutput,
TransactionWitnessSet,
};
use crate::{assets::AssetName, Coin, ExUnitPrices, NetworkId, PolicyId, Value, Withdrawals};
use cbor_event::{de::Deserializer, se::Serializer};
use cml_core::ordered_hash_map::OrderedHashMap;
use cml_core::serialization::{CBORReadLen, Deserialize};
use cml_core::{ArithmeticError, DeserializeError, DeserializeFailure, Slot};
use cml_crypto::{Ed25519KeyHash, ScriptDataHash, ScriptHash, Serialize};
use fraction::Zero;
use rand::Rng;
use std::collections::{BTreeSet, HashMap};
use std::convert::TryInto;
use std::io::{BufRead, Seek, Write};
use std::ops::DerefMut;
use wasm_bindgen::prelude::wasm_bindgen;
#[derive(Clone, Debug)]
pub struct TransactionUnspentOutput {
pub input: TransactionInput,
pub output: TransactionOutput,
}
impl TransactionUnspentOutput {
pub fn new(input: TransactionInput, output: TransactionOutput) -> Self {
Self { input, output }
}
}
impl cbor_event::se::Serialize for TransactionUnspentOutput {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
serializer.write_array(cbor_event::Len::Len(2))?;
self.input.serialize(serializer, false)?;
self.output.serialize(serializer, false)
}
}
impl Deserialize for TransactionUnspentOutput {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
let len = raw.array_sz()?;
let mut read_len = CBORReadLen::new(len);
read_len.read_elems(2)?;
read_len.finish()?;
(|| -> Result<_, DeserializeError> {
let input = TransactionInput::deserialize(raw)
.map_err(|e: DeserializeError| e.annotate("input"))?;
let output = TransactionOutput::deserialize(raw)
.map_err(|e: DeserializeError| e.annotate("output"))?;
match len {
cbor_event::LenSz::Len(_, _) => (),
cbor_event::LenSz::Indefinite => match raw.special()? {
cbor_event::Special::Break => (),
_ => return Err(DeserializeFailure::EndingBreakMissing.into()),
},
}
Ok(Self { input, output })
})()
.map_err(|e| e.annotate("TransactionUnspentOutput"))
}
}
#[derive(Clone, Default, Debug)]
struct WitnessBuilders {
pub witness_set_builder: TransactionWitnessSetBuilder,
pub fake_required_witnesses: RequiredWitnessSet,
pub redeemer_set_builder: RedeemerSetBuilder,
}
impl WitnessBuilders {
fn merge_data(
&self,
include_fake: bool,
) -> Result<TransactionWitnessSetBuilder, WitnessBuilderError> {
let redeemers = self.redeemer_set_builder.build(true)?;
let mut witness_set_clone = self.witness_set_builder.clone();
redeemers
.into_iter()
.for_each(|r| witness_set_clone.add_redeemer(r));
if include_fake {
merge_fake_witness(&mut witness_set_clone, &self.fake_required_witnesses);
let own_requirements = witness_set_clone.required_wits.clone();
merge_fake_witness(&mut witness_set_clone, &own_requirements);
}
Ok(witness_set_clone)
}
pub fn build_fake(&self) -> Result<TransactionWitnessSet, WitnessBuilderError> {
self.merge_data(true).map(|wit_builder| wit_builder.build())
}
pub fn build_unchecked(&self) -> Result<TransactionWitnessSetBuilder, WitnessBuilderError> {
self.merge_data(false)
}
}
fn fake_full_tx(
tx_builder: &TransactionBuilder,
body: TransactionBody,
) -> Result<Transaction, TxBuilderError> {
Ok(Transaction::new(
body,
tx_builder.witness_builders.build_fake()?,
true,
tx_builder.auxiliary_data.clone(),
))
}
#[derive(Debug, Copy, Clone)]
pub enum TxBuilderConfigField {
FeeAlgo,
PoolDeposit,
KeyDeposit,
MaxValueSize,
MaxTxSize,
CoinsPerUtxoBytes,
ExUnitPrices,
CollateralPercentage,
MaxCollateralInputs,
}
#[derive(Debug, thiserror::Error)]
pub enum TxBuilderError {
#[error("Witness build failed: {0}")]
WitnessBuildFailed(#[from] WitnessBuilderError),
#[error("Redeem build failed: {0}")]
RedeemerBuildFailed(#[from] RedeemerBuilderError),
#[error("Output build failed: {0}")]
OutputBuildFailed(#[from] OutputBuilderError),
#[error("Arithmetic: {0}")]
Arithmetic(#[from] ArithmeticError),
#[error("Asset arithmetic: {0}")]
AssetArithmetic(#[from] AssetArithmeticError),
#[error("Uninitialized field: {0:?}")]
UninitializedField(TxBuilderConfigField),
#[error(
"Multiasset values not supported by RandomImprove. Please use RandomImproveMultiAsset"
)]
RandomImproveCantContainMultiasset,
#[error("UTxO Balance Insufficient")]
UTxOBalanceInsufficient,
#[error("NFTs too large for change output")]
NFTTooLargeForChange,
#[error("Collateral can only be payment keys (scripts not allowed)")]
CollateralMustBePayment,
#[error("Max collateral input count {0} exceeded")]
MaxCollateralInputExceeded(u32),
#[error("Max value size of {0} exceeded. Found: {1}")]
MaxValueSizeExceeded(u32, usize),
#[error("Max transaction size of {0} exceeded. Found: {1}")]
MaxTxSizeExceeded(u32, usize),
#[error("Value {0} less tan the minimum UTXO value {1}")]
ValueBelowMinUTXOValue(u64, u64),
#[error("Fee not specified")]
FeeNotSpecified,
#[error("Reference Script hash {0} not found in reference script witness set {1:?}")]
RefScriptNotFound(ScriptHash, BTreeSet<ScriptHash>),
#[error("Not enough ADA leftover to include non-ADA assets in a change address")]
InsufficientADAForAssets,
#[error("Missing input or output (possibly some native asset)")]
MissingInputOrOutput,
#[error("Cannot use collateral return without also having collateral input")]
CollateralReturnRequiresCollateralInput,
#[error("ScriptDatumHash failed: {0}")]
ScriptDatumHashFailed(#[from] ScriptDataHashError),
#[error("Duplicate Mint Asset: {0:?}:{1:?}")]
DuplicateMint(PolicyId, AssetName),
}
fn min_fee(tx_builder: &TransactionBuilder) -> Result<Coin, TxBuilderError> {
let full_tx = fake_full_tx(tx_builder, tx_builder.build_body()?)?;
crate::fees::min_no_script_fee(&full_tx, &tx_builder.config.fee_algo).map_err(Into::into)
}
fn min_fee_with_exunits(tx_builder: &TransactionBuilder) -> Result<Coin, TxBuilderError> {
let full_tx = fake_full_tx(tx_builder, tx_builder.build_body()?)?;
crate::fees::min_fee(
&full_tx,
&tx_builder.config.fee_algo,
&tx_builder.config.ex_unit_prices,
)
.map_err(Into::into)
}
#[wasm_bindgen]
pub enum CoinSelectionStrategyCIP2 {
LargestFirst,
RandomImprove,
LargestFirstMultiAsset,
RandomImproveMultiAsset,
}
#[derive(Clone, Debug)]
pub struct TransactionBuilderConfig {
fee_algo: LinearFee,
pool_deposit: u64, key_deposit: u64, max_value_size: u32, max_tx_size: u32, coins_per_utxo_byte: Coin, ex_unit_prices: ExUnitPrices, cost_models: CostModels, _collateral_percentage: u32, max_collateral_inputs: u32, prefer_pure_change: bool,
}
#[derive(Clone, Debug, Default)]
pub struct TransactionBuilderConfigBuilder {
fee_algo: Option<LinearFee>,
pool_deposit: Option<u64>, key_deposit: Option<u64>, max_value_size: Option<u32>, max_tx_size: Option<u32>, coins_per_utxo_byte: Option<Coin>, ex_unit_prices: Option<ExUnitPrices>, cost_models: Option<CostModels>, collateral_percentage: Option<u32>, max_collateral_inputs: Option<u32>, prefer_pure_change: bool,
}
impl TransactionBuilderConfigBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn fee_algo(mut self, fee_algo: LinearFee) -> Self {
self.fee_algo = Some(fee_algo);
self
}
pub fn coins_per_utxo_byte(mut self, coins_per_utxo_byte: Coin) -> Self {
self.coins_per_utxo_byte = Some(coins_per_utxo_byte);
self
}
pub fn pool_deposit(mut self, pool_deposit: u64) -> Self {
self.pool_deposit = Some(pool_deposit);
self
}
pub fn key_deposit(mut self, key_deposit: u64) -> Self {
self.key_deposit = Some(key_deposit);
self
}
pub fn max_value_size(mut self, max_value_size: u32) -> Self {
self.max_value_size = Some(max_value_size);
self
}
pub fn max_tx_size(mut self, max_tx_size: u32) -> Self {
self.max_tx_size = Some(max_tx_size);
self
}
pub fn prefer_pure_change(mut self, prefer_pure_change: bool) -> Self {
self.prefer_pure_change = prefer_pure_change;
self
}
pub fn ex_unit_prices(mut self, ex_unit_prices: ExUnitPrices) -> Self {
self.ex_unit_prices = Some(ex_unit_prices);
self
}
pub fn cost_models(mut self, cost_models: CostModels) -> Self {
self.cost_models = Some(cost_models);
self
}
pub fn collateral_percentage(mut self, collateral_percentage: u32) -> Self {
self.collateral_percentage = Some(collateral_percentage);
self
}
pub fn max_collateral_inputs(mut self, max_collateral_inputs: u32) -> Self {
self.max_collateral_inputs = Some(max_collateral_inputs);
self
}
pub fn build(self) -> Result<TransactionBuilderConfig, TxBuilderError> {
Ok(TransactionBuilderConfig {
fee_algo: self.fee_algo.ok_or(TxBuilderError::UninitializedField(
TxBuilderConfigField::FeeAlgo,
))?,
pool_deposit: self.pool_deposit.ok_or(TxBuilderError::UninitializedField(
TxBuilderConfigField::PoolDeposit,
))?,
key_deposit: self.key_deposit.ok_or(TxBuilderError::UninitializedField(
TxBuilderConfigField::KeyDeposit,
))?,
max_value_size: self
.max_value_size
.ok_or(TxBuilderError::UninitializedField(
TxBuilderConfigField::MaxValueSize,
))?,
max_tx_size: self.max_tx_size.ok_or(TxBuilderError::UninitializedField(
TxBuilderConfigField::MaxTxSize,
))?,
coins_per_utxo_byte: self.coins_per_utxo_byte.ok_or(
TxBuilderError::UninitializedField(TxBuilderConfigField::CoinsPerUtxoBytes),
)?,
ex_unit_prices: self
.ex_unit_prices
.ok_or(TxBuilderError::UninitializedField(
TxBuilderConfigField::ExUnitPrices,
))?,
cost_models: if self.cost_models.is_some() {
self.cost_models.unwrap()
} else {
CostModels::new()
},
_collateral_percentage: self.collateral_percentage.ok_or(
TxBuilderError::UninitializedField(TxBuilderConfigField::CollateralPercentage),
)?,
max_collateral_inputs: self.max_collateral_inputs.ok_or(
TxBuilderError::UninitializedField(TxBuilderConfigField::MaxCollateralInputs),
)?,
prefer_pure_change: self.prefer_pure_change,
})
}
}
#[derive(Clone, Debug)]
pub struct TransactionBuilder {
config: TransactionBuilderConfig,
inputs: Vec<TransactionUnspentOutput>,
outputs: Vec<TransactionOutput>,
fee: Option<Coin>,
ttl: Option<Slot>, certs: Option<Vec<Certificate>>,
withdrawals: Option<Withdrawals>,
auxiliary_data: Option<AuxiliaryData>,
validity_start_interval: Option<Slot>,
mint: Option<Mint>,
collateral: Option<Vec<TransactionUnspentOutput>>,
required_signers: Option<BTreeSet<Ed25519KeyHash>>,
network_id: Option<NetworkId>,
witness_builders: WitnessBuilders,
utxos: Vec<InputBuilderResult>,
collateral_return: Option<TransactionOutput>,
reference_inputs: Option<Vec<TransactionUnspentOutput>>,
}
impl TransactionBuilder {
pub fn select_utxos(
&mut self,
strategy: CoinSelectionStrategyCIP2,
) -> Result<(), TxBuilderError> {
let available_inputs = self.utxos.clone();
let mut input_total = self.get_total_input()?;
let mut output_total = self
.get_total_output()?
.checked_add(&Value::from(self.min_fee(false)?))?;
match strategy {
CoinSelectionStrategyCIP2::LargestFirst => {
self.cip2_largest_first_by(
&available_inputs,
&mut (0..available_inputs.len()).collect(),
&mut input_total,
&mut output_total,
|value| Some(value.coin),
)?;
}
CoinSelectionStrategyCIP2::RandomImprove => {
if self
.outputs
.iter()
.any(|output| output.amount().has_multiassets())
{
return Err(TxBuilderError::RandomImproveCantContainMultiasset);
}
let mut rng = rand::thread_rng();
let mut available_indices =
(0..available_inputs.len()).collect::<BTreeSet<usize>>();
self.cip2_random_improve_by(
&available_inputs,
&mut available_indices,
&mut input_total,
&mut output_total,
|value| Some(value.coin),
&mut rng,
)?;
while input_total.coin < output_total.coin {
if available_indices.is_empty() {
return Err(TxBuilderError::UTxOBalanceInsufficient);
}
let i = *available_indices
.iter()
.nth(rng.gen_range(0..available_indices.len()))
.unwrap();
available_indices.remove(&i);
let input = &available_inputs[i];
let input_fee = self.fee_for_input(input)?;
self.add_input(input.clone()).unwrap();
input_total = input_total.checked_add(input.utxo_info.amount())?;
output_total = output_total.checked_add(&Value::from(input_fee))?;
}
}
CoinSelectionStrategyCIP2::LargestFirstMultiAsset => {
let mut available_indices = (0..available_inputs.len()).collect::<Vec<usize>>();
for (policy_id, assets) in output_total.multiasset.clone().iter() {
for (asset_name, _) in assets.iter() {
self.cip2_largest_first_by(
&available_inputs,
&mut available_indices,
&mut input_total,
&mut output_total,
|value| value.multiasset.get(policy_id, asset_name),
)?;
}
}
self.cip2_largest_first_by(
&available_inputs,
&mut available_indices,
&mut input_total,
&mut output_total,
|value| Some(value.coin),
)?;
}
CoinSelectionStrategyCIP2::RandomImproveMultiAsset => {
let mut rng = rand::thread_rng();
let mut available_indices =
(0..available_inputs.len()).collect::<BTreeSet<usize>>();
for (policy_id, assets) in output_total.multiasset.clone().iter() {
for (asset_name, _) in assets.iter() {
self.cip2_random_improve_by(
&available_inputs,
&mut available_indices,
&mut input_total,
&mut output_total,
|value| value.multiasset.get(policy_id, asset_name),
&mut rng,
)?;
}
}
self.cip2_random_improve_by(
&available_inputs,
&mut available_indices,
&mut input_total,
&mut output_total,
|value| Some(value.coin),
&mut rng,
)?;
while input_total.coin < output_total.coin {
if available_indices.is_empty() {
return Err(TxBuilderError::UTxOBalanceInsufficient);
}
let i = *available_indices
.iter()
.nth(rng.gen_range(0..available_indices.len()))
.unwrap();
available_indices.remove(&i);
let input = &available_inputs[i];
let input_fee = self.fee_for_input(input)?;
self.add_input(input.clone()).unwrap();
input_total = input_total.checked_add(input.utxo_info.amount())?;
output_total = output_total.checked_add(&Value::from(input_fee))?;
}
}
}
Ok(())
}
fn cip2_largest_first_by<F>(
&mut self,
available_inputs: &[InputBuilderResult],
available_indices: &mut Vec<usize>,
input_total: &mut Value,
output_total: &mut Value,
by: F,
) -> Result<(), TxBuilderError>
where
F: Fn(&Value) -> Option<u64>,
{
let mut relevant_indices = available_indices.clone();
relevant_indices.retain(|i| by(available_inputs[*i].utxo_info.amount()).is_some());
relevant_indices
.sort_by_key(|i| by(available_inputs[*i].utxo_info.amount()).expect("filtered above"));
for i in relevant_indices.iter().rev() {
if by(input_total).unwrap_or_else(u64::zero)
>= by(output_total).expect("do not call on asset types that aren't in the output")
{
break;
}
let input = &available_inputs[*i];
let input_fee = self.fee_for_input(input)?;
self.add_input(input.clone()).unwrap();
*input_total = input_total.checked_add(input.utxo_info.amount())?;
*output_total = output_total.checked_add(&Value::from(input_fee))?;
available_indices.swap_remove(available_indices.iter().position(|j| i == j).unwrap());
}
if by(input_total).unwrap_or_else(u64::zero)
< by(output_total).expect("do not call on asset types that aren't in the output")
{
return Err(TxBuilderError::UTxOBalanceInsufficient);
}
Ok(())
}
fn cip2_random_improve_by<F, R: Rng + ?Sized>(
&mut self,
available_inputs: &[InputBuilderResult],
available_indices: &mut BTreeSet<usize>,
input_total: &mut Value,
output_total: &mut Value,
by: F,
rng: &mut R,
) -> Result<(), TxBuilderError>
where
F: Fn(&Value) -> Option<u64>,
{
let mut relevant_indices = available_indices
.iter()
.filter(|i| by(available_inputs[**i].utxo_info.amount()).is_some())
.cloned()
.collect::<Vec<usize>>();
let mut associated_indices: HashMap<TransactionOutput, Vec<usize>> = HashMap::new();
let mut outputs = self
.outputs
.iter()
.filter(|output| by(output.amount()).is_some())
.cloned()
.collect::<Vec<TransactionOutput>>();
outputs.sort_by_key(|output| by(output.amount()).expect("filtered above"));
for output in outputs.iter().rev() {
let mut added = u64::zero();
let needed = by(output.amount()).unwrap();
while added < needed {
if relevant_indices.is_empty() {
return Err(TxBuilderError::UTxOBalanceInsufficient);
}
let random_index = rng.gen_range(0..relevant_indices.len());
let i = relevant_indices.swap_remove(random_index);
available_indices.remove(&i);
let input = &available_inputs[i];
added = added
.checked_add(
by(input.utxo_info.amount())
.expect("do not call on asset types that aren't in the output"),
)
.ok_or(ArithmeticError::IntegerOverflow)?;
associated_indices
.entry(output.clone())
.or_default()
.push(i);
}
}
if !relevant_indices.is_empty() {
for output in outputs.iter_mut() {
let associated = associated_indices.get_mut(output).unwrap();
for i in associated.iter_mut() {
let random_index = rng.gen_range(0..relevant_indices.len());
let j: &mut usize = relevant_indices.get_mut(random_index).unwrap();
let should_improve = {
let input = &available_inputs[*i];
let new_input = &available_inputs[*j];
let cur = input.utxo_info.amount().coin;
let new = new_input.utxo_info.amount().coin;
let min = output.amount().coin;
let ideal = 2 * min;
let max = 3 * min;
let move_closer = (ideal as i128 - new as i128).abs()
< (ideal as i128 - cur as i128).abs();
let not_exceed_max = new < max;
move_closer && not_exceed_max
};
if should_improve {
available_indices.insert(*i);
available_indices.remove(j);
std::mem::swap(i, j);
}
}
}
}
for output in outputs.iter() {
for i in associated_indices.get(output).unwrap().iter() {
let input = &available_inputs[*i];
let input_fee = self.fee_for_input(input)?;
self.add_input(input.clone()).unwrap();
*input_total = input_total.checked_add(input.utxo_info.amount())?;
*output_total = output_total.checked_add(&Value::from(input_fee))?;
}
}
Ok(())
}
pub fn add_input(&mut self, result: InputBuilderResult) -> Result<(), TxBuilderError> {
if let Some(script_ref) = result.utxo_info.script_ref() {
self.witness_builders
.witness_set_builder
.required_wits
.script_refs
.insert(script_ref.hash());
}
self.witness_builders
.redeemer_set_builder
.add_spend(&result);
self.witness_builders
.witness_set_builder
.add_required_wits(result.required_wits);
self.inputs.push(TransactionUnspentOutput {
input: result.input,
output: result.utxo_info,
});
if let Some(data) = result.aggregate_witness {
self.witness_builders
.witness_set_builder
.add_input_aggregate_real_witness_data(&data);
self.witness_builders
.fake_required_witnesses
.add_input_aggregate_fake_witness_data(&data);
if let InputAggregateWitnessData::PlutusScript(script_witness, required_signers, _) =
data
{
required_signers
.into_iter()
.for_each(|signer| self.add_required_signer(signer));
match &script_witness.script {
PlutusScriptWitness::Ref(ref_script) => {
if self
.witness_builders
.witness_set_builder
.required_wits
.script_refs
.get(ref_script)
.is_none()
{
Err(TxBuilderError::RefScriptNotFound(
*ref_script,
self.witness_builders
.witness_set_builder
.required_wits
.script_refs
.clone(),
))
} else {
Ok(())
}
}
_ => Ok(()),
}
.unwrap();
}
}
Ok(())
}
pub fn add_utxo(&mut self, result: InputBuilderResult) {
self.utxos.push(result);
}
pub fn fee_for_input(&self, result: &InputBuilderResult) -> Result<Coin, TxBuilderError> {
let mut self_copy = self.clone();
self_copy.set_fee(0);
let fee_before = min_fee(&self_copy)?;
self_copy.add_input(result.clone()).unwrap();
let fee_after = min_fee(&self_copy)?;
fee_after
.checked_sub(fee_before)
.ok_or_else(|| ArithmeticError::IntegerOverflow.into())
}
pub fn add_reference_input(&mut self, utxo: TransactionUnspentOutput) {
let reference_inputs = match self.reference_inputs.as_mut() {
None => {
self.reference_inputs = Some(Vec::<TransactionUnspentOutput>::new());
self.reference_inputs.as_mut().unwrap()
}
Some(inputs) => inputs,
};
if let Some(script_ref) = utxo.output.script_ref() {
self.witness_builders
.witness_set_builder
.required_wits
.script_refs
.insert(script_ref.hash());
}
reference_inputs.push(utxo);
}
pub fn add_output(
&mut self,
builder_result: SingleOutputBuilderResult,
) -> Result<(), TxBuilderError> {
let output = builder_result.output;
let value_size = output.amount().to_cbor_bytes().len();
if value_size > self.config.max_value_size as usize {
return Err(TxBuilderError::MaxValueSizeExceeded(
self.config.max_value_size,
value_size,
));
}
let min_ada = min_ada_required(&output, self.config.coins_per_utxo_byte)?;
if output.amount().coin < min_ada {
Err(TxBuilderError::ValueBelowMinUTXOValue(
output.amount().coin,
min_ada,
))
} else {
if let Some(datum) = builder_result.communication_datum {
self.witness_builders
.witness_set_builder
.add_plutus_datum(datum);
}
self.outputs.push(output);
Ok(())
}
}
pub fn fee_for_output(
&self,
builder: &SingleOutputBuilderResult,
) -> Result<Coin, TxBuilderError> {
let mut self_copy = self.clone();
self_copy.set_fee(0);
let fee_before = min_fee(&self_copy)?;
self_copy.add_output(builder.clone())?;
let fee_after = min_fee(&self_copy)?;
fee_after
.checked_sub(fee_before)
.ok_or_else(|| ArithmeticError::IntegerOverflow.into())
}
pub fn set_fee(&mut self, fee: Coin) {
self.fee = Some(fee)
}
pub fn set_ttl(&mut self, ttl: Slot) {
self.ttl = Some(ttl)
}
pub fn set_validity_start_interval(&mut self, validity_start_interval: Slot) {
self.validity_start_interval = Some(validity_start_interval)
}
pub fn add_cert(&mut self, result: CertificateBuilderResult) {
self.witness_builders.redeemer_set_builder.add_cert(&result);
if self.certs.is_none() {
self.certs = Some(Vec::new());
}
self.certs.as_mut().unwrap().push(result.cert);
if let Some(data) = result.aggregate_witness {
self.witness_builders
.witness_set_builder
.add_input_aggregate_real_witness_data(&data);
self.witness_builders
.fake_required_witnesses
.add_input_aggregate_fake_witness_data(&data);
if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
required_signers
.into_iter()
.for_each(|signer| self.add_required_signer(signer));
}
}
self.witness_builders
.witness_set_builder
.add_required_wits(result.required_wits);
}
pub fn get_withdrawals(&self) -> Option<Withdrawals> {
self.withdrawals.clone()
}
pub fn add_withdrawal(&mut self, result: WithdrawalBuilderResult) {
self.witness_builders
.redeemer_set_builder
.add_reward(&result);
if self.withdrawals.is_none() {
self.withdrawals = Some(OrderedHashMap::default());
}
self.withdrawals
.as_mut()
.unwrap()
.insert(result.address, result.amount);
if let Some(data) = result.aggregate_witness {
self.witness_builders
.witness_set_builder
.add_input_aggregate_real_witness_data(&data);
self.witness_builders
.fake_required_witnesses
.add_input_aggregate_fake_witness_data(&data);
if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
required_signers
.into_iter()
.for_each(|signer| self.add_required_signer(signer));
}
}
self.witness_builders
.witness_set_builder
.add_required_wits(result.required_wits);
}
pub fn get_auxiliary_data(&self) -> Option<AuxiliaryData> {
self.auxiliary_data.clone()
}
pub fn set_auxiliary_data(&mut self, new_aux_data: AuxiliaryData) {
self.auxiliary_data = Some(new_aux_data)
}
pub fn add_auxiliary_data(&mut self, new_aux_data: AuxiliaryData) {
match self.auxiliary_data.as_mut() {
Some(data) => {
data.add(new_aux_data);
}
None => {
self.auxiliary_data = Some(new_aux_data);
}
}
}
pub fn add_mint(&mut self, result: MintBuilderResult) -> Result<(), TxBuilderError> {
self.witness_builders.redeemer_set_builder.add_mint(&result);
self.witness_builders
.witness_set_builder
.add_required_wits(result.required_wits.clone());
let mut mint = self.mint.take().unwrap_or_default();
let combined_assets = mint.deref_mut().entry(result.policy_id).or_default();
for (asset_name, asset_value) in result.assets.iter() {
if combined_assets
.insert(asset_name.clone(), *asset_value)
.is_some()
{
return Err(TxBuilderError::DuplicateMint(
result.policy_id,
asset_name.clone(),
));
}
}
self.mint = Some(mint);
if let Some(data) = result.aggregate_witness {
self.witness_builders
.witness_set_builder
.add_input_aggregate_real_witness_data(&data);
self.witness_builders
.fake_required_witnesses
.add_input_aggregate_fake_witness_data(&data);
if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
required_signers
.into_iter()
.for_each(|signer| self.add_required_signer(signer));
}
}
Ok(())
}
pub fn get_mint(&self) -> Option<Mint> {
self.mint.clone()
}
pub fn new(cfg: TransactionBuilderConfig) -> Self {
Self {
config: cfg,
inputs: Vec::new(),
outputs: Vec::new(),
fee: None,
ttl: None,
certs: None,
withdrawals: None,
auxiliary_data: None,
validity_start_interval: None,
mint: None,
collateral: None,
required_signers: None,
network_id: None,
witness_builders: WitnessBuilders::default(),
utxos: Vec::new(),
collateral_return: None,
reference_inputs: None,
}
}
pub fn add_collateral(&mut self, result: InputBuilderResult) -> Result<(), TxBuilderError> {
if result.aggregate_witness.is_some() {
return Err(TxBuilderError::CollateralMustBePayment);
}
let new_input = TransactionUnspentOutput {
input: result.input,
output: result.utxo_info,
};
match &mut self.collateral {
None => self.collateral = Some(vec![new_input]),
Some(collateral) => {
if self.config.max_collateral_inputs <= collateral.len().try_into().unwrap() {
return Err(TxBuilderError::MaxCollateralInputExceeded(
self.config.max_collateral_inputs,
));
}
collateral.push(new_input);
}
}
if let Some(data) = result.aggregate_witness {
self.witness_builders
.witness_set_builder
.add_input_aggregate_real_witness_data(&data);
self.witness_builders
.fake_required_witnesses
.add_input_aggregate_fake_witness_data(&data);
if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
required_signers
.iter()
.for_each(|signer| self.add_required_signer(*signer));
}
}
self.witness_builders
.witness_set_builder
.add_required_wits(result.required_wits);
Ok(())
}
pub fn add_required_signer(&mut self, hash: Ed25519KeyHash) {
let mut set = RequiredWitnessSet::new();
set.add_vkey_key_hash(hash);
self.witness_builders
.witness_set_builder
.add_required_wits(set);
match &mut self.required_signers {
None => {
let mut required_signers = BTreeSet::new();
required_signers.insert(hash);
self.required_signers = Some(required_signers);
}
Some(required_signers) => {
required_signers.insert(hash);
}
}
}
pub fn set_network_id(&mut self, network_id: NetworkId) {
self.network_id = Some(network_id)
}
pub fn network_id(&self) -> Option<NetworkId> {
self.network_id
}
pub fn get_explicit_input(&self) -> Result<Value, TxBuilderError> {
self.inputs
.iter()
.try_fold(Value::zero(), |acc, tx_builder_input| {
acc.checked_add(tx_builder_input.output.amount())
})
.map_err(Into::into)
}
pub fn get_implicit_input(&self) -> Result<Value, TxBuilderError> {
internal_get_implicit_input(
self.withdrawals.as_ref(),
self.certs.as_deref(),
self.config.pool_deposit,
self.config.key_deposit,
)
.map_err(Into::into)
}
fn get_mint_as_values(&self) -> (Value, Value) {
self.mint
.as_ref()
.map(|m| {
(
Value::from(m.as_positive_multiasset()),
Value::from(m.as_negative_multiasset()),
)
})
.unwrap_or((Value::zero(), Value::zero()))
}
pub fn get_total_input(&self) -> Result<Value, TxBuilderError> {
let (mint_value, _) = self.get_mint_as_values();
self.get_explicit_input()?
.checked_add(&self.get_implicit_input()?)
.and_then(|x| x.checked_add(&mint_value))
.map_err(Into::into)
}
pub fn get_total_output(&self) -> Result<Value, TxBuilderError> {
let (_, burn_value) = self.get_mint_as_values();
self.get_explicit_output()?
.checked_add(&Value::from(self.get_deposit()?))
.and_then(|x| x.checked_add(&burn_value))
.map_err(Into::into)
}
pub fn get_explicit_output(&self) -> Result<Value, TxBuilderError> {
self.outputs
.iter()
.try_fold(Value::from(0), |acc, output| {
acc.checked_add(output.amount())
})
.map_err(Into::into)
}
pub fn get_deposit(&self) -> Result<Coin, TxBuilderError> {
internal_get_deposit(
self.certs.as_deref(),
self.config.pool_deposit,
self.config.key_deposit,
)
.map_err(Into::into)
}
pub fn get_fee_if_set(&self) -> Option<Coin> {
self.fee
}
pub fn set_collateral_return(&mut self, output: TransactionOutput) {
self.collateral_return = Some(output);
}
fn calc_collateral_total(&self) -> Result<Option<Coin>, TxBuilderError> {
match self.collateral_return.as_ref() {
None => Ok(None),
Some(coll_ret) => {
let input_sum = match self.collateral.as_ref() {
Some(collateral) => collateral.iter().try_fold(Coin::zero(), |acc, next| {
acc.checked_add(next.output.amount().coin)
.ok_or(ArithmeticError::IntegerOverflow)
}),
None => return Err(TxBuilderError::CollateralReturnRequiresCollateralInput),
}?;
let coll_tot = input_sum
.checked_sub(coll_ret.amount().coin)
.ok_or(ArithmeticError::IntegerOverflow)?;
Ok(Some(coll_tot))
}
}
}
fn build_and_size(&self) -> Result<(TransactionBody, usize), TxBuilderError> {
let fee = self.fee.ok_or(TxBuilderError::FeeNotSpecified)?;
let redeemers = self.witness_builders.redeemer_set_builder.build(true)?;
let has_dummy_exunit = redeemers
.iter()
.any(|redeemer| redeemer.ex_units == ExUnits::dummy());
let script_data_hash = match self.witness_builders.redeemer_set_builder.is_empty() {
true => None,
false => match has_dummy_exunit {
true => Some(ScriptDataHash::from([0u8; ScriptDataHash::BYTE_COUNT])),
false => {
let scripts = self.witness_builders.witness_set_builder.scripts.values();
let mut languages =
scripts.fold(BTreeSet::<Language>::new(), |mut langs, script| {
if let Some(lang) = script.language() {
langs.insert(lang);
}
langs
});
if let Some(reference_inputs) = self.reference_inputs.as_ref() {
reference_inputs
.iter()
.fold(&mut languages, |langs, input| {
if let Some(script_ref) = &input.output.script_ref() {
if let Some(lang) = script_ref.language() {
langs.insert(lang);
}
}
langs
});
};
self.inputs
.clone()
.iter()
.fold(&mut languages, |langs, input| {
if let Some(script_ref) = &input.output.script_ref() {
if let Some(lang) = script_ref.language() {
langs.insert(lang);
}
}
langs
});
calc_script_data_hash(
&redeemers,
&self.witness_builders.witness_set_builder.get_plutus_datum(),
&self.config.cost_models,
&languages.iter().copied().collect::<Vec<_>>(),
None,
)?
}
},
};
let mut built = TransactionBody {
inputs: self
.inputs
.iter()
.map(|tx_builder_input| tx_builder_input.input.clone())
.collect(),
outputs: self.outputs.clone(),
fee,
ttl: self.ttl,
certs: self.certs.clone(),
withdrawals: self.withdrawals.clone(),
auxiliary_data_hash: self.auxiliary_data.as_ref().map(hash_auxiliary_data),
validity_interval_start: self.validity_start_interval,
mint: self.mint.clone(),
script_data_hash,
collateral_inputs: self
.collateral
.as_ref()
.map(|collateral| collateral.iter().map(|c| c.input.clone()).collect()),
required_signers: self
.required_signers
.as_ref()
.map(|set| set.iter().cloned().collect()),
network_id: self.network_id,
collateral_return: self.collateral_return.clone(),
total_collateral: self.calc_collateral_total()?,
reference_inputs: self
.reference_inputs
.as_ref()
.map(|inputs| inputs.iter().map(|utxo| utxo.input.clone()).collect()),
voting_procedures: None,
proposal_procedures: None,
current_treasury_value: None,
donation: None,
encodings: None,
};
{
built
.inputs
.sort_by(|a, b| match a.transaction_id.cmp(&b.transaction_id) {
std::cmp::Ordering::Equal => a.index.cmp(&b.index),
rest => rest,
});
if let Some(withdrawals) = built.withdrawals {
let mut sorted_keys = withdrawals.keys().collect::<Vec<_>>();
sorted_keys.sort();
let mut sorted_linked_hashmap = Withdrawals::new();
sorted_linked_hashmap =
sorted_keys
.iter()
.fold(sorted_linked_hashmap, |mut accum, key| {
accum.insert((*key).clone(), *withdrawals.get(key).unwrap());
accum
});
built.withdrawals = Some(sorted_linked_hashmap)
};
}
let full_tx = fake_full_tx(self, built)?;
let full_tx_size = full_tx.to_cbor_bytes().len();
Ok((full_tx.body, full_tx_size))
}
pub fn full_size(&self) -> Result<usize, TxBuilderError> {
self.build_and_size().map(|r| r.1)
}
pub fn output_sizes(&self) -> Vec<usize> {
self.outputs
.iter()
.map(|o| o.to_cbor_bytes().len())
.collect()
}
fn build_body(&self) -> Result<TransactionBody, TxBuilderError> {
let (body, full_tx_size) = self.build_and_size()?;
if full_tx_size > self.config.max_tx_size as usize {
Err(TxBuilderError::MaxTxSizeExceeded(
self.config.max_tx_size,
full_tx_size,
))
} else {
Ok(body)
}
}
pub fn build_for_evaluation(
&self,
algo: ChangeSelectionAlgo,
change_address: &Address,
) -> Result<TxRedeemerBuilder, TxBuilderError> {
let mut tx = self.clone();
choose_change_selection_algo(algo)(&mut tx, change_address, false)?;
Ok(TxRedeemerBuilder {
draft_body: tx.build_body()?,
witness_builders: tx.witness_builders.clone(),
auxiliary_data: tx.auxiliary_data.clone(),
})
}
pub fn build(
&mut self,
algo: ChangeSelectionAlgo,
change_address: &Address,
) -> Result<SignedTxBuilder, TxBuilderError> {
choose_change_selection_algo(algo)(self, change_address, true)?;
Ok(SignedTxBuilder {
body: self.build_body()?,
witness_set: self.witness_builders.build_unchecked()?,
is_valid: true,
auxiliary_data: self.auxiliary_data.clone(),
})
}
pub fn set_exunits(&mut self, redeemer: RedeemerWitnessKey, ex_units: ExUnits) {
self.witness_builders
.redeemer_set_builder
.update_ex_units(redeemer, ex_units);
}
pub fn min_fee(&self, script_calulation: bool) -> Result<Coin, TxBuilderError> {
if !script_calulation {
let mut self_copy = self.clone();
self_copy.fee = Some(u64::MAX);
min_fee(&self_copy)
} else {
let mut self_copy = self.clone();
self_copy.fee = Some(u64::MAX);
min_fee_with_exunits(&self_copy)
}
}
}
#[derive(Debug, Clone)]
pub struct TxRedeemerBuilder {
draft_body: TransactionBody,
witness_builders: WitnessBuilders,
auxiliary_data: Option<AuxiliaryData>,
}
impl TxRedeemerBuilder {
pub fn build(&self) -> Result<Vec<Redeemer>, RedeemerBuilderError> {
self.witness_builders.redeemer_set_builder.build(true)
}
pub fn set_exunits(&mut self, redeemer: RedeemerWitnessKey, ex_units: ExUnits) {
self.witness_builders
.redeemer_set_builder
.update_ex_units(redeemer, ex_units);
}
pub fn draft_body(&self) -> TransactionBody {
self.draft_body.clone()
}
pub fn auxiliary_data(&self) -> Option<AuxiliaryData> {
self.auxiliary_data.clone()
}
pub fn draft_tx(&self) -> Result<Transaction, WitnessBuilderError> {
Ok(Transaction::new(
self.draft_body.clone(),
self.witness_builders.build_fake()?,
true,
self.auxiliary_data.clone(),
))
}
}
#[derive(Debug, Clone)]
pub struct SignedTxBuilder {
body: TransactionBody,
witness_set: TransactionWitnessSetBuilder,
is_valid: bool,
auxiliary_data: Option<AuxiliaryData>,
}
impl SignedTxBuilder {
pub fn new_with_data(
body: TransactionBody,
witness_set: TransactionWitnessSetBuilder,
is_valid: bool,
auxiliary_data: AuxiliaryData,
) -> SignedTxBuilder {
SignedTxBuilder {
body,
witness_set,
is_valid,
auxiliary_data: Some(auxiliary_data),
}
}
pub fn new_without_data(
body: TransactionBody,
witness_set: TransactionWitnessSetBuilder,
is_valid: bool,
) -> SignedTxBuilder {
SignedTxBuilder {
body,
witness_set,
is_valid,
auxiliary_data: None,
}
}
pub fn build_checked(self) -> Result<Transaction, WitnessBuilderError> {
Ok(Transaction::new(
self.body,
self.witness_set.try_build()?,
self.is_valid,
self.auxiliary_data,
))
}
pub fn build_unchecked(self) -> Transaction {
Transaction::new(
self.body,
self.witness_set.build(),
self.is_valid,
self.auxiliary_data,
)
}
pub fn add_vkey(&mut self, vkey: Vkeywitness) {
self.witness_set.add_vkey(vkey);
}
pub fn add_bootstrap(&mut self, bootstrap: BootstrapWitness) {
self.witness_set.add_bootstrap(bootstrap);
}
pub fn body(&self) -> TransactionBody {
self.body.clone()
}
pub fn witness_set(&self) -> TransactionWitnessSetBuilder {
self.witness_set.clone()
}
pub fn is_valid(&self) -> bool {
self.is_valid
}
pub fn auxiliary_data(&self) -> Option<AuxiliaryData> {
self.auxiliary_data.clone()
}
}
#[wasm_bindgen]
pub enum ChangeSelectionAlgo {
Default,
}
pub fn choose_change_selection_algo(
algo: ChangeSelectionAlgo,
) -> fn(&mut TransactionBuilder, &Address, include_exunits: bool) -> Result<bool, TxBuilderError> {
match algo {
ChangeSelectionAlgo::Default => add_change_if_needed,
}
}
pub fn add_change_if_needed(
builder: &mut TransactionBuilder,
address: &Address,
include_exunits: bool,
) -> Result<bool, TxBuilderError> {
let fee = match &builder.fee {
None => builder.min_fee(include_exunits),
Some(_x) => return Ok(false),
}?;
let datum = None;
let script_ref = None;
let communication_datum = None;
let input_total = builder.get_total_input()?;
let output_total = builder.get_total_output()?;
use std::cmp::Ordering;
match &input_total.partial_cmp(&output_total.checked_add(&Value::from(fee))?) {
Some(Ordering::Equal) => {
builder.set_fee(input_total.checked_sub(&output_total)?.coin);
Ok(false)
}
Some(Ordering::Less) => Err(TxBuilderError::UTxOBalanceInsufficient),
Some(Ordering::Greater) => {
let change_estimator = input_total.checked_sub(&output_total)?;
if change_estimator.has_multiassets() {
fn will_adding_asset_make_output_overflow(
output: &TransactionOutput,
current_assets: &OrderedHashMap<AssetName, u64>,
asset_to_add: (PolicyId, AssetName, u64),
max_value_size: u32,
coins_per_utxo_byte: Coin,
) -> bool {
let (policy, asset_name, value) = asset_to_add;
let mut current_assets_clone = current_assets.clone();
current_assets_clone.insert(asset_name, value);
let mut amount_clone = output.amount().clone();
let mut val = Value::from(Coin::zero());
val.multiasset.insert(policy, current_assets_clone);
amount_clone = amount_clone.checked_add(&val).unwrap();
let mut output_clone = output.clone();
output_clone.set_amount(val);
let min_ada = min_ada_required(&output_clone, coins_per_utxo_byte).unwrap();
amount_clone.coin = min_ada;
amount_clone.to_cbor_bytes().len() > max_value_size as usize
}
fn pack_nfts_for_change(
max_value_size: u32,
coins_per_utxo_byte: Coin,
change_address: &Address,
change_estimator: &Value,
datum: Option<DatumOption>,
script_ref: &Option<ScriptRef>,
_communication_datum: &Option<PlutusData>,
) -> Result<Vec<MultiAsset>, TxBuilderError> {
let mut change_assets: Vec<MultiAsset> = Vec::new();
let mut base_coin = Value::from(change_estimator.coin);
let mut output = TransactionOutput::new(
change_address.clone(),
base_coin.clone(),
datum.clone(),
script_ref.clone(),
);
for (policy, assets) in change_estimator.multiasset.iter() {
let mut old_amount = output.amount().clone();
let mut val = Value::from(Coin::zero());
let mut next_nft = MultiAsset::default();
let mut rebuilt_assets = OrderedHashMap::new();
for (asset_name, value) in assets.iter() {
if will_adding_asset_make_output_overflow(
&output,
&rebuilt_assets,
(*policy, asset_name.clone(), *value),
max_value_size,
coins_per_utxo_byte,
) {
next_nft.insert(*policy, rebuilt_assets);
val.multiasset = next_nft;
output.set_amount(output.amount().checked_add(&val)?);
change_assets.push(output.amount().multiasset.clone());
base_coin = Value::from(Coin::zero());
output = TransactionOutput::new(
change_address.clone(),
base_coin.clone(),
datum.clone(),
script_ref.clone(),
);
old_amount = output.amount().clone();
val = Value::from(Coin::zero());
next_nft = MultiAsset::default();
rebuilt_assets = OrderedHashMap::new();
}
rebuilt_assets.insert(asset_name.clone(), *value);
}
next_nft.insert(*policy, rebuilt_assets);
val.multiasset = next_nft;
output.set_amount(output.amount().checked_add(&val)?);
let mut output_copy = output.clone();
output_copy.set_amount(val);
let min_ada = min_ada_required(&output_copy, coins_per_utxo_byte).unwrap();
let mut amount_clone = output.amount().clone();
amount_clone.coin = min_ada;
if amount_clone.to_cbor_bytes().len() > max_value_size as usize {
output.set_amount(old_amount);
break;
}
}
change_assets.push(output.amount().multiasset.clone());
Ok(change_assets)
}
let mut change_left = input_total.checked_sub(&output_total)?;
let mut new_fee = fee;
let minimum_utxo_val = min_ada_required(
&TransactionOutput::new(
address.clone(),
Value::from(1000000),
datum.clone(),
script_ref.clone(),
),
builder.config.coins_per_utxo_byte,
)?;
while let Some(Ordering::Greater) =
change_left.multiasset.partial_cmp(&MultiAsset::default())
{
let nft_changes = pack_nfts_for_change(
builder.config.max_value_size,
builder.config.coins_per_utxo_byte,
address,
&change_left,
datum.clone(),
&script_ref,
&communication_datum,
)?;
if nft_changes.is_empty() {
return Err(TxBuilderError::NFTTooLargeForChange);
}
for nft_change in nft_changes {
let change_output = (TransactionOutputBuilder {
address: Some(address.clone()),
datum: datum.clone(),
communication_datum: communication_datum.clone(),
script_ref: script_ref.clone(),
})
.next()?
.with_asset_and_min_required_coin(
nft_change,
builder.config.coins_per_utxo_byte,
)?
.build()?;
let fee_for_change = builder.fee_for_output(&change_output)?;
new_fee = new_fee
.checked_add(fee_for_change)
.ok_or(ArithmeticError::IntegerOverflow)?;
let change_ada_plus_fee = change_output
.output
.amount()
.coin
.checked_add(new_fee)
.ok_or(ArithmeticError::IntegerOverflow)?;
if change_left.coin < change_ada_plus_fee {
return Err(TxBuilderError::InsufficientADAForAssets);
}
change_left = change_left.checked_sub(change_output.output.amount())?;
builder.add_output(change_output)?;
}
}
change_left = change_left.checked_sub(&Value::from(new_fee))?;
let left_above_minimum = change_left.coin > minimum_utxo_val;
if builder.config.prefer_pure_change && left_above_minimum {
let pure_output = SingleOutputBuilderResult::new(TransactionOutput::new(
address.clone(),
change_left.clone(),
datum.clone(),
script_ref.clone(),
));
let additional_fee = builder.fee_for_output(&pure_output)?;
let potential_pure_value =
change_left.checked_sub(&Value::from(additional_fee))?;
let potential_pure_above_minimum = potential_pure_value.coin > minimum_utxo_val;
if potential_pure_above_minimum {
new_fee = new_fee
.checked_add(additional_fee)
.ok_or(ArithmeticError::IntegerOverflow)?;
change_left = Value::zero();
let change_output = SingleOutputBuilderResult::new(TransactionOutput::new(
address.clone(),
potential_pure_value,
datum,
script_ref,
));
builder.add_output(change_output)?;
}
}
builder.set_fee(new_fee);
if !change_left.is_zero() {
let last_with_remaining = builder
.outputs
.last()
.unwrap()
.amount()
.checked_add(&change_left)?;
builder
.outputs
.last_mut()
.unwrap()
.set_amount(last_with_remaining);
}
Ok(true)
} else {
let min_ada = min_ada_required(
&TransactionOutput::new(
address.clone(),
change_estimator.clone(),
datum.clone(),
script_ref.clone(),
),
builder.config.coins_per_utxo_byte,
)?;
fn burn_extra(
builder: &mut TransactionBuilder,
burn_amount: u64,
) -> Result<bool, TxBuilderError> {
builder.set_fee(burn_amount);
Ok(false) }
match change_estimator.coin >= min_ada {
false => burn_extra(builder, change_estimator.coin),
true => {
let fee_for_change = builder.fee_for_output(
&SingleOutputBuilderResult::new(TransactionOutput::new(
address.clone(),
change_estimator.clone(),
datum.clone(),
script_ref.clone(),
)),
)?;
let new_fee = fee
.checked_add(fee_for_change)
.ok_or(ArithmeticError::IntegerOverflow)?;
match change_estimator.coin
>= min_ada
.checked_add(new_fee)
.ok_or(ArithmeticError::IntegerOverflow)?
{
false => burn_extra(builder, change_estimator.coin),
true => {
builder.set_fee(new_fee);
let change_output =
SingleOutputBuilderResult::new(TransactionOutput::new(
address.clone(),
change_estimator.checked_sub(&Value::from(new_fee))?,
datum,
script_ref,
));
builder.add_output(change_output)?;
Ok(true)
}
}
}
}
}
}
None => Err(TxBuilderError::MissingInputOrOutput),
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use std::ops::Deref;
use cml_core::Int;
use cml_crypto::{
Bip32PrivateKey, Bip32PublicKey, DatumHash, Deserialize, PrivateKey, RawBytesEncoding,
TransactionHash,
};
use crate::address::{Address, BaseAddress, EnterpriseAddress, Pointer, PointerAddress};
use crate::auxdata::{Metadata, MetadatumMap, TransactionMetadatum, TransactionMetadatumLabel};
use crate::builders::witness_builder::{PartialPlutusWitness, PlutusScriptWitness};
use crate::builders::{
input_builder::SingleInputBuilder, mint_builder::SingleMintBuilder,
witness_builder::NativeScriptWitnessInfo,
};
use crate::byron::{AddressContent, ByronAddress};
use crate::certs::StakeCredential;
use crate::crypto::hash::hash_transaction;
use crate::crypto::utils::make_vkey_witness;
use crate::genesis::network_info::{plutus_alonzo_cost_models, NetworkInfo};
use crate::plutus::{PlutusScript, PlutusV1Script, PlutusV2Script, RedeemerTag};
use crate::transaction::NativeScript;
use crate::{Script, SubCoin};
use super::*;
use crate::builders::output_builder::TransactionOutputBuilder;
const MAX_VALUE_SIZE: u32 = 4000;
const MAX_TX_SIZE: u32 = 8000; static COINS_PER_UTXO_BYTE: u64 = 4310;
impl TransactionBuilder {
fn add_change_if_needed_for_tests(
&mut self,
change_address: &Address,
) -> Result<bool, TxBuilderError> {
choose_change_selection_algo(ChangeSelectionAlgo::Default)(self, change_address, false)
}
}
fn genesis_id() -> TransactionHash {
TransactionHash::from([0u8; TransactionHash::BYTE_COUNT])
}
fn root_key_15() -> Bip32PrivateKey {
let entropy = [
0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22,
0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12,
];
Bip32PrivateKey::from_bip39_entropy(&entropy, &[])
}
fn fake_key_hash(x: u8) -> Ed25519KeyHash {
Ed25519KeyHash::from_raw_bytes(&[
x, 239, 181, 120, 142, 135, 19, 200, 68, 223, 211, 43, 46, 145, 222, 30, 48, 159, 239,
255, 213, 85, 248, 39, 204, 158, 225, 100,
])
.unwrap()
}
fn harden(index: u32) -> u32 {
index | 0x80_00_00_00
}
fn byron_address() -> Address {
ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3")
.unwrap()
.to_address()
}
fn create_linear_fee(coefficient: u64, constant: u64) -> LinearFee {
LinearFee::new(coefficient, constant)
}
fn create_default_linear_fee() -> LinearFee {
create_linear_fee(500, 2)
}
fn create_tx_builder_full(
linear_fee: LinearFee,
pool_deposit: u64,
key_deposit: u64,
max_val_size: u32,
coins_per_utxo_byte: u64,
) -> TransactionBuilder {
let cfg = TransactionBuilderConfigBuilder::default()
.fee_algo(linear_fee)
.pool_deposit(pool_deposit)
.key_deposit(key_deposit)
.max_value_size(max_val_size)
.max_tx_size(MAX_TX_SIZE)
.coins_per_utxo_byte(coins_per_utxo_byte)
.ex_unit_prices(ExUnitPrices::new(
SubCoin::new(577, 10000),
SubCoin::new(721, 10000000),
))
.collateral_percentage(150)
.max_collateral_inputs(3)
.cost_models(plutus_alonzo_cost_models())
.build()
.unwrap();
TransactionBuilder::new(cfg)
}
fn create_tx_builder(
linear_fee: LinearFee,
coins_per_utxo_byte: u64,
pool_deposit: u64,
key_deposit: u64,
) -> TransactionBuilder {
create_tx_builder_full(
linear_fee,
pool_deposit,
key_deposit,
MAX_VALUE_SIZE,
coins_per_utxo_byte,
)
}
fn create_realistic_tx_builder() -> TransactionBuilder {
create_tx_builder(
create_linear_fee(44, 155381),
COINS_PER_UTXO_BYTE,
500000000,
2000000,
)
}
fn create_tx_builder_with_fee_and_val_size(
linear_fee: LinearFee,
max_val_size: u32,
) -> TransactionBuilder {
create_tx_builder_full(linear_fee, 1, 1, max_val_size, 1)
}
fn create_tx_builder_with_fee(linear_fee: LinearFee) -> TransactionBuilder {
create_tx_builder(linear_fee, 1, 1, 1)
}
fn create_tx_builder_with_fee_and_pure_change(linear_fee: LinearFee) -> TransactionBuilder {
TransactionBuilder::new(
TransactionBuilderConfigBuilder::default()
.fee_algo(linear_fee)
.pool_deposit(1)
.key_deposit(1)
.max_value_size(MAX_VALUE_SIZE)
.max_tx_size(MAX_TX_SIZE)
.coins_per_utxo_byte(1)
.ex_unit_prices(ExUnitPrices::new(SubCoin::new(0, 0), SubCoin::new(0, 0)))
.collateral_percentage(150)
.max_collateral_inputs(3)
.prefer_pure_change(true)
.build()
.unwrap(),
)
}
fn create_tx_builder_with_key_deposit(deposit: u64) -> TransactionBuilder {
create_tx_builder(create_default_linear_fee(), 1, 1, deposit)
}
fn create_default_tx_builder() -> TransactionBuilder {
create_tx_builder_with_fee(create_default_linear_fee())
}
fn create_account() -> (
(Bip32PublicKey, StakeCredential),
(Bip32PublicKey, StakeCredential),
Address,
) {
let spend = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(0)
.derive(0)
.to_public();
let stake = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(2)
.derive(0)
.to_public();
let spend_cred = StakeCredential::new_pub_key(spend.to_raw_key().hash());
let stake_cred = StakeCredential::new_pub_key(stake.to_raw_key().hash());
let address = BaseAddress::new(
NetworkInfo::testnet().network_id(),
spend_cred.clone(),
stake_cred.clone(),
)
.to_address();
((spend, spend_cred), (stake, stake_cred), address)
}
#[test]
fn build_tx_with_change() {
let mut tx_builder = create_default_tx_builder();
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(222)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1000);
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr);
assert!(added_change.unwrap());
assert_eq!(tx_builder.outputs.len(), 2);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap(),
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
);
assert_eq!(tx_builder.full_size().unwrap(), 285);
assert_eq!(tx_builder.output_sizes(), vec![62, 65]);
let _final_tx = tx_builder.build(ChangeSelectionAlgo::Default, &change_addr);
}
#[test]
fn build_tx_without_change() {
let mut tx_builder = create_default_tx_builder();
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(880_000)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1000);
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr);
assert!(!added_change.unwrap());
assert_eq!(tx_builder.outputs.len(), 1);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap(),
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
);
let _final_tx = tx_builder.build(ChangeSelectionAlgo::Default, &change_addr);
}
#[test]
fn build_tx_with_certs() {
let mut tx_builder = create_tx_builder_with_key_deposit(1_000_000);
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (stake, stake_cred), addr_net_0) = create_account();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0, Value::from(5_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder.set_ttl(1000);
let cert =
SingleCertificateBuilder::new(Certificate::new_stake_registration(stake_cred.clone()))
.payment_key()
.unwrap();
tx_builder.add_cert(cert);
let cert = SingleCertificateBuilder::new(Certificate::new_stake_delegation(
stake_cred.clone(),
stake.to_raw_key().hash(), ))
.payment_key()
.unwrap();
tx_builder.add_cert(cert);
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert_eq!(tx_builder.min_fee(false).unwrap(), 214002);
assert_eq!(tx_builder.get_fee_if_set().unwrap(), 214002);
assert_eq!(tx_builder.get_deposit().unwrap(), 1000000);
assert_eq!(tx_builder.outputs.len(), 1);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap(),
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
.checked_add(&Value::from(tx_builder.get_deposit().unwrap()))
.unwrap()
);
let _final_tx = tx_builder.build(ChangeSelectionAlgo::Default, &change_addr);
}
#[test]
fn build_tx_exact_amount() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(222), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(222)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(0);
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(!added_change);
let final_tx = tx_builder.build_body().unwrap();
assert_eq!(final_tx.outputs.len(), 1);
}
#[test]
fn build_tx_exact_change() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(542), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(222)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(0);
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(added_change);
let final_tx = tx_builder.build_body().unwrap();
assert_eq!(final_tx.outputs.len(), 2);
assert_eq!(final_tx.outputs[1].amount().coin, 320);
}
#[test]
#[should_panic]
fn build_tx_insufficient_deposit() {
let mut tx_builder = create_tx_builder_with_key_deposit(5);
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(5), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(5)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(0);
let cert =
SingleCertificateBuilder::new(Certificate::new_stake_registration(stake_cred.clone()))
.payment_key()
.unwrap();
tx_builder.add_cert(cert);
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
}
#[test]
fn build_tx_with_inputs() {
let mut tx_builder = create_default_tx_builder();
let ((spend, spend_cred), (_, stake_cred), _) = create_account();
let input = {
let address =
EnterpriseAddress::new(NetworkInfo::testnet().network_id(), spend_cred.clone())
.to_address();
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
assert_eq!(tx_builder.fee_for_input(&input).unwrap(), 69500);
tx_builder.add_input(input).unwrap();
let input = {
let address = BaseAddress::new(
NetworkInfo::testnet().network_id(),
spend_cred.clone(),
stake_cred,
)
.to_address();
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
let input = {
let address = PointerAddress::new(
NetworkInfo::testnet().network_id(),
spend_cred,
Pointer::new(0, 0, 0),
)
.to_address();
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
let input = {
let address =
AddressContent::icarus_from_key(spend, NetworkInfo::testnet().protocol_magic())
.to_address()
.to_address();
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
assert_eq!(tx_builder.inputs.len(), 4);
}
#[test]
fn build_tx_with_mint_all_sent() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 1));
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let ((_, spend_cred), (_, stake_cred), _) = create_account();
let input = {
let address =
EnterpriseAddress::new(NetworkInfo::testnet().network_id(), spend_cred.clone())
.to_address();
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(764), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
let addr_net_0 = BaseAddress::new(
NetworkInfo::testnet().network_id(),
spend_cred,
stake_cred.clone(),
)
.to_address();
let (min_script, policy_id) = mint_script_and_policy(0);
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let amount = 1234u64;
let result = SingleMintBuilder::new_single_asset(name.clone(), amount as i64)
.native_script(
min_script,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let mut mass = MultiAsset::new();
mass.set(policy_id, name, amount);
let mut output_amount = Value::from(264);
output_amount.multiasset = mass;
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(added_change);
assert_eq!(tx_builder.outputs.len(), 2);
let change = tx_builder.outputs[1].amount();
assert_eq!(change.coin, 499);
assert!(!change.has_multiassets());
}
#[test]
fn build_tx_with_mint_in_change() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 1));
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let ((_spend, spend_cred), (_, stake_cred), _) = create_account();
let input = {
let address =
EnterpriseAddress::new(NetworkInfo::testnet().network_id(), spend_cred.clone())
.to_address();
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(564), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
let addr_net_0 = BaseAddress::new(
NetworkInfo::testnet().network_id(),
spend_cred,
stake_cred.clone(),
)
.to_address();
let (min_script, policy_id) = mint_script_and_policy(0);
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let amount_minted = 1000i64;
let amount_sent = 500u64;
let result = SingleMintBuilder::new_single_asset(name.clone(), amount_minted)
.native_script(
min_script,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let mut mass = MultiAsset::new();
mass.set(policy_id, name.clone(), amount_sent);
let mut output_amount = Value::from(264);
output_amount.multiasset = mass;
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(added_change);
assert_eq!(tx_builder.outputs.len(), 2);
let change = tx_builder.outputs[1].amount();
assert_eq!(change.coin, 299);
assert!(change.has_multiassets());
assert_eq!(
change.multiasset.get(&policy_id, &name).unwrap() as i128,
amount_minted.checked_sub(amount_sent as i64).unwrap() as i128,
);
}
#[test]
fn build_tx_with_native_assets_change() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 1));
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let policy_id = PolicyId::from([0u8; 28]);
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let ma_input1 = 100;
let ma_input2 = 200;
let ma_output1 = 60;
let multiassets = [ma_input1, ma_input2, ma_output1]
.iter()
.map(|input| {
let mut multiasset = MultiAsset::new();
multiasset.set(policy_id, name.clone(), *input);
multiasset
})
.collect::<Vec<MultiAsset>>();
for (multiasset, ada) in multiassets.iter().zip([500u64, 500].iter().cloned()) {
let mut input_amount = Value::from(ada);
input_amount.multiasset = multiasset.clone();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), input_amount, None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
}
let mut output_amount = Value::from(263);
output_amount.multiasset = multiassets[2].clone();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(added_change);
let final_tx = tx_builder.build_body().unwrap();
assert_eq!(final_tx.outputs.len(), 2);
assert_eq!(
final_tx.outputs[1]
.amount()
.multiasset
.get(&policy_id, &name)
.unwrap(),
ma_input1 + ma_input2 - ma_output1
);
assert_eq!(final_tx.outputs[1].amount().coin, 736);
}
#[test]
fn build_tx_with_native_assets_change_and_purification() {
let coin_per_utxo_byte = 1;
let mut tx_builder = create_tx_builder_with_fee_and_pure_change(create_linear_fee(0, 1));
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let policy_id = &PolicyId::from([0u8; 28]);
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let ma_input1 = 100;
let ma_input2 = 200;
let ma_output1 = 60;
let multiassets = [ma_input1, ma_input2, ma_output1]
.iter()
.map(|input| {
let mut multiasset = MultiAsset::new();
multiasset.set(*policy_id, name.clone(), *input);
multiasset
})
.collect::<Vec<MultiAsset>>();
for (multiasset, ada) in multiassets.iter().zip([500u64, 500].iter().cloned()) {
let mut input_amount = Value::from(ada);
input_amount.multiasset = multiasset.clone();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), input_amount, None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
}
let mut output_amount = Value::from(263);
output_amount.multiasset = multiassets[2].clone();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(added_change);
let final_tx = tx_builder.build_body().unwrap();
assert_eq!(final_tx.outputs.len(), 3);
assert_eq!(final_tx.outputs[0].amount().coin, 263);
assert_eq!(
final_tx.outputs[1]
.amount()
.multiasset
.get(policy_id, &name)
.unwrap(),
ma_input1 + ma_input2 - ma_output1
);
let min_coin_for_dirty_change =
min_ada_required(&final_tx.outputs[1], coin_per_utxo_byte).unwrap();
assert_eq!(final_tx.outputs[1].amount().coin, min_coin_for_dirty_change);
assert_eq!(final_tx.outputs[2].amount().coin, 473);
assert!(!final_tx.outputs[2].amount().has_multiassets());
}
#[test]
fn build_tx_with_native_assets_change_and_no_purification_cuz_not_enough_pure_coin() {
let mut tx_builder = create_tx_builder_with_fee_and_pure_change(create_linear_fee(1, 1));
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let policy_id = &PolicyId::from([0u8; 28]);
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let ma_input1 = 100;
let ma_input2 = 200;
let ma_output1 = 60;
let multiassets = [ma_input1, ma_input2, ma_output1]
.iter()
.map(|input| {
let mut multiasset = MultiAsset::new();
multiasset.set(*policy_id, name.clone(), *input);
multiasset
})
.collect::<Vec<MultiAsset>>();
for (multiasset, ada) in multiassets.iter().zip([500u64, 500].iter().cloned()) {
let mut input_amount = Value::from(ada);
input_amount.multiasset = multiasset.clone();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), input_amount, None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
}
let output_amount = Value::new(263, multiassets[2].clone());
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(added_change);
let final_tx = tx_builder.build_body().unwrap();
assert_eq!(final_tx.outputs.len(), 2);
assert_eq!(final_tx.outputs[0].amount().coin, 263);
assert_eq!(
final_tx.outputs[1]
.amount()
.multiasset
.get(policy_id, &name)
.unwrap(),
ma_input1 + ma_input2 - ma_output1
);
assert_eq!(final_tx.outputs[1].amount().coin, 336);
}
#[test]
#[should_panic]
fn build_tx_leftover_assets() {
let mut tx_builder = create_default_tx_builder();
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let policy_id = PolicyId::from([0u8; 28]);
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let input_amount = {
let mut input_multiasset = MultiAsset::new();
input_multiasset.set(policy_id, name, 100);
Value::new(1_000_000, input_multiasset)
};
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), input_amount, None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(880_000)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1000);
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr);
assert!(!added_change.unwrap());
assert_eq!(tx_builder.outputs.len(), 1);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap(),
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
);
let _final_tx = tx_builder.build_body(); }
#[test]
fn build_tx_burn_less_than_min_ada() {
let mut tx_builder = create_realistic_tx_builder();
let output_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
)
.unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(output_addr.to_address())
.next()
.unwrap()
.with_value(Value::from(2_000_000))
.build()
.unwrap(),
)
.unwrap();
let input = {
let address = ByronAddress::from_base58(
"Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
)
.unwrap()
.to_address();
let builder = SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(2_400_000), None, None),
);
builder.payment_key().unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder.set_ttl(1);
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap();
let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr.to_address());
assert!(!added_change.unwrap());
assert_eq!(tx_builder.outputs.len(), 1);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap(),
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
);
let _final_tx = tx_builder.build_body(); }
#[test]
fn build_tx_burn_empty_assets() {
let mut tx_builder = create_realistic_tx_builder();
let output_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
)
.unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(output_addr.to_address())
.next()
.unwrap()
.with_value(Value::from(2_000_000))
.build()
.unwrap(),
)
.unwrap();
let input_value = Value::from(2_400_000);
let input = {
let address = ByronAddress::from_base58(
"Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
)
.unwrap()
.to_address();
let builder = SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, input_value, None, None),
);
builder.payment_key().unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder.set_ttl(1);
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap();
let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr.to_address());
assert!(!added_change.unwrap());
assert_eq!(tx_builder.outputs.len(), 1);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap()
.coin,
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
.coin
);
let _final_tx = tx_builder.build_body(); }
#[test]
fn build_tx_no_useless_multiasset() {
let mut tx_builder = create_realistic_tx_builder();
let policy_id = PolicyId::from([0u8; 28]);
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let input_amount = {
let mut input_multiasset = MultiAsset::new();
input_multiasset.set(policy_id, name.clone(), 100);
Value::new(5_000_000, input_multiasset)
};
let input = {
let address = ByronAddress::from_base58(
"Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
)
.unwrap()
.to_address();
let builder = SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, input_amount, None, None),
);
builder.payment_key().unwrap()
};
tx_builder.add_input(input).unwrap();
let output_amount = {
let mut output_multiasset = MultiAsset::new();
output_multiasset.set(policy_id, name, 100);
Value::new(2_000_000, output_multiasset)
};
let output_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
)
.unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(output_addr.to_address())
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1);
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap();
let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr.to_address());
assert!(added_change.unwrap());
assert_eq!(tx_builder.outputs.len(), 2);
let final_tx = tx_builder.build_body().unwrap();
let change_output = &final_tx.outputs[1];
assert!(!change_output.amount().has_multiassets());
}
fn create_multiasset() -> (MultiAsset, [ScriptHash; 3], [AssetName; 3]) {
let policy_ids = [
PolicyId::from([0u8; 28]),
PolicyId::from([1u8; 28]),
PolicyId::from([2u8; 28]),
];
let names = [
AssetName::new(vec![99u8; 32]).unwrap(),
AssetName::new(vec![0u8, 1, 2, 3]).unwrap(),
AssetName::new(vec![4u8, 5, 6, 7, 8, 9]).unwrap(),
];
let multiasset = policy_ids.iter().zip(names.iter()).fold(
MultiAsset::new(),
|mut acc, (policy_id, name)| {
acc.set(*policy_id, name.clone(), 500);
acc
},
);
(multiasset, policy_ids, names)
}
#[test]
fn build_tx_add_change_split_nfts() {
let max_value_size = 100; let mut tx_builder =
create_tx_builder_with_fee_and_val_size(create_linear_fee(0, 1), max_value_size);
let (multiasset, policy_ids, names) = create_multiasset();
let mut input_value = Value::from(1000);
input_value.multiasset = multiasset;
let input = {
let address = ByronAddress::from_base58(
"Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
)
.unwrap()
.to_address();
let builder = SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, input_value, None, None),
);
builder.payment_key().unwrap()
};
tx_builder.add_input(input).unwrap();
let output_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
)
.unwrap()
.to_address();
let output_amount = Value::from(208);
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(output_addr)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
let added_change = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(added_change);
let final_tx = tx_builder.build_body().unwrap();
assert_eq!(final_tx.outputs.len(), 3);
for (policy_id, asset_name) in policy_ids.iter().zip(names.iter()) {
assert!(final_tx.outputs.iter().any(|output| output
.amount()
.multiasset
.iter()
.any(|(pid, a)| pid == policy_id && a.iter().any(|(name, _)| name == asset_name))));
}
for output in final_tx.outputs.iter() {
assert!(output.amount().to_cbor_bytes().len() <= max_value_size as usize);
}
}
#[test]
fn build_tx_too_big_output() {
let mut tx_builder = create_tx_builder_with_fee_and_val_size(create_linear_fee(0, 1), 10);
let input = {
let address = ByronAddress::from_base58(
"Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
)
.unwrap()
.to_address();
let builder = SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(500), None, None),
);
builder.payment_key().unwrap()
};
tx_builder.add_input(input).unwrap();
let output_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
)
.unwrap()
.to_address();
let mut output_amount = Value::from(50);
output_amount.multiasset = create_multiasset().0;
assert!(tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(output_addr)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap()
)
.is_err());
}
#[test]
fn build_tx_add_change_nfts_not_enough_ada() {
let mut tx_builder = create_tx_builder_with_fee_and_val_size(
create_linear_fee(0, 1),
150, );
let policy_ids = [
PolicyId::from([0u8; 28]),
PolicyId::from([1u8; 28]),
PolicyId::from([2u8; 28]),
];
let names = [
AssetName::new(vec![99u8; 32]).unwrap(),
AssetName::new(vec![0u8, 1, 2, 3]).unwrap(),
AssetName::new(vec![4u8, 5, 6, 7, 8, 9]).unwrap(),
];
let multiasset = policy_ids.iter().zip(names.iter()).fold(
MultiAsset::new(),
|mut acc, (policy_id, name)| {
acc.set(*policy_id, name.clone(), 500);
acc
},
);
let mut input_value = Value::from(58);
input_value.multiasset = multiasset;
let input = {
let address = ByronAddress::from_base58(
"Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
)
.unwrap()
.to_address();
let builder = SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, input_value, None, None),
);
builder.payment_key().unwrap()
};
tx_builder.add_input(input).unwrap();
let output_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
)
.unwrap()
.to_address();
let output_amount = Value::from(208);
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(output_addr)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
assert!(tx_builder
.add_change_if_needed_for_tests(&change_addr)
.is_err())
}
fn make_input(input_hash_byte: u8, value: Value) -> InputBuilderResult {
let (_, _, address) = create_account();
SingleInputBuilder::new(
TransactionInput::new(TransactionHash::from([input_hash_byte; 32]), 0),
TransactionOutputBuilder::new()
.with_address(address)
.next()
.unwrap()
.with_value(value)
.build()
.unwrap()
.output,
)
.payment_key()
.unwrap()
}
#[test]
fn tx_builder_cip2_largest_first_increasing_fees() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(1, 0));
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(10000)
.build()
.unwrap(),
)
.unwrap();
tx_builder.add_utxo(make_input(0u8, Value::from(1500)));
tx_builder.add_utxo(make_input(1u8, Value::from(2000)));
tx_builder.add_utxo(make_input(2u8, Value::from(8000)));
tx_builder.add_utxo(make_input(3u8, Value::from(4000)));
tx_builder.add_utxo(make_input(4u8, Value::from(1000)));
tx_builder
.select_utxos(CoinSelectionStrategyCIP2::LargestFirst)
.unwrap();
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
let change_added = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(change_added);
let tx = tx_builder.build_body().unwrap();
assert_eq!(2, tx.outputs.len());
assert_eq!(2, tx.inputs.len());
assert_eq!(2u8, tx.inputs[0].transaction_id.to_raw_bytes()[0]);
assert_eq!(3u8, tx.inputs[1].transaction_id.to_raw_bytes()[0]);
}
#[test]
fn tx_builder_cip2_largest_first_static_fees() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(1200)
.build()
.unwrap(),
)
.unwrap();
tx_builder.add_utxo(make_input(0u8, Value::from(150)));
tx_builder.add_utxo(make_input(1u8, Value::from(200)));
tx_builder.add_utxo(make_input(2u8, Value::from(800)));
tx_builder.add_utxo(make_input(3u8, Value::from(400)));
tx_builder.add_utxo(make_input(4u8, Value::from(100)));
tx_builder
.select_utxos(CoinSelectionStrategyCIP2::LargestFirst)
.unwrap();
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
let change_added = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(!change_added);
let tx = tx_builder.build_body().unwrap();
assert_eq!(1, tx.outputs.len());
assert_eq!(2, tx.inputs.len());
assert_eq!(2u8, tx.inputs[0].transaction_id.to_raw_bytes()[0]);
assert_eq!(3u8, tx.inputs[1].transaction_id.to_raw_bytes()[0]);
}
#[test]
fn tx_builder_cip2_largest_first_multiasset() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
let pid1 = PolicyId::from([1u8; 28]);
let pid2 = PolicyId::from([2u8; 28]);
let asset_name1 = AssetName::new(vec![1u8; 8]).unwrap();
let asset_name2 = AssetName::new(vec![2u8; 11]).unwrap();
let asset_name3 = AssetName::new(vec![3u8; 9]).unwrap();
let mut output_value = Value::from(415);
let mut output_ma = MultiAsset::new();
output_ma.set(pid1, asset_name1.clone(), 5);
output_ma.set(pid1, asset_name2.clone(), 1);
output_ma.set(pid2, asset_name2.clone(), 2);
output_ma.set(pid2, asset_name3.clone(), 4);
output_value.multiasset = output_ma;
tx_builder
.add_output(SingleOutputBuilderResult::new(TransactionOutput::new(
Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z")
.unwrap(),
output_value.clone(),
None,
None,
)))
.unwrap();
tx_builder.add_utxo(make_input(0u8, Value::from(150)));
let mut ma1 = MultiAsset::new();
ma1.set(pid1, asset_name1.clone(), 10);
ma1.set(pid1, asset_name2.clone(), 1);
ma1.set(pid2, asset_name2.clone(), 2);
let input1 = make_input(1u8, Value::new(200, ma1));
tx_builder.add_utxo(input1);
let mut ma2 = MultiAsset::new();
ma2.set(pid1, asset_name1.clone(), 20);
ma2.set(pid2, asset_name3.clone(), 4);
let input2 = make_input(2u8, Value::new(10, ma2));
tx_builder.add_utxo(input2.clone());
let mut ma3 = MultiAsset::new();
ma3.set(pid2, asset_name1.clone(), 5);
ma3.set(pid1, asset_name2.clone(), 15);
let input3 = make_input(3u8, Value::new(50, ma3));
tx_builder.add_utxo(input3.clone());
let mut ma4 = MultiAsset::new();
ma4.set(pid1, asset_name1.clone(), 10);
ma4.set(pid1, asset_name2.clone(), 10);
let input4 = make_input(4u8, Value::new(10, ma4));
tx_builder.add_utxo(input4);
let mut ma5 = MultiAsset::new();
ma5.set(pid1, asset_name2.clone(), 10);
ma5.set(pid2, asset_name2.clone(), 3);
let input5 = make_input(5u8, Value::new(10, ma5));
tx_builder.add_utxo(input5.clone());
let input6 = make_input(6u8, Value::from(700));
tx_builder.add_utxo(input6.clone());
tx_builder.add_utxo(make_input(7u8, Value::from(100)));
tx_builder
.select_utxos(CoinSelectionStrategyCIP2::LargestFirstMultiAsset)
.unwrap();
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
let change_added = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(change_added);
let tx = tx_builder.build_body().unwrap();
assert_eq!(2, tx.outputs.len());
assert_eq!(4, tx.inputs.len());
assert_eq!(2u8, tx.inputs[0].transaction_id.to_raw_bytes()[0]);
assert_eq!(3u8, tx.inputs[1].transaction_id.to_raw_bytes()[0]);
assert_eq!(5u8, tx.inputs[2].transaction_id.to_raw_bytes()[0]);
assert_eq!(6u8, tx.inputs[3].transaction_id.to_raw_bytes()[0]);
let change = tx.outputs[1].amount();
assert_eq!(change.coin, 355);
let change_ma = &change.multiasset;
assert_eq!(15, change_ma.get(&pid1, &asset_name1).unwrap());
assert_eq!(24, change_ma.get(&pid1, &asset_name2).unwrap());
assert_eq!(1, change_ma.get(&pid2, &asset_name2).unwrap());
assert_eq!(0, change_ma.get(&pid2, &asset_name3).unwrap_or_default());
let expected_input = input2
.utxo_info
.amount()
.checked_add(input3.utxo_info.amount())
.unwrap()
.checked_add(input5.utxo_info.amount())
.unwrap()
.checked_add(input6.utxo_info.amount())
.unwrap();
let expected_change = expected_input.checked_sub(&output_value).unwrap();
assert_eq!(expected_change, *change);
}
#[test]
#[flaky_test::flaky_test]
fn tx_builder_cip2_random_improve_multiasset() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
let pid1 = PolicyId::from([1u8; 28]);
let pid2 = PolicyId::from([2u8; 28]);
let asset_name1 = AssetName::new(vec![1u8; 8]).unwrap();
let asset_name2 = AssetName::new(vec![2u8; 11]).unwrap();
let asset_name3 = AssetName::new(vec![3u8; 9]).unwrap();
let mut output_ma = MultiAsset::new();
output_ma.set(pid1, asset_name1.clone(), 5);
output_ma.set(pid1, asset_name2.clone(), 1);
output_ma.set(pid2, asset_name2.clone(), 2);
output_ma.set(pid2, asset_name3.clone(), 4);
let output_value = Value::new(415, output_ma);
tx_builder
.add_output(SingleOutputBuilderResult::new(TransactionOutput::new(
Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z")
.unwrap(),
output_value.clone(),
None,
None,
)))
.unwrap();
tx_builder.add_utxo(make_input(0u8, Value::from(150)));
let mut ma1 = MultiAsset::new();
ma1.set(pid1, asset_name1.clone(), 10);
ma1.set(pid1, asset_name2.clone(), 1);
ma1.set(pid2, asset_name2.clone(), 2);
let input1 = make_input(1u8, Value::new(200, ma1));
tx_builder.add_utxo(input1);
let mut ma2 = MultiAsset::new();
ma2.set(pid1, asset_name1.clone(), 20);
ma2.set(pid2, asset_name3.clone(), 4);
let input2 = make_input(2u8, Value::new(10, ma2));
tx_builder.add_utxo(input2);
let mut ma3 = MultiAsset::new();
ma3.set(pid2, asset_name1.clone(), 5);
ma3.set(pid1, asset_name2.clone(), 15);
let input3 = make_input(3u8, Value::new(50, ma3));
tx_builder.add_utxo(input3);
let mut ma4 = MultiAsset::new();
ma4.set(pid1, asset_name1, 10);
ma4.set(pid1, asset_name2.clone(), 10);
let input4 = make_input(4u8, Value::new(10, ma4));
tx_builder.add_utxo(input4);
let mut ma5 = MultiAsset::new();
ma5.set(pid1, asset_name2.clone(), 10);
ma5.set(pid2, asset_name2.clone(), 3);
let input5 = make_input(5u8, Value::new(10, ma5));
tx_builder.add_utxo(input5);
let input6 = make_input(6u8, Value::from(400));
tx_builder.add_utxo(input6);
tx_builder.add_utxo(make_input(7u8, Value::from(100)));
let mut ma8 = MultiAsset::new();
ma8.set(pid2, asset_name2, 10);
let input8 = make_input(8u8, Value::new(10, ma8));
tx_builder.add_utxo(input8);
let mut ma9 = MultiAsset::new();
ma9.set(pid2, asset_name3, 10);
let input9 = make_input(9u8, Value::new(10, ma9));
tx_builder.add_utxo(input9);
tx_builder
.select_utxos(CoinSelectionStrategyCIP2::RandomImproveMultiAsset)
.unwrap();
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
let change_added = tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert!(change_added);
let tx = tx_builder.build_body().unwrap();
assert_eq!(2, tx.outputs.len());
let input_total = tx_builder.get_explicit_input().unwrap();
assert!(input_total >= output_value);
}
#[test]
#[flaky_test::flaky_test]
fn tx_builder_cip2_random_improve() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(1, 0));
const COST: u64 = 10000;
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(COST)
.build()
.unwrap(),
)
.unwrap();
tx_builder.utxos.push(make_input(0u8, Value::from(1500)));
tx_builder.utxos.push(make_input(1u8, Value::from(2000)));
tx_builder.utxos.push(make_input(2u8, Value::from(8000)));
tx_builder.utxos.push(make_input(3u8, Value::from(4000)));
tx_builder.utxos.push(make_input(4u8, Value::from(1000)));
tx_builder.utxos.push(make_input(5u8, Value::from(2000)));
tx_builder.utxos.push(make_input(6u8, Value::from(1500)));
let add_inputs_res = tx_builder.select_utxos(CoinSelectionStrategyCIP2::RandomImprove);
assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err());
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
let add_change_res = tx_builder.add_change_if_needed_for_tests(&change_addr);
assert!(add_change_res.is_ok(), "{:?}", add_change_res.err());
let tx_build_res = tx_builder.build_body();
assert!(tx_build_res.is_ok(), "{:?}", tx_build_res.err());
let tx = tx_build_res.unwrap();
let mut input_values = BTreeMap::new();
for utxo in tx_builder.utxos.iter() {
input_values.insert(utxo.input.transaction_id, utxo.utxo_info.amount().clone());
}
let mut encountered = std::collections::HashSet::new();
let mut input_total = Value::from(Coin::zero());
for input in tx.inputs.iter() {
let txid = &input.transaction_id;
if !encountered.insert(*txid) {
panic!("Input {:?} duplicated", txid);
}
let value = input_values.get(txid).unwrap();
input_total = input_total.checked_add(value).unwrap();
}
assert!(
input_total
>= Value::from(
tx_builder
.min_fee(false)
.unwrap()
.checked_add(COST)
.unwrap()
)
);
}
#[test]
#[flaky_test::flaky_test]
fn tx_builder_cip2_random_improve_exclude_used_indices() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(44, 155381));
const COST: u64 = 1000000;
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(COST)
.build()
.unwrap(),
)
.unwrap();
tx_builder.add_utxo(make_input(0u8, Value::from(1000000)));
tx_builder.add_utxo(make_input(1u8, Value::from(10000000)));
let mut input_total = tx_builder.get_total_input().unwrap();
let mut output_total = tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_deposit().unwrap()))
.unwrap()
.checked_add(&Value::from(tx_builder.min_fee(false).unwrap()))
.unwrap();
let available_inputs = tx_builder.utxos.clone();
let mut available_indices: BTreeSet<usize> = (0..available_inputs.len()).collect();
assert!(available_indices.len() == 2);
use rand::SeedableRng;
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(1);
tx_builder
.cip2_random_improve_by(
&available_inputs,
&mut available_indices,
&mut input_total,
&mut output_total,
|value| Some(value.coin),
&mut rng,
)
.unwrap();
assert!(!available_indices.contains(&0));
assert!(available_indices.contains(&1));
assert!(available_indices.len() < 2);
}
#[test]
#[flaky_test::flaky_test]
fn tx_builder_cip2_random_improve_when_using_all_available_inputs() {
let linear_fee = LinearFee::new(1, 0);
let cfg = TransactionBuilderConfigBuilder::default()
.fee_algo(linear_fee)
.pool_deposit(0)
.key_deposit(0)
.max_value_size(9999)
.max_tx_size(9999)
.coins_per_utxo_byte(Coin::zero())
.ex_unit_prices(ExUnitPrices::new(
SubCoin::new(u64::zero(), u64::zero()),
SubCoin::new(u64::zero(), u64::zero()),
))
.collateral_percentage(150)
.max_collateral_inputs(3)
.build()
.unwrap();
let mut tx_builder = TransactionBuilder::new(cfg);
const COST: u64 = 1000;
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(COST)
.build()
.unwrap(),
)
.unwrap();
tx_builder.add_utxo(make_input(1u8, Value::from(800)));
tx_builder.add_utxo(make_input(2u8, Value::from(800)));
let add_inputs_res = tx_builder.select_utxos(CoinSelectionStrategyCIP2::RandomImprove);
assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err());
}
#[test]
#[flaky_test::flaky_test]
fn tx_builder_cip2_random_improve_adds_enough_for_fees() {
let linear_fee = LinearFee::new(1, 0);
let cfg = TransactionBuilderConfigBuilder::default()
.fee_algo(linear_fee)
.pool_deposit(0)
.key_deposit(0)
.max_value_size(9999)
.max_tx_size(9999)
.coins_per_utxo_byte(Coin::zero())
.ex_unit_prices(ExUnitPrices::new(SubCoin::new(0, 0), SubCoin::new(0, 0)))
.collateral_percentage(150)
.max_collateral_inputs(3)
.build()
.unwrap();
let mut tx_builder = TransactionBuilder::new(cfg);
const COST: u64 = 100;
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(COST)
.build()
.unwrap(),
)
.unwrap();
assert_eq!(tx_builder.min_fee(false).unwrap(), 53);
tx_builder.add_utxo(make_input(1u8, Value::from(150)));
tx_builder.add_utxo(make_input(2u8, Value::from(150)));
tx_builder.add_utxo(make_input(3u8, Value::from(150)));
let add_inputs_res = tx_builder.select_utxos(CoinSelectionStrategyCIP2::RandomImprove);
assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err());
assert_eq!(tx_builder.min_fee(false).unwrap(), 264);
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
let add_change_res = tx_builder.add_change_if_needed_for_tests(&change_addr);
assert!(add_change_res.is_ok(), "{:?}", add_change_res.err());
}
#[test]
fn build_tx_pay_to_multisig() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(10, 2));
let (_, _, addr_net_0) = create_account();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(999_000)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1000);
tx_builder.set_fee(1_000);
assert_eq!(tx_builder.outputs.len(), 1);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap(),
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
);
let final_tx = tx_builder.build_body().unwrap();
let deser_t = TransactionBody::from_cbor_bytes(&final_tx.to_cbor_bytes()).unwrap();
assert_eq!(deser_t.to_cbor_bytes(), final_tx.to_cbor_bytes());
}
#[test]
fn build_tx_multisig_spend_1on1_unsigned() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(10, 2));
let ((spend, _), (_, stake_cred), addr_multisig) = create_account();
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let addr_output =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_multisig, Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_output)
.next()
.unwrap()
.with_value(999_000)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1000);
tx_builder.set_fee(1_000);
let mut auxiliary_data = AuxiliaryData::new();
let mut pubkey_native_scripts = Vec::new();
let mut oneof_native_scripts = Vec::new();
let spending_hash = spend.to_raw_key().hash();
pubkey_native_scripts.push(NativeScript::new_script_pubkey(spending_hash));
oneof_native_scripts.push(NativeScript::new_script_n_of_k(1, pubkey_native_scripts));
auxiliary_data.add_native_scripts(oneof_native_scripts);
tx_builder.add_auxiliary_data(auxiliary_data.clone());
assert_eq!(tx_builder.outputs.len(), 1);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap(),
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
);
let final_tx = tx_builder.build_body().unwrap();
let deser_t = TransactionBody::from_cbor_bytes(&final_tx.to_cbor_bytes()).unwrap();
assert_eq!(deser_t.to_cbor_bytes(), final_tx.to_cbor_bytes());
assert_eq!(
deser_t.auxiliary_data_hash.unwrap(),
hash_auxiliary_data(&auxiliary_data)
);
}
#[test]
fn build_tx_multisig_1on1_signed() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(10, 2));
let spend = root_key_15()
.derive(harden(1854)) .derive(harden(1815))
.derive(harden(0))
.derive(0)
.derive(0)
.to_public();
let stake = root_key_15()
.derive(harden(1854)) .derive(harden(1815))
.derive(harden(0))
.derive(2)
.derive(0)
.to_public();
let spend_cred = StakeCredential::new_pub_key(spend.to_raw_key().hash());
let stake_cred = StakeCredential::new_pub_key(stake.to_raw_key().hash());
let addr_net_0 =
BaseAddress::new(NetworkInfo::testnet().network_id(), spend_cred, stake_cred)
.to_address();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(1_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(999_000)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1000);
tx_builder.set_fee(1_000);
let mut auxiliary_data = AuxiliaryData::new();
let mut pubkey_native_scripts = Vec::new();
let mut oneof_native_scripts = Vec::new();
let spending_hash = spend.to_raw_key().hash();
pubkey_native_scripts.push(NativeScript::new_script_pubkey(spending_hash));
oneof_native_scripts.push(NativeScript::new_script_n_of_k(1, pubkey_native_scripts));
auxiliary_data.add_native_scripts(oneof_native_scripts);
tx_builder.add_auxiliary_data(auxiliary_data.clone());
let body = tx_builder.build_body().unwrap();
assert_eq!(tx_builder.outputs.len(), 1);
assert_eq!(
tx_builder
.get_explicit_input()
.unwrap()
.checked_add(&tx_builder.get_implicit_input().unwrap())
.unwrap(),
tx_builder
.get_explicit_output()
.unwrap()
.checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
.unwrap()
);
let mut witness_set = TransactionWitnessSet::new();
witness_set.vkeywitnesses = Some(vec![make_vkey_witness(
&hash_transaction(&body),
&PrivateKey::from_normal_bytes(
&hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
.unwrap(),
)
.unwrap(),
)]);
let final_tx = Transaction::new(body, witness_set, true, None);
let deser_t = Transaction::from_cbor_bytes(&final_tx.to_cbor_bytes()).unwrap();
assert_eq!(deser_t.to_cbor_bytes(), final_tx.to_cbor_bytes());
assert_eq!(
deser_t.body.auxiliary_data_hash.unwrap(),
hash_auxiliary_data(&auxiliary_data)
);
}
#[test]
fn add_change_splits_change_into_multiple_outputs_when_nfts_overflow_output_size() {
let linear_fee = LinearFee::new(0, 1);
let max_value_size = 100; let mut tx_builder = TransactionBuilder::new(
TransactionBuilderConfigBuilder::default()
.fee_algo(linear_fee)
.pool_deposit(0)
.key_deposit(0)
.max_value_size(max_value_size)
.max_tx_size(MAX_TX_SIZE)
.coins_per_utxo_byte(1)
.ex_unit_prices(ExUnitPrices::new(SubCoin::new(0, 0), SubCoin::new(0, 0)))
.collateral_percentage(150)
.max_collateral_inputs(3)
.prefer_pure_change(true)
.build()
.unwrap(),
);
let policy_id = PolicyId::from([0u8; 28]);
let names = [
AssetName::new(vec![0u8, 1, 2, 3]).unwrap(),
AssetName::new(vec![4u8, 5, 6, 7]).unwrap(),
AssetName::new(vec![5u8, 5, 6, 7]).unwrap(),
AssetName::new(vec![6u8, 5, 6, 7]).unwrap(),
AssetName::new(vec![99u8; 32]).unwrap(),
];
let mut multiasset = MultiAsset::new();
for name in names.iter() {
multiasset.set(policy_id, name.clone(), 500);
}
let input_value = Value::new(1300, multiasset);
let input = {
let builder = SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(
ByronAddress::from_base58(
"Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
)
.unwrap()
.to_address(),
input_value,
None,
None,
),
);
builder.payment_key().unwrap()
};
tx_builder.add_input(input).unwrap();
let output_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
)
.unwrap()
.to_address();
let output_amount = Value::from(208);
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(output_addr)
.next()
.unwrap()
.with_value(output_amount)
.build()
.unwrap(),
)
.unwrap();
let change_addr = ByronAddress::from_base58(
"Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
)
.unwrap()
.to_address();
let add_change_result = tx_builder.add_change_if_needed_for_tests(&change_addr);
assert!(add_change_result.is_ok());
assert_eq!(tx_builder.outputs.len(), 4);
let change1 = &tx_builder.outputs[1];
let change2 = &tx_builder.outputs[2];
let change3 = &tx_builder.outputs[3];
assert_eq!(*change1.address(), change_addr);
assert_eq!(change1.address(), change2.address());
assert_eq!(change1.address(), change3.address());
assert_eq!(change1.amount().coin, 274);
assert_eq!(change2.amount().coin, 279);
assert_eq!(change3.amount().coin, 538);
assert!(change1.amount().has_multiassets());
assert!(change2.amount().has_multiassets());
assert!(!change3.amount().has_multiassets()); let masset1 = &change1.amount().multiasset;
let masset2 = &change2.amount().multiasset;
assert_eq!(masset1.keys().len(), 1);
assert_eq!(
masset1.keys().collect::<Vec<_>>(),
masset2.keys().collect::<Vec<_>>()
);
let asset1 = masset1.deref().get(&policy_id).unwrap();
let asset2 = masset2.deref().get(&policy_id).unwrap();
assert_eq!(asset1.len(), 4);
assert_eq!(asset2.len(), 1);
names.iter().for_each(|name| {
let v1 = asset1.get(name);
let v2 = asset2.get(name);
assert_ne!(v1.is_some(), v2.is_some());
assert_eq!(*v1.or(v2).unwrap(), 500);
});
}
fn create_metadatum() -> TransactionMetadatum {
let mut entries = MetadatumMap::new();
entries.set(
TransactionMetadatum::new_text("qwe".into()).unwrap(),
TransactionMetadatum::new_int(123i64.into()),
);
TransactionMetadatum::new_map(entries)
}
fn create_general_metadata(metadatum_key: TransactionMetadatumLabel) -> Metadata {
let mut metadata = Metadata::new();
metadata.set(metadatum_key, create_metadatum());
metadata
}
fn create_aux_with_metadata(metadatum_key: TransactionMetadatumLabel) -> AuxiliaryData {
let metadata = create_general_metadata(metadatum_key);
let mut aux = AuxiliaryData::new_shelley(metadata);
aux.add_native_scripts(vec![NativeScript::new_script_invalid_before(123)]);
aux
}
fn assert_json_metadatum(dat: &TransactionMetadatum) {
match dat {
TransactionMetadatum::Map(map) => {
assert_eq!(map.len(), 1);
let key = TransactionMetadatum::new_text(String::from("qwe")).unwrap();
let val = map.get(&key).unwrap();
match val {
TransactionMetadatum::Int(x) => assert_eq!(*x, 123u64.into()),
_ => panic!(),
}
}
_ => panic!(),
}
}
#[test]
fn set_metadata_with_empty_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let num = 42;
{
let mut aux_data = AuxiliaryData::new();
aux_data.metadata_mut().set(num, create_metadatum());
tx_builder.add_auxiliary_data(aux_data);
}
assert!(tx_builder.auxiliary_data.is_some());
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_none());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.len(), 1);
assert_json_metadatum(met.get(num).unwrap());
}
#[test]
fn set_metadata_with_existing_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let num1 = 42;
tx_builder.add_auxiliary_data(create_aux_with_metadata(num1));
let num2 = 84;
{
let mut aux_data = AuxiliaryData::new();
aux_data.metadata_mut().set(num2, create_metadatum());
tx_builder.set_auxiliary_data(aux_data);
}
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_none());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.len(), 1);
assert!(met.get(num1).is_none());
assert_json_metadatum(met.get(num2).unwrap());
}
#[test]
fn add_metadatum_with_empty_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let num = 42;
{
let mut aux_data = AuxiliaryData::new();
aux_data.metadata_mut().set(num, create_metadatum());
tx_builder.add_auxiliary_data(aux_data);
}
assert!(tx_builder.auxiliary_data.is_some());
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_none());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.len(), 1);
assert_json_metadatum(met.get(num).unwrap());
}
#[test]
fn add_metadatum_with_existing_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let num1 = 42;
tx_builder.add_auxiliary_data(create_aux_with_metadata(num1));
let num2 = 84;
tx_builder.add_auxiliary_data(create_aux_with_metadata(num2));
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_some());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.len(), 2);
assert_json_metadatum(met.get(num1).unwrap());
assert_json_metadatum(met.get(num2).unwrap());
}
#[test]
fn add_json_metadatum_with_empty_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let num = 42;
tx_builder.add_auxiliary_data(AuxiliaryData::new_shelley(create_general_metadata(num)));
assert!(tx_builder.auxiliary_data.is_some());
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_none());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.len(), 1);
assert_json_metadatum(met.get(num).unwrap());
}
#[test]
fn add_json_metadatum_with_existing_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let num1 = 42;
tx_builder.add_auxiliary_data(create_aux_with_metadata(num1));
let num2 = 84;
tx_builder.add_auxiliary_data(create_aux_with_metadata(num2));
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_some());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.len(), 2);
assert_json_metadatum(met.get(num1).unwrap());
assert_json_metadatum(met.get(num2).unwrap());
}
#[test]
fn add_metadata_with_empty_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let key = 42;
let value = TransactionMetadatum::new_text("Hello World".to_string()).unwrap();
{
let mut aux_data = AuxiliaryData::new();
aux_data.metadata_mut().set(key, value.clone());
tx_builder.add_auxiliary_data(aux_data);
}
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_none());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.len(), 1);
assert_eq!(*met.get(key).unwrap(), value);
}
#[test]
fn add_json_metadata_with_empty_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let key = 42;
tx_builder.add_auxiliary_data(AuxiliaryData::new_shelley(create_general_metadata(key)));
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_none());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.len(), 1);
assert_json_metadatum(met.get(key).unwrap());
}
#[test]
fn add_metadata_with_existing_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let key1 = 42;
tx_builder.add_auxiliary_data(create_aux_with_metadata(key1));
let key2 = 84;
let val2 = TransactionMetadatum::new_text("Hello World".to_string()).unwrap();
{
let mut aux_data = AuxiliaryData::new();
aux_data.metadata_mut().set(key2, val2.clone());
tx_builder.add_auxiliary_data(aux_data);
}
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_some());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.entries.len(), 2);
assert_json_metadatum(met.get(key1).unwrap());
assert_eq!(*met.get(key2).unwrap(), val2);
}
#[test]
fn add_json_metadata_with_existing_auxiliary() {
let mut tx_builder = create_default_tx_builder();
let key1 = 42;
tx_builder.add_auxiliary_data(create_aux_with_metadata(key1));
let key2 = 84;
tx_builder.add_auxiliary_data(create_aux_with_metadata(key2));
let aux = tx_builder.auxiliary_data.unwrap();
assert!(aux.metadata().is_some());
assert!(aux.native_scripts().is_some());
assert!(aux.plutus_v1_scripts().is_none());
assert!(aux.plutus_v2_scripts().is_none());
let met = aux.metadata().unwrap();
assert_eq!(met.entries.len(), 2);
assert_json_metadatum(met.get(key1).unwrap());
assert_json_metadatum(met.get(key2).unwrap());
}
fn create_asset_name() -> AssetName {
AssetName::new(vec![0u8, 1, 2, 3]).unwrap()
}
fn create_mint_asset_builder() -> SingleMintBuilder {
SingleMintBuilder::new_single_asset(create_asset_name(), 1234)
}
fn create_multiasset_one_asset(policy_id: &PolicyId) -> MultiAsset {
let mut mint = MultiAsset::default();
mint.set(*policy_id, create_asset_name(), 1234);
mint
}
fn assert_mint_asset(mint: &Mint, policy_id: &PolicyId) {
let result_asset = mint.deref().get(policy_id).unwrap();
assert_eq!(result_asset.len(), 1);
assert_eq!(
*result_asset.deref().get(&create_asset_name()).unwrap(),
1234
);
}
fn mint_script_and_policy_and_hash(x: u8) -> (NativeScript, PolicyId, Ed25519KeyHash) {
let hash = fake_key_hash(x);
let mint_script = NativeScript::new_script_pubkey(hash);
let policy_id = mint_script.hash();
(mint_script, policy_id, hash)
}
fn mint_script_and_policy(x: u8) -> (NativeScript, PolicyId) {
let (m, p, _) = mint_script_and_policy_and_hash(x);
(m, p)
}
#[test]
fn set_mint_asset_with_empty_mint() {
let mut tx_builder = create_default_tx_builder();
let (mint_script, policy_id) = mint_script_and_policy(0);
let result = create_mint_asset_builder().native_script(
mint_script,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
assert!(tx_builder.mint.is_some());
let mint = tx_builder.mint.unwrap();
assert_eq!(mint.len(), 1);
assert_mint_asset(&mint, &policy_id);
}
#[test]
fn set_mint_asset_with_existing_mint() {
let mut tx_builder = create_default_tx_builder();
let (mint_script1, policy_id1) = mint_script_and_policy(0);
let (mint_script2, policy_id2) = mint_script_and_policy(1);
let result = create_mint_asset_builder().native_script(
mint_script1,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let result = create_mint_asset_builder().native_script(
mint_script2,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
assert!(tx_builder.mint.is_some());
let mint = tx_builder.mint.unwrap();
assert_eq!(mint.len(), 2);
assert_mint_asset(&mint, &policy_id1);
assert_mint_asset(&mint, &policy_id2);
}
#[test]
fn add_mint_asset_with_empty_mint() {
let mut tx_builder = create_default_tx_builder();
let (mint_script, policy_id) = mint_script_and_policy(0);
let result = create_mint_asset_builder().native_script(
mint_script,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
assert!(tx_builder.mint.is_some());
let mint = tx_builder.mint.unwrap();
assert_eq!(mint.len(), 1);
assert_mint_asset(&mint, &policy_id);
}
#[test]
fn add_mint_asset_with_existing_mint() {
let mut tx_builder = create_default_tx_builder();
let (mint_script1, policy_id1) = mint_script_and_policy(0);
let (mint_script2, policy_id2) = mint_script_and_policy(1);
let result = create_mint_asset_builder().native_script(
mint_script1,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let result = create_mint_asset_builder().native_script(
mint_script2,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
assert!(tx_builder.mint.is_some());
let mint = tx_builder.mint.unwrap();
assert_eq!(mint.len(), 2);
assert_mint_asset(&mint, &policy_id1);
assert_mint_asset(&mint, &policy_id2);
}
#[test]
fn add_mint_same_policy() {
let mut tx_builder = create_default_tx_builder();
let (mint_script1, policy_id1) = mint_script_and_policy(1);
let (mint_script2, policy_id2) = mint_script_and_policy(2);
let (mint_script3, policy_id3) = mint_script_and_policy(3);
let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let name2 = AssetName::new(vec![1u8, 1, 2, 3]).unwrap();
let name3 = AssetName::new(vec![2u8, 1, 2, 3]).unwrap();
let name4 = AssetName::new(vec![3u8, 1, 2, 3]).unwrap();
let amount = 1234;
let input = {
let ((_spend, _), _, address) = create_account();
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(10_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
let input = {
let cred = StakeCredential::new_script(policy_id1);
let address = BaseAddress::new(NetworkInfo::testnet().network_id(), cred.clone(), cred)
.to_address();
let builder = SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(address, Value::from(10_000_000), None, None),
);
builder
.native_script(
mint_script1.clone(),
NativeScriptWitnessInfo::assume_signature_count(),
)
.unwrap()
};
tx_builder.add_input(input).unwrap();
let original_tx_fee = tx_builder.min_fee(false).unwrap();
assert_eq!(original_tx_fee, 164502);
let result = SingleMintBuilder::new_single_asset(name1, amount).native_script(
mint_script1,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let result = SingleMintBuilder::new_single_asset(name2, amount).native_script(
mint_script2,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let result = SingleMintBuilder::new_single_asset(name3, amount).native_script(
mint_script3.clone(),
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let result = SingleMintBuilder::new_single_asset(name4, amount).native_script(
mint_script3,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let mint = tx_builder.get_mint().unwrap();
assert_eq!(mint.len(), 3);
assert_eq!(mint.deref().get(&policy_id1).unwrap().len(), 1);
assert_eq!(mint.deref().get(&policy_id2).unwrap().len(), 1);
assert_eq!(mint.deref().get(&policy_id3).unwrap().len(), 2);
let mint_scripts = tx_builder.witness_builders.build_fake().unwrap();
assert_eq!(mint_scripts.native_scripts.unwrap().len(), 3);
assert_eq!(mint_scripts.vkeywitnesses.unwrap().len(), 6);
assert!(mint_scripts.bootstrap_witnesses.is_none());
assert!(mint_scripts.plutus_datums.is_none());
assert!(mint_scripts.plutus_v1_scripts.is_none());
assert!(mint_scripts.redeemers.is_none());
}
#[test]
fn add_output_amount() {
let mut tx_builder = create_default_tx_builder();
let policy_id1 = PolicyId::from([0u8; 28]);
let multiasset = create_multiasset_one_asset(&policy_id1);
let value = Value::new(249, multiasset);
let address = byron_address();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(address.clone())
.next()
.unwrap()
.with_value(value.clone())
.build()
.unwrap(),
)
.unwrap();
assert_eq!(tx_builder.outputs.len(), 1);
let out = &tx_builder.outputs[0];
assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
assert_eq!(*out.amount(), value);
}
#[test]
fn add_output_coin() {
let mut tx_builder = create_default_tx_builder();
let address = byron_address();
let coin = 208;
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(address.clone())
.next()
.unwrap()
.with_value(coin)
.build()
.unwrap(),
)
.unwrap();
assert_eq!(tx_builder.outputs.len(), 1);
let out = &tx_builder.outputs[0];
assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
assert_eq!(out.amount().coin, coin);
assert!(!out.amount().has_multiassets());
}
#[test]
fn add_output_coin_and_multiasset() {
let mut tx_builder = create_default_tx_builder();
let policy_id1 = PolicyId::from([0u8; 28]);
let multiasset = create_multiasset_one_asset(&policy_id1);
let address = byron_address();
let coin = 249;
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(address.clone())
.next()
.unwrap()
.with_value(Value::new(coin, multiasset.clone()))
.build()
.unwrap(),
)
.unwrap();
assert_eq!(tx_builder.outputs.len(), 1);
let out = &tx_builder.outputs[0];
assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
assert_eq!(out.amount().coin, coin);
assert_eq!(out.amount().multiasset, multiasset);
}
#[test]
fn add_output_asset_and_min_required_coin() {
let mut tx_builder = create_realistic_tx_builder();
let policy_id1 = PolicyId::from([0u8; 28]);
let multiasset = create_multiasset_one_asset(&policy_id1);
let address = byron_address();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(address.clone())
.next()
.unwrap()
.with_asset_and_min_required_coin(
multiasset.clone(),
tx_builder.config.coins_per_utxo_byte,
)
.unwrap()
.build()
.unwrap(),
)
.unwrap();
assert_eq!(tx_builder.outputs.len(), 1);
let out = &tx_builder.outputs[0];
assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
assert_eq!(out.amount().multiasset, multiasset);
assert_eq!(out.amount().coin, 1086120);
}
#[test]
fn add_mint_asset_and_output() {
let mut tx_builder = create_default_tx_builder();
let (mint_script0, policy_id0) = mint_script_and_policy(0);
let (mint_script1, policy_id1) = mint_script_and_policy(1);
let name = create_asset_name();
let amount = 1234;
let address = byron_address();
let coin = 249;
let result = SingleMintBuilder::new_single_asset(name.clone(), amount).native_script(
mint_script0,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let multiasset = {
let mut multiasset = MultiAsset::new();
multiasset.set(policy_id1, name.clone(), 1234);
multiasset
};
let output = TransactionOutputBuilder::new()
.with_address(address.clone())
.next()
.unwrap()
.with_value(Value::new(coin, multiasset))
.build()
.unwrap();
tx_builder.add_output(output).unwrap();
let result = SingleMintBuilder::new_single_asset(name.clone(), amount).native_script(
mint_script1,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
assert!(tx_builder.mint.is_some());
let mint = tx_builder.mint.as_ref().unwrap();
assert_eq!(mint.len(), 2);
assert_mint_asset(mint, &policy_id0);
assert_mint_asset(mint, &policy_id1);
assert_eq!(tx_builder.outputs.len(), 1);
let out = &tx_builder.outputs[0];
assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
assert_eq!(out.amount().coin, coin);
let multiasset = &out.amount().multiasset;
assert_eq!(multiasset.len(), 1);
assert!(multiasset.deref().get(&policy_id0).is_none());
assert!(multiasset.deref().get(&policy_id1).is_some());
let asset = multiasset.deref().get(&policy_id1).unwrap();
assert_eq!(asset.len(), 1);
assert_eq!(*asset.get(&name).unwrap(), 1234);
}
#[test]
fn add_mint_asset_and_min_required_coin() {
let mut tx_builder = create_realistic_tx_builder();
let (mint_script0, policy_id0) = mint_script_and_policy(0);
let (mint_script1, policy_id1) = mint_script_and_policy(1);
let name = create_asset_name();
let amount = 1234;
let address = byron_address();
let result = SingleMintBuilder::new_single_asset(name.clone(), amount).native_script(
mint_script0,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let multiasset = {
let mut multiasset = MultiAsset::new();
multiasset.set(policy_id1, name.clone(), 1234);
multiasset
};
let output = TransactionOutputBuilder::new()
.with_address(address.clone())
.next()
.unwrap()
.with_asset_and_min_required_coin(multiasset, tx_builder.config.coins_per_utxo_byte)
.unwrap()
.build()
.unwrap();
tx_builder.add_output(output).unwrap();
let result = SingleMintBuilder::new_single_asset(name.clone(), amount).native_script(
mint_script1,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
assert!(tx_builder.mint.is_some());
let mint = tx_builder.mint.as_ref().unwrap();
assert_eq!(mint.len(), 2);
assert_mint_asset(mint, &policy_id0);
assert_mint_asset(mint, &policy_id1);
assert_eq!(tx_builder.outputs.len(), 1);
let out = &tx_builder.outputs[0];
assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
assert_eq!(out.amount().coin, 1086120);
let multiasset = &out.amount().multiasset;
assert_eq!(multiasset.len(), 1);
assert!(multiasset.deref().get(&policy_id0).is_none());
assert!(multiasset.deref().get(&policy_id1).is_some());
let asset = multiasset.deref().get(&policy_id1).unwrap();
assert_eq!(asset.len(), 1);
assert_eq!(*asset.get(&name).unwrap(), 1234);
}
#[test]
fn total_input_with_mint_and_burn() {
let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 1));
let (_, (_stake, _), addr_test_0) = create_account();
let (mint_script1, policy_id1) = mint_script_and_policy(0);
let (mint_script2, policy_id2) = mint_script_and_policy(1);
let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
let ma_input1 = 100;
let ma_input2 = 200;
let ma_output1 = 60;
let multiassets = [ma_input1, ma_input2, ma_output1]
.iter()
.map(|input| {
let mut multiasset = MultiAsset::new();
multiasset.set(policy_id1, name.clone(), *input);
multiasset.set(policy_id2, name.clone(), *input);
multiasset
})
.collect::<Vec<MultiAsset>>();
for (multiasset, ada) in multiassets.iter().zip([100u64, 100, 100].iter().cloned()) {
let mut input_amount = Value::from(ada);
input_amount.multiasset = multiasset.clone();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_test_0.clone(), input_amount, None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
}
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(byron_address())
.next()
.unwrap()
.with_value(208)
.build()
.unwrap(),
)
.unwrap();
let total_input_before_mint = tx_builder.get_total_input().unwrap();
let total_output_before_mint = tx_builder.get_total_output().unwrap();
assert_eq!(total_input_before_mint.coin, 300);
assert_eq!(total_output_before_mint.coin, 208);
let ma1_input = &total_input_before_mint.multiasset;
let ma1_output = &total_output_before_mint;
assert_eq!(ma1_input.get(&policy_id1, &name).unwrap(), 360);
assert_eq!(ma1_input.get(&policy_id2, &name).unwrap(), 360);
assert!(!ma1_output.has_multiassets());
let result = SingleMintBuilder::new_single_asset(name.clone(), 40).native_script(
mint_script1,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let result = SingleMintBuilder::new_single_asset(name.clone(), -40).native_script(
mint_script2,
NativeScriptWitnessInfo::assume_signature_count(),
);
tx_builder.add_mint(result).unwrap();
let total_input_after_mint = tx_builder.get_total_input().unwrap();
let total_output_after_mint = tx_builder.get_total_output().unwrap();
assert_eq!(total_input_after_mint.coin, 300);
assert_eq!(total_output_before_mint.coin, 208);
let ma2_input = total_input_after_mint.multiasset;
let ma2_output = total_output_after_mint.multiasset;
assert_eq!(ma2_input.get(&policy_id1, &name).unwrap(), 400);
assert_eq!(ma2_input.get(&policy_id2, &name).unwrap(), 360);
assert_eq!(ma2_output.get(&policy_id2, &name).unwrap(), 40);
}
#[test]
fn test_contract() {
let mut tx_builder = create_realistic_tx_builder();
let mut spacebudz_asset = MultiAsset::new();
spacebudz_asset.set(
PolicyId::from_hex("6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52").unwrap(),
AssetName::new(hex::decode("537061636542756442696433303533").unwrap()).unwrap(),
1,
);
let private_key = PrivateKey::from_normal_bytes(
&hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
.unwrap(),
)
.unwrap();
{
let required_signers = vec![
private_key.to_public().hash(),
];
let input_utxo = TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(Value::new(70000000, spacebudz_asset.clone()))
.build()
.unwrap();
tx_builder.add_input(SingleInputBuilder::new(
TransactionInput::new(
TransactionHash::from_hex("473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c").unwrap(),
0
),
input_utxo.output
).plutus_script(
PartialPlutusWitness::new(
PlutusScriptWitness::from(
PlutusScript::PlutusV1(PlutusV1Script::new(
hex::decode("59193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
))
),
PlutusData::from_cbor_bytes(&hex::decode("D866820380").unwrap()).unwrap(),
),
required_signers,
PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()
).unwrap()).unwrap();
}
{
let output_utxo = TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
)
.unwrap(),
)
.with_data(DatumOption::new_hash(
DatumHash::from_hex(
"f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8",
)
.unwrap(),
))
.next()
.unwrap()
.with_value(Value::new(1851850, spacebudz_asset))
.build()
.unwrap();
tx_builder.add_output(output_utxo).unwrap();
}
{
let output_utxo = TransactionOutputBuilder::new()
.with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
.next()
.unwrap()
.with_value(67250397)
.build()
.unwrap();
tx_builder.add_output(output_utxo).unwrap();
}
{
let input_utxo = TransactionOutputBuilder::new()
.with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
.next()
.unwrap()
.with_value(5000000)
.build()
.unwrap();
tx_builder
.add_collateral(
SingleInputBuilder::new(
TransactionInput::new(
TransactionHash::from_hex(
"a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b",
)
.unwrap(),
0,
),
input_utxo.output,
)
.payment_key()
.unwrap(),
)
.unwrap();
}
{
let mut map = MetadatumMap::new();
map.set(
TransactionMetadatum::new_int(Int::from(0u64)),
TransactionMetadatum::new_bytes(hex::decode("d866820080").unwrap()).unwrap(),
);
let mut aux_data = AuxiliaryData::new();
aux_data
.metadata_mut()
.set(405, TransactionMetadatum::new_map(map));
tx_builder.add_auxiliary_data(aux_data);
}
let original_tx_fee = tx_builder.min_fee(false).unwrap();
assert_eq!(original_tx_fee, 469629);
tx_builder.set_fee(897753);
{
tx_builder.set_exunits(
RedeemerWitnessKey::new(RedeemerTag::Spend, 0),
ExUnits::new(5000000, 2000000000),
);
}
let tx = tx_builder.build(ChangeSelectionAlgo::Default, &Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap()).unwrap();
assert_eq!(hex::encode(tx.body.to_cbor_bytes()), "a70081825820473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c00018283581d71aba3c2914116298a146af57d8156b1583f183fc05c0aa48ee95bec71821a001c41caa1581c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52a14f537061636542756442696433303533015820f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8825839015627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9a013112333b21ec5063ae54f31b0ea883635b64530b70785a49c95041a040228dd021a000db2d907582029ed935cc80249c4de9f3e96fdcea6b7da123a543bbe75fffe9e2c66119e426d0b58201907c235a0df870e95152669f7c147d6e3a7e251b57e4d5227556d1fd0caca0b0d81825820a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b000e81581c1c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c");
}
#[test]
fn test_contract_dummy_exunit() {
let mut tx_builder = create_realistic_tx_builder();
let mut spacebudz_asset = MultiAsset::new();
spacebudz_asset.set(
PolicyId::from_hex("6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52").unwrap(),
AssetName::new(hex::decode("537061636542756442696433303533").unwrap()).unwrap(),
1,
);
let private_key = &PrivateKey::from_normal_bytes(
&hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
.unwrap(),
)
.unwrap();
{
let required_signers = vec![
private_key.to_public().hash(),
];
let input_utxo = TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(Value::new(70000000, spacebudz_asset.clone()))
.build()
.unwrap();
tx_builder.add_input(SingleInputBuilder::new(
TransactionInput::new(
TransactionHash::from_hex("473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c").unwrap(),
0
),
input_utxo.output
).plutus_script(
PartialPlutusWitness::new(
PlutusScriptWitness::from(
PlutusScript::PlutusV1(PlutusV1Script::new(
hex::decode("59193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
))
),
PlutusData::from_cbor_bytes(&hex::decode("D866820380").unwrap()).unwrap(),
),
required_signers,
PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()
).unwrap()).unwrap();
}
{
let output_utxo = TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
)
.unwrap(),
)
.with_data(DatumOption::new_hash(
DatumHash::from_hex(
"f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8",
)
.unwrap(),
))
.next()
.unwrap()
.with_value(Value::new(1851850, spacebudz_asset))
.build()
.unwrap();
tx_builder.add_output(output_utxo).unwrap();
}
{
let output_utxo = TransactionOutputBuilder::new()
.with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
.next()
.unwrap()
.with_value(67250397)
.build()
.unwrap();
tx_builder.add_output(output_utxo).unwrap();
}
{
let input_utxo = TransactionOutputBuilder::new()
.with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
.next()
.unwrap()
.with_value(5000000)
.build()
.unwrap();
tx_builder
.add_collateral(
SingleInputBuilder::new(
TransactionInput::new(
TransactionHash::from_hex(
"a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b",
)
.unwrap(),
0,
),
input_utxo.output,
)
.payment_key()
.unwrap(),
)
.unwrap();
}
{
let mut map = MetadatumMap::new();
map.set(
TransactionMetadatum::new_int(0u64.into()),
TransactionMetadatum::new_bytes(hex::decode("d866820080").unwrap()).unwrap(),
);
let mut aux_data = AuxiliaryData::new();
aux_data
.metadata_mut()
.set(405, TransactionMetadatum::new_map(map));
tx_builder.add_auxiliary_data(aux_data);
}
tx_builder.set_fee(897753);
let mut tx_redeemer_builder = tx_builder
.build_for_evaluation(
ChangeSelectionAlgo::Default,
&Address::from_bech32("addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed")
.unwrap(),
)
.unwrap();
let fake_script_hash = tx_redeemer_builder.draft_body().script_data_hash.unwrap();
assert_eq!(
fake_script_hash.to_hex(),
"0000000000000000000000000000000000000000000000000000000000000000"
);
{
tx_redeemer_builder.set_exunits(
RedeemerWitnessKey::new(RedeemerTag::Spend, 0),
ExUnits::new(5000000, 2000000000),
);
tx_builder.set_exunits(
RedeemerWitnessKey::new(RedeemerTag::Spend, 0),
ExUnits::new(5000000, 2000000000),
);
}
let signed_tx_builder = tx_builder
.build(
ChangeSelectionAlgo::Default,
&Address::from_bech32("addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed")
.unwrap(),
)
.unwrap();
let real_script_hash = signed_tx_builder.body.script_data_hash.as_ref().unwrap();
assert_eq!(
real_script_hash.to_hex(),
"1907c235a0df870e95152669f7c147d6e3a7e251b57e4d5227556d1fd0caca0b"
);
let tx = &signed_tx_builder.body;
assert_eq!(hex::encode(tx.to_cbor_bytes()), "a70081825820473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c00018283581d71aba3c2914116298a146af57d8156b1583f183fc05c0aa48ee95bec71821a001c41caa1581c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52a14f537061636542756442696433303533015820f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8825839015627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9a013112333b21ec5063ae54f31b0ea883635b64530b70785a49c95041a040228dd021a000db2d907582029ed935cc80249c4de9f3e96fdcea6b7da123a543bbe75fffe9e2c66119e426d0b58201907c235a0df870e95152669f7c147d6e3a7e251b57e4d5227556d1fd0caca0b0d81825820a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b000e81581c1c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c");
}
#[test]
fn test_collateral() {
let mut tx_builder = create_realistic_tx_builder();
let mut spacebudz_asset = MultiAsset::new();
spacebudz_asset.set(
PolicyId::from_hex("6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52").unwrap(),
AssetName::new(hex::decode("537061636542756442696433303533").unwrap()).unwrap(),
1,
);
let private_key = PrivateKey::from_normal_bytes(
&hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
.unwrap(),
)
.unwrap();
{
let required_signers = vec![
private_key.to_public().hash(),
];
let input_utxo = TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
)
.unwrap(),
)
.next()
.unwrap()
.with_value(Value::new(70000000, spacebudz_asset.clone()))
.build()
.unwrap();
tx_builder.add_input(SingleInputBuilder::new(
TransactionInput::new(
TransactionHash::from_hex("473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c").unwrap(),
0
),
input_utxo.output
).plutus_script(
PartialPlutusWitness::new(
PlutusScriptWitness::from(
PlutusScript::PlutusV1(PlutusV1Script::new(
hex::decode("59193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
))
),
PlutusData::from_cbor_bytes(&hex::decode("D866820380").unwrap()).unwrap(),
),
required_signers,
PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()
).unwrap()).unwrap();
}
{
let output_utxo = TransactionOutputBuilder::new()
.with_address(
Address::from_bech32(
"addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
)
.unwrap(),
)
.with_data(DatumOption::new_hash(
DatumHash::from_hex(
"f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8",
)
.unwrap(),
))
.next()
.unwrap()
.with_value(Value::new(1851850, spacebudz_asset))
.build()
.unwrap();
tx_builder.add_output(output_utxo).unwrap();
}
{
let output_utxo = TransactionOutputBuilder::new()
.with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
.next()
.unwrap()
.with_value(67250397)
.build()
.unwrap();
tx_builder.add_output(output_utxo).unwrap();
}
{
let input_utxo = TransactionOutputBuilder::new()
.with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
.next()
.unwrap()
.with_value(5000000)
.build()
.unwrap();
tx_builder
.add_collateral(
SingleInputBuilder::new(
TransactionInput::new(
TransactionHash::from_hex(
"a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b",
)
.unwrap(),
0,
),
input_utxo.output,
)
.payment_key()
.unwrap(),
)
.unwrap();
}
{
let mut map = MetadatumMap::new();
map.set(
TransactionMetadatum::new_int(0u64.into()),
TransactionMetadatum::new_bytes(hex::decode("d866820080").unwrap()).unwrap(),
);
let mut aux_data = AuxiliaryData::new();
aux_data
.metadata_mut()
.set(405, TransactionMetadatum::new_map(map));
tx_builder.add_auxiliary_data(aux_data);
}
tx_builder.set_collateral_return(
TransactionOutputBuilder::new()
.with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
.next()
.unwrap()
.with_value(2000000)
.build()
.unwrap()
.output
);
tx_builder.set_fee(897753);
{
tx_builder.set_exunits(
RedeemerWitnessKey::new(RedeemerTag::Spend, 0),
ExUnits::new(5000000, 2000000000),
);
}
let signed_tx_builder = tx_builder
.build(
ChangeSelectionAlgo::Default,
&Address::from_bech32("addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed")
.unwrap(),
)
.unwrap();
assert_eq!(signed_tx_builder.body.total_collateral, Some(3000000));
}
#[test]
fn build_tx_with_ref_input() {
let mut tx_builder = create_default_tx_builder();
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(5_000_000), None, None),
)
.payment_key()
.unwrap()
};
tx_builder.add_input(input).unwrap();
{
let input_utxo = TransactionOutputBuilder::new()
.with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
.next()
.unwrap()
.with_value(5000000)
.build()
.unwrap();
tx_builder
.add_collateral(
SingleInputBuilder::new(
TransactionInput::new(
TransactionHash::from_hex(
"a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b",
)
.unwrap(),
0,
),
input_utxo.output,
)
.payment_key()
.unwrap(),
)
.unwrap();
}
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
{
let output = TransactionOutputBuilder::new()
.with_address(BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred.clone(), stake_cred.clone()).to_address())
.with_reference_script(
Script::new_plutus_v1(
PlutusV1Script::new(
hex::decode("59193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
)
)
)
.with_data(DatumOption::new_datum(PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()))
.next().unwrap()
.with_value(880_000)
.build().unwrap();
tx_builder.add_reference_input(TransactionUnspentOutput::new(
TransactionInput::new(genesis_id(), 1),
output.output,
));
}
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(880_000)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1000);
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert_eq!(tx_builder.outputs.len(), 2);
let final_tx = tx_builder
.build(ChangeSelectionAlgo::Default, &change_addr)
.unwrap()
.build_unchecked();
assert_eq!(final_tx.body.reference_inputs.unwrap().len(), 1);
assert!(final_tx.witness_set.plutus_v1_scripts.is_none());
}
#[test]
fn build_tx_with_ref_input_script() {
let mut tx_builder = create_default_tx_builder();
let change_key = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(1)
.derive(0)
.to_public();
let (_, (_, stake_cred), addr_net_0) = create_account();
let script = Script::new_plutus_v2(
PlutusV2Script::new(
hex::decode("59193d020000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
)
);
let script_hash = script.hash();
let script_base_address = BaseAddress::new(
NetworkInfo::testnet().network_id(),
StakeCredential::new_script(script_hash),
stake_cred.clone(),
);
let input = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 0),
TransactionOutput::new(addr_net_0.clone(), Value::from(5_000_000), None, None),
)
.payment_key()
.unwrap()
};
let input2 = {
SingleInputBuilder::new(
TransactionInput::new(genesis_id(), 1),
TransactionOutput::new(
script_base_address.to_address(),
Value::from(5_000_000),
None,
None,
),
)
.plutus_script(
PartialPlutusWitness::new(
PlutusScriptWitness::from(script_hash),
PlutusData::new_bytes(vec![]),
),
vec![],
PlutusData::from_cbor_bytes(&hex::decode("D866820380").unwrap()).unwrap(),
)
.unwrap()
};
let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
let output = TransactionOutputBuilder::new()
.with_address(BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred.clone(), stake_cred.clone()).to_address())
.with_reference_script(
Script::new_plutus_v2(
PlutusV2Script::new(
hex::decode("59193d020000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
)
)
)
.with_data(DatumOption::new_datum(PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()))
.next().unwrap()
.with_value(880_000)
.build().unwrap();
tx_builder.add_reference_input(TransactionUnspentOutput::new(
TransactionInput::new(genesis_id(), 1),
output.output,
));
tx_builder.add_input(input).unwrap();
tx_builder.add_input(input2).unwrap();
tx_builder
.add_output(
TransactionOutputBuilder::new()
.with_address(addr_net_0)
.next()
.unwrap()
.with_value(880_000)
.build()
.unwrap(),
)
.unwrap();
tx_builder.set_ttl(1000);
let change_addr =
BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
.to_address();
tx_builder
.add_change_if_needed_for_tests(&change_addr)
.unwrap();
assert_eq!(tx_builder.outputs.len(), 2);
let final_tx = tx_builder
.build(ChangeSelectionAlgo::Default, &change_addr)
.unwrap()
.build_unchecked();
assert_eq!(final_tx.body.reference_inputs.unwrap().len(), 1);
assert!(final_tx.witness_set.plutus_v2_scripts.is_none());
assert!(final_tx.witness_set.plutus_v1_scripts.is_none());
}
}