Skip to content

Commit

Permalink
Remove WASM32 mass calculator + change createTransaction() signature (#…
Browse files Browse the repository at this point in the history
…81)

* Kip9 updates to WASM/wallet framework mass calc (#66)

* WIP

* update kip9 processing in WASM mass calculator

* XPrv.toPrivateKey support

* replace lazy_static with OnceLock

* remove NetworkParams Inner

* make signatureScript optional on ITransactionInput (WASM32)

* WIP mass calc (WASM32)

* remove WASM32 mass calc, replace with dedicated functions

* use OnceCell for NetworkParams (wallet-core)

* Update changelog

* fmt

---------

Co-authored-by: Surinder Singh Matoo <surinder83singh@gmail.com>
  • Loading branch information
aspect and surinder83singh authored Jul 26, 2024
1 parent eb9552c commit fad7bd1
Show file tree
Hide file tree
Showing 26 changed files with 313 additions and 383 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

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

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ ipnet = "2.9.0"
itertools = "0.11.0"
js-sys = "0.3.67"
keccak = "0.1.4"
lazy_static = "1.4.0"
local-ip-address = "0.5.6"
log = "0.4.20"
log4rs = "1.2.0"
Expand Down
8 changes: 6 additions & 2 deletions consensus/client/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const TS_TRANSACTION: &'static str = r#"
*/
export interface ITransactionInput {
previousOutpoint: ITransactionOutpoint;
signatureScript: HexString;
signatureScript?: HexString;
sequence: bigint;
sigOpCount: number;
utxo?: UtxoEntryReference;
Expand Down Expand Up @@ -95,6 +95,10 @@ impl TransactionInput {
self.inner().sig_op_count
}

pub fn signature_script_length(&self) -> usize {
self.inner().signature_script.len()
}

pub fn utxo(&self) -> Option<UtxoEntryReference> {
self.inner().utxo.clone()
}
Expand Down Expand Up @@ -187,7 +191,7 @@ impl TryCastFromJs for TransactionInput {
Self::resolve_cast(&value, || {
if let Some(object) = Object::try_from(value.as_ref()) {
let previous_outpoint: TransactionOutpoint = object.get_value("previousOutpoint")?.as_ref().try_into()?;
let signature_script = object.get_vec_u8("signatureScript")?;
let signature_script = object.get_vec_u8("signatureScript").unwrap_or_default();
let sequence = object.get_u64("sequence")?;
let sig_op_count = object.get_u8("sigOpCount")?;
let utxo = object.try_get_cast::<UtxoEntryReference>("utxo")?.map(Cast::into_owned);
Expand Down
4 changes: 2 additions & 2 deletions consensus/client/src/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl TransactionOutput {
self.inner.lock().unwrap()
}

pub fn script_length(&self) -> usize {
pub fn script_public_key_length(&self) -> usize {
self.inner().script_public_key.script().len()
}
}
Expand All @@ -79,7 +79,7 @@ impl TransactionOutput {
}

#[wasm_bindgen(getter, js_name = value)]
pub fn get_value(&self) -> u64 {
pub fn value(&self) -> u64 {
self.inner().value
}

Expand Down
2 changes: 1 addition & 1 deletion consensus/client/src/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub fn sign_with_multiple_v3(tx: Transaction, privkeys: &[[u8; 32]]) -> crate::r
let mut additional_signatures_required = false;
{
let input_len = tx.inner().inputs.len();
let (cctx, utxos) = tx.tx_and_utxos();
let (cctx, utxos) = tx.tx_and_utxos()?;
let populated_transaction = PopulatedTransaction::new(&cctx, utxos);
for i in 0..input_len {
let script_pub_key = match tx.inner().inputs[i].script_public_key() {
Expand Down
52 changes: 45 additions & 7 deletions consensus/client/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,18 +359,18 @@ impl Transaction {
})
}

pub fn tx_and_utxos(&self) -> (cctx::Transaction, Vec<UtxoEntry>) {
let mut utxos = vec![];
pub fn tx_and_utxos(&self) -> Result<(cctx::Transaction, Vec<UtxoEntry>)> {
let mut inputs = vec![];
let inner = self.inner();
let inputs: Vec<cctx::TransactionInput> = inner
let utxos: Vec<cctx::UtxoEntry> = inner
.inputs
.clone()
.into_iter()
.map(|input| {
utxos.push((&input.get_utxo().unwrap().entry()).into());
input.as_ref().into()
inputs.push(input.as_ref().into());
Ok(input.get_utxo().ok_or(Error::MissingUtxoEntry)?.entry().as_ref().into())
})
.collect::<Vec<cctx::TransactionInput>>();
.collect::<Result<Vec<_>>>()?;
let outputs: Vec<cctx::TransactionOutput> =
inner.outputs.clone().into_iter().map(|output| output.as_ref().into()).collect::<Vec<cctx::TransactionOutput>>();
let tx = cctx::Transaction::new(
Expand All @@ -383,7 +383,37 @@ impl Transaction {
inner.payload.clone(),
);

(tx, utxos)
Ok((tx, utxos))
}

pub fn utxo_entry_references(&self) -> Result<Vec<UtxoEntryReference>> {
let inner = self.inner();
let utxo_entry_references = inner
.inputs
.clone()
.into_iter()
.map(|input| input.get_utxo().ok_or(Error::MissingUtxoEntry))
.collect::<Result<Vec<UtxoEntryReference>>>()?;
Ok(utxo_entry_references)
}

pub fn outputs(&self) -> Vec<cctx::TransactionOutput> {
let inner = self.inner();
let outputs = inner.outputs.iter().map(|output| output.into()).collect::<Vec<cctx::TransactionOutput>>();
outputs
}

pub fn inputs(&self) -> Vec<cctx::TransactionInput> {
let inner = self.inner();
let inputs = inner.inputs.iter().map(Into::into).collect::<Vec<cctx::TransactionInput>>();
inputs
}

pub fn inputs_outputs(&self) -> (Vec<cctx::TransactionInput>, Vec<cctx::TransactionOutput>) {
let inner = self.inner();
let inputs = inner.inputs.iter().map(Into::into).collect::<Vec<cctx::TransactionInput>>();
let outputs = inner.outputs.iter().map(Into::into).collect::<Vec<cctx::TransactionOutput>>();
(inputs, outputs)
}

pub fn set_signature_script(&self, input_index: usize, signature_script: Vec<u8>) -> Result<()> {
Expand All @@ -393,6 +423,14 @@ impl Transaction {
self.inner().inputs[input_index].set_signature_script(signature_script);
Ok(())
}

pub fn payload(&self) -> Vec<u8> {
self.inner().payload.clone()
}

pub fn payload_len(&self) -> usize {
self.inner().payload.len()
}
}

#[wasm_bindgen]
Expand Down
6 changes: 6 additions & 0 deletions consensus/client/src/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ impl UtxoEntry {
}
}

impl AsRef<UtxoEntry> for UtxoEntry {
fn as_ref(&self) -> &UtxoEntry {
self
}
}

impl From<&UtxoEntry> for cctx::UtxoEntry {
fn from(utxo: &UtxoEntry) -> Self {
cctx::UtxoEntry {
Expand Down
11 changes: 11 additions & 0 deletions consensus/core/src/mass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ use crate::{
};
use kaspa_hashes::HASH_SIZE;

/// Temp enum for the transition phases of KIP9
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Kip9Version {
/// Initial KIP9 mass calculation, w/o the relaxed formula and summing storage mass and compute mass
Alpha,

/// Currently proposed KIP9 mass calculation, with the relaxed formula (for the cases `|O| = 1 OR |O| <= |I| <= 2`),
/// and using a maximum operator over storage and compute mass
Beta,
}

// transaction_estimated_serialized_size is the estimated size of a transaction in some
// serialization. This has to be deterministic, but not necessarily accurate, since
// it's only used as the size component in the transaction and block mass limit
Expand Down
12 changes: 1 addition & 11 deletions consensus/src/processes/mass.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
pub use kaspa_consensus_core::mass::Kip9Version;
use kaspa_consensus_core::{
mass::transaction_estimated_serialized_size,
tx::{Transaction, VerifiableTransaction},
};

/// Temp enum for the transition phases of KIP9
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Kip9Version {
/// Initial KIP9 mass calculation, w/o the relaxed formula and summing storage mass and compute mass
Alpha,

/// Currently proposed KIP9 mass calculation, with the relaxed formula (for the cases `|O| = 1 OR |O| <= |I| <= 2`),
/// and using a maximum operator over storage and compute mass
Beta,
}

// TODO (aspect) - review and potentially merge this with the new MassCalculator currently located in the wallet core
// (i.e. migrate mass calculator from wallet core here or to consensus core)
#[derive(Clone)]
Expand Down
1 change: 0 additions & 1 deletion wallet/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ kaspa-wallet-macros.workspace = true
kaspa-wasm-core.workspace = true
kaspa-wrpc-client.workspace = true
kaspa-wrpc-wasm.workspace = true
lazy_static.workspace = true
md-5.workspace = true
pad.workspace = true
pbkdf2.workspace = true
Expand Down
3 changes: 1 addition & 2 deletions wallet/core/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ pub use crate::rpc::Rpc;
pub use crate::rpc::{DynRpcApi, RpcCtl};
pub use crate::serializer::*;
pub use crate::storage::*;
pub use crate::tx::MassCombinationStrategy;
pub use crate::utxo::balance::Balance;
pub use crate::utxo::scan::{Scan, ScanExtent};
pub use crate::utxo::{Maturity, NetworkParams, OutgoingTransaction, UtxoContext, UtxoEntryReference, UtxoProcessor};
Expand Down Expand Up @@ -49,7 +48,7 @@ pub use std::collections::{HashMap, HashSet};
pub use std::pin::Pin;
pub use std::str::FromStr;
pub use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
pub use std::sync::{Arc, Mutex, MutexGuard, RwLock};
pub use std::sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock};
pub use std::task::{Context, Poll};
pub use wasm_bindgen::prelude::*;
pub use workflow_core::prelude::*;
Expand Down
24 changes: 14 additions & 10 deletions wallet/core/src/tx/generator/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ use crate::tx::{
use crate::utxo::{NetworkParams, UtxoContext, UtxoEntryReference};
use kaspa_consensus_client::UtxoEntry;
use kaspa_consensus_core::constants::UNACCEPTED_DAA_SCORE;
use kaspa_consensus_core::mass::Kip9Version;
use kaspa_consensus_core::subnets::SUBNETWORK_ID_NATIVE;
use kaspa_consensus_core::tx::{Transaction, TransactionInput, TransactionOutpoint, TransactionOutput};
use kaspa_txscript::pay_to_address_script;
Expand Down Expand Up @@ -215,7 +216,7 @@ struct Data {

impl Data {
fn new(calc: &MassCalculator) -> Self {
let aggregate_mass = calc.blank_transaction_mass();
let aggregate_mass = calc.blank_transaction_compute_mass();

Data {
inputs: vec![],
Expand Down Expand Up @@ -266,7 +267,7 @@ struct Inner {
// Current network id
network_id: NetworkId,
// Current network params
network_params: NetworkParams,
network_params: &'static NetworkParams,

// Source Utxo Context (Used for source UtxoEntry aggregation)
source_utxo_context: Option<UtxoContext>,
Expand Down Expand Up @@ -357,7 +358,7 @@ impl Generator {

let network_type = NetworkType::from(network_id);
let network_params = NetworkParams::from(network_id);
let mass_calculator = MassCalculator::new(&network_id.into(), &network_params);
let mass_calculator = MassCalculator::new(&network_id.into(), network_params);

let (final_transaction_outputs, final_transaction_amount) = match final_transaction_destination {
PaymentDestination::Change => {
Expand Down Expand Up @@ -402,11 +403,11 @@ impl Generator {
}

let standard_change_output_mass =
mass_calculator.calc_mass_for_output(&TransactionOutput::new(0, pay_to_address_script(&change_address)));
let signature_mass_per_input = mass_calculator.calc_signature_mass(minimum_signatures);
let final_transaction_outputs_compute_mass = mass_calculator.calc_mass_for_outputs(&final_transaction_outputs);
mass_calculator.calc_compute_mass_for_output(&TransactionOutput::new(0, pay_to_address_script(&change_address)));
let signature_mass_per_input = mass_calculator.calc_compute_mass_for_signature(minimum_signatures);
let final_transaction_outputs_compute_mass = mass_calculator.calc_compute_mass_for_outputs(&final_transaction_outputs);
let final_transaction_payload = final_transaction_payload.unwrap_or_default();
let final_transaction_payload_mass = mass_calculator.calc_mass_for_payload(final_transaction_payload.len());
let final_transaction_payload_mass = mass_calculator.calc_compute_mass_for_payload(final_transaction_payload.len());
let final_transaction_outputs_harmonic =
mass_calculator.calc_storage_mass_output_harmonic(&final_transaction_outputs).ok_or(Error::MassCalculationError)?;

Expand Down Expand Up @@ -477,7 +478,7 @@ impl Generator {

/// Returns current [`NetworkParams`]
pub fn network_params(&self) -> &NetworkParams {
&self.inner.network_params
self.inner.network_params
}

/// The underlying [`UtxoContext`] (if available).
Expand Down Expand Up @@ -662,7 +663,7 @@ impl Generator {

let input = TransactionInput::new(utxo.outpoint.clone().into(), vec![], 0, self.inner.sig_op_count);
let input_amount = utxo.amount();
let input_compute_mass = calc.calc_mass_for_input(&input) + self.inner.signature_mass_per_input;
let input_compute_mass = calc.calc_compute_mass_for_input(&input) + self.inner.signature_mass_per_input;

// NOTE: relay transactions have no storage mass
// mass threshold reached, yield transaction
Expand Down Expand Up @@ -865,8 +866,11 @@ impl Generator {
calc.calc_storage_mass_output_harmonic_single(change_value) + self.inner.final_transaction_outputs_harmonic;
let storage_mass_with_change = self.calc_storage_mass(data, output_harmonic_with_change);

// TODO - review and potentially simplify:
// this profiles the storage mass with change and without change
// and decides which one to use based on the fees
if storage_mass_with_change == 0
|| (self.inner.network_params.mass_combination_strategy() == MassCombinationStrategy::Max
|| (self.inner.network_params.kip9_version() == Kip9Version::Beta // max(compute vs storage)
&& storage_mass_with_change < compute_mass_with_change)
{
0
Expand Down
8 changes: 4 additions & 4 deletions wallet/core/src/tx/generator/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,10 @@ fn validate(pt: &PendingTransaction) {

let calc = MassCalculator::new(&pt.network_type().into(), network_params);
let additional_mass = if pt.is_final() { 0 } else { network_params.additional_compound_transaction_mass() };
let compute_mass = calc.calc_mass_for_signed_transaction(&tx, 1);
let compute_mass = calc.calc_compute_mass_for_signed_transaction(&tx, 1);

let utxo_entries = pt.utxo_entries().values().cloned().collect::<Vec<_>>();
let storage_mass = calc.calc_storage_mass_for_transaction(false, &utxo_entries, &tx.outputs).unwrap_or_default();
let storage_mass = calc.calc_storage_mass_for_transaction_parts(&utxo_entries, &tx.outputs).unwrap_or_default();

let calculated_mass = calc.combine_mass(compute_mass, storage_mass) + additional_mass;

Expand Down Expand Up @@ -201,10 +201,10 @@ where
let calc = MassCalculator::new(&pt.network_type().into(), network_params);
let additional_mass = if pt.is_final() { 0 } else { network_params.additional_compound_transaction_mass() };

let compute_mass = calc.calc_mass_for_signed_transaction(&tx, 1);
let compute_mass = calc.calc_compute_mass_for_signed_transaction(&tx, 1);

let utxo_entries = pt.utxo_entries().values().cloned().collect::<Vec<_>>();
let storage_mass = calc.calc_storage_mass_for_transaction(false, &utxo_entries, &tx.outputs).unwrap_or_default();
let storage_mass = calc.calc_storage_mass_for_transaction_parts(&utxo_entries, &tx.outputs).unwrap_or_default();
if DISPLAY_LOGS && storage_mass != 0 {
println!(
"calculated storage mass: {} calculated_compute_mass: {} total: {}",
Expand Down
Loading

0 comments on commit fad7bd1

Please sign in to comment.