Skip to content

Commit

Permalink
Replaced redundand fields in tx-builder with one config field
Browse files Browse the repository at this point in the history
  • Loading branch information
vsubhuman committed Nov 22, 2021
1 parent 32008de commit e7c5cbe
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 61 deletions.
2 changes: 0 additions & 2 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

130 changes: 71 additions & 59 deletions rust/src/tx_builder.rs
Expand Up @@ -129,7 +129,7 @@ fn fake_full_tx(tx_builder: &TransactionBuilder, body: TransactionBody) -> Resul

fn min_fee(tx_builder: &TransactionBuilder) -> Result<Coin, JsError> {
let full_tx = fake_full_tx(tx_builder, tx_builder.build()?)?;
fees::min_fee(&full_tx, &tx_builder.fee_algo)
fees::min_fee(&full_tx, &tx_builder.config.fee_algo)
}


Expand All @@ -153,15 +153,23 @@ pub enum CoinSelectionStrategyCIP2 {
RandomImprove,
}

#[wasm_bindgen]
#[derive(Clone, Builder, Debug)]
pub struct TransactionBuilderConfig {
fee_algo: fees::LinearFee,
pool_deposit: BigNum, // protocol parameter
key_deposit: BigNum, // protocol parameter
max_value_size: u32, // protocol parameter
max_tx_size: u32, // protocol parameter
coins_per_utxo_word: Coin, // protocol parameter
#[builder(default = "false")]
prefer_pure_change: bool,
}

#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct TransactionBuilder {
coins_per_utxo_word: BigNum,
pool_deposit: BigNum,
key_deposit: BigNum,
max_value_size: u32,
max_tx_size: u32,
fee_algo: fees::LinearFee,
config: TransactionBuilderConfig,
inputs: Vec<TxBuilderInput>,
outputs: TransactionOutputs,
fee: Option<Coin>,
Expand All @@ -173,20 +181,6 @@ pub struct TransactionBuilder {
input_types: MockWitnessSet,
mint: Option<Mint>,
inputs_auto_added: bool,
prefer_pure_change: bool,
}

#[wasm_bindgen]
#[derive(Clone, Builder, Debug)]
pub struct TransactionBuilderConfig {
linear_fee: fees::LinearFee,
pool_deposit: BigNum, // protocol parameter
key_deposit: BigNum, // protocol parameter
max_value_size: u32, // protocol parameter
max_tx_size: u32, // protocol parameter
coins_per_utxo_word: Coin, // protocol parameter
#[builder(default = "false")]
prefer_pure_change: bool,
}

#[wasm_bindgen]
Expand Down Expand Up @@ -430,25 +424,32 @@ impl TransactionBuilder {
address: &Address,
multiasset: &MultiAsset,
) -> Result<(), JsError> {
let min_possible_coin = min_pure_ada(&self.coins_per_utxo_word)?;
let min_possible_coin = min_pure_ada(&self.config.coins_per_utxo_word)?;
let mut value = Value::new(&min_possible_coin);
value.set_multiasset(multiasset);
let required_coin =
min_ada_required(&value, false, &self.coins_per_utxo_word)?;
let required_coin = min_ada_required(
&value,
false,
&self.config.coins_per_utxo_word,
)?;
self.add_output_coin_and_asset(address, &required_coin, multiasset)
}

/// Add explicit output via a TransactionOutput object
pub fn add_output(&mut self, output: &TransactionOutput) -> Result<(), JsError> {
let value_size = output.amount.to_bytes().len();
if value_size > self.max_value_size as usize {
if value_size > self.config.max_value_size as usize {
return Err(JsError::from_str(&format!(
"Maximum value size of {} exceeded. Found: {}",
self.max_value_size,
self.config.max_value_size,
value_size
)));
}
let min_ada = min_ada_required(&output.amount(), false, &self.coins_per_utxo_word)?;
let min_ada = min_ada_required(
&output.amount(),
false,
&self.config.coins_per_utxo_word,
)?;
if output.amount().coin() < min_ada {
Err(JsError::from_str(&format!(
"Value {} less than the minimum UTXO value {}",
Expand Down Expand Up @@ -628,20 +629,9 @@ impl TransactionBuilder {
self.add_output_asset_and_min_required_coin(address, &multiasset)
}

/// If set to true, add_change_if_needed will try
/// to put pure Coin in a separate output from assets
pub fn set_prefer_pure_change(&mut self, prefer_pure_change: bool) {
self.prefer_pure_change = prefer_pure_change;
}

pub fn new(cfg: &TransactionBuilderConfig) -> Self {
Self {
coins_per_utxo_word: cfg.coins_per_utxo_word.clone(),
key_deposit: cfg.key_deposit.clone(),
pool_deposit: cfg.pool_deposit.clone(),
max_value_size: cfg.max_value_size,
max_tx_size: cfg.max_tx_size,
fee_algo: cfg.linear_fee.clone(),
config: cfg.clone(),
inputs: Vec::new(),
outputs: TransactionOutputs::new(),
fee: None,
Expand All @@ -657,7 +647,6 @@ impl TransactionBuilder {
validity_start_interval: None,
mint: None,
inputs_auto_added: false,
prefer_pure_change: cfg.prefer_pure_change,
}
}

Expand All @@ -675,8 +664,8 @@ impl TransactionBuilder {
internal_get_implicit_input(
&self.withdrawals,
&self.certs,
&self.pool_deposit,
&self.key_deposit,
&self.config.pool_deposit,
&self.config.key_deposit,
)
}

Expand Down Expand Up @@ -709,8 +698,8 @@ impl TransactionBuilder {
pub fn get_deposit(&self) -> Result<Coin, JsError> {
internal_get_deposit(
&self.certs,
&self.pool_deposit,
&self.key_deposit,
&self.config.pool_deposit,
&self.config.key_deposit,
)
}

Expand Down Expand Up @@ -800,17 +789,25 @@ impl TransactionBuilder {
let mut new_fee = fee.clone();
// we might need multiple change outputs for cases where the change has many asset types
// which surpass the max UTXO size limit
let minimum_utxo_val = min_pure_ada(&self.coins_per_utxo_word)?;
let minimum_utxo_val = min_pure_ada(&self.config.coins_per_utxo_word)?;
while let Some(Ordering::Greater) = change_left.multiasset.as_ref().map_or_else(|| None, |ma| ma.partial_cmp(&MultiAsset::new())) {
let nft_change = pack_nfts_for_change(self.max_value_size, address, &change_left)?;
let nft_change = pack_nfts_for_change(
self.config.max_value_size,
address,
&change_left,
)?;
if nft_change.len() == 0 {
// this likely should never happen
return Err(JsError::from_str("NFTs too large for change output"));
}
// we only add the minimum needed (for now) to cover this output
let mut change_value = Value::new(&Coin::zero());
change_value.set_multiasset(&nft_change);
let min_ada = min_ada_required(&change_value, false, &self.coins_per_utxo_word)?;
let min_ada = min_ada_required(
&change_value,
false,
&self.config.coins_per_utxo_word,
)?;
change_value.set_coin(&min_ada);
let change_output = TransactionOutput::new(address, &change_value);
// increase fee
Expand All @@ -825,7 +822,7 @@ impl TransactionBuilder {
change_left = change_left.checked_sub(&Value::new(&new_fee))?;
// add potentially a separate pure ADA change output
let left_above_minimum = change_left.coin.compare(&minimum_utxo_val) > 0;
if self.prefer_pure_change && left_above_minimum {
if self.config.prefer_pure_change && left_above_minimum {
let pure_output = TransactionOutput::new(address, &change_left);
let additional_fee = self.fee_for_output(&pure_output)?;
let potential_pure_value = change_left.checked_sub(&Value::new(&additional_fee))?;
Expand All @@ -843,7 +840,11 @@ impl TransactionBuilder {
}
Ok(true)
} else {
let min_ada = min_ada_required(&change_estimator, false, &self.coins_per_utxo_word)?;
let min_ada = min_ada_required(
&change_estimator,
false,
&self.config.coins_per_utxo_word,
)?;
// no-asset case so we have no problem burning the rest if there is no other option
fn burn_extra(builder: &mut TransactionBuilder, burn_amount: &BigNum) -> Result<bool, JsError> {
// recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being
Expand Down Expand Up @@ -926,10 +927,10 @@ impl TransactionBuilder {
/// You can use `get_auxiliary_date` or `build_tx`
pub fn build(&self) -> Result<TransactionBody, JsError> {
let (body, full_tx_size) = self.build_and_size()?;
if full_tx_size > self.max_tx_size as usize {
if full_tx_size > self.config.max_tx_size as usize {
Err(JsError::from_str(&format!(
"Maximum transaction size of {} exceeded. Found: {}",
self.max_tx_size,
self.config.max_tx_size,
full_tx_size
)))
} else {
Expand Down Expand Up @@ -1016,7 +1017,7 @@ mod tests {
coins_per_utxo_word: u64,
) -> TransactionBuilder {
let cfg = TransactionBuilderConfigBuilder::default()
.linear_fee(linear_fee.clone())
.fee_algo(linear_fee.clone())
.pool_deposit(to_bignum(pool_deposit))
.key_deposit(to_bignum(key_deposit))
.max_value_size(max_val_size)
Expand Down Expand Up @@ -1053,6 +1054,19 @@ mod tests {
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.clone())
.pool_deposit(to_bignum(1))
.key_deposit(to_bignum(1))
.max_value_size(MAX_VALUE_SIZE)
.max_tx_size(MAX_TX_SIZE)
.coins_per_utxo_word(to_bignum(1))
.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)
}
Expand Down Expand Up @@ -1760,9 +1774,8 @@ mod tests {
fn build_tx_with_native_assets_change_and_purification() {
let minimum_utxo_value = to_bignum(1);
let coin_per_utxo_word = to_bignum(1);
let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 1));
// Prefer pure change!
tx_builder.set_prefer_pure_change(true);
let mut tx_builder = create_tx_builder_with_fee_and_pure_change(&create_linear_fee(0, 1));
let spend = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
Expand Down Expand Up @@ -1888,9 +1901,8 @@ mod tests {
#[test]
fn build_tx_with_native_assets_change_and_no_purification_cuz_not_enough_pure_coin() {
let minimum_utxo_value = to_bignum(10);
let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(1, 1));
// Prefer pure change!
tx_builder.set_prefer_pure_change(true);
let mut tx_builder = create_tx_builder_with_fee_and_pure_change(&create_linear_fee(1, 1));
let spend = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
Expand Down Expand Up @@ -2464,7 +2476,7 @@ mod tests {
// we have a = 1 to test increasing fees when more inputs are added
let linear_fee = LinearFee::new(&to_bignum(1), &to_bignum(0));
let cfg = TransactionBuilderConfigBuilder::default()
.linear_fee(linear_fee)
.fee_algo(linear_fee)
.pool_deposit(to_bignum(0))
.key_deposit(to_bignum(0))
.max_value_size(9999)
Expand All @@ -2491,7 +2503,7 @@ mod tests {
// we have a = 1 to test increasing fees when more inputs are added
let linear_fee = LinearFee::new(&to_bignum(1), &to_bignum(0));
let cfg = TransactionBuilderConfigBuilder::default()
.linear_fee(linear_fee)
.fee_algo(linear_fee)
.pool_deposit(to_bignum(0))
.key_deposit(to_bignum(0))
.max_value_size(9999)
Expand Down Expand Up @@ -3181,7 +3193,7 @@ mod tests {
mint.insert(&policy_id3, &mass);

let mint_len = mint.to_bytes().len();
let fee_coefficient = tx_builder.fee_algo.coefficient();
let fee_coefficient = tx_builder.config.fee_algo.coefficient();

let raw_mint_fee = fee_coefficient
.checked_mul(&to_bignum(mint_len as u64))
Expand Down

0 comments on commit e7c5cbe

Please sign in to comment.