diff --git a/Cargo.lock b/Cargo.lock index 773e65f4df..59ba9406d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2149,7 +2149,7 @@ dependencies = [ [[package]] name = "kaspa-addresses" -version = "0.13.5" +version = "0.13.6" dependencies = [ "borsh", "criterion", @@ -2165,7 +2165,7 @@ dependencies = [ [[package]] name = "kaspa-addressmanager" -version = "0.13.5" +version = "0.13.6" dependencies = [ "borsh", "igd-next", @@ -2188,14 +2188,14 @@ dependencies = [ [[package]] name = "kaspa-alloc" -version = "0.13.5" +version = "0.13.6" dependencies = [ "mimalloc", ] [[package]] name = "kaspa-bip32" -version = "0.13.5" +version = "0.13.6" dependencies = [ "borsh", "bs58", @@ -2221,7 +2221,7 @@ dependencies = [ [[package]] name = "kaspa-cli" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "borsh", @@ -2265,7 +2265,7 @@ dependencies = [ [[package]] name = "kaspa-connectionmanager" -version = "0.13.5" +version = "0.13.6" dependencies = [ "duration-string", "futures-util", @@ -2282,7 +2282,7 @@ dependencies = [ [[package]] name = "kaspa-consensus" -version = "0.13.5" +version = "0.13.6" dependencies = [ "arc-swap", "async-channel 2.1.1", @@ -2325,7 +2325,7 @@ dependencies = [ [[package]] name = "kaspa-consensus-core" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "bincode", @@ -2362,7 +2362,7 @@ dependencies = [ [[package]] name = "kaspa-consensus-notify" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "cfg-if 1.0.0", @@ -2381,7 +2381,7 @@ dependencies = [ [[package]] name = "kaspa-consensus-wasm" -version = "0.13.5" +version = "0.13.6" dependencies = [ "faster-hex 0.6.1", "js-sys", @@ -2403,7 +2403,7 @@ dependencies = [ [[package]] name = "kaspa-consensusmanager" -version = "0.13.5" +version = "0.13.6" dependencies = [ "duration-string", "futures", @@ -2421,7 +2421,7 @@ dependencies = [ [[package]] name = "kaspa-core" -version = "0.13.5" +version = "0.13.6" dependencies = [ "cfg-if 1.0.0", "ctrlc", @@ -2439,7 +2439,7 @@ dependencies = [ [[package]] name = "kaspa-daemon" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "borsh", @@ -2461,7 +2461,7 @@ dependencies = [ [[package]] name = "kaspa-database" -version = "0.13.5" +version = "0.13.6" dependencies = [ "bincode", "enum-primitive-derive", @@ -2483,7 +2483,7 @@ dependencies = [ [[package]] name = "kaspa-grpc-client" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "async-stream", @@ -2512,7 +2512,7 @@ dependencies = [ [[package]] name = "kaspa-grpc-core" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "async-stream", @@ -2541,7 +2541,7 @@ dependencies = [ [[package]] name = "kaspa-grpc-server" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "async-stream", @@ -2576,7 +2576,7 @@ dependencies = [ [[package]] name = "kaspa-hashes" -version = "0.13.5" +version = "0.13.6" dependencies = [ "blake2b_simd", "borsh", @@ -2597,7 +2597,7 @@ dependencies = [ [[package]] name = "kaspa-index-core" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "async-trait", @@ -2616,7 +2616,7 @@ dependencies = [ [[package]] name = "kaspa-index-processor" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "async-trait", @@ -2644,7 +2644,7 @@ dependencies = [ [[package]] name = "kaspa-math" -version = "0.13.5" +version = "0.13.6" dependencies = [ "borsh", "criterion", @@ -2665,14 +2665,14 @@ dependencies = [ [[package]] name = "kaspa-merkle" -version = "0.13.5" +version = "0.13.6" dependencies = [ "kaspa-hashes", ] [[package]] name = "kaspa-metrics-core" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "borsh", @@ -2688,7 +2688,7 @@ dependencies = [ [[package]] name = "kaspa-mining" -version = "0.13.5" +version = "0.13.6" dependencies = [ "criterion", "futures-util", @@ -2714,7 +2714,7 @@ dependencies = [ [[package]] name = "kaspa-mining-errors" -version = "0.13.5" +version = "0.13.6" dependencies = [ "kaspa-consensus-core", "thiserror", @@ -2722,7 +2722,7 @@ dependencies = [ [[package]] name = "kaspa-muhash" -version = "0.13.5" +version = "0.13.6" dependencies = [ "criterion", "kaspa-hashes", @@ -2735,7 +2735,7 @@ dependencies = [ [[package]] name = "kaspa-notify" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "async-trait", @@ -2764,7 +2764,7 @@ dependencies = [ [[package]] name = "kaspa-os" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "borsh", @@ -2798,7 +2798,7 @@ dependencies = [ [[package]] name = "kaspa-p2p-flows" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "chrono", @@ -2829,7 +2829,7 @@ dependencies = [ [[package]] name = "kaspa-p2p-lib" -version = "0.13.5" +version = "0.13.6" dependencies = [ "borsh", "ctrlc", @@ -2860,7 +2860,7 @@ dependencies = [ [[package]] name = "kaspa-perf-monitor" -version = "0.13.5" +version = "0.13.6" dependencies = [ "kaspa-core", "log", @@ -2872,7 +2872,7 @@ dependencies = [ [[package]] name = "kaspa-pow" -version = "0.13.5" +version = "0.13.6" dependencies = [ "criterion", "js-sys", @@ -2886,7 +2886,7 @@ dependencies = [ [[package]] name = "kaspa-rpc-core" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "async-trait", @@ -2921,7 +2921,7 @@ dependencies = [ [[package]] name = "kaspa-rpc-macros" -version = "0.13.5" +version = "0.13.6" dependencies = [ "convert_case 0.6.0", "proc-macro-error", @@ -2933,7 +2933,7 @@ dependencies = [ [[package]] name = "kaspa-rpc-service" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "kaspa-addresses", @@ -2961,7 +2961,7 @@ dependencies = [ [[package]] name = "kaspa-testing-integration" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "bincode", @@ -3014,7 +3014,7 @@ dependencies = [ [[package]] name = "kaspa-txscript" -version = "0.13.5" +version = "0.13.6" dependencies = [ "blake2b_simd", "borsh", @@ -3040,7 +3040,7 @@ dependencies = [ [[package]] name = "kaspa-txscript-errors" -version = "0.13.5" +version = "0.13.6" dependencies = [ "secp256k1", "thiserror", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "kaspa-utils" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "async-trait", @@ -3076,7 +3076,7 @@ dependencies = [ [[package]] name = "kaspa-utils-tower" -version = "0.13.5" +version = "0.13.6" dependencies = [ "cfg-if 1.0.0", "futures", @@ -3090,7 +3090,7 @@ dependencies = [ [[package]] name = "kaspa-utxoindex" -version = "0.13.5" +version = "0.13.6" dependencies = [ "futures", "kaspa-consensus", @@ -3111,7 +3111,7 @@ dependencies = [ [[package]] name = "kaspa-wallet" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-std", "async-trait", @@ -3123,7 +3123,7 @@ dependencies = [ [[package]] name = "kaspa-wallet-cli-wasm" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "js-sys", @@ -3137,7 +3137,7 @@ dependencies = [ [[package]] name = "kaspa-wallet-core" -version = "0.13.5" +version = "0.13.6" dependencies = [ "aes", "ahash 0.8.6", @@ -3210,7 +3210,7 @@ dependencies = [ [[package]] name = "kaspa-wallet-macros" -version = "0.13.5" +version = "0.13.6" dependencies = [ "convert_case 0.5.0", "proc-macro-error", @@ -3223,7 +3223,7 @@ dependencies = [ [[package]] name = "kaspa-wasm" -version = "0.13.5" +version = "0.13.6" dependencies = [ "js-sys", "kaspa-addresses", @@ -3243,7 +3243,7 @@ dependencies = [ [[package]] name = "kaspa-wrpc-client" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-std", "async-trait", @@ -3273,7 +3273,7 @@ dependencies = [ [[package]] name = "kaspa-wrpc-proxy" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "clap 4.4.11", @@ -3292,7 +3292,7 @@ dependencies = [ [[package]] name = "kaspa-wrpc-server" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-trait", "borsh", @@ -3319,14 +3319,14 @@ dependencies = [ [[package]] name = "kaspa-wrpc-wasm" -version = "0.13.5" +version = "0.13.6" dependencies = [ "kaspa-wrpc-client", ] [[package]] name = "kaspad" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "clap 4.4.11", @@ -4652,7 +4652,7 @@ dependencies = [ [[package]] name = "rothschild" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "clap 4.4.11", @@ -5052,7 +5052,7 @@ dependencies = [ [[package]] name = "simpa" -version = "0.13.5" +version = "0.13.6" dependencies = [ "async-channel 2.1.1", "clap 4.4.11", diff --git a/Cargo.toml b/Cargo.toml index db34790d4c..ca39e082e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ members = [ [workspace.package] rust-version = "1.77.0" -version = "0.13.5" +version = "0.13.6" authors = ["Kaspa developers"] license = "MIT/Apache-2.0" repository = "https://github.com/kaspanet/rusty-kaspa" @@ -75,58 +75,58 @@ include = [ ] [workspace.dependencies] -# kaspa-testing-integration = { version = "0.13.5", path = "testing/integration" } -kaspa-addresses = { version = "0.13.5", path = "crypto/addresses" } -kaspa-addressmanager = { version = "0.13.5", path = "components/addressmanager" } -kaspa-bip32 = { version = "0.13.5", path = "wallet/bip32" } -kaspa-cli = { version = "0.13.5", path = "cli" } -kaspa-connectionmanager = { version = "0.13.5", path = "components/connectionmanager" } -kaspa-consensus = { version = "0.13.5", path = "consensus" } -kaspa-consensus-core = { version = "0.13.5", path = "consensus/core" } -kaspa-consensus-notify = { version = "0.13.5", path = "consensus/notify" } -kaspa-consensus-wasm = { version = "0.13.5", path = "consensus/wasm" } -kaspa-consensusmanager = { version = "0.13.5", path = "components/consensusmanager" } -kaspa-core = { version = "0.13.5", path = "core" } -kaspa-daemon = { version = "0.13.5", path = "daemon" } -kaspa-database = { version = "0.13.5", path = "database" } -kaspa-grpc-client = { version = "0.13.5", path = "rpc/grpc/client" } -kaspa-grpc-core = { version = "0.13.5", path = "rpc/grpc/core" } -kaspa-grpc-server = { version = "0.13.5", path = "rpc/grpc/server" } -kaspa-hashes = { version = "0.13.5", path = "crypto/hashes" } -kaspa-index-core = { version = "0.13.5", path = "indexes/core" } -kaspa-index-processor = { version = "0.13.5", path = "indexes/processor" } -kaspa-math = { version = "0.13.5", path = "math" } -kaspa-merkle = { version = "0.13.5", path = "crypto/merkle" } -kaspa-metrics-core = { version = "0.13.5", path = "metrics/core" } -kaspa-mining = { version = "0.13.5", path = "mining" } -kaspa-mining-errors = { version = "0.13.5", path = "mining/errors" } -kaspa-muhash = { version = "0.13.5", path = "crypto/muhash" } -kaspa-notify = { version = "0.13.5", path = "notify" } -kaspa-os = { version = "0.13.5", path = "kaspa-os" } -kaspa-p2p-flows = { version = "0.13.5", path = "protocol/flows" } -kaspa-p2p-lib = { version = "0.13.5", path = "protocol/p2p" } -kaspa-perf-monitor = { version = "0.13.5", path = "metrics/perf_monitor" } -kaspa-pow = { version = "0.13.5", path = "consensus/pow" } -kaspa-rpc-core = { version = "0.13.5", path = "rpc/core" } -kaspa-rpc-macros = { version = "0.13.5", path = "rpc/macros" } -kaspa-rpc-service = { version = "0.13.5", path = "rpc/service" } -kaspa-txscript = { version = "0.13.5", path = "crypto/txscript" } -kaspa-txscript-errors = { version = "0.13.5", path = "crypto/txscript/errors" } -kaspa-utils = { version = "0.13.5", path = "utils" } -kaspa-utils-tower = { version = "0.13.5", path = "utils/tower" } -kaspa-utxoindex = { version = "0.13.5", path = "indexes/utxoindex" } -kaspa-wallet = { version = "0.13.5", path = "wallet/native" } -kaspa-wallet-cli-wasm = { version = "0.13.5", path = "wallet/wasm" } -kaspa-wallet-core = { version = "0.13.5", path = "wallet/core" } -kaspa-wallet-macros = { version = "0.13.5", path = "wallet/macros" } -kaspa-wasm = { version = "0.13.5", path = "wasm" } -kaspa-wrpc-client = { version = "0.13.5", path = "rpc/wrpc/client" } -kaspa-wrpc-core = { version = "0.13.5", path = "rpc/wrpc/core" } -kaspa-wrpc-proxy = { version = "0.13.5", path = "rpc/wrpc/proxy" } -kaspa-wrpc-server = { version = "0.13.5", path = "rpc/wrpc/server" } -kaspa-wrpc-wasm = { version = "0.13.5", path = "rpc/wrpc/wasm" } -kaspad = { version = "0.13.5", path = "kaspad" } -kaspa-alloc = { version = "0.13.5", path = "utils/alloc" } +# kaspa-testing-integration = { version = "0.13.6", path = "testing/integration" } +kaspa-addresses = { version = "0.13.6", path = "crypto/addresses" } +kaspa-addressmanager = { version = "0.13.6", path = "components/addressmanager" } +kaspa-bip32 = { version = "0.13.6", path = "wallet/bip32" } +kaspa-cli = { version = "0.13.6", path = "cli" } +kaspa-connectionmanager = { version = "0.13.6", path = "components/connectionmanager" } +kaspa-consensus = { version = "0.13.6", path = "consensus" } +kaspa-consensus-core = { version = "0.13.6", path = "consensus/core" } +kaspa-consensus-notify = { version = "0.13.6", path = "consensus/notify" } +kaspa-consensus-wasm = { version = "0.13.6", path = "consensus/wasm" } +kaspa-consensusmanager = { version = "0.13.6", path = "components/consensusmanager" } +kaspa-core = { version = "0.13.6", path = "core" } +kaspa-daemon = { version = "0.13.6", path = "daemon" } +kaspa-database = { version = "0.13.6", path = "database" } +kaspa-grpc-client = { version = "0.13.6", path = "rpc/grpc/client" } +kaspa-grpc-core = { version = "0.13.6", path = "rpc/grpc/core" } +kaspa-grpc-server = { version = "0.13.6", path = "rpc/grpc/server" } +kaspa-hashes = { version = "0.13.6", path = "crypto/hashes" } +kaspa-index-core = { version = "0.13.6", path = "indexes/core" } +kaspa-index-processor = { version = "0.13.6", path = "indexes/processor" } +kaspa-math = { version = "0.13.6", path = "math" } +kaspa-merkle = { version = "0.13.6", path = "crypto/merkle" } +kaspa-metrics-core = { version = "0.13.6", path = "metrics/core" } +kaspa-mining = { version = "0.13.6", path = "mining" } +kaspa-mining-errors = { version = "0.13.6", path = "mining/errors" } +kaspa-muhash = { version = "0.13.6", path = "crypto/muhash" } +kaspa-notify = { version = "0.13.6", path = "notify" } +kaspa-os = { version = "0.13.6", path = "kaspa-os" } +kaspa-p2p-flows = { version = "0.13.6", path = "protocol/flows" } +kaspa-p2p-lib = { version = "0.13.6", path = "protocol/p2p" } +kaspa-perf-monitor = { version = "0.13.6", path = "metrics/perf_monitor" } +kaspa-pow = { version = "0.13.6", path = "consensus/pow" } +kaspa-rpc-core = { version = "0.13.6", path = "rpc/core" } +kaspa-rpc-macros = { version = "0.13.6", path = "rpc/macros" } +kaspa-rpc-service = { version = "0.13.6", path = "rpc/service" } +kaspa-txscript = { version = "0.13.6", path = "crypto/txscript" } +kaspa-txscript-errors = { version = "0.13.6", path = "crypto/txscript/errors" } +kaspa-utils = { version = "0.13.6", path = "utils" } +kaspa-utils-tower = { version = "0.13.6", path = "utils/tower" } +kaspa-utxoindex = { version = "0.13.6", path = "indexes/utxoindex" } +kaspa-wallet = { version = "0.13.6", path = "wallet/native" } +kaspa-wallet-cli-wasm = { version = "0.13.6", path = "wallet/wasm" } +kaspa-wallet-core = { version = "0.13.6", path = "wallet/core" } +kaspa-wallet-macros = { version = "0.13.6", path = "wallet/macros" } +kaspa-wasm = { version = "0.13.6", path = "wasm" } +kaspa-wrpc-client = { version = "0.13.6", path = "rpc/wrpc/client" } +kaspa-wrpc-core = { version = "0.13.6", path = "rpc/wrpc/core" } +kaspa-wrpc-proxy = { version = "0.13.6", path = "rpc/wrpc/proxy" } +kaspa-wrpc-server = { version = "0.13.6", path = "rpc/wrpc/server" } +kaspa-wrpc-wasm = { version = "0.13.6", path = "rpc/wrpc/wasm" } +kaspad = { version = "0.13.6", path = "kaspad" } +kaspa-alloc = { version = "0.13.6", path = "utils/alloc" } # external diff --git a/consensus/core/src/constants.rs b/consensus/core/src/constants.rs index c4bf1a1445..450c12f678 100644 --- a/consensus/core/src/constants.rs +++ b/consensus/core/src/constants.rs @@ -12,7 +12,7 @@ pub const MAX_SCRIPT_PUBLIC_KEY_VERSION: u16 = 0; /// SompiPerKaspa is the number of sompi in one kaspa (1 KAS). pub const SOMPI_PER_KASPA: u64 = 100_000_000; -/// The parameter for scaling inverse KAS value to mass units (unpublished KIP-0009) +/// The parameter for scaling inverse KAS value to mass units (KIP-0009) pub const STORAGE_MASS_PARAMETER: u64 = SOMPI_PER_KASPA * 10_000; /// MaxSompi is the maximum transaction amount allowed in sompi. diff --git a/consensus/core/src/tx.rs b/consensus/core/src/tx.rs index dbc426d931..302cbac5df 100644 --- a/consensus/core/src/tx.rs +++ b/consensus/core/src/tx.rs @@ -354,7 +354,7 @@ pub struct MutableTransaction = std::sync::Arc>, /// Populated fee pub calculated_fee: Option, - /// Populated mass (does not include the storage mass) + /// Populated compute mass (does not include the storage mass) pub calculated_compute_mass: Option, } diff --git a/consensus/src/consensus/mod.rs b/consensus/src/consensus/mod.rs index 77dc300332..80babbef0f 100644 --- a/consensus/src/consensus/mod.rs +++ b/consensus/src/consensus/mod.rs @@ -440,8 +440,9 @@ impl ConsensusApi for Consensus { self.services.mass_calculator.calc_tx_compute_mass(transaction) } - fn calculate_transaction_storage_mass(&self, transaction: &MutableTransaction) -> Option { - self.services.mass_calculator.calc_tx_storage_mass(&transaction.as_verifiable()) + fn calculate_transaction_storage_mass(&self, _transaction: &MutableTransaction) -> Option { + // self.services.mass_calculator.calc_tx_storage_mass(&transaction.as_verifiable()) + unimplemented!("unsupported at the API level until KIP9 is finalized") } fn get_stats(&self) -> ConsensusStats { diff --git a/consensus/src/pipeline/virtual_processor/utxo_validation.rs b/consensus/src/pipeline/virtual_processor/utxo_validation.rs index 788c7ad865..3331d3f9ed 100644 --- a/consensus/src/pipeline/virtual_processor/utxo_validation.rs +++ b/consensus/src/pipeline/virtual_processor/utxo_validation.rs @@ -5,9 +5,12 @@ use crate::{ RuleError::{BadAcceptedIDMerkleRoot, BadCoinbaseTransaction, BadUTXOCommitment, InvalidTransactionsInUtxoContext}, }, model::stores::{block_transactions::BlockTransactionsStoreReader, daa::DaaStoreReader, ghostdag::GhostdagData}, - processes::transaction_validator::{ - errors::{TxResult, TxRuleError}, - transaction_validator_populated::TxValidationFlags, + processes::{ + mass::Kip9Version, + transaction_validator::{ + errors::{TxResult, TxRuleError}, + transaction_validator_populated::TxValidationFlags, + }, }, }; use kaspa_consensus_core::{ @@ -290,12 +293,15 @@ impl VirtualStateProcessor { ) -> TxResult<()> { self.populate_mempool_transaction_in_utxo_context(mutable_tx, utxo_view)?; + // For non-activated nets (mainnet, TN10) we can update mempool rules to KIP9 beta asap. For + // TN11 we need to hard-fork consensus first (since the new beta rules are more relaxed) + let kip9_version = if self.storage_mass_activation_daa_score == u64::MAX { Kip9Version::Beta } else { Kip9Version::Alpha }; + // Calc the full contextual mass including storage mass let contextual_mass = self .transaction_validator .mass_calculator - .calc_tx_storage_mass(&mutable_tx.as_verifiable()) - .and_then(|m| m.checked_add(mutable_tx.calculated_compute_mass.unwrap())) + .calc_tx_overall_mass(&mutable_tx.as_verifiable(), mutable_tx.calculated_compute_mass, kip9_version) .ok_or(TxRuleError::MassIncomputable)?; // Set the inner mass field diff --git a/consensus/src/processes/mass.rs b/consensus/src/processes/mass.rs index 5169ea6dad..68b42a41de 100644 --- a/consensus/src/processes/mass.rs +++ b/consensus/src/processes/mass.rs @@ -3,6 +3,17 @@ use kaspa_consensus_core::{ 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)] @@ -46,7 +57,7 @@ impl MassCalculator { /// 2. At least one input (unless coinbase) /// /// Otherwise this function should never fail. - pub fn calc_tx_storage_mass(&self, tx: &impl VerifiableTransaction) -> Option { + pub fn calc_tx_storage_mass(&self, tx: &impl VerifiableTransaction, version: Kip9Version) -> Option { if tx.is_coinbase() { return Some(0); } @@ -58,7 +69,7 @@ impl MassCalculator { input values, H(S) := |S|/sum_{s in S} 1 / s is the harmonic mean over the set S and A(S) := sum_{s in S} / |S| is the arithmetic mean. - See the (to date unpublished) KIP-0009 for more details + See KIP-0009 for more details */ // Since we are doing integer division, we perform the multiplication with C over the inner @@ -66,6 +77,9 @@ impl MassCalculator { // // If sum of fractions overflowed (nearly impossible, requires 10^7 outputs for C = 10^12), // we return `None` indicating mass is incomputable + // + // Note: in theory this can be tighten by subtracting input mass in the process (possibly avoiding the overflow), + // however the overflow case is so unpractical with current mass limits so we avoid the hassle let harmonic_outs = tx .tx() .outputs @@ -73,9 +87,23 @@ impl MassCalculator { .map(|out| self.storage_mass_parameter / out.value) .try_fold(0u64, |total, current| total.checked_add(current))?; // C·|O|/H(O) + let outs_len = tx.tx().outputs.len() as u64; + let ins_len = tx.tx().inputs.len() as u64; + + /* + KIP-0009 relaxed formula for the cases |O| = 1 OR |O| <= |I| <= 2: + max( 0 , C·( |O|/H(O) - |I|/H(I) ) ) + */ + if version == Kip9Version::Beta && (outs_len == 1 || (outs_len <= ins_len && ins_len <= 2)) { + let harmonic_ins = tx + .populated_inputs() + .map(|(_, entry)| self.storage_mass_parameter / entry.amount) + .fold(0u64, |total, current| total.saturating_add(current)); // C·|I|/H(I) + return Some(harmonic_outs.saturating_sub(harmonic_ins)); // max( 0 , C·( |O|/H(O) - |I|/H(I) ) ); + } + // Total supply is bounded, so a sum of existing UTXO entries cannot overflow (nor can it be zero) let sum_ins = tx.populated_inputs().map(|(_, entry)| entry.amount).sum::(); // |I|·A(I) - let ins_len = tx.tx().inputs.len() as u64; let mean_ins = sum_ins / ins_len; // Inner fraction must be with C and over the mean value, in order to maximize precision. @@ -84,6 +112,24 @@ impl MassCalculator { Some(harmonic_outs.saturating_sub(arithmetic_ins)) // max( 0 , C·( |O|/H(O) - |I|/A(I) ) ) } + + /// Calculates the overall mass of this transaction, combining both compute and storage masses. + /// The combination strategy depends on the version passed. + pub fn calc_tx_overall_mass( + &self, + tx: &impl VerifiableTransaction, + cached_compute_mass: Option, + version: Kip9Version, + ) -> Option { + match version { + Kip9Version::Alpha => self + .calc_tx_storage_mass(tx, version) + .and_then(|mass| mass.checked_add(cached_compute_mass.unwrap_or_else(|| self.calc_tx_compute_mass(tx.tx())))), + Kip9Version::Beta => self + .calc_tx_storage_mass(tx, version) + .map(|mass| mass.max(cached_compute_mass.unwrap_or_else(|| self.calc_tx_compute_mass(tx.tx())))), + } + } } #[cfg(test)] @@ -155,17 +201,20 @@ mod tests { }, ]; let mut tx = MutableTransaction::with_entries(tx, entries); + let test_version = Kip9Version::Alpha; // Assert the formula: max( 0 , C·( |O|/H(O) - |I|/A(I) ) ) - let storage_mass = MassCalculator::new(0, 0, 0, 10u64.pow(12)).calc_tx_storage_mass(&tx.as_verifiable()).unwrap(); + let storage_mass = + MassCalculator::new(0, 0, 0, 10u64.pow(12)).calc_tx_storage_mass(&tx.as_verifiable(), test_version).unwrap(); assert_eq!(storage_mass, 0); // Compounds from 3 to 2, with symmetric outputs and no fee, should be zero // Create asymmetry tx.tx.outputs[0].value = 50; tx.tx.outputs[1].value = 550; let storage_mass_parameter = 10u64.pow(12); - let storage_mass = MassCalculator::new(0, 0, 0, storage_mass_parameter).calc_tx_storage_mass(&tx.as_verifiable()).unwrap(); + let storage_mass = + MassCalculator::new(0, 0, 0, storage_mass_parameter).calc_tx_storage_mass(&tx.as_verifiable(), test_version).unwrap(); assert_eq!(storage_mass, storage_mass_parameter / 50 + storage_mass_parameter / 550 - 3 * (storage_mass_parameter / 200)); // Create a tx with more outs than ins @@ -238,12 +287,14 @@ mod tests { let mut tx = MutableTransaction::with_entries(tx, entries); let storage_mass_parameter = STORAGE_MASS_PARAMETER; - let storage_mass = MassCalculator::new(0, 0, 0, storage_mass_parameter).calc_tx_storage_mass(&tx.as_verifiable()).unwrap(); + let storage_mass = + MassCalculator::new(0, 0, 0, storage_mass_parameter).calc_tx_storage_mass(&tx.as_verifiable(), test_version).unwrap(); assert_eq!(storage_mass, 4); // Inputs are above C so they don't contribute negative mass, 4 outputs exactly equal C each charge 1 let mut tx2 = tx.clone(); tx2.tx.outputs[0].value = 10 * SOMPI_PER_KASPA; - let storage_mass = MassCalculator::new(0, 0, 0, storage_mass_parameter).calc_tx_storage_mass(&tx2.as_verifiable()).unwrap(); + let storage_mass = + MassCalculator::new(0, 0, 0, storage_mass_parameter).calc_tx_storage_mass(&tx2.as_verifiable(), test_version).unwrap(); assert_eq!(storage_mass, 1003); // Increase values over the lim @@ -251,7 +302,8 @@ mod tests { out.value += 1 } tx.entries[0].as_mut().unwrap().amount += tx.tx.outputs.len() as u64; - let storage_mass = MassCalculator::new(0, 0, 0, storage_mass_parameter).calc_tx_storage_mass(&tx.as_verifiable()).unwrap(); + let storage_mass = + MassCalculator::new(0, 0, 0, storage_mass_parameter).calc_tx_storage_mass(&tx.as_verifiable(), test_version).unwrap(); assert_eq!(storage_mass, 0); drop(script_pub_key); diff --git a/consensus/src/processes/transaction_validator/transaction_validator_populated.rs b/consensus/src/processes/transaction_validator/transaction_validator_populated.rs index 87737c914d..fa387ab588 100644 --- a/consensus/src/processes/transaction_validator/transaction_validator_populated.rs +++ b/consensus/src/processes/transaction_validator/transaction_validator_populated.rs @@ -98,8 +98,7 @@ impl TransactionValidator { fn check_mass_commitment(&self, tx: &impl VerifiableTransaction) -> TxResult<()> { let calculated_contextual_mass = self .mass_calculator - .calc_tx_storage_mass(tx) - .and_then(|m| m.checked_add(self.mass_calculator.calc_tx_compute_mass(tx.tx()))) + .calc_tx_overall_mass(tx, None, crate::processes::mass::Kip9Version::Alpha) .ok_or(TxRuleError::MassIncomputable)?; let committed_contextual_mass = tx.tx().mass(); if committed_contextual_mass != calculated_contextual_mass { diff --git a/mining/src/mempool/validate_and_insert_transaction.rs b/mining/src/mempool/validate_and_insert_transaction.rs index f54f7d7917..591fa5c4aa 100644 --- a/mining/src/mempool/validate_and_insert_transaction.rs +++ b/mining/src/mempool/validate_and_insert_transaction.rs @@ -103,6 +103,17 @@ impl Mempool { } fn validate_transaction_in_context(&self, transaction: &MutableTransaction) -> RuleResult<()> { + // TEMP: apply parts of go-kaspad mempool dust prevention patch + let has_coinbase_input = transaction.entries.iter().any(|e| e.as_ref().unwrap().is_coinbase); + let num_extra_outs = transaction.tx.outputs.len() as i64 - transaction.tx.inputs.len() as i64; + if !has_coinbase_input + && num_extra_outs > 2 + && transaction.calculated_fee.unwrap() < num_extra_outs as u64 * kaspa_consensus_core::constants::SOMPI_PER_KASPA + { + kaspa_core::trace!("Rejected spam tx {} from mempool ({} outputs)", transaction.id(), transaction.tx.outputs.len()); + return Err(RuleError::RejectSpamTransaction(transaction.id())); + } + if !self.config.accept_non_standard { self.check_transaction_standard_in_context(transaction)?; } diff --git a/protocol/flows/src/v5/txrelay/flow.rs b/protocol/flows/src/v5/txrelay/flow.rs index b824872bfe..6a177e57ff 100644 --- a/protocol/flows/src/v5/txrelay/flow.rs +++ b/protocol/flows/src/v5/txrelay/flow.rs @@ -229,10 +229,11 @@ impl RelayTransactionsFlow { // TODO: discuss a banning process return Err(ProtocolError::MisbehavingPeer(format!("rejected invalid transaction {}", transaction_id))); } - Err(MiningManagerError::MempoolError(RuleError::RejectSpamTransaction(_))) => { + Err(MiningManagerError::MempoolError(RuleError::RejectSpamTransaction(_))) + | Err(MiningManagerError::MempoolError(RuleError::RejectNonStandard(..))) => { self.spam_counter += 1; if self.spam_counter % 100 == 0 { - kaspa_core::warn!("Peer {} has shared {} spam txs", self.router, self.spam_counter); + kaspa_core::warn!("Peer {} has shared {} spam/non-standard txs ({:?})", self.router, self.spam_counter, res); } } Err(_) => {} diff --git a/simpa/src/simulator/miner.rs b/simpa/src/simulator/miner.rs index fd3fb0c3fa..4e167583b2 100644 --- a/simpa/src/simulator/miner.rs +++ b/simpa/src/simulator/miner.rs @@ -153,8 +153,7 @@ impl Miner { let signed_tx = sign(mutable_tx, schnorr_key); let mass = self .mass_calculator - .calc_tx_storage_mass(&signed_tx.as_verifiable()) - .and_then(|v| v.checked_add(self.mass_calculator.calc_tx_compute_mass(&signed_tx.tx))) + .calc_tx_overall_mass(&signed_tx.as_verifiable(), None, kaspa_consensus::processes::mass::Kip9Version::Alpha) .unwrap(); signed_tx.tx.set_mass(mass); let mut signed_tx = signed_tx.tx;