Skip to content

Commit

Permalink
WASM SDK improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
0xA001113 committed Oct 22, 2024
1 parent c40d2e6 commit 490af48
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 23 deletions.
2 changes: 1 addition & 1 deletion consensus/client/src/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ impl UtxoEntryReference {
let outpoint = TransactionOutpoint::simulated();
let script_public_key = spectre_txscript::pay_to_address_script(address);
let block_daa_score = 0;
let is_coinbase = true;
let is_coinbase = false;

let utxo_entry =
UtxoEntry { address: Some(address.clone()), outpoint, amount, script_public_key, block_daa_score, is_coinbase };
Expand Down
2 changes: 1 addition & 1 deletion rpc/core/src/wasm/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ cfg_if::cfg_if! {
subnetwork_id: inner.subnetwork_id.clone(),
gas: inner.gas,
payload: inner.payload.clone(),
mass: tx.get_mass(),
mass: inner.mass,
verbose_data: None,
}
}
Expand Down
4 changes: 2 additions & 2 deletions rpc/core/src/wasm/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1454,8 +1454,8 @@ try_from! ( args: ISubmitTransactionRequest, SubmitTransactionRequest, {
} else {
let tx = Transaction::try_cast_from(&transaction)?;
SubmitTransactionRequest {
transaction : tx.as_ref().into(),
allow_orphan,
transaction : tx.as_ref().into(),
allow_orphan,
}
};
Ok(request)
Expand Down
1 change: 0 additions & 1 deletion wallet/core/src/tx/generator/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,6 @@ impl Generator {
Ok((DataKind::NoOp, data))
} else if stage.number_of_transactions > 0 {
data.aggregate_mass += self.inner.standard_change_output_compute_mass;
data.change_output_value = Some(data.aggregate_input_value - data.transaction_fees);
Ok((DataKind::Edge, data))
} else if data.aggregate_input_value < data.transaction_fees {
Err(Error::InsufficientFunds { additional_needed: data.transaction_fees - data.aggregate_input_value, origin: "relay" })
Expand Down
59 changes: 54 additions & 5 deletions wallet/core/src/tx/generator/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use workflow_log::style;

use super::*;

const DISPLAY_LOGS: bool = true;
const DISPLAY_LOGS: bool = false;
const DISPLAY_EXPECTED: bool = true;

#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -173,7 +173,7 @@ fn validate(pt: &PendingTransaction) {
let compute_mass = calc.calc_compute_mass_for_unsigned_consensus_transaction(&tx, pt.minimum_signatures());

let utxo_entries = pt.utxo_entries().values().cloned().collect::<Vec<_>>();
let storage_mass = calc.calc_storage_mass_for_transaction_parts(&utxo_entries, &tx.outputs).unwrap_or_default();
let storage_mass = calc.calc_storage_mass_for_transaction_parts(&utxo_entries, &tx.outputs).unwrap_or(u64::MAX);
let calculated_mass = calc.combine_mass(compute_mass, storage_mass) + additional_mass;

assert_eq!(pt.inner.mass, calculated_mass, "pending transaction mass does not match calculated mass");
Expand Down Expand Up @@ -203,7 +203,7 @@ where
let compute_mass = calc.calc_compute_mass_for_unsigned_consensus_transaction(&tx, pt.minimum_signatures());

let utxo_entries = pt.utxo_entries().values().cloned().collect::<Vec<_>>();
let storage_mass = calc.calc_storage_mass_for_transaction_parts(&utxo_entries, &tx.outputs).unwrap_or_default();
let storage_mass = calc.calc_storage_mass_for_transaction_parts(&utxo_entries, &tx.outputs).unwrap_or(u64::MAX);
if DISPLAY_LOGS && storage_mass != 0 {
println!("calculated storage mass: {} calculated_compute_mass: {}", storage_mass, compute_mass,);
}
Expand Down Expand Up @@ -323,6 +323,21 @@ impl Harness {
self.clone()
}

pub fn accumulate(self: &Rc<Self>, count: usize) -> Rc<Self> {
for _n in 0..count {
if DISPLAY_LOGS {
println!(
"{}",
style(format!("accumulate gathering transaction: {} ({})", _n, self.accumulator.borrow().list.len())).magenta()
);
}
let ptx = self.generator.generate_transaction().unwrap().unwrap();
ptx.accumulate(&mut self.accumulator.borrow_mut());
}
// println!("accumulated `{}` transactions", self.accumulator.borrow().list.len());
self.clone()
}

pub fn validate(self: &Rc<Self>) -> Rc<Self> {
while let Some(pt) = self.generator.generate_transaction().unwrap() {
pt.accumulate(&mut self.accumulator.borrow_mut()).validate();
Expand All @@ -332,7 +347,16 @@ impl Harness {

pub fn finalize(self: Rc<Self>) {
let pt = self.generator.generate_transaction().unwrap();
assert!(pt.is_none(), "expected no more transactions");
if pt.is_some() {
let mut pending = self.generator.generate_transaction().unwrap();
let mut count = 1;
while pending.is_some() {
count += 1;
pending = self.generator.generate_transaction().unwrap();
}

panic!("received extra `{}` unexpected transactions", count);
}
let summary = self.generator.summary();
if DISPLAY_LOGS {
println!("{:#?}", summary);
Expand Down Expand Up @@ -652,7 +676,7 @@ fn test_generator_inputs_100_outputs_1_fees_exclude_insufficient_funds() -> Resu
}

#[test]
fn test_generator_inputs_903_outputs_2_fees_exclude() -> Result<()> {
fn test_generator_inputs_1k_outputs_2_fees_exclude() -> Result<()> {
generator(test_network_id(), &[10.0; 1_000], &[], Fees::sender(Spectre(5.0)), [(output_address, Spectre(9_000.0))].as_slice())
.unwrap()
.harness()
Expand Down Expand Up @@ -684,3 +708,28 @@ fn test_generator_inputs_903_outputs_2_fees_exclude() -> Result<()> {

Ok(())
}

#[test]
fn test_generator_inputs_32k_outputs_2_fees_exclude() -> Result<()> {
let f = 130.0;
generator(
test_network_id(),
&[f; 32_747],
&[],
Fees::sender(Spectre(10_000.0)),
[(output_address, Spectre(f * 32_747.0 - 10_001.0))].as_slice(),
)
.unwrap()
.harness()
.accumulate(379)
.finalize();
Ok(())
}

#[test]
fn test_generator_inputs_250k_outputs_2_sweep() -> Result<()> {
let f = 130.0;
let generator = make_generator(test_network_id(), &[f; 250_000], &[], Fees::None, change_address, PaymentDestination::Change);
generator.unwrap().harness().accumulate(2875).finalize();
Ok(())
}
58 changes: 47 additions & 11 deletions wallet/core/src/wasm/tx/mass.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
use crate::imports::NetworkParams;
use crate::result::Result;
use crate::tx::mass;
use crate::tx::{mass, MAXIMUM_STANDARD_TRANSACTION_MASS};
use spectre_consensus_client::*;
use spectre_consensus_core::config::params::Params;
use spectre_consensus_core::network::{NetworkId, NetworkIdT};
use wasm_bindgen::prelude::*;
use workflow_wasm::convert::*;

/// `maximumStandardTransactionMass()` returns the maximum transaction
/// size allowed by the network.
///
/// @category Wallet SDK
/// @see {@link calculateTransactionMass}
/// @see {@link updateTransactionMass}
/// @see {@link calculateTransactionFee}
#[wasm_bindgen(js_name = maximumStandardTransactionMass)]
pub fn maximum_standard_transaction_mass() -> u64 {
MAXIMUM_STANDARD_TRANSACTION_MASS
}

/// `calculateTransactionMass()` returns the mass of the passed transaction.
/// If the transaction is invalid, or the mass can not be calculated
/// the function throws an error.
///
/// The mass value must not exceed the maximum standard transaction mass
/// that can be obtained using `maximumStandardTransactionMass()`.
///
/// @category Wallet SDK
/// @see {@link maximumStandardTransactionMass}
///
#[wasm_bindgen(js_name = calculateTransactionMass)]
pub fn calculate_unsigned_transaction_mass(network_id: NetworkIdT, tx: &TransactionT, minimum_signatures: Option<u16>) -> Result<u64> {
Expand All @@ -24,39 +40,59 @@ pub fn calculate_unsigned_transaction_mass(network_id: NetworkIdT, tx: &Transact
}

/// `updateTransactionMass()` updates the mass property of the passed transaction.
/// If the transaction is invalid, or the mass is larger than transaction mass allowed
/// by the network, the function throws an error.
/// If the transaction is invalid, the function throws an error.
///
/// The function returns `true` if the mass is within the maximum standard transaction mass and
/// the transaction mass is updated. Otherwise, the function returns `false`.
///
/// This is the same as `calculateTransactionMass()` but modifies the supplied
/// This is similar to `calculateTransactionMass()` but modifies the supplied
/// `Transaction` object.
///
/// @category Wallet SDK
/// @see {@link maximumStandardTransactionMass}
/// @see {@link calculateTransactionMass}
/// @see {@link calculateTransactionFee}
///
#[wasm_bindgen(js_name = updateTransactionMass)]
pub fn update_unsigned_transaction_mass(network_id: NetworkIdT, tx: &Transaction, minimum_signatures: Option<u16>) -> Result<()> {
pub fn update_unsigned_transaction_mass(network_id: NetworkIdT, tx: &Transaction, minimum_signatures: Option<u16>) -> Result<bool> {
let network_id = NetworkId::try_owned_from(network_id)?;
let consensus_params = Params::from(network_id);
let network_params = NetworkParams::from(network_id);
let mc = mass::MassCalculator::new(&consensus_params, network_params);
let mass = mc.calc_overall_mass_for_unsigned_client_transaction(tx, minimum_signatures.unwrap_or(1))?;
tx.set_mass(mass);
Ok(())
if mass > MAXIMUM_STANDARD_TRANSACTION_MASS {
Ok(false)
} else {
tx.set_mass(mass);
Ok(true)
}
}

/// `calculateTransactionFee()` returns minimum fees needed for the transaction to be
/// accepted by the network. If the transaction is invalid or the mass can not be calculated,
/// the function throws an error.
/// the function throws an error. If the mass exceeds the maximum standard transaction mass,
/// the function returns `undefined`.
///
/// @category Wallet SDK
/// @see {@link maximumStandardTransactionMass}
/// @see {@link calculateTransactionMass}
/// @see {@link updateTransactionMass}
///
#[wasm_bindgen(js_name = calculateTransactionFee)]
pub fn calculate_unsigned_transaction_fee(network_id: NetworkIdT, tx: &TransactionT, minimum_signatures: Option<u16>) -> Result<u64> {
pub fn calculate_unsigned_transaction_fee(
network_id: NetworkIdT,
tx: &TransactionT,
minimum_signatures: Option<u16>,
) -> Result<Option<u64>> {
let tx = Transaction::try_cast_from(tx)?;
let network_id = NetworkId::try_owned_from(network_id)?;
let consensus_params = Params::from(network_id);
let network_params = NetworkParams::from(network_id);
let mc = mass::MassCalculator::new(&consensus_params, network_params);
let mass = mc.calc_overall_mass_for_unsigned_client_transaction(tx.as_ref(), minimum_signatures.unwrap_or(1))?;
let fee = mc.calc_fee_for_mass(mass);
Ok(fee)
if mass > MAXIMUM_STANDARD_TRANSACTION_MASS {
Ok(None)
} else {
Ok(Some(mc.calc_fee_for_mass(mass)))
}
}
17 changes: 15 additions & 2 deletions wallet/keys/src/publickey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
//! ```
//!
use spectre_consensus_core::network::NetworkType;

use crate::imports::*;

use spectre_consensus_core::network::NetworkType;
use ripemd::{Digest, Ripemd160};
use sha2::Sha256;

/// Data structure that envelopes a PublicKey.
/// Only supports Schnorr-based addresses.
/// @category Wallet SDK
Expand Down Expand Up @@ -69,6 +71,17 @@ impl PublicKey {
pub fn to_x_only_public_key(&self) -> XOnlyPublicKey {
self.xonly_public_key.into()
}

/// Compute a 4-byte key fingerprint for this public key as a hex string.
/// Default implementation uses `RIPEMD160(SHA256(public_key))`.
pub fn fingerprint(&self) -> Option<HexString> {
if let Some(public_key) = self.public_key.as_ref() {
let digest = Ripemd160::digest(Sha256::digest(public_key.serialize().as_slice()));
Some(digest[..4].as_ref().to_hex().into())
} else {
None
}
}
}

impl PublicKey {
Expand Down

0 comments on commit 490af48

Please sign in to comment.