From fe65a1c4b26db08e88f3f4c2557e7651fdc46aed Mon Sep 17 00:00:00 2001 From: smartgoo Date: Sun, 27 Oct 2024 11:41:24 -0400 Subject: [PATCH] Python wallet tx mass and estimate functions (#118) * tx mass wallet fns * .pyi * fmt --- python/kaspa.pyi | 18 +++++++++++ python/src/lib.rs | 4 +++ wallet/core/src/python/tx/mass.rs | 51 ++++++++++++++++++++++++++++++ wallet/core/src/python/tx/mod.rs | 1 + wallet/core/src/python/tx/utils.rs | 31 +++++++++++++++++- 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 wallet/core/src/python/tx/mass.rs diff --git a/python/kaspa.pyi b/python/kaspa.pyi index 1cc53e219..e55a01125 100644 --- a/python/kaspa.pyi +++ b/python/kaspa.pyi @@ -682,6 +682,13 @@ class GeneratorSummary: def final_transaction_id(self) -> Optional[str]: ... +def calculate_transaction_fee(network_id: str, tx: Transaction, minimum_signatures: Optional[int]) -> Optional[int]: ... + +def calculate_transaction_mass(network_id: str, tx: Transaction, minimum_signatures: Optional[int]) -> int: ... + +def update_transaction_mass(network_id: str, tx: Transaction, minimum_signatures: Optional[int]) -> bool: ... + + class PaymentOutput: def __init__(self, address: Address, amount: int) -> None: ... @@ -714,6 +721,17 @@ def create_transactions( minimum_signatures: Optional[int] ) -> dict: ... +def estimate_transactions( + network_id: str, + entries: list[dict], + outputs: list[dict], + change_address: Address, + payload: Optional[str], + priority_fee: Optional[int], + priority_entries: Optional[list[dict]], + sig_op_count: Optional[int], + minimum_signatures: Optional[int] +) -> GeneratorSummary: ... def sign_transaction(tx: Transaction, signer: list[PrivateKey], verify_sig: bool) -> Transaction: ... diff --git a/python/src/lib.rs b/python/src/lib.rs index dd2550c84..672076ec0 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -32,8 +32,12 @@ cfg_if::cfg_if! { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::tx::mass::calculate_unsigned_transaction_fee, m)?)?; + m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::tx::mass::calculate_unsigned_transaction_mass, m)?)?; + m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::tx::mass::update_unsigned_transaction_mass, m)?)?; m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::tx::utils::create_transaction_py, m)?)?; m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::tx::utils::create_transactions_py, m)?)?; + m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::tx::utils::estimate_transactions_py, m)?)?; m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::utils::kaspa_to_sompi, m)?)?; m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::utils::sompi_to_kaspa, m)?)?; m.add_function(wrap_pyfunction!(kaspa_wallet_core::python::utils::sompi_to_kaspa_string_with_suffix, m)?)?; diff --git a/wallet/core/src/python/tx/mass.rs b/wallet/core/src/python/tx/mass.rs new file mode 100644 index 000000000..6b41c5353 --- /dev/null +++ b/wallet/core/src/python/tx/mass.rs @@ -0,0 +1,51 @@ +use crate::imports::*; +use crate::result::Result; +use crate::tx::{mass, MAXIMUM_STANDARD_TRANSACTION_MASS}; +use kaspa_consensus_client::Transaction; +use kaspa_consensus_core::config::params::Params; + +#[pyfunction] +pub fn maximum_standard_transaction_mass() -> u64 { + MAXIMUM_STANDARD_TRANSACTION_MASS +} + +#[pyfunction] +#[pyo3(name = "calculate_transaction_mass")] +pub fn calculate_unsigned_transaction_mass(network_id: &str, tx: &Transaction, minimum_signatures: Option) -> Result { + let network_id = NetworkId::from_str(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); + mc.calc_overall_mass_for_unsigned_client_transaction(tx, minimum_signatures.unwrap_or(1)) +} + +#[pyfunction] +#[pyo3(name = "update_transaction_mass")] +pub fn update_unsigned_transaction_mass(network_id: &str, tx: &Transaction, minimum_signatures: Option) -> Result { + let network_id = NetworkId::from_str(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))?; + if mass > MAXIMUM_STANDARD_TRANSACTION_MASS { + Ok(false) + } else { + tx.set_mass(mass); + Ok(true) + } +} + +#[pyfunction] +#[pyo3(name = "calculate_transaction_fee")] +pub fn calculate_unsigned_transaction_fee(network_id: &str, tx: &Transaction, minimum_signatures: Option) -> Result> { + let network_id = NetworkId::from_str(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))?; + if mass > MAXIMUM_STANDARD_TRANSACTION_MASS { + Ok(None) + } else { + Ok(Some(mc.calc_fee_for_mass(mass))) + } +} diff --git a/wallet/core/src/python/tx/mod.rs b/wallet/core/src/python/tx/mod.rs index 122f04e3f..f76b3b2bf 100644 --- a/wallet/core/src/python/tx/mod.rs +++ b/wallet/core/src/python/tx/mod.rs @@ -1,2 +1,3 @@ pub mod generator; +pub mod mass; pub mod utils; diff --git a/wallet/core/src/python/tx/utils.rs b/wallet/core/src/python/tx/utils.rs index f9c6a75ba..03b8b92ef 100644 --- a/wallet/core/src/python/tx/utils.rs +++ b/wallet/core/src/python/tx/utils.rs @@ -1,5 +1,5 @@ use crate::imports::*; -use crate::python::tx::generator::{Generator, PendingTransaction}; +use crate::python::tx::generator::{Generator, GeneratorSummary, PendingTransaction}; use crate::tx::payment::PaymentOutput; use kaspa_consensus_client::*; use kaspa_consensus_core::subnets::SUBNETWORK_ID_NATIVE; @@ -79,3 +79,32 @@ pub fn create_transactions_py<'a>( dict.set_item("summary", &summary)?; Ok(dict) } + +#[pyfunction] +#[pyo3(name = "estimate_transactions")] +pub fn estimate_transactions_py<'a>( + network_id: String, + entries: Vec<&PyDict>, + outputs: Vec<&PyDict>, + change_address: Address, + payload: Option, + priority_fee: Option, + priority_entries: Option>, + sig_op_count: Option, + minimum_signatures: Option, +) -> PyResult { + let generator = Generator::ctor( + network_id, + entries, + outputs, + change_address, + payload.map(Into::into), + priority_fee, + priority_entries, + sig_op_count, + minimum_signatures, + )?; + + generator.iter().collect::>>()?; + Ok(generator.summary()) +}