diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ab536b5ce27..3ec3dc6c250 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -37,7 +37,7 @@ jobs: - name: Rust files id: changed-files-rust - uses: tj-actions/changed-files@v35.9.0 + uses: tj-actions/changed-files@v35.9.2 with: files: | **/*.rs @@ -49,7 +49,7 @@ jobs: - name: Workflow files id: changed-files-workflows - uses: tj-actions/changed-files@v35.9.0 + uses: tj-actions/changed-files@v35.9.2 with: files: | .github/workflows/*.yml diff --git a/Cargo.lock b/Cargo.lock index f4c7729d56f..2819a8e86cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ checksum = "74f5722bc48763cb9d81d8427ca05b6aa2842f6632cf8e4c0a29eef9baececcc" dependencies = [ "darling 0.10.2", "ident_case", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "synstructure", @@ -214,7 +214,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -225,9 +225,9 @@ version = "0.1.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -380,7 +380,7 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "regex", "rustc-hash", @@ -1071,10 +1071,10 @@ dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "scratch", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -1084,9 +1084,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5a1d8bcc452ce138e068129073b0b6b9669dda4eec735bc599c88d83249e373" dependencies = [ "codespan-reporting", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -1101,7 +1101,7 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -1128,12 +1128,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.4" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "e7c99d16b88c92aef47e58dadd53e87b4bd234c29934947a6cec8b466300f99b" dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", + "darling_core 0.20.0", + "darling_macro 0.20.0", ] [[package]] @@ -1144,7 +1144,7 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "strsim 0.9.3", "syn 1.0.109", @@ -1158,7 +1158,7 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "strsim 0.10.0", "syn 1.0.109", @@ -1166,16 +1166,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "2ea05d2fcb27b53f7a98faddaf5f2914760330ab7703adfc9df13332b42189f9" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "strsim 0.10.0", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -1202,13 +1202,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "7bfb82b62b1b8a2a9808fb4caf844ede819a76cfc23b2827d7f94eefb49551eb" dependencies = [ - "darling_core 0.14.4", + "darling_core 0.20.0", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -1276,7 +1276,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -1358,7 +1358,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "355f93763ef7b0ae1c43c4d8eccc9d5848d84ad1a1d8ce61c421d1ac85a19d05" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -1624,9 +1624,9 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -1709,7 +1709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -1791,7 +1791,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90454ce4de40b7ca6a8968b5ef367bdab48413962588d0d2b1638d60090c35d7" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2193,7 +2193,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2372,7 +2372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" dependencies = [ "proc-macro-crate 0.1.5", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -2683,9 +2683,9 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -2930,7 +2930,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -3097,7 +3097,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -3251,7 +3251,7 @@ checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -3292,7 +3292,7 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -3378,7 +3378,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "syn 1.0.109", ] @@ -3419,7 +3419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "version_check", @@ -3431,7 +3431,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "version_check", ] @@ -3447,9 +3447,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -3526,7 +3526,7 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -3595,7 +3595,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -3615,7 +3615,7 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", ] [[package]] @@ -4283,9 +4283,9 @@ version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -4333,9 +4333,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331bb8c3bf9b92457ab7abecf07078c13f7d270ba490103e84e8b014490cd0b0" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" dependencies = [ "base64 0.13.1", "chrono", @@ -4343,7 +4343,7 @@ dependencies = [ "indexmap", "serde", "serde_json", - "serde_with_macros 2.3.2", + "serde_with_macros 2.3.3", "time 0.3.20", ] @@ -4354,21 +4354,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "serde_with_macros" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "darling 0.14.4", - "proc-macro2 1.0.53", + "darling 0.20.0", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -4492,7 +4492,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bdfb59103e43a0f99a346b57860d50f2138a7008d08acd964e9ac0fef3ae9a5" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -4567,7 +4567,7 @@ checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -4595,18 +4595,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.8" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "unicode-ident", ] @@ -4623,7 +4623,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "unicode-xid 0.2.4", @@ -4687,9 +4687,9 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -4800,9 +4800,9 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", - "syn 2.0.8", + "syn 2.0.15", ] [[package]] @@ -4989,7 +4989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" dependencies = [ "prettyplease", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "prost-build", "quote 1.0.26", "syn 1.0.109", @@ -5108,7 +5108,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", ] @@ -5492,7 +5492,7 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "wasm-bindgen-shared", @@ -5526,7 +5526,7 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "wasm-bindgen-backend", @@ -5963,7 +5963,7 @@ dependencies = [ "serde", "serde-big-array", "serde_json", - "serde_with 2.3.2", + "serde_with 2.3.3", "sha2 0.9.9", "spandoc", "static_assertions", @@ -6296,7 +6296,7 @@ version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2 1.0.53", + "proc-macro2 1.0.56", "quote 1.0.26", "syn 1.0.109", "synstructure", diff --git a/zebra-chain/Cargo.toml b/zebra-chain/Cargo.toml index 379f677490b..52eef36a0a1 100644 --- a/zebra-chain/Cargo.toml +++ b/zebra-chain/Cargo.toml @@ -81,7 +81,7 @@ tracing = "0.1.37" # Serialization hex = { version = "0.4.3", features = ["serde"] } serde = { version = "1.0.160", features = ["serde_derive", "rc"] } -serde_with = "2.3.2" +serde_with = "2.3.3" serde-big-array = "0.5.1" # Processing diff --git a/zebra-chain/src/transaction.rs b/zebra-chain/src/transaction.rs index d6de1f895fb..0b2c25583d2 100644 --- a/zebra-chain/src/transaction.rs +++ b/zebra-chain/src/transaction.rs @@ -34,7 +34,9 @@ pub use serialize::{ MIN_TRANSPARENT_TX_V5_SIZE, }; pub use sighash::{HashType, SigHash}; -pub use unmined::{UnminedTx, UnminedTxId, VerifiedUnminedTx, MEMPOOL_TRANSACTION_COST_THRESHOLD}; +pub use unmined::{ + zip317, UnminedTx, UnminedTxId, VerifiedUnminedTx, MEMPOOL_TRANSACTION_COST_THRESHOLD, +}; use crate::{ amount::{Amount, Error as AmountError, NegativeAllowed, NonNegative}, diff --git a/zebra-chain/src/transaction/unmined.rs b/zebra-chain/src/transaction/unmined.rs index 9dfc03a27f6..9c081018c97 100644 --- a/zebra-chain/src/transaction/unmined.rs +++ b/zebra-chain/src/transaction/unmined.rs @@ -36,7 +36,7 @@ use proptest_derive::Arbitrary; #[allow(unused_imports)] use crate::block::MAX_BLOCK_BYTES; -mod zip317; +pub mod zip317; /// The minimum cost value for a transaction in the mempool. /// @@ -353,17 +353,19 @@ impl VerifiedUnminedTx { transaction: UnminedTx, miner_fee: Amount, legacy_sigop_count: u64, - ) -> Self { + ) -> Result { let fee_weight_ratio = zip317::conventional_fee_weight_ratio(&transaction, miner_fee); let unpaid_actions = zip317::unpaid_actions(&transaction, miner_fee); - Self { + zip317::mempool_checks(unpaid_actions, miner_fee, transaction.size)?; + + Ok(Self { transaction, miner_fee, legacy_sigop_count, fee_weight_ratio, unpaid_actions, - } + }) } /// Returns `true` if the transaction pays at least the [ZIP-317] conventional fee. diff --git a/zebra-chain/src/transaction/unmined/zip317.rs b/zebra-chain/src/transaction/unmined/zip317.rs index cc4c998c48c..44ef709aacd 100644 --- a/zebra-chain/src/transaction/unmined/zip317.rs +++ b/zebra-chain/src/transaction/unmined/zip317.rs @@ -5,6 +5,7 @@ use std::cmp::max; use num_integer::div_ceil; +use thiserror::Error; use crate::{ amount::{Amount, NonNegative}, @@ -13,6 +14,9 @@ use crate::{ transaction::{Transaction, UnminedTx}, }; +#[cfg(test)] +mod tests; + /// The marginal fee for the ZIP-317 fee calculation, in zatoshis per logical action. // // TODO: allow Amount in constants @@ -37,6 +41,27 @@ const BLOCK_PRODUCTION_WEIGHT_RATIO_CAP: f32 = 4.0; /// This avoids special handling for transactions with zero weight. const MIN_BLOCK_PRODUCTION_SUBSTITUTE_FEE: i64 = 1; +/// The ZIP-317 recommended limit on the number of unpaid actions per block. +/// `block_unpaid_action_limit` in ZIP-317. +pub const BLOCK_PRODUCTION_UNPAID_ACTION_LIMIT: u32 = 50; + +/// The minimum fee per kilobyte for Zebra mempool transactions. +/// Also used as the minimum fee for a mempool transaction. +/// +/// Based on `DEFAULT_MIN_RELAY_TX_FEE` in `zcashd`: +/// +/// +/// This is a `usize` to simplify transaction size-based calculation code. +pub const MIN_MEMPOOL_TX_FEE_RATE: usize = 100; + +/// The fee cap for [`MIN_MEMPOOL_TX_FEE_RATE`] minimum required mempool fees. +/// +/// Based on `LEGACY_DEFAULT_FEE` in `zcashd`: +/// +/// +/// This is a `usize` to simplify transaction size-based calculation code. +pub const MEMPOOL_TX_FEE_REQUIREMENT_CAP: usize = 1000; + /// Returns the conventional fee for `transaction`, as defined by [ZIP-317]. /// /// [ZIP-317]: https://zips.z.cash/zip-0317#fee-calculation @@ -139,3 +164,72 @@ fn conventional_actions(transaction: &Transaction) -> u32 { max(GRACE_ACTIONS, logical_actions) } + +/// Make ZIP-317 checks before inserting a transaction into the mempool. +pub fn mempool_checks( + unpaid_actions: u32, + miner_fee: Amount, + transaction_size: usize, +) -> Result<(), Error> { + // # Standard Rule + // + // > If a transaction has more than `block_unpaid_action_limit` "unpaid actions" as defined by the + // > Recommended algorithm for block template construction, it will never be mined by that algorithm. + // > Nodes MAY drop these transactions. + // + // + if unpaid_actions > BLOCK_PRODUCTION_UNPAID_ACTION_LIMIT { + return Err(Error::UnpaidActions); + } + + // # Standard Rule + // + // > Nodes that normally relay transactions are expected to do so for transactions that pay at least the + // > conventional fee as specified in this ZIP. + // + // + // + // In Zebra, we use a similar minimum fee rate to `zcashd` v5.5.0 and later. + // Transactions must pay a fee of at least 100 zatoshis per 1000 bytes of serialized size, + // with a maximum fee of 1000 zatoshis. + // + // + // + // In zcashd this is `DEFAULT_MIN_RELAY_TX_FEE` and `LEGACY_DEFAULT_FEE`: + // + // + + const KILOBYTE: usize = 1000; + + // This calculation can't overflow, because transactions are limited to 2 MB, + // and usize is at least 4 GB. + assert!( + MIN_MEMPOOL_TX_FEE_RATE + < usize::MAX / usize::try_from(MAX_BLOCK_BYTES).expect("constant fits in usize"), + "the fee rate multiplication must never overflow", + ); + + let min_fee = (MIN_MEMPOOL_TX_FEE_RATE * transaction_size / KILOBYTE) + .clamp(MIN_MEMPOOL_TX_FEE_RATE, MEMPOOL_TX_FEE_REQUIREMENT_CAP); + let min_fee: u64 = min_fee + .try_into() + .expect("clamped value always fits in u64"); + let min_fee: Amount = min_fee.try_into().expect("clamped value is positive"); + + if miner_fee < min_fee { + return Err(Error::FeeBelowMinimumRate); + } + + Ok(()) +} + +/// Errors related to ZIP-317. +#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)] +#[allow(missing_docs)] +pub enum Error { + #[error("Unpaid actions is higher than the limit")] + UnpaidActions, + + #[error("Transaction fee is below the minimum fee rate")] + FeeBelowMinimumRate, +} diff --git a/zebra-chain/src/transaction/unmined/zip317/tests.rs b/zebra-chain/src/transaction/unmined/zip317/tests.rs new file mode 100644 index 00000000000..fb708a73c0b --- /dev/null +++ b/zebra-chain/src/transaction/unmined/zip317/tests.rs @@ -0,0 +1,18 @@ +//! ZIP-317 tests. + +use super::{mempool_checks, Amount, Error}; +#[test] +fn zip317_unpaid_actions_err() { + let check = mempool_checks(51, Amount::try_from(1).unwrap(), 1); + + assert!(check.is_err()); + assert_eq!(check.err(), Some(Error::UnpaidActions)); +} + +#[test] +fn zip317_minimum_rate_fee_err() { + let check = mempool_checks(50, Amount::try_from(1).unwrap(), 1000); + + assert!(check.is_err()); + assert_eq!(check.err(), Some(Error::FeeBelowMinimumRate)); +} diff --git a/zebra-consensus/src/error.rs b/zebra-consensus/src/error.rs index d089073eb29..e5852368a98 100644 --- a/zebra-consensus/src/error.rs +++ b/zebra-consensus/src/error.rs @@ -221,6 +221,10 @@ pub enum TransactionError { outpoint: transparent::OutPoint, min_spend_height: block::Height, }, + + #[error("failed to verify ZIP-317 transaction rules, transaction was not inserted to mempool")] + #[cfg_attr(any(test, feature = "proptest-impl"), proptest(skip))] + Zip317(#[from] zebra_chain::transaction::zip317::Error), } impl From for TransactionError { diff --git a/zebra-consensus/src/transaction.rs b/zebra-consensus/src/transaction.rs index 28546485b0a..744b8570c95 100644 --- a/zebra-consensus/src/transaction.rs +++ b/zebra-consensus/src/transaction.rs @@ -466,14 +466,15 @@ where miner_fee, legacy_sigop_count, }, - Request::Mempool { transaction, .. } => Response::Mempool { - transaction: VerifiedUnminedTx::new( + Request::Mempool { transaction, .. } => { + let transaction = VerifiedUnminedTx::new( transaction, miner_fee.expect( "unexpected mempool coinbase transaction: should have already rejected", ), legacy_sigop_count, - ), + )?; + Response::Mempool { transaction } }, }; diff --git a/zebra-consensus/src/transaction/tests.rs b/zebra-consensus/src/transaction/tests.rs index 89fd0276762..9f9fd6fbfd5 100644 --- a/zebra-consensus/src/transaction/tests.rs +++ b/zebra-consensus/src/transaction/tests.rs @@ -23,7 +23,7 @@ use zebra_chain::{ fake_v5_transactions_for_network, insert_fake_orchard_shielded_data, test_transactions, transactions_from_blocks, }, - Hash, HashType, JoinSplitData, LockTime, Transaction, + zip317, Hash, HashType, JoinSplitData, LockTime, Transaction, }, transparent::{self, CoinbaseData}, }; @@ -240,7 +240,12 @@ async fn mempool_request_with_present_input_is_accepted() { .activation_height(Network::Mainnet) .expect("Canopy activation height is specified"); let fund_height = (height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(10001).expect("invalid value"), + ); // Create a non-coinbase V4 tx with the last valid expiry height. let tx = Transaction::V4 { @@ -302,7 +307,12 @@ async fn mempool_request_with_invalid_lock_time_is_rejected() { .activation_height(Network::Mainnet) .expect("Canopy activation height is specified"); let fund_height = (height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a non-coinbase V4 tx with the last valid expiry height. let tx = Transaction::V4 { @@ -376,7 +386,12 @@ async fn mempool_request_with_unlocked_lock_time_is_accepted() { .activation_height(Network::Mainnet) .expect("Canopy activation height is specified"); let fund_height = (height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(10001).expect("invalid value"), + ); // Create a non-coinbase V4 tx with the last valid expiry height. let tx = Transaction::V4 { @@ -438,7 +453,12 @@ async fn mempool_request_with_lock_time_max_sequence_number_is_accepted() { .activation_height(Network::Mainnet) .expect("Canopy activation height is specified"); let fund_height = (height - 1).expect("fake source fund block height is too small"); - let (mut input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (mut input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(10001).expect("invalid value"), + ); // Ignore the lock time. input.set_sequence(u32::MAX); @@ -503,7 +523,12 @@ async fn mempool_request_with_past_lock_time_is_accepted() { .activation_height(Network::Mainnet) .expect("Canopy activation height is specified"); let fund_height = (height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(10001).expect("invalid value"), + ); // Create a non-coinbase V4 tx with the last valid expiry height. let tx = Transaction::V4 { @@ -577,7 +602,12 @@ async fn mempool_request_with_immature_spent_is_rejected() { .activation_height(Network::Mainnet) .expect("Canopy activation height is specified"); let fund_height = (height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(10001).expect("invalid value"), + ); // Create a non-coinbase V4 tx with the last valid expiry height. let tx = Transaction::V4 { @@ -836,7 +866,12 @@ async fn v4_transaction_with_transparent_transfer_is_accepted() { (transaction_block_height - 1).expect("fake source fund block height is too small"); // Create a fake transparent transfer that should succeed - let (input, output, known_utxos) = mock_transparent_transfer(fake_source_fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fake_source_fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a V4 transaction let transaction = Transaction::V4 { @@ -881,7 +916,12 @@ async fn v4_transaction_with_last_valid_expiry_height() { .activation_height(Network::Mainnet) .expect("Canopy activation height is specified"); let fund_height = (block_height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a non-coinbase V4 tx with the last valid expiry height. let transaction = Transaction::V4 { @@ -965,7 +1005,12 @@ async fn v4_transaction_with_too_low_expiry_height() { .expect("Canopy activation height is specified"); let fund_height = (block_height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // This expiry height is too low so that the tx should seem expired to the verifier. let expiry_height = (block_height - 1).expect("original block height is too small"); @@ -1010,7 +1055,12 @@ async fn v4_transaction_with_exceeding_expiry_height() { let block_height = block::Height::MAX; let fund_height = (block_height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // This expiry height exceeds the maximum defined by the specification. let expiry_height = block::Height(500_000_000); @@ -1163,7 +1213,12 @@ async fn v4_transaction_with_transparent_transfer_is_rejected_by_the_script() { (transaction_block_height - 1).expect("fake source fund block height is too small"); // Create a fake transparent transfer that should not succeed - let (input, output, known_utxos) = mock_transparent_transfer(fake_source_fund_height, false, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fake_source_fund_height, + false, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a V4 transaction let transaction = Transaction::V4 { @@ -1213,7 +1268,12 @@ async fn v4_transaction_with_conflicting_transparent_spend_is_rejected() { (transaction_block_height - 1).expect("fake source fund block height is too small"); // Create a fake transparent transfer that should succeed - let (input, output, known_utxos) = mock_transparent_transfer(fake_source_fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fake_source_fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a V4 transaction let transaction = Transaction::V4 { @@ -1401,7 +1461,12 @@ async fn v5_transaction_with_transparent_transfer_is_accepted() { (transaction_block_height - 1).expect("fake source fund block height is too small"); // Create a fake transparent transfer that should succeed - let (input, output, known_utxos) = mock_transparent_transfer(fake_source_fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fake_source_fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a V5 transaction let transaction = Transaction::V5 { @@ -1447,7 +1512,12 @@ async fn v5_transaction_with_last_valid_expiry_height() { .activation_height(Network::Testnet) .expect("Nu5 activation height for testnet is specified"); let fund_height = (block_height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a non-coinbase V5 tx with the last valid expiry height. let transaction = Transaction::V5 { @@ -1602,7 +1672,12 @@ async fn v5_transaction_with_too_low_expiry_height() { .activation_height(Network::Testnet) .expect("Nu5 activation height for testnet is specified"); let fund_height = (block_height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // This expiry height is too low so that the tx should seem expired to the verifier. let expiry_height = (block_height - 1).expect("original block height is too small"); @@ -1648,7 +1723,12 @@ async fn v5_transaction_with_exceeding_expiry_height() { let block_height = block::Height::MAX; let fund_height = (block_height - 1).expect("fake source fund block height is too small"); - let (input, output, known_utxos) = mock_transparent_transfer(fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // This expiry height exceeds the maximum defined by the specification. let expiry_height = block::Height(500_000_000); @@ -1753,7 +1833,12 @@ async fn v5_transaction_with_transparent_transfer_is_rejected_by_the_script() { (transaction_block_height - 1).expect("fake source fund block height is too small"); // Create a fake transparent transfer that should not succeed - let (input, output, known_utxos) = mock_transparent_transfer(fake_source_fund_height, false, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fake_source_fund_height, + false, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a V5 transaction let transaction = Transaction::V5 { @@ -1805,7 +1890,12 @@ async fn v5_transaction_with_conflicting_transparent_spend_is_rejected() { (transaction_block_height - 1).expect("fake source fund block height is too small"); // Create a fake transparent transfer that should succeed - let (input, output, known_utxos) = mock_transparent_transfer(fake_source_fund_height, true, 0); + let (input, output, known_utxos) = mock_transparent_transfer( + fake_source_fund_height, + true, + 0, + Amount::try_from(1).expect("invalid value"), + ); // Create a V4 transaction let transaction = Transaction::V5 { @@ -2263,6 +2353,7 @@ fn mock_transparent_transfer( previous_utxo_height: block::Height, script_should_succeed: bool, outpoint_index: u32, + previous_output_value: Amount, ) -> ( transparent::Input, transparent::Output, @@ -2286,7 +2377,7 @@ fn mock_transparent_transfer( }; let previous_output = transparent::Output { - value: Amount::try_from(1).expect("1 is an invalid amount"), + value: previous_output_value, lock_script, }; @@ -2726,3 +2817,145 @@ fn shielded_outputs_are_not_decryptable_for_fake_v5_blocks() { ); } } + +#[tokio::test] +async fn mempool_zip317_error() { + let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests(); + let verifier = Verifier::new(Network::Mainnet, state.clone()); + + let height = NetworkUpgrade::Nu5 + .activation_height(Network::Mainnet) + .expect("Nu5 activation height is specified"); + let fund_height = (height - 1).expect("fake source fund block height is too small"); + + // Will produce a small enough miner fee to fail the check. + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(10).expect("invalid value"), + ); + + // Create a non-coinbase V5 tx. + let tx = Transaction::V5 { + inputs: vec![input], + outputs: vec![output], + lock_time: LockTime::unlocked(), + network_upgrade: NetworkUpgrade::Nu5, + expiry_height: height, + sapling_shielded_data: None, + orchard_shielded_data: None, + }; + + let input_outpoint = match tx.inputs()[0] { + transparent::Input::PrevOut { outpoint, .. } => outpoint, + transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"), + }; + + tokio::spawn(async move { + state + .expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint)) + .await + .expect("verifier should call mock state service with correct request") + .respond(zebra_state::Response::UnspentBestChainUtxo( + known_utxos + .get(&input_outpoint) + .map(|utxo| utxo.utxo.clone()), + )); + + state + .expect_request_that(|req| { + matches!( + req, + zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_) + ) + }) + .await + .expect("verifier should call mock state service with correct request") + .respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors); + }); + + let verifier_response = verifier + .oneshot(Request::Mempool { + transaction: tx.into(), + height, + }) + .await; + + // Mempool refuses to add this transaction into storage. + assert!(verifier_response.is_err()); + assert_eq!( + verifier_response.err(), + Some(TransactionError::Zip317(zip317::Error::FeeBelowMinimumRate)) + ); +} + +#[tokio::test] +async fn mempool_zip317_ok() { + let mut state: MockService<_, _, _, _> = MockService::build().for_prop_tests(); + let verifier = Verifier::new(Network::Mainnet, state.clone()); + + let height = NetworkUpgrade::Nu5 + .activation_height(Network::Mainnet) + .expect("Nu5 activation height is specified"); + let fund_height = (height - 1).expect("fake source fund block height is too small"); + + // Will produce a big enough miner fee to pass the check. + let (input, output, known_utxos) = mock_transparent_transfer( + fund_height, + true, + 0, + Amount::try_from(10001).expect("invalid value"), + ); + + // Create a non-coinbase V5 tx. + let tx = Transaction::V5 { + inputs: vec![input], + outputs: vec![output], + lock_time: LockTime::unlocked(), + network_upgrade: NetworkUpgrade::Nu5, + expiry_height: height, + sapling_shielded_data: None, + orchard_shielded_data: None, + }; + + let input_outpoint = match tx.inputs()[0] { + transparent::Input::PrevOut { outpoint, .. } => outpoint, + transparent::Input::Coinbase { .. } => panic!("requires a non-coinbase transaction"), + }; + + tokio::spawn(async move { + state + .expect_request(zebra_state::Request::UnspentBestChainUtxo(input_outpoint)) + .await + .expect("verifier should call mock state service with correct request") + .respond(zebra_state::Response::UnspentBestChainUtxo( + known_utxos + .get(&input_outpoint) + .map(|utxo| utxo.utxo.clone()), + )); + + state + .expect_request_that(|req| { + matches!( + req, + zebra_state::Request::CheckBestChainTipNullifiersAndAnchors(_) + ) + }) + .await + .expect("verifier should call mock state service with correct request") + .respond(zebra_state::Response::ValidBestChainTipNullifiersAndAnchors); + }); + + let verifier_response = verifier + .oneshot(Request::Mempool { + transaction: tx.into(), + height, + }) + .await; + + assert!( + verifier_response.is_ok(), + "expected successful verification, got: {verifier_response:?}" + ); +} diff --git a/zebra-consensus/src/transaction/tests/prop.rs b/zebra-consensus/src/transaction/tests/prop.rs index aaddb3649d6..a2c22377464 100644 --- a/zebra-consensus/src/transaction/tests/prop.rs +++ b/zebra-consensus/src/transaction/tests/prop.rs @@ -7,6 +7,7 @@ use proptest::{collection::vec, prelude::*}; use tower::ServiceExt; use zebra_chain::{ + amount::Amount, block, parameters::{Network, NetworkUpgrade}, serialization::arbitrary::{datetime_full, datetime_u32}, @@ -387,8 +388,12 @@ fn mock_transparent_transfers( .try_into() .expect("too many mock transparent transfers requested"); - let (input, output, new_utxos) = - mock_transparent_transfer(fake_source_fund_height, true, outpoint_index); + let (input, output, new_utxos) = mock_transparent_transfer( + fake_source_fund_height, + true, + outpoint_index, + Amount::try_from(1).expect("invalid value"), + ); inputs.push(input); outputs.push(output); diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/zip317.rs b/zebra-rpc/src/methods/get_block_template_rpcs/zip317.rs index 2b086522667..3737f339ef0 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/zip317.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/zip317.rs @@ -15,7 +15,7 @@ use zebra_chain::{ amount::NegativeOrZero, block::{Height, MAX_BLOCK_BYTES}, parameters::Network, - transaction::VerifiedUnminedTx, + transaction::{zip317::BLOCK_PRODUCTION_UNPAID_ACTION_LIMIT, VerifiedUnminedTx}, transparent, }; use zebra_consensus::MAX_BLOCK_SIGOPS; @@ -24,10 +24,6 @@ use crate::methods::get_block_template_rpcs::{ get_block_template::generate_coinbase_transaction, types::transaction::TransactionTemplate, }; -/// The ZIP-317 recommended limit on the number of unpaid actions per block. -/// `block_unpaid_action_limit` in ZIP-317. -pub const BLOCK_PRODUCTION_UNPAID_ACTION_LIMIT: u32 = 50; - /// Selects mempool transactions for block production according to [ZIP-317], /// using a fake coinbase transaction and the mempool. /// diff --git a/zebrad/src/components/inbound/tests/fake_peer_set.rs b/zebrad/src/components/inbound/tests/fake_peer_set.rs index 9a9b797c027..1a226390c82 100644 --- a/zebrad/src/components/inbound/tests/fake_peer_set.rs +++ b/zebrad/src/components/inbound/tests/fake_peer_set.rs @@ -164,11 +164,14 @@ async fn mempool_push_transaction() -> Result<(), crate::BoxError> { .expect("unexpected non-mempool request"); // Set a dummy fee and sigops. - responder.respond(transaction::Response::from(VerifiedUnminedTx::new( - transaction, - Amount::zero(), - 0, - ))); + responder.respond(transaction::Response::from( + VerifiedUnminedTx::new( + transaction, + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + )); }); let (push_response, _) = futures::join!(request, verification); @@ -266,11 +269,14 @@ async fn mempool_advertise_transaction_ids() -> Result<(), crate::BoxError> { .expect("unexpected non-mempool request"); // Set a dummy fee and sigops. - responder.respond(transaction::Response::from(VerifiedUnminedTx::new( - transaction, - Amount::zero(), - 0, - ))); + responder.respond(transaction::Response::from( + VerifiedUnminedTx::new( + transaction, + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + )); }); let (advertise_response, _, _) = futures::join!(request, peer_set_responder, verification); @@ -365,11 +371,14 @@ async fn mempool_transaction_expiration() -> Result<(), crate::BoxError> { .expect("unexpected non-mempool request"); // Set a dummy fee and sigops. - responder.respond(transaction::Response::from(VerifiedUnminedTx::new( - transaction, - Amount::zero(), - 0, - ))); + responder.respond(transaction::Response::from( + VerifiedUnminedTx::new( + transaction, + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + )); }); let (push_response, _) = futures::join!(request, verification); @@ -499,11 +508,14 @@ async fn mempool_transaction_expiration() -> Result<(), crate::BoxError> { .expect("unexpected non-mempool request"); // Set a dummy fee and sigops. - responder.respond(transaction::Response::from(VerifiedUnminedTx::new( - transaction, - Amount::zero(), - 0, - ))); + responder.respond(transaction::Response::from( + VerifiedUnminedTx::new( + transaction, + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + )); }); let (push_response, _) = futures::join!(request, verification); diff --git a/zebrad/src/components/mempool/storage/tests.rs b/zebrad/src/components/mempool/storage/tests.rs index 8fdf5431a39..cad844108c4 100644 --- a/zebrad/src/components/mempool/storage/tests.rs +++ b/zebrad/src/components/mempool/storage/tests.rs @@ -37,5 +37,12 @@ pub fn unmined_transactions_in_blocks( selected_blocks .flat_map(|block| block.transactions) .map(UnminedTx::from) - .map(|transaction| VerifiedUnminedTx::new(transaction, Amount::zero(), 0)) + .map(|transaction| { + VerifiedUnminedTx::new( + transaction, + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass") + }) } diff --git a/zebrad/src/components/mempool/storage/tests/prop.rs b/zebrad/src/components/mempool/storage/tests/prop.rs index 9b3a447c9e0..eca65935acb 100644 --- a/zebrad/src/components/mempool/storage/tests/prop.rs +++ b/zebrad/src/components/mempool/storage/tests/prop.rs @@ -475,8 +475,20 @@ impl SpendConflictTestInput { }; ( - VerifiedUnminedTx::new(first.0.into(), Amount::zero(), 0), - VerifiedUnminedTx::new(second.0.into(), Amount::zero(), 0), + VerifiedUnminedTx::new( + first.0.into(), + // make sure miner fee is big enough for all cases + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + VerifiedUnminedTx::new( + second.0.into(), + // make sure miner fee is big enough for all cases + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), ) } @@ -493,8 +505,20 @@ impl SpendConflictTestInput { Self::remove_orchard_conflicts(&mut first, &mut second); ( - VerifiedUnminedTx::new(first.0.into(), Amount::zero(), 0), - VerifiedUnminedTx::new(second.0.into(), Amount::zero(), 0), + VerifiedUnminedTx::new( + first.0.into(), + // make sure miner fee is big enough for all cases + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + VerifiedUnminedTx::new( + second.0.into(), + // make sure miner fee is big enough for all cases + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), ) } diff --git a/zebrad/src/components/mempool/storage/tests/vectors.rs b/zebrad/src/components/mempool/storage/tests/vectors.rs index 5977662f19b..35827574946 100644 --- a/zebrad/src/components/mempool/storage/tests/vectors.rs +++ b/zebrad/src/components/mempool/storage/tests/vectors.rs @@ -271,7 +271,14 @@ fn mempool_expired_basic_for_network(network: Network) -> Result<()> { let tx_id = tx.unmined_id(); // Insert the transaction into the mempool, with a fake zero miner fee and sigops - storage.insert(VerifiedUnminedTx::new(tx.into(), Amount::zero(), 0))?; + storage.insert( + VerifiedUnminedTx::new( + tx.into(), + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + )?; assert_eq!(storage.transaction_count(), 1); diff --git a/zebrad/src/components/mempool/tests/vector.rs b/zebrad/src/components/mempool/tests/vector.rs index 3dad0f1a466..4dbed9426bb 100644 --- a/zebrad/src/components/mempool/tests/vector.rs +++ b/zebrad/src/components/mempool/tests/vector.rs @@ -805,11 +805,14 @@ async fn mempool_reverifies_after_tip_change() -> Result<(), Report> { .expect("unexpected non-mempool request"); // Set a dummy fee and sigops. - responder.respond(transaction::Response::from(VerifiedUnminedTx::new( - transaction, - Amount::zero(), - 0, - ))); + responder.respond(transaction::Response::from( + VerifiedUnminedTx::new( + transaction, + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + )); }) .await; @@ -862,11 +865,14 @@ async fn mempool_reverifies_after_tip_change() -> Result<(), Report> { .expect("unexpected non-mempool request"); // Set a dummy fee and sigops. - responder.respond(transaction::Response::from(VerifiedUnminedTx::new( - transaction, - Amount::zero(), - 0, - ))); + responder.respond(transaction::Response::from( + VerifiedUnminedTx::new( + transaction, + Amount::try_from(1_000_000).expect("invalid value"), + 0, + ) + .expect("verification should pass"), + )); }) .await;