From 3d76a8f5921edf9fc8288de4c9f6a6bd324bf532 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Wed, 11 Aug 2021 00:15:48 -0500 Subject: [PATCH 1/8] TxBuilder: max output size + multiple change address support Users with many NFTs could run into problems creating certain txs that returned many of them as a change output as it would surpass the maximum output size limit in the ledger. The TxBuilder now takes this limit into account and errors if it is passed, and during the change output creation will attempt to spread out the NFTs over multiple outputs to not surpass the ledger limit. This is done as a greedy selection for now as the limit is currently 4kb or so and each asset is at most 64 bytes each so this should suffice. --- rust/src/lib.rs | 8 +- rust/src/tx_builder.rs | 352 ++++++++++++++++++++++++++++++++++------- rust/src/utils.rs | 8 +- 3 files changed, 303 insertions(+), 65 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ba3d0448..429437f3 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -385,7 +385,7 @@ impl TransactionInput { #[derive(Clone, Debug)] pub struct TransactionOutput { address: Address, - amount: Value, + pub (crate) amount: Value, data_hash: Option, } @@ -2461,7 +2461,9 @@ pub type PolicyIDs = ScriptHashes; #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct Assets(std::collections::BTreeMap); +pub struct Assets(pub (crate) std::collections::BTreeMap); + +to_from_bytes!(Assets); #[wasm_bindgen] impl Assets { @@ -2488,7 +2490,7 @@ impl Assets { #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq)] -pub struct MultiAsset(std::collections::BTreeMap); +pub struct MultiAsset(pub (crate) std::collections::BTreeMap); to_from_bytes!(MultiAsset); diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index 2adf60c9..cdb2f99d 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -121,6 +121,7 @@ pub struct TransactionBuilder { minimum_utxo_val: BigNum, pool_deposit: BigNum, key_deposit: BigNum, + max_output_size: u32, fee_algo: fees::LinearFee, inputs: Vec, outputs: TransactionOutputs, @@ -214,7 +215,7 @@ impl TransactionBuilder { let mut self_copy = self.clone(); // we need some value for these for it to be a a valid transaction - // but since we're only calculating the different between the fee of two transactions + // but since we're only calculating the difference between the fee of two transactions // it doesn't matter what these are set as, since it cancels out self_copy.set_fee(&to_bignum(0)); @@ -226,6 +227,14 @@ impl TransactionBuilder { } pub fn add_output(&mut self, output: &TransactionOutput) -> Result<(), JsError> { + let output_size = output.to_bytes().len(); + if output_size > self.max_output_size as usize { + return Err(JsError::from_str(&format!( + "Maximum output size of {} exceeded. Found: {}", + self.max_output_size, + output_size + ))); + } let min_ada = min_ada_required(&output.amount(), &self.minimum_utxo_val); if output.amount().coin() < min_ada { Err(JsError::from_str(&format!( @@ -240,7 +249,7 @@ impl TransactionBuilder { } /// calculates how much the fee would increase if you added a given output - pub fn fee_for_output(&mut self, output: &TransactionOutput) -> Result { + pub fn fee_for_output(&self, output: &TransactionOutput) -> Result { let mut self_copy = self.clone(); // we need some value for these for it to be a a valid transaction @@ -291,11 +300,13 @@ impl TransactionBuilder { minimum_utxo_val: &Coin, pool_deposit: &BigNum, // protocol parameter key_deposit: &BigNum, // protocol parameter + max_output_size: u32, // protocol parameter ) -> Self { Self { minimum_utxo_val: minimum_utxo_val.clone(), key_deposit: key_deposit.clone(), pool_deposit: pool_deposit.clone(), + max_output_size, fee_algo: linear_fee.clone(), inputs: Vec::new(), outputs: TransactionOutputs::new(), @@ -383,19 +394,97 @@ impl TransactionBuilder { }, Some(Ordering::Less) => Err(JsError::from_str("Insufficient input in transaction")), Some(Ordering::Greater) => { + fn has_assets(ma: Option) -> bool { + ma.map(|assets| assets.len() > 0).unwrap_or(false) + } let change_estimator = input_total.checked_sub(&output_total)?; - let min_ada = min_ada_required(&change_estimator.clone(), &self.minimum_utxo_val); - - let enough_input = | - builder: &mut TransactionBuilder, - burn_fee: &dyn Fn(&mut TransactionBuilder, &BigNum) -> Result, - add_change: &dyn Fn(&mut TransactionBuilder, &BigNum) -> Result - | { + if has_assets(change_estimator.multiasset()) { + fn pack_nfts_for_change(max_output_size: u32, change_address: &Address, change_estimator: &Value) -> Result { + // we insert the entire available ADA temporarily here since that could potentially impact the size + // as it could be 1, 2 3 or 4 bytes for Coin. + let mut base_coin = Value::new(&change_estimator.coin()); + base_coin.set_multiasset(&MultiAsset::new()); + let mut output = TransactionOutput::new(change_address, &base_coin); + // If this becomes slow on large TXs we can optimize it like the folowing + // to avoid cloning + reserializing the entire output. + // This would probably be more relevant if we use a smarter packing algorithm + // which might need to compare more size differences than greedy + //let mut bytes_used = output.to_bytes().len(); + + // a greedy packing is done here to avoid an exponential bin-packing + // which in most cases likely shouldn't be the difference between + // having an extra change output or not unless there are gigantic + // differences in NFT policy sizes + for (policy, assets) in change_estimator.multiasset().unwrap().0.iter() { + // for simplicity we also don't split assets within a single policy since + // you would need to have a very high amoun of assets (which add 1-36 bytes each) + // in a single policy to make a difference. In the future if this becomes an issue + // we can change that here. + + // this is the other part of the optimization but we need to take into account + // the difference between CBOR encoding which can change which happens in two places: + // a) length within assets of one policy id + // b) length of the entire multiasset + // so for simplicity we will just do it the safe, naive way unless + // performance becomes an issue. + //let extra_bytes = policy.to_bytes().len() + assets.to_bytes().len() + 2 + cbor_len_diff; + //if bytes_used + extra_bytes <= max_output_size as usize { + let old_amount = output.amount.clone(); + let mut val = Value::new(&Coin::zero()); + let mut next_nft = MultiAsset::new(); + next_nft.insert(policy, assets); + val.set_multiasset(&next_nft); + output.amount = output.amount.checked_add(&val)?; + if output.to_bytes().len() > max_output_size as usize { + output.amount = old_amount; + break; + } + } + Ok(output.amount.multiasset().unwrap()) + } + let mut change_left = input_total.checked_sub(&output_total)?; + 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 + 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_output_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, &self.minimum_utxo_val); + change_value.set_coin(&min_ada); + let change_output = TransactionOutput::new(address, &change_value); + // increase fee + let fee_for_change = self.fee_for_output(&change_output)?; + new_fee = new_fee.checked_add(&fee_for_change)?; + if change_left.coin() < min_ada.checked_add(&new_fee)? { + return Err(JsError::from_str("Not enough ADA leftover to include non-ADA assets in a change address")); + } + change_left = change_left.checked_sub(&change_value)?; + self.add_output(&change_output)?; + } + change_left = change_left.checked_sub(&Value::new(&new_fee))?; + self.set_fee(&new_fee); + // add in the rest of the ADA + self.outputs.0.last_mut().unwrap().amount = self.outputs.0.last().unwrap().amount.checked_add(&change_left)?; + Ok(true) + } else { + let min_ada = min_ada_required(&change_estimator, &self.minimum_utxo_val); + // 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 { + // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being + builder.set_fee(burn_amount); + Ok(false) // not enough input to covert the extra fee from adding an output so we just burn whatever is left + }; match change_estimator.coin() >= min_ada { - false => burn_fee(builder, &change_estimator.coin()), + false => burn_extra(self, &change_estimator.coin()), true => { // check how much the fee would increase if we added a change output - let fee_for_change = builder.fee_for_output(&TransactionOutput { + let fee_for_change = self.fee_for_output(&TransactionOutput { address: address.clone(), amount: change_estimator.clone(), // TODO: data hash? @@ -404,49 +493,23 @@ impl TransactionBuilder { let new_fee = fee.checked_add(&fee_for_change)?; match change_estimator.coin() >= min_ada.checked_add(&Value::new(&new_fee).coin())? { - false => burn_fee(builder, &change_estimator.coin()), - true => add_change(builder, &new_fee) + false => burn_extra(self, &change_estimator.coin()), + true => { + // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being + self.set_fee(&new_fee); + + self.add_output(&TransactionOutput { + address: address.clone(), + amount: change_estimator.checked_sub(&Value::new(&new_fee.clone()))?, + data_hash: None, // TODO: How do we get DataHash? + })?; + + Ok(true) + } } } } - }; - - let burn_extra = | - builder: &mut TransactionBuilder, - burn_amount: &BigNum - | { - let has_assets = change_estimator.multiasset().map(|assets| assets.len() > 0).unwrap_or(false); - match has_assets { - false => { - // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being - builder.set_fee(burn_amount); - Ok(false) // not enough input to covert the extra fee from adding an output so we just burn whatever is left - }, - true => Err(JsError::from_str("Not enough ADA leftover to include non-ADA assets in a change address")), - } - }; - - let add_change = | - builder: &mut TransactionBuilder, - new_fee: &BigNum - | { - // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being - builder.set_fee(&new_fee); - - builder.add_output(&TransactionOutput { - address: address.clone(), - amount: change_estimator.checked_sub(&Value::new(&new_fee.clone()))?, - data_hash: None, // TODO: How do we get DataHash? - })?; - - Ok(true) - }; - - enough_input( - self, - &burn_extra, - &add_change - ) + } } None => Err(JsError::from_str("missing input for some native asset")), } @@ -491,6 +554,8 @@ mod tests { use super::*; use fees::*; + const MAX_OUTPUT_SIZE: u32 = 4000; + fn genesis_id() -> TransactionHash { TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]) } @@ -509,7 +574,7 @@ mod tests { fn build_tx_with_change() { let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1)); + TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1), MAX_OUTPUT_SIZE); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -564,7 +629,7 @@ mod tests { fn build_tx_without_change() { let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1)); + TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1), MAX_OUTPUT_SIZE); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -623,6 +688,7 @@ mod tests { &to_bignum(1), &to_bignum(1), &to_bignum(1_000_000), + MAX_OUTPUT_SIZE ); let spend = root_key_15() .derive(harden(1852)) @@ -686,7 +752,7 @@ mod tests { // transactions where sum(input) == sum(output) exact should pass let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(0)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(0)); + TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(0), MAX_OUTPUT_SIZE); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -737,7 +803,7 @@ mod tests { // transactions where we have exactly enough ADA to add change should pass let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(0)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(0)); + TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(0), MAX_OUTPUT_SIZE); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -797,7 +863,7 @@ mod tests { // transactions should fail with insufficient fees if a deposit is required let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(0)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(5)); + TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(5), MAX_OUTPUT_SIZE); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -862,7 +928,7 @@ mod tests { fn build_tx_with_inputs() { let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1)); + TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1), MAX_OUTPUT_SIZE); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -941,6 +1007,7 @@ mod tests { &minimum_utxo_value, &to_bignum(0), &to_bignum(0), + MAX_OUTPUT_SIZE ); let spend = root_key_15() .derive(harden(1852)) @@ -1051,7 +1118,7 @@ mod tests { fn build_tx_leftover_assets() { let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1)); + TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1), MAX_OUTPUT_SIZE); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -1119,7 +1186,7 @@ mod tests { fn build_tx_burn_less_than_min_ada() { let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1000000), &to_bignum(500000000), &to_bignum(2000000)); + TransactionBuilder::new(&linear_fee, &to_bignum(1000000), &to_bignum(500000000), &to_bignum(2000000), MAX_OUTPUT_SIZE); let output_addr = ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b").unwrap(); tx_builder.add_output(&TransactionOutput::new( @@ -1155,7 +1222,7 @@ mod tests { fn build_tx_burn_empty_assets() { let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1000000), &to_bignum(500000000), &to_bignum(2000000)); + TransactionBuilder::new(&linear_fee, &to_bignum(1000000), &to_bignum(500000000), &to_bignum(2000000), MAX_OUTPUT_SIZE); let output_addr = ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b").unwrap(); tx_builder.add_output(&TransactionOutput::new( @@ -1188,4 +1255,169 @@ mod tests { ); let _final_tx = tx_builder.build(); // just test that it doesn't throw } + + #[test] + fn build_tx_add_change_split_nfts() { + let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(1)); + let minimum_utxo_value = to_bignum(1); + let max_output_size = 150; // super low max output size to test with fewer assets + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &minimum_utxo_value, + &to_bignum(0), + &to_bignum(0), + max_output_size + ); + + 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.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &to_bignum(500)); + assets + }); + acc + }); + + let mut input_value = Value::new(&to_bignum(10)); + input_value.set_multiasset(&multiasset); + + tx_builder.add_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3").unwrap().to_address(), + &TransactionInput::new( + &genesis_id(), + 0 + ), + &input_value + ); + + let output_addr = ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b").unwrap().to_address(); + let output_amount = Value::new(&to_bignum(1)); + + tx_builder + .add_output(&TransactionOutput::new(&output_addr, &output_amount)) + .unwrap(); + + let change_addr = ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho").unwrap().to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(added_change, true); + let final_tx = tx_builder.build().unwrap(); + assert_eq!(final_tx.outputs().len(), 3); + for (policy_id, asset_name) in policy_ids.iter().zip(names.iter()) { + assert!(final_tx + .outputs + .0 + .iter() + .find(|output| output.amount.multiasset.as_ref().map_or_else( + || false, + |ma| ma.0.iter().find(|(pid, a)| *pid == policy_id + && a.0.iter().find(|(name, _)| *name == asset_name).is_some()).is_some() + )).is_some() + ); + } + for output in final_tx.outputs.0.iter() { + assert!(output.to_bytes().len() <= max_output_size as usize); + } + } + + #[test] + fn build_tx_too_big_output() { + let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(1)); + let minimum_utxo_value = to_bignum(1); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &minimum_utxo_value, + &to_bignum(0), + &to_bignum(0), + 10 // super low max output size to test + ); + + tx_builder.add_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3").unwrap().to_address(), + &TransactionInput::new( + &genesis_id(), + 0 + ), + &Value::new(&to_bignum(10)) + ); + + let output_addr = ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b").unwrap().to_address(); + let output_amount = Value::new(&to_bignum(1)); + + assert!(tx_builder.add_output(&TransactionOutput::new(&output_addr, &output_amount)).is_err()); + } + + #[test] + fn build_tx_add_change_nfts_not_enough_ada() { + let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(1)); + let minimum_utxo_value = to_bignum(1); + let max_output_size = 150; // super low max output size to test with fewer assets + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &minimum_utxo_value, + &to_bignum(0), + &to_bignum(0), + max_output_size + ); + + 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.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &to_bignum(500)); + assets + }); + acc + }); + + let mut input_value = Value::new(&to_bignum(2)); + input_value.set_multiasset(&multiasset); + + tx_builder.add_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3").unwrap().to_address(), + &TransactionInput::new( + &genesis_id(), + 0 + ), + &input_value + ); + + let output_addr = ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b").unwrap().to_address(); + let output_amount = Value::new(&to_bignum(1)); + + tx_builder + .add_output(&TransactionOutput::new(&output_addr, &output_amount)) + .unwrap(); + + let change_addr = ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho").unwrap().to_address(); + + assert!(tx_builder.add_change_if_needed(&change_addr).is_err()) + } } diff --git a/rust/src/utils.rs b/rust/src/utils.rs index cd513057..cba0b96a 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -156,6 +156,10 @@ impl BigNum { format!("{}", self.0) } + pub fn zero() -> Self { + Self(0) + } + pub fn checked_mul(&self, other: &BigNum) -> Result { match self.0.checked_mul(other.0) { Some(value) => Ok(BigNum(value)), @@ -222,8 +226,8 @@ pub type Coin = BigNum; #[wasm_bindgen] #[derive(Clone, Debug, Eq, /*Hash,*/ Ord, PartialEq)] pub struct Value { - coin: Coin, - multiasset: Option, + pub (crate) coin: Coin, + pub (crate) multiasset: Option, } to_from_bytes!(Value); From a6cdde5fed9cbd39618af1c162581aed4e9ab766 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Wed, 11 Aug 2021 18:20:54 -0500 Subject: [PATCH 2/8] TxBuilder: check against max tx size protocol param --- rust/src/tx_builder.rs | 123 ++++++++++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 25 deletions(-) diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index cdb2f99d..ca511f59 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -122,6 +122,7 @@ pub struct TransactionBuilder { pool_deposit: BigNum, key_deposit: BigNum, max_output_size: u32, + max_tx_size: u32, fee_algo: fees::LinearFee, inputs: Vec, outputs: TransactionOutputs, @@ -301,12 +302,14 @@ impl TransactionBuilder { pool_deposit: &BigNum, // protocol parameter key_deposit: &BigNum, // protocol parameter max_output_size: u32, // protocol parameter + max_tx_size: u32, // protocol parameter ) -> Self { Self { minimum_utxo_val: minimum_utxo_val.clone(), key_deposit: key_deposit.clone(), pool_deposit: pool_deposit.clone(), max_output_size, + max_tx_size, fee_algo: linear_fee.clone(), inputs: Vec::new(), outputs: TransactionOutputs::new(), @@ -517,7 +520,7 @@ impl TransactionBuilder { pub fn build(&self) -> Result { let fee = self.fee.ok_or_else(|| JsError::from_str("Fee not specified"))?; - Ok(TransactionBody { + let built = TransactionBody { inputs: TransactionInputs(self.inputs.iter().map(|ref tx_builder_input| tx_builder_input.input.clone()).collect()), outputs: self.outputs.clone(), fee: fee, @@ -536,7 +539,17 @@ impl TransactionBuilder { collateral: None, required_signers: None, network_id: None, - }) + }; + let built_size = built.to_bytes().len(); + if built_size > self.max_tx_size as usize { + Err(JsError::from_str(&format!( + "Maximum transaction size of {} exceeded. Found: {}", + self.max_tx_size, + built_size + ))) + } else { + Ok(built) + } } /// warning: sum of all parts of a transaction must equal 0. You cannot just set the fee to the min value and forget about it @@ -555,6 +568,7 @@ mod tests { use fees::*; const MAX_OUTPUT_SIZE: u32 = 4000; + const MAX_TX_SIZE: u32 = 8000; // might be out of date but suffices for our tests fn genesis_id() -> TransactionHash { TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]) @@ -573,8 +587,14 @@ mod tests { #[test] fn build_tx_with_change() { let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1), + &to_bignum(1), + &to_bignum(1), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -628,8 +648,14 @@ mod tests { #[test] fn build_tx_without_change() { let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1), + &to_bignum(1), + &to_bignum(1), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -688,7 +714,8 @@ mod tests { &to_bignum(1), &to_bignum(1), &to_bignum(1_000_000), - MAX_OUTPUT_SIZE + MAX_OUTPUT_SIZE, + MAX_TX_SIZE ); let spend = root_key_15() .derive(harden(1852)) @@ -751,8 +778,14 @@ mod tests { fn build_tx_exact_amount() { // transactions where sum(input) == sum(output) exact should pass let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(0)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(0), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1), + &to_bignum(0), + &to_bignum(0), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -802,8 +835,14 @@ mod tests { fn build_tx_exact_change() { // transactions where we have exactly enough ADA to add change should pass let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(0)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(0), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1), + &to_bignum(0), + &to_bignum(0), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -862,8 +901,14 @@ mod tests { fn build_tx_insufficient_deposit() { // transactions should fail with insufficient fees if a deposit is required let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(0)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(0), &to_bignum(5), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1), + &to_bignum(0), + &to_bignum(5), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -927,8 +972,14 @@ mod tests { #[test] fn build_tx_with_inputs() { let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1), + &to_bignum(1), + &to_bignum(1), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -1007,7 +1058,8 @@ mod tests { &minimum_utxo_value, &to_bignum(0), &to_bignum(0), - MAX_OUTPUT_SIZE + MAX_OUTPUT_SIZE, + MAX_TX_SIZE ); let spend = root_key_15() .derive(harden(1852)) @@ -1117,8 +1169,14 @@ mod tests { #[should_panic] fn build_tx_leftover_assets() { let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1), &to_bignum(1), &to_bignum(1), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1), + &to_bignum(1), + &to_bignum(1), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let spend = root_key_15() .derive(harden(1852)) .derive(harden(1815)) @@ -1185,8 +1243,14 @@ mod tests { #[test] fn build_tx_burn_less_than_min_ada() { let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1000000), &to_bignum(500000000), &to_bignum(2000000), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1000000), + &to_bignum(500000000), + &to_bignum(2000000), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let output_addr = ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b").unwrap(); tx_builder.add_output(&TransactionOutput::new( @@ -1221,8 +1285,14 @@ mod tests { #[test] fn build_tx_burn_empty_assets() { let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1000000), &to_bignum(500000000), &to_bignum(2000000), MAX_OUTPUT_SIZE); + let mut tx_builder = TransactionBuilder::new( + &linear_fee, + &to_bignum(1000000), + &to_bignum(500000000), + &to_bignum(2000000), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE + ); let output_addr = ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b").unwrap(); tx_builder.add_output(&TransactionOutput::new( @@ -1266,7 +1336,8 @@ mod tests { &minimum_utxo_value, &to_bignum(0), &to_bignum(0), - max_output_size + max_output_size, + MAX_TX_SIZE ); let policy_ids = [ @@ -1343,7 +1414,8 @@ mod tests { &minimum_utxo_value, &to_bignum(0), &to_bignum(0), - 10 // super low max output size to test + 10, // super low max output size to test, + MAX_TX_SIZE ); tx_builder.add_input( @@ -1371,7 +1443,8 @@ mod tests { &minimum_utxo_value, &to_bignum(0), &to_bignum(0), - max_output_size + max_output_size, + MAX_TX_SIZE ); let policy_ids = [ From 916ecdb66913c92058079ff1f0ccb1476adeb07e Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Wed, 11 Aug 2021 19:40:46 -0500 Subject: [PATCH 3/8] TxBuilder check against Transaction sie instead of TransactionBody size --- rust/src/tx_builder.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index ca511f59..1bedabd4 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -37,9 +37,10 @@ fn witness_keys_for_cert(cert_enum: &Certificate, keys: &mut BTreeSet Result { - let body = tx_builder.build()?; - +// tx_body must be the result of building from tx_builder +// constructs the rest of the Transaction using fake witness data of the correct length +// for use in calculating the size of the final Transaction +fn fake_full_tx(tx_builder: &TransactionBuilder, body: TransactionBody) -> Result { let fake_key_root = Bip32PrivateKey::from_bip39_entropy( // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy &[0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, 0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12], @@ -92,11 +93,15 @@ fn min_fee(tx_builder: &TransactionBuilder) -> Result { plutus_data: None, redeemers: None, }; - let full_tx = Transaction { + Ok(Transaction { body, witness_set, auxiliary_data: tx_builder.auxiliary_data.clone(), - }; + }) +} + +fn min_fee(tx_builder: &TransactionBuilder) -> Result { + let full_tx = fake_full_tx(tx_builder, tx_builder.build()?)?; fees::min_fee(&full_tx, &tx_builder.fee_algo) } @@ -540,15 +545,17 @@ impl TransactionBuilder { required_signers: None, network_id: None, }; - let built_size = built.to_bytes().len(); - if built_size > self.max_tx_size as usize { + // we must build a tx with fake data (of correct size) to check the final Transaction size + let full_tx = fake_full_tx(self, built)?; + let full_tx_size = full_tx.to_bytes().len(); + if full_tx_size > self.max_tx_size as usize { Err(JsError::from_str(&format!( "Maximum transaction size of {} exceeded. Found: {}", self.max_tx_size, - built_size + full_tx_size ))) } else { - Ok(built) + Ok(full_tx.body) } } From d4f7b2ec1f17e1a723b32e947121b49a8ff0569b Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Sat, 18 Sep 2021 23:12:10 +0300 Subject: [PATCH 4/8] cargo lock update --- rust/Cargo.lock | 2 -- 1 file changed, 2 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 05ebfe86..525226e5 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,7 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - [[package]] name = "aho-corasick" version = "0.7.15" From 15da1a75e30e189c9b7926b7ff1296e13cf2b4a3 Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Sun, 19 Sep 2021 00:04:05 +0300 Subject: [PATCH 5/8] new builder getter `full_size` --- rust/src/tx_builder.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index abe34bd1..5abda82a 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -523,7 +523,7 @@ impl TransactionBuilder { } } - pub fn build(&self) -> Result { + fn build_and_size(&self) -> (TransactionBody, usize) { let fee = self.fee.ok_or_else(|| JsError::from_str("Fee not specified"))?; let built = TransactionBody { inputs: TransactionInputs(self.inputs.iter().map(|ref tx_builder_input| tx_builder_input.input.clone()).collect()), @@ -548,6 +548,15 @@ impl TransactionBuilder { // we must build a tx with fake data (of correct size) to check the final Transaction size let full_tx = fake_full_tx(self, built)?; let full_tx_size = full_tx.to_bytes().len(); + return (full_tx.body, full_tx_size); + } + + pub fn full_size(&self) -> usize { + return self.build_and_size().1; + } + + pub fn build(&self) -> Result { + let (body, full_tx_size) = self.build_and_size(); if full_tx_size > self.max_tx_size as usize { Err(JsError::from_str(&format!( "Maximum transaction size of {} exceeded. Found: {}", @@ -555,7 +564,7 @@ impl TransactionBuilder { full_tx_size ))) } else { - Ok(full_tx.body) + Ok(body) } } From d6ed85ab33d960abe5ed994f47b5b5d818b4239c Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Sun, 19 Sep 2021 00:23:54 +0300 Subject: [PATCH 6/8] new builder getter `output_sizes` --- rust/src/tx_builder.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index 5abda82a..a1f03874 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -523,7 +523,7 @@ impl TransactionBuilder { } } - fn build_and_size(&self) -> (TransactionBody, usize) { + fn build_and_size(&self) -> Result<(TransactionBody, usize), JsError> { let fee = self.fee.ok_or_else(|| JsError::from_str("Fee not specified"))?; let built = TransactionBody { inputs: TransactionInputs(self.inputs.iter().map(|ref tx_builder_input| tx_builder_input.input.clone()).collect()), @@ -548,15 +548,19 @@ impl TransactionBuilder { // we must build a tx with fake data (of correct size) to check the final Transaction size let full_tx = fake_full_tx(self, built)?; let full_tx_size = full_tx.to_bytes().len(); - return (full_tx.body, full_tx_size); + return Ok((full_tx.body, full_tx_size)); } - pub fn full_size(&self) -> usize { - return self.build_and_size().1; + pub fn full_size(&self) -> Result { + return self.build_and_size().map(|r| { r.1 }); + } + + pub fn output_sizes(&self) -> Vec { + return self.outputs.0.iter().map(|o| { o.to_bytes().len() }).collect(); } pub fn build(&self) -> Result { - let (body, full_tx_size) = self.build_and_size(); + let (body, full_tx_size) = self.build_and_size()?; if full_tx_size > self.max_tx_size as usize { Err(JsError::from_str(&format!( "Maximum transaction size of {} exceeded. Found: {}", @@ -658,6 +662,8 @@ mod tests { tx_builder.get_explicit_input().unwrap().checked_add(&tx_builder.get_implicit_input().unwrap()).unwrap(), tx_builder.get_explicit_output().unwrap().checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())).unwrap() ); + assert_eq!(tx_builder.full_size().unwrap(), 283); + assert_eq!(tx_builder.output_sizes(), vec![61, 65]); let _final_tx = tx_builder.build(); // just test that it doesn't throw } @@ -1346,7 +1352,14 @@ mod tests { fn build_tx_no_useless_multiasset() { let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); let mut tx_builder = - TransactionBuilder::new(&linear_fee, &to_bignum(1000000), &to_bignum(500000000), &to_bignum(2000000)); + TransactionBuilder::new( + &linear_fee, + &to_bignum(1000000), + &to_bignum(500000000), + &to_bignum(2000000), + MAX_OUTPUT_SIZE, + MAX_TX_SIZE, + ); let policy_id = &PolicyID::from([0u8; 28]); let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); From b010a0627bf1d4e4d3f88eb8d7986a60f564c8eb Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Sun, 19 Sep 2021 14:16:01 +0300 Subject: [PATCH 7/8] version bump and flow update --- package-lock.json | 2 +- package.json | 6 +- rust/Cargo.lock | 2 +- rust/Cargo.toml | 2 +- rust/pkg/.gitignore | 1 - rust/pkg/cardano_serialization_lib.js.flow | 149 +++++++++++++-------- 6 files changed, 98 insertions(+), 64 deletions(-) delete mode 100644 rust/pkg/.gitignore diff --git a/package-lock.json b/package-lock.json index b9e67edb..1a9b2593 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cardano-serialization-lib", - "version": "8.1.0", + "version": "9.0.0-beta.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 8ba0f61c..2ef1b12b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cardano-serialization-lib", - "version": "8.1.0", + "version": "9.0.0-beta.1", "description": "(De)serialization functions for the Cardano blockchain along with related utility functions", "scripts": { "rust:build-nodejs": "rimraf ./rust/pkg && cd rust; wasm-pack build --target=nodejs; wasm-pack pack; cd .. && npm run js:flowgen", @@ -12,8 +12,8 @@ "rust:test": "cd rust; cargo test; cd ..", "js:flowgen": "flowgen ./rust/pkg/cardano_serialization_lib.d.ts -o ./rust/pkg/cardano_serialization_lib.js.flow --add-flow-header", "js:prepublish": "npm run rust:test && rimraf ./publish && cp -r ./rust/pkg ./publish && cp README.md publish/ && cp LICENSE publish/", - "js:publish-nodejs": "npm run rust:build-nodejs && npm run js:prepublish && node ./scripts/publish-helper -nodejs && cd publish && npm publish --access public", - "js:publish-browser": "npm run rust:build-browser && npm run js:prepublish && node ./scripts/publish-helper -browser && cd publish && npm publish --access public", + "js:publish-nodejs": "npm run rust:build-nodejs && npm run js:prepublish && node ./scripts/publish-helper -nodejs && cd publish && npm publish --tag beta --access public", + "js:publish-browser": "npm run rust:build-browser && npm run js:prepublish && node ./scripts/publish-helper -browser && cd publish && npm publish --tag beta --access public", "js:publish-asm": "npm run rust:build-asm && npm run js:prepublish && node ./scripts/publish-helper -asmjs && cd publish && npm publish --access public", "postinstall": "git submodule update --init --recursive && cd binaryen; cmake . && make" }, diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 72031072..0b8e2de0 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -50,7 +50,7 @@ checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" [[package]] name = "cardano-serialization-lib" -version = "8.1.0" +version = "9.0.0-beta.1" dependencies = [ "bech32", "cbor_event", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 81952100..e2e529c4 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cardano-serialization-lib" -version = "8.1.0" +version = "9.0.0-beta.1" edition = "2018" authors = ["EMURGO"] license = "MIT" diff --git a/rust/pkg/.gitignore b/rust/pkg/.gitignore deleted file mode 100644 index f59ec20a..00000000 --- a/rust/pkg/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* \ No newline at end of file diff --git a/rust/pkg/cardano_serialization_lib.js.flow b/rust/pkg/cardano_serialization_lib.js.flow index 3611b465..950cd8e2 100644 --- a/rust/pkg/cardano_serialization_lib.js.flow +++ b/rust/pkg/cardano_serialization_lib.js.flow @@ -5,6 +5,42 @@ * @flow */ +/** + * @param {Uint8Array} bytes + * @returns {TransactionMetadatum} + */ +declare export function encode_arbitrary_bytes_as_metadatum( + bytes: Uint8Array +): TransactionMetadatum; + +/** + * @param {TransactionMetadatum} metadata + * @returns {Uint8Array} + */ +declare export function decode_arbitrary_bytes_from_metadatum( + metadata: TransactionMetadatum +): Uint8Array; + +/** + * @param {string} json + * @param {number} schema + * @returns {TransactionMetadatum} + */ +declare export function encode_json_str_to_metadatum( + json: string, + schema: number +): TransactionMetadatum; + +/** + * @param {TransactionMetadatum} metadatum + * @param {number} schema + * @returns {string} + */ +declare export function decode_metadatum_to_json_str( + metadatum: TransactionMetadatum, + schema: number +): string; + /** * @param {string} password * @param {string} salt @@ -120,42 +156,6 @@ declare export function min_ada_required( minimum_utxo_val: BigNum ): BigNum; -/** - * @param {Uint8Array} bytes - * @returns {TransactionMetadatum} - */ -declare export function encode_arbitrary_bytes_as_metadatum( - bytes: Uint8Array -): TransactionMetadatum; - -/** - * @param {TransactionMetadatum} metadata - * @returns {Uint8Array} - */ -declare export function decode_arbitrary_bytes_from_metadatum( - metadata: TransactionMetadatum -): Uint8Array; - -/** - * @param {string} json - * @param {number} schema - * @returns {TransactionMetadatum} - */ -declare export function encode_json_str_to_metadatum( - json: string, - schema: number -): TransactionMetadatum; - -/** - * @param {TransactionMetadatum} metadatum - * @param {number} schema - * @returns {string} - */ -declare export function decode_metadatum_to_json_str( - metadatum: TransactionMetadatum, - schema: number -): string; - /** */ @@ -225,6 +225,26 @@ declare export var NetworkIdKind: {| +Mainnet: 1, // 1 |}; +/** + */ + +declare export var TransactionMetadatumKind: {| + +MetadataMap: 0, // 0 + +MetadataList: 1, // 1 + +Int: 2, // 2 + +Bytes: 3, // 3 + +Text: 4, // 4 +|}; + +/** + */ + +declare export var MetadataJsonSchema: {| + +NoConversions: 0, // 0 + +BasicConversions: 1, // 1 + +DetailedSchema: 2, // 2 +|}; + /** */ @@ -253,26 +273,6 @@ declare export var RedeemerTagKind: {| +Reward: 3, // 3 |}; -/** - */ - -declare export var TransactionMetadatumKind: {| - +MetadataMap: 0, // 0 - +MetadataList: 1, // 1 - +Int: 2, // 2 - +Bytes: 3, // 3 - +Text: 4, // 4 -|}; - -/** - */ - -declare export var MetadataJsonSchema: {| - +NoConversions: 0, // 0 - +BasicConversions: 1, // 1 - +DetailedSchema: 2, // 2 -|}; - /** */ declare export class Address { @@ -375,6 +375,17 @@ declare export class AssetNames { declare export class Assets { free(): void; + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Assets} + */ + static from_bytes(bytes: Uint8Array): Assets; + /** * @returns {Assets} */ @@ -612,6 +623,11 @@ declare export class BigNum { */ to_str(): string; + /** + * @returns {BigNum} + */ + static zero(): BigNum; + /** * @param {BigNum} other * @returns {BigNum} @@ -4889,13 +4905,17 @@ declare export class TransactionBuilder { * @param {BigNum} minimum_utxo_val * @param {BigNum} pool_deposit * @param {BigNum} key_deposit + * @param {number} max_output_size + * @param {number} max_tx_size * @returns {TransactionBuilder} */ static new( linear_fee: LinearFee, minimum_utxo_val: BigNum, pool_deposit: BigNum, - key_deposit: BigNum + key_deposit: BigNum, + max_output_size: number, + max_tx_size: number ): TransactionBuilder; /** @@ -4933,6 +4953,21 @@ declare export class TransactionBuilder { */ add_change_if_needed(address: Address): boolean; + /** + * @returns {number} + */ + full_size(): number; + + /** + * @returns {Uint32Array} + */ + output_sizes(): Uint32Array; + + /** + * @returns {Uint32Array} + */ + output_value_sizes(): Uint32Array; + /** * @returns {TransactionBody} */ From de1388a722fbd5f54916869aaf1b6c2b89b803ba Mon Sep 17 00:00:00 2001 From: vantuz-subhuman Date: Sun, 19 Sep 2021 14:43:43 +0300 Subject: [PATCH 8/8] removed tag beta --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2ef1b12b..ba7d1ef3 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "rust:test": "cd rust; cargo test; cd ..", "js:flowgen": "flowgen ./rust/pkg/cardano_serialization_lib.d.ts -o ./rust/pkg/cardano_serialization_lib.js.flow --add-flow-header", "js:prepublish": "npm run rust:test && rimraf ./publish && cp -r ./rust/pkg ./publish && cp README.md publish/ && cp LICENSE publish/", - "js:publish-nodejs": "npm run rust:build-nodejs && npm run js:prepublish && node ./scripts/publish-helper -nodejs && cd publish && npm publish --tag beta --access public", - "js:publish-browser": "npm run rust:build-browser && npm run js:prepublish && node ./scripts/publish-helper -browser && cd publish && npm publish --tag beta --access public", + "js:publish-nodejs": "npm run rust:build-nodejs && npm run js:prepublish && node ./scripts/publish-helper -nodejs && cd publish && npm publish --access public", + "js:publish-browser": "npm run rust:build-browser && npm run js:prepublish && node ./scripts/publish-helper -browser && cd publish && npm publish --access public", "js:publish-asm": "npm run rust:build-asm && npm run js:prepublish && node ./scripts/publish-helper -asmjs && cd publish && npm publish --access public", "postinstall": "git submodule update --init --recursive && cd binaryen; cmake . && make" },