From a4881c22f15d2341b4c9a2be0fe2eee5f563ca9e Mon Sep 17 00:00:00 2001 From: John Tromp Date: Fri, 23 Oct 2020 12:51:16 +0200 Subject: [PATCH 01/24] add FeeFields type --- api/src/types.rs | 18 +- chain/src/pipe.rs | 2 +- chain/tests/mine_nrd_kernel.rs | 4 +- chain/tests/mine_simple_chain.rs | 6 +- chain/tests/nrd_validation_rules.rs | 6 +- chain/tests/process_block_cut_through.rs | 2 +- chain/tests/test_coinbase_maturity.rs | 4 +- core/src/consensus.rs | 6 +- core/src/core/block.rs | 7 +- core/src/core/transaction.rs | 347 ++++++++++++++++------- core/src/global.rs | 8 +- core/src/libtx/build.rs | 8 +- core/src/libtx/mod.rs | 7 +- core/tests/block.rs | 24 +- core/tests/common.rs | 8 +- core/tests/core.rs | 21 +- core/tests/transaction.rs | 4 +- p2p/src/msg.rs | 2 +- pool/src/pool.rs | 32 +-- pool/src/transaction_pool.rs | 12 +- pool/src/types.rs | 3 +- pool/tests/block_max_weight.rs | 8 +- pool/tests/common.rs | 8 +- pool/tests/nrd_kernel_relative_height.rs | 4 +- pool/tests/nrd_kernels_disabled.rs | 2 +- pool/tests/nrd_kernels_enabled.rs | 2 +- 26 files changed, 352 insertions(+), 203 deletions(-) diff --git a/api/src/types.rs b/api/src/types.rs index e870216257..04c3ee46f3 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -15,7 +15,7 @@ use crate::chain; use crate::core::core::hash::Hashed; use crate::core::core::merkle_proof::MerkleProof; -use crate::core::core::{KernelFeatures, TxKernel}; +use crate::core::core::{extract_fee_fields, KernelFeatures, TxKernel}; use crate::core::{core, ser}; use crate::p2p; use crate::util::secp::pedersen; @@ -499,6 +499,7 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TxKernelPrintable { pub features: String, + pub fee_shift: u64, pub fee: u64, pub lock_height: u64, pub excess: String, @@ -508,17 +509,22 @@ pub struct TxKernelPrintable { impl TxKernelPrintable { pub fn from_txkernel(k: &core::TxKernel) -> TxKernelPrintable { let features = k.features.as_string(); - let (fee, lock_height) = match k.features { - KernelFeatures::Plain { fee } => (fee, 0), + let (fee_fields, lock_height) = match k.features { + KernelFeatures::Plain { fee_fields } => (fee_fields, 0), KernelFeatures::Coinbase => (0, 0), - KernelFeatures::HeightLocked { fee, lock_height } => (fee, lock_height), + KernelFeatures::HeightLocked { + fee_fields, + lock_height, + } => (fee_fields, lock_height), KernelFeatures::NoRecentDuplicate { - fee, + fee_fields, relative_height, - } => (fee, relative_height.into()), + } => (fee_fields, relative_height.into()), }; + let (fee, fee_shift) = extract_fee_fields(fee_fields); TxKernelPrintable { features, + fee_shift, fee, lock_height, excess: k.excess.to_hex(), diff --git a/chain/src/pipe.rs b/chain/src/pipe.rs index 39f64bd4b9..05c19d7664 100644 --- a/chain/src/pipe.rs +++ b/chain/src/pipe.rs @@ -353,7 +353,7 @@ fn validate_header(header: &BlockHeader, ctx: &mut BlockContext<'_>) -> Result<( } // Block header is invalid (and block is invalid) if this lower bound is too heavy for a full block. - let weight = TransactionBody::weight_as_block(0, num_outputs, num_kernels); + let weight = TransactionBody::weight_by_iok(0, num_outputs, num_kernels); if weight > global::max_block_weight() { return Err(ErrorKind::Block(block::Error::TooHeavy).into()); } diff --git a/chain/tests/mine_nrd_kernel.rs b/chain/tests/mine_nrd_kernel.rs index 21c33d14ce..29724dac02 100644 --- a/chain/tests/mine_nrd_kernel.rs +++ b/chain/tests/mine_nrd_kernel.rs @@ -84,7 +84,7 @@ fn mine_block_with_nrd_kernel_and_nrd_feature_enabled() { let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier(); let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee: 20000, + fee_fields: 20000, relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[ @@ -131,7 +131,7 @@ fn mine_invalid_block_with_nrd_kernel_and_nrd_feature_enabled_before_hf() { let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier(); let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee: 20000, + fee_fields: 20000, relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[ diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 12b3cdaa61..dd2ddfc78e 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -569,7 +569,7 @@ fn spend_rewind_spend() { let key_id30 = ExtKeychainPath::new(1, 30, 0, 0, 0).to_identifier(); let tx1 = build::transaction( - KernelFeatures::Plain { fee: 20000 }, + KernelFeatures::Plain { fee_fields: 20000 }, &[ build::coinbase_input(consensus::REWARD, key_id_coinbase.clone()), build::output(consensus::REWARD - 20000, key_id30.clone()), @@ -642,7 +642,7 @@ fn spend_in_fork_and_compact() { let key_id31 = ExtKeychainPath::new(1, 31, 0, 0, 0).to_identifier(); let tx1 = build::transaction( - KernelFeatures::Plain { fee: 20000 }, + KernelFeatures::Plain { fee_fields: 20000 }, &[ build::coinbase_input(consensus::REWARD, key_id2.clone()), build::output(consensus::REWARD - 20000, key_id30.clone()), @@ -660,7 +660,7 @@ fn spend_in_fork_and_compact() { chain.validate(false).unwrap(); let tx2 = build::transaction( - KernelFeatures::Plain { fee: 20000 }, + KernelFeatures::Plain { fee_fields: 20000 }, &[ build::input(consensus::REWARD - 20000, key_id30.clone()), build::output(consensus::REWARD - 40000, key_id31.clone()), diff --git a/chain/tests/nrd_validation_rules.rs b/chain/tests/nrd_validation_rules.rs index a46e7528a4..44d693740f 100644 --- a/chain/tests/nrd_validation_rules.rs +++ b/chain/tests/nrd_validation_rules.rs @@ -100,7 +100,7 @@ fn process_block_nrd_validation() -> Result<(), Error> { assert_eq!(chain.head()?.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee: 20000, + fee_fields: 20000, relative_height: NRDRelativeHeight::new(2)?, }); @@ -216,7 +216,7 @@ fn process_block_nrd_validation_relative_height_1() -> Result<(), Error> { assert_eq!(chain.head()?.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee: 20000, + fee_fields: 20000, relative_height: NRDRelativeHeight::new(1)?, }); @@ -315,7 +315,7 @@ fn process_block_nrd_validation_fork() -> Result<(), Error> { assert_eq!(header_8.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee: 20000, + fee_fields: 20000, relative_height: NRDRelativeHeight::new(2)?, }); diff --git a/chain/tests/process_block_cut_through.rs b/chain/tests/process_block_cut_through.rs index 274ff1e8a6..85aa7ffe4b 100644 --- a/chain/tests/process_block_cut_through.rs +++ b/chain/tests/process_block_cut_through.rs @@ -104,7 +104,7 @@ fn process_block_cut_through() -> Result<(), chain::Error> { // Note: We reuse key_ids resulting in an input and an output sharing the same commitment. // The input is coinbase and the output is plain. let tx = build::transaction( - KernelFeatures::Plain { fee: 0 }, + KernelFeatures::Plain { fee_fields: 0 }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), build::coinbase_input(consensus::REWARD, key_id2.clone()), diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index 3f5587c973..997ac08797 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -100,7 +100,7 @@ fn test_coinbase_maturity() { // here we build a tx that attempts to spend the earlier coinbase output // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[ build::coinbase_input(amount, key_id1.clone()), build::output(amount - 2, key_id2.clone()), @@ -182,7 +182,7 @@ fn test_coinbase_maturity() { // here we build a tx that attempts to spend the earlier coinbase output // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[ build::coinbase_input(amount, key_id1.clone()), build::output(amount - 2, key_id2.clone()), diff --git a/core/src/consensus.rs b/core/src/consensus.rs index bea63da943..c75a8e6596 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -102,13 +102,13 @@ pub const CUT_THROUGH_HORIZON: u32 = WEEK_HEIGHT as u32; pub const STATE_SYNC_THRESHOLD: u32 = 2 * DAY_HEIGHT as u32; /// Weight of an input when counted against the max block weight capacity -pub const BLOCK_INPUT_WEIGHT: u64 = 1; +pub const INPUT_WEIGHT: u64 = 1; /// Weight of an output when counted against the max block weight capacity -pub const BLOCK_OUTPUT_WEIGHT: u64 = 21; +pub const OUTPUT_WEIGHT: u64 = 21; /// Weight of a kernel when counted against the max block weight capacity -pub const BLOCK_KERNEL_WEIGHT: u64 = 3; +pub const KERNEL_WEIGHT: u64 = 3; /// Total maximum block weight. At current sizes, this means a maximum /// theoretical size of: diff --git a/core/src/core/block.rs b/core/src/core/block.rs index 81454e325e..d17e17ce45 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -469,11 +469,8 @@ impl Readable for UntrustedBlockHeader { } // Validate global output and kernel MMR sizes against upper bounds based on block height. - let global_weight = TransactionBody::weight_as_block( - 0, - header.output_mmr_count(), - header.kernel_mmr_count(), - ); + let global_weight = + TransactionBody::weight_by_iok(0, header.output_mmr_count(), header.kernel_mmr_count()); if global_weight > global::max_block_weight() * (header.height + 1) { return Err(ser::Error::CorruptedData); } diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 04ac4ad17e..88af30e4dd 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -36,6 +36,105 @@ use util::static_secp_instance; use util::RwLock; use util::ToHex; +/// Fee fields as in fix-fees RFC: { future_use: 20, fee_shift: 4, fee: 40 } +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +pub struct FeeFields(u64); + +impl DefaultHashable for FeeFields {} + +impl Writeable for FeeFields { + fn write(&self, writer: &mut W) -> Result<(), ser::Error> { + writer.write_u64(self.0) + } +} + +impl Readable for FeeFields { + fn read(reader: &mut R) -> Result { + let fee_fields = reader.read_u64()?; + Ok(Self(fee_fields)) + } +} + +/// Conversion from a u64 to a valid FeeFields. +/// Valid height is between 1 and WEEK_HEIGHT inclusive. +impl TryFrom for FeeFields { + type Error = Error; + + fn try_from(fee: u64) -> Result { + if fee == 0 { + Err(Error::InvalidFeeFields) + } else if fee > FeeFields::FEE_MASK { + Err(Error::InvalidFeeFields) + } else { + Ok(Self(fee)) + } + } +} + +impl From for u64 { + fn from(fee_fields: FeeFields) -> Self { + fee_fields.0 as u64 + } +} + +impl FeeFields { + /// Fees are limited to 40 bits + const FEE_BITS: u32 = 40; + /// Used to extract fee field + const FEE_MASK: u64 = 1u64 << FeeFields::FEE_BITS - 1; + + /// Fee shifts are limited to 4 bits + pub const FEE_SHIFT_BITS: u32 = 4; + /// Used to extract fee_shift field + pub const FEE_SHIFT_MASK: u64 = 1u64 << FeeFields::FEE_SHIFT_BITS - 1; + + /// Create a zero FeeFields with 0 fee and 0 fee_shift + pub fn zero() -> Self { + Self(0) + } + + /// Create a new FeeFields from the provided shift and fee + /// Checks both are valid (in range) + pub fn new(fee_shift: u64, fee: u64) -> Result { + if fee == 0 { + Err(Error::InvalidFeeFields) + } else if fee > FeeFields::FEE_MASK { + Err(Error::InvalidFeeFields) + } else if fee_shift > FeeFields::FEE_SHIFT_MASK { + Err(Error::InvalidFeeFields) + } else { + Ok(Self(fee_shift << FeeFields::FEE_BITS | fee)) + } + } + + /// Extract fee_shift field + pub fn fee_shift(&self) -> u64 { + (self.0 >> FeeFields::FEE_BITS) & FeeFields::FEE_SHIFT_MASK + } + + /// Extract fee field + pub fn fee(&self) -> u64 { + self.0 & FeeFields::FEE_MASK + } + + /// Extract bitfields fee_shift and fee into tuple + /// ignore upper 64-FEE_BITS-FEE_SHIFT_BITS bits + pub fn as_tuple(&self) -> (u64, u64) { + let fee = self.0 & FeeFields::FEE_MASK; + let fee_shift = (self.0 >> FeeFields::FEE_BITS) & FeeFields::FEE_SHIFT_MASK; + (fee, fee_shift) + } + + /// Accumulate two fee_fields by adding fees and maxing fee_shift + /// THIS CAN'T WORK SINCE A TX CAN HAVE MULTIPLE MAXED-OUT FEES + /// WHICH ADDED UP DON'T FIT + pub fn accumulate(&self, other: FeeFields) -> Result { + let (fee, fee_shift) = self.as_tuple(); + let (other_fee, other_fee_shift) = other.as_tuple(); + FeeFields::new(max(fee_shift, other_fee_shift), fee + other_fee) + } +} + /// Relative height field on NRD kernel variant. /// u16 representing a height between 1 and MAX (consensus::WEEK_HEIGHT). #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] @@ -106,21 +205,21 @@ pub enum KernelFeatures { /// Plain kernel (the default for Grin txs). Plain { /// Plain kernels have fees. - fee: u64, + fee_fields: FeeFields, }, /// A coinbase kernel. Coinbase, /// A kernel with an explicit lock height (and fee). HeightLocked { /// Height locked kernels have fees. - fee: u64, + fee_fields: FeeFields, /// Height locked kernels have lock heights. lock_height: u64, }, /// "No Recent Duplicate" (NRD) kernels enforcing relative lock height between instances. NoRecentDuplicate { /// These have fees. - fee: u64, + fee_fields: FeeFields, /// Relative lock height. relative_height: NRDRelativeHeight, }, @@ -153,20 +252,23 @@ impl KernelFeatures { } } - /// msg = hash(features) for coinbase kernels - /// hash(features || fee) for plain kernels - /// hash(features || fee || lock_height) for height locked kernels - /// hash(features || fee || relative_height) for NRD kernels + /// msg = hash(features) for coinbase kernels + /// hash(features || fee_fields) for plain kernels + /// hash(features || fee_fields || lock_height) for height locked kernels + /// hash(features || fee_fields || relative_height) for NRD kernels pub fn kernel_sig_msg(&self) -> Result { let x = self.as_u8(); let hash = match self { - KernelFeatures::Plain { fee } => (x, fee).hash(), + KernelFeatures::Plain { fee_fields } => (x, fee_fields).hash(), KernelFeatures::Coinbase => x.hash(), - KernelFeatures::HeightLocked { fee, lock_height } => (x, fee, lock_height).hash(), + KernelFeatures::HeightLocked { + fee_fields, + lock_height, + } => (x, fee_fields, lock_height).hash(), KernelFeatures::NoRecentDuplicate { - fee, + fee_fields, relative_height, - } => (x, fee, relative_height).hash(), + } => (x, fee_fields, relative_height).hash(), }; let msg = secp::Message::from_slice(&hash.as_bytes())?; @@ -174,29 +276,32 @@ impl KernelFeatures { } /// Write tx kernel features out in v1 protocol format. - /// Always include the fee and lock_height, writing 0 value if unused. + /// Always include the fee_fields and lock_height, writing 0 value if unused. fn write_v1(&self, writer: &mut W) -> Result<(), ser::Error> { writer.write_u8(self.as_u8())?; match self { - KernelFeatures::Plain { fee } => { - writer.write_u64(*fee)?; + KernelFeatures::Plain { fee_fields } => { + fee_fields.write(writer)?; // Write "empty" bytes for feature specific data (8 bytes). writer.write_empty_bytes(8)?; } KernelFeatures::Coinbase => { - // Write "empty" bytes for fee (8 bytes) and feature specific data (8 bytes). + // Write "empty" bytes for fee_fields (8 bytes) and feature specific data (8 bytes). writer.write_empty_bytes(16)?; } - KernelFeatures::HeightLocked { fee, lock_height } => { - writer.write_u64(*fee)?; + KernelFeatures::HeightLocked { + fee_fields, + lock_height, + } => { + fee_fields.write(writer)?; // 8 bytes of feature specific data containing the lock height as big-endian u64. writer.write_u64(*lock_height)?; } KernelFeatures::NoRecentDuplicate { - fee, + fee_fields, relative_height, } => { - writer.write_u64(*fee)?; + fee_fields.write(writer)?; // 8 bytes of feature specific data. First 6 bytes are empty. // Last 2 bytes contain the relative lock height as big-endian u16. @@ -211,28 +316,31 @@ impl KernelFeatures { /// Write tx kernel features out in v2 protocol format. /// These are variable sized based on feature variant. - /// Only write fee out for feature variants that support it. + /// Only write fee_fields out for feature variants that support it. /// Only write lock_height out for feature variants that support it. fn write_v2(&self, writer: &mut W) -> Result<(), ser::Error> { writer.write_u8(self.as_u8())?; match self { - KernelFeatures::Plain { fee } => { + KernelFeatures::Plain { fee_fields } => { // Fee only, no additional data on plain kernels. - writer.write_u64(*fee)?; + fee_fields.write(writer)?; } KernelFeatures::Coinbase => { // No additional data. } - KernelFeatures::HeightLocked { fee, lock_height } => { - writer.write_u64(*fee)?; + KernelFeatures::HeightLocked { + fee_fields, + lock_height, + } => { + fee_fields.write(writer)?; // V2 height locked kernels use 8 bytes for the lock height. writer.write_u64(*lock_height)?; } KernelFeatures::NoRecentDuplicate { - fee, + fee_fields, relative_height, } => { - writer.write_u64(*fee)?; + fee_fields.write(writer)?; // V2 NRD kernels use 2 bytes for the relative lock height. relative_height.write(writer)?; } @@ -240,7 +348,7 @@ impl KernelFeatures { Ok(()) } - // Always read feature byte, 8 bytes for fee and 8 bytes for additional data + // Always read feature byte, 8 bytes for fee_fields and 8 bytes for additional data // representing lock height or relative height. // Fee and additional data may be unused for some kernel variants but we need // to read these bytes and verify they are 0 if unused. @@ -248,22 +356,25 @@ impl KernelFeatures { let feature_byte = reader.read_u8()?; let features = match feature_byte { KernelFeatures::PLAIN_U8 => { - let fee = reader.read_u64()?; + let fee_fields = FeeFields::read(reader)?; // 8 "empty" bytes as additional data is not used. reader.read_empty_bytes(8)?; - KernelFeatures::Plain { fee } + KernelFeatures::Plain { fee_fields } } KernelFeatures::COINBASE_U8 => { - // 8 "empty" bytes as fee is not used. + // 8 "empty" bytes as fee_fields is not used. // 8 "empty" bytes as additional data is not used. reader.read_empty_bytes(16)?; KernelFeatures::Coinbase } KernelFeatures::HEIGHT_LOCKED_U8 => { - let fee = reader.read_u64()?; + let fee_fields = FeeFields::read(reader)?; // 8 bytes of feature specific data, lock height as big-endian u64. let lock_height = reader.read_u64()?; - KernelFeatures::HeightLocked { fee, lock_height } + KernelFeatures::HeightLocked { + fee_fields, + lock_height, + } } KernelFeatures::NO_RECENT_DUPLICATE_U8 => { // NRD kernels are invalid if NRD feature flag is not enabled. @@ -271,7 +382,7 @@ impl KernelFeatures { return Err(ser::Error::CorruptedData); } - let fee = reader.read_u64()?; + let fee_fields = FeeFields::read(reader)?; // 8 bytes of feature specific data. // The first 6 bytes must be "empty". @@ -279,7 +390,7 @@ impl KernelFeatures { reader.read_empty_bytes(6)?; let relative_height = NRDRelativeHeight::read(reader)?; KernelFeatures::NoRecentDuplicate { - fee, + fee_fields, relative_height, } } @@ -295,14 +406,17 @@ impl KernelFeatures { fn read_v2(reader: &mut R) -> Result { let features = match reader.read_u8()? { KernelFeatures::PLAIN_U8 => { - let fee = reader.read_u64()?; - KernelFeatures::Plain { fee } + let fee_fields = FeeFields::read(reader)?; + KernelFeatures::Plain { fee_fields } } KernelFeatures::COINBASE_U8 => KernelFeatures::Coinbase, KernelFeatures::HEIGHT_LOCKED_U8 => { - let fee = reader.read_u64()?; + let fee_fields = FeeFields::read(reader)?; let lock_height = reader.read_u64()?; - KernelFeatures::HeightLocked { fee, lock_height } + KernelFeatures::HeightLocked { + fee_fields, + lock_height, + } } KernelFeatures::NO_RECENT_DUPLICATE_U8 => { // NRD kernels are invalid if NRD feature flag is not enabled. @@ -310,10 +424,10 @@ impl KernelFeatures { return Err(ser::Error::CorruptedData); } - let fee = reader.read_u64()?; + let fee_fields = FeeFields::read(reader)?; let relative_height = NRDRelativeHeight::read(reader)?; KernelFeatures::NoRecentDuplicate { - fee, + fee_fields, relative_height, } } @@ -384,6 +498,8 @@ pub enum Error { /// Validation error relating to kernel features. /// It is invalid for a transaction to contain a coinbase kernel, for example. InvalidKernelFeatures, + /// feeshift is limited to 4 bits and fee must be positive and fit in 40 bits. + InvalidFeeFields, /// NRD kernel relative height is limited to 1 week duration and must be greater than 0. InvalidNRDRelativeHeight, /// Signature verification error. @@ -435,7 +551,7 @@ impl From for Error { /// A proof that a transaction sums to zero. Includes both the transaction's /// Pedersen commitment and the signature, that guarantees that the commitments /// amount to zero. -/// The signature signs the fee and the lock_height, which are retained for +/// The signature signs the fee_fields and the lock_height, which are retained for /// signature validation. #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub struct TxKernel { @@ -450,7 +566,7 @@ pub struct TxKernel { )] pub excess: Commitment, /// The signature proving the excess is a valid public key, which signs - /// the transaction fee. + /// the transaction fee_fields. #[serde(with = "secp_ser::sig_serde")] pub excess_sig: secp::Signature, } @@ -562,14 +678,14 @@ impl TxKernel { } /// The msg signed as part of the tx kernel. - /// Based on kernel features and associated fields (fee and lock_height). + /// Based on kernel features and associated fields (fee_fields and lock_height). pub fn msg_to_sign(&self) -> Result { let msg = self.features.kernel_sig_msg()?; Ok(msg) } /// Verify the transaction proof validity. Entails handling the commitment - /// as a public key and checking the signature verifies with the fee as + /// as a public key and checking the signature verifies with the fee_fields as /// message. pub fn verify(&self) -> Result<(), Error> { let secp = static_secp_instance(); @@ -616,7 +732,9 @@ impl TxKernel { /// Build an empty tx kernel with zero values. pub fn empty() -> TxKernel { - TxKernel::with_features(KernelFeatures::Plain { fee: 0 }) + TxKernel::with_features(KernelFeatures::Plain { + fee_fields: FeeFields::zero(), + }) } /// Build an empty tx kernel with the provided kernel features. @@ -687,8 +805,7 @@ impl Readable for TransactionBody { // Quick block weight check before proceeding. // Note: We use weight_as_block here (inputs have weight). - let tx_block_weight = - TransactionBody::weight_as_block(num_inputs, num_outputs, num_kernels); + let tx_block_weight = TransactionBody::weight_by_iok(num_inputs, num_outputs, num_kernels); if tx_block_weight > global::max_block_weight() { return Err(ser::Error::TooLargeReadErr); } @@ -866,53 +983,54 @@ impl TransactionBody { .iter() .filter_map(|k| match k.features { KernelFeatures::Coinbase => None, - KernelFeatures::Plain { fee } => Some(fee), - KernelFeatures::HeightLocked { fee, .. } => Some(fee), - KernelFeatures::NoRecentDuplicate { fee, .. } => Some(fee), + KernelFeatures::Plain { fee_fields } => Some(fee_fields), + KernelFeatures::HeightLocked { fee_fields, .. } => Some(fee_fields), + KernelFeatures::NoRecentDuplicate { fee_fields, .. } => Some(fee_fields), }) - .fold(0, |acc, fee| acc.saturating_add(fee)) + .fold(0, |acc, fee_fields| acc.saturating_add(fee_fields.fee())) } - fn overage(&self) -> i64 { - self.fee() as i64 + /// Shifted fee for a TransactionBody is the sum of fees shifted right by the maximum fee_shift + /// this is used to determine whether a tx can be relayed or accepted in a mempool + /// where transactions can specify a higher block-inclusion priority as a positive shift up to 15 + /// but are required to overpaythe minimum required fees by a factor of 2^priority + pub fn shifted_fee(&self) -> u64 { + let (fee, fee_shift) = self + .kernels + .iter() + .filter_map(|k| match k.features { + KernelFeatures::Coinbase => None, + KernelFeatures::Plain { fee_fields } => Some(fee_fields), + KernelFeatures::HeightLocked { fee_fields, .. } => Some(fee_fields), + KernelFeatures::NoRecentDuplicate { fee_fields, .. } => Some(fee_fields), + }) + .fold(FeeFields::zero, |acc: FeeFields, fee_fields| { + acc.accumulate(fee_fields)? + }) + .as_tuple(); + fee >> fee_shift } - /// Calculate transaction weight - pub fn body_weight(&self) -> u64 { - TransactionBody::weight( - self.inputs.len() as u64, - self.outputs.len() as u64, - self.kernels.len() as u64, - ) + fn overage(&self) -> i64 { + self.fee() as i64 } /// Calculate weight of transaction using block weighing - pub fn body_weight_as_block(&self) -> u64 { - TransactionBody::weight_as_block( + pub fn weight(&self) -> u64 { + TransactionBody::weight_by_iok( self.inputs.len() as u64, self.outputs.len() as u64, self.kernels.len() as u64, ) } - /// Calculate transaction weight from transaction details. This is non - /// consensus critical and compared to block weight, incentivizes spending - /// more outputs (to lower the fee). - pub fn weight(num_inputs: u64, num_outputs: u64, num_kernels: u64) -> u64 { - let body_weight = num_outputs - .saturating_mul(4) - .saturating_add(num_kernels) - .saturating_sub(num_inputs); - max(body_weight, 1) - } - /// Calculate transaction weight using block weighing from transaction /// details. Consensus critical and uses consensus weight values. - pub fn weight_as_block(num_inputs: u64, num_outputs: u64, num_kernels: u64) -> u64 { + pub fn weight_by_iok(num_inputs: u64, num_outputs: u64, num_kernels: u64) -> u64 { num_inputs - .saturating_mul(consensus::BLOCK_INPUT_WEIGHT as u64) - .saturating_add(num_outputs.saturating_mul(consensus::BLOCK_OUTPUT_WEIGHT as u64)) - .saturating_add(num_kernels.saturating_mul(consensus::BLOCK_KERNEL_WEIGHT as u64)) + .saturating_mul(consensus::INPUT_WEIGHT as u64) + .saturating_add(num_outputs.saturating_mul(consensus::OUTPUT_WEIGHT as u64)) + .saturating_add(num_kernels.saturating_mul(consensus::KERNEL_WEIGHT as u64)) } /// Lock height of a body is the max lock height of the kernels. @@ -932,7 +1050,7 @@ impl TransactionBody { fn verify_weight(&self, weighting: Weighting) -> Result<(), Error> { // A coinbase reward is a single output and a single kernel (for now). // We need to account for this when verifying max tx weights. - let coinbase_weight = consensus::BLOCK_OUTPUT_WEIGHT + consensus::BLOCK_KERNEL_WEIGHT; + let coinbase_weight = consensus::OUTPUT_WEIGHT + consensus::KERNEL_WEIGHT; // If "tx" body then remember to reduce the max_block_weight by the weight of a kernel. // If "limited tx" then compare against the provided max_weight. @@ -943,7 +1061,7 @@ impl TransactionBody { // for the additional coinbase reward (1 output + 1 kernel). // let max_weight = match weighting { - Weighting::AsTransaction => global::max_block_weight().saturating_sub(coinbase_weight), + Weighting::AsTransaction => global::max_tx_weight(), Weighting::AsLimitedTransaction(max_weight) => { min(global::max_block_weight(), max_weight).saturating_sub(coinbase_weight) } @@ -954,7 +1072,7 @@ impl TransactionBody { } }; - if self.body_weight_as_block() > max_weight { + if self.weight() > max_weight { return Err(Error::TooHeavy); } Ok(()) @@ -1262,6 +1380,11 @@ impl Transaction { self.body.fee() } + /// Shifted fee for a transaction is the sum of fees of all kernels shifted right by the maximum fee shift + pub fn shifted_fee(&self) -> u64 { + self.body.shifted_fee() + } + /// Total overage across all kernels. pub fn overage(&self) -> i64 { self.body.overage() @@ -1297,25 +1420,20 @@ impl Transaction { Ok(()) } - /// Can be used to compare txs by their fee/weight ratio. + /// Can be used to compare txs by their fee/weight ratio, aka feerate. /// Don't use these values for anything else though due to precision multiplier. - pub fn fee_to_weight(&self) -> u64 { - self.fee() * 1_000 / self.tx_weight() as u64 + pub fn fee_rate(&self) -> u64 { + self.fee() / self.weight() as u64 } /// Calculate transaction weight - pub fn tx_weight(&self) -> u64 { - self.body.body_weight() - } - - /// Calculate transaction weight as a block - pub fn tx_weight_as_block(&self) -> u64 { - self.body.body_weight_as_block() + pub fn weight(&self) -> u64 { + self.body.weight() } /// Calculate transaction weight from transaction details - pub fn weight(num_inputs: u64, num_outputs: u64, num_kernels: u64) -> u64 { - TransactionBody::weight(num_inputs, num_outputs, num_kernels) + pub fn weight_by_iok(num_inputs: u64, num_outputs: u64, num_kernels: u64) -> u64 { + TransactionBody::weight_by_iok(num_inputs, num_outputs, num_kernels) } } @@ -2113,7 +2231,9 @@ mod test { let sig = secp::Signature::from_raw_data(&[0; 64]).unwrap(); let kernel = TxKernel { - features: KernelFeatures::Plain { fee: 10 }, + features: KernelFeatures::Plain { + fee_fields: FeeFields::new(0, 10), + }, excess: commit, excess_sig: sig.clone(), }; @@ -2123,7 +2243,12 @@ mod test { let mut vec = vec![]; ser::serialize(&mut vec, version, &kernel).expect("serialized failed"); let kernel2: TxKernel = ser::deserialize(&mut &vec[..], version).unwrap(); - assert_eq!(kernel2.features, KernelFeatures::Plain { fee: 10 }); + assert_eq!( + kernel2.features, + KernelFeatures::Plain { + fee_fields: FeeFields::new(0, 10) + } + ); assert_eq!(kernel2.excess, commit); assert_eq!(kernel2.excess_sig, sig.clone()); } @@ -2132,7 +2257,12 @@ mod test { let mut vec = vec![]; ser::serialize_default(&mut vec, &kernel).expect("serialized failed"); let kernel2: TxKernel = ser::deserialize_default(&mut &vec[..]).unwrap(); - assert_eq!(kernel2.features, KernelFeatures::Plain { fee: 10 }); + assert_eq!( + kernel2.features, + KernelFeatures::Plain { + fee_fields: FeeFields::new(0, 10) + } + ); assert_eq!(kernel2.excess, commit); assert_eq!(kernel2.excess_sig, sig.clone()); } @@ -2151,7 +2281,7 @@ mod test { // now check a kernel with lock_height serialize/deserialize correctly let kernel = TxKernel { features: KernelFeatures::HeightLocked { - fee: 10, + fee_fields: FeeFields::new(0, 10), lock_height: 100, }, excess: commit, @@ -2193,7 +2323,7 @@ mod test { // now check an NRD kernel will serialize/deserialize correctly let kernel = TxKernel { features: KernelFeatures::NoRecentDuplicate { - fee: 10, + fee_fields: FeeFields::new(0, 10), relative_height: NRDRelativeHeight(100), }, excess: commit, @@ -2225,7 +2355,7 @@ mod test { let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee: 10, + fee_fields: FeeFields::new(0, 10), relative_height: NRDRelativeHeight(100), }); @@ -2251,25 +2381,27 @@ mod test { // Modify the fee and check signature no longer verifies. kernel.features = KernelFeatures::NoRecentDuplicate { - fee: 9, + fee_fields: FeeFields::new(0, 9), relative_height: NRDRelativeHeight(100), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Modify the relative_height and check signature no longer verifies. kernel.features = KernelFeatures::NoRecentDuplicate { - fee: 10, + fee_fields: FeeFields::new(0, 10), relative_height: NRDRelativeHeight(101), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Swap the features out for something different and check signature no longer verifies. - kernel.features = KernelFeatures::Plain { fee: 10 }; + kernel.features = KernelFeatures::Plain { + fee_fields: FeeFields::new(0, 10), + }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Check signature verifies if we use the original features. kernel.features = KernelFeatures::NoRecentDuplicate { - fee: 10, + fee_fields: FeeFields::new(0, 10), relative_height: NRDRelativeHeight(100), }; assert_eq!(kernel.verify(), Ok(())); @@ -2330,7 +2462,12 @@ mod test { let mut vec = vec![]; ser::serialize_default(&mut vec, &(0u8, 10u64, 0u64))?; let features: KernelFeatures = ser::deserialize_default(&mut &vec[..])?; - assert_eq!(features, KernelFeatures::Plain { fee: 10 }); + assert_eq!( + features, + KernelFeatures::Plain { + fee_fields: FeeFields::new(0, 10) + } + ); let mut vec = vec![]; ser::serialize_default(&mut vec, &(1u8, 0u64, 0u64))?; @@ -2343,7 +2480,7 @@ mod test { assert_eq!( features, KernelFeatures::HeightLocked { - fee: 10, + fee_fields: FeeFields::new(0, 10), lock_height: 100 } ); @@ -2373,7 +2510,7 @@ mod test { assert_eq!( features, KernelFeatures::NoRecentDuplicate { - fee: 10, + fee_fields: FeeFields::new(0, 10), relative_height: NRDRelativeHeight(100) } ); diff --git a/core/src/global.rs b/core/src/global.rs index 196e0b5259..ed819d39be 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -17,9 +17,9 @@ //! should be used sparingly. use crate::consensus::{ - graph_weight, valid_header_version, HeaderInfo, BASE_EDGE_BITS, BLOCK_KERNEL_WEIGHT, - BLOCK_OUTPUT_WEIGHT, BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON, DAY_HEIGHT, - DEFAULT_MIN_EDGE_BITS, DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, MAX_BLOCK_WEIGHT, + graph_weight, valid_header_version, HeaderInfo, BASE_EDGE_BITS, BLOCK_TIME_SEC, + COINBASE_MATURITY, CUT_THROUGH_HORIZON, DAY_HEIGHT, DEFAULT_MIN_EDGE_BITS, + DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, KERNEL_WEIGHT, MAX_BLOCK_WEIGHT, OUTPUT_WEIGHT, PROOFSIZE, SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD, }; use crate::core::block::HeaderVersion; @@ -325,7 +325,7 @@ pub fn max_block_weight() -> u64 { /// Maximum allowed transaction weight (1 weight unit ~= 32 bytes) pub fn max_tx_weight() -> u64 { - let coinbase_weight = BLOCK_OUTPUT_WEIGHT + BLOCK_KERNEL_WEIGHT; + let coinbase_weight = OUTPUT_WEIGHT + KERNEL_WEIGHT; max_block_weight().saturating_sub(coinbase_weight) as u64 } diff --git a/core/src/libtx/build.rs b/core/src/libtx/build.rs index 4af04d15fc..aa605c5768 100644 --- a/core/src/libtx/build.rs +++ b/core/src/libtx/build.rs @@ -23,7 +23,7 @@ //! //! Example: //! build::transaction( -//! KernelFeatures::Plain{ fee: 2 }, +//! KernelFeatures::Plain{ fee_fields: 2 }, //! vec![ //! input_rand(75), //! output_rand(42), @@ -279,7 +279,7 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[input(10, key_id1), input(12, key_id2), output(20, key_id3)], &keychain, &builder, @@ -301,7 +301,7 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[input(10, key_id1), input(12, key_id2), output(20, key_id3)], &keychain, &builder, @@ -322,7 +322,7 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { fee: 4 }, + KernelFeatures::Plain { fee_fields: 4 }, &[input(6, key_id1), output(2, key_id2)], &keychain, &builder, diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index d3e9a3fbe8..c367b92839 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -34,7 +34,9 @@ use crate::core::Transaction; pub use self::proof::ProofBuilder; pub use crate::libtx::error::{Error, ErrorKind}; -const DEFAULT_BASE_FEE: u64 = consensus::MILLI_GRIN; +/// make output (of weight 21) cost about 1 Grin-cent, keeping a round number +/// This should really be set by the `accept_fee_base` parameter in grin-server.toml +pub const DEFAULT_BASE_FEE: u64 = consensus::GRIN_BASE / 100 / 20; // 500000 /// Transaction fee calculation pub fn tx_fee( @@ -48,5 +50,6 @@ pub fn tx_fee( None => DEFAULT_BASE_FEE, }; - Transaction::weight(input_len as u64, output_len as u64, kernel_len as u64) * use_base_fee + Transaction::weight_by_iok(input_len as u64, output_len as u64, kernel_len as u64) + * use_base_fee } diff --git a/core/tests/block.rs b/core/tests/block.rs index 8212461c0e..a1d40c64e4 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -14,7 +14,7 @@ mod common; use crate::common::{new_block, tx1i2o, tx2i1o, txspend1i1o}; -use crate::core::consensus::{self, BLOCK_OUTPUT_WEIGHT, TESTING_THIRD_HARD_FORK}; +use crate::core::consensus::{self, OUTPUT_WEIGHT, TESTING_THIRD_HARD_FORK}; use crate::core::core::block::{Block, BlockHeader, Error, HeaderVersion, UntrustedBlockHeader}; use crate::core::core::hash::Hashed; use crate::core::core::id::ShortIdentifiable; @@ -47,7 +47,7 @@ fn too_large_block() { test_setup(); let keychain = ExtKeychain::from_random_seed(false).unwrap(); let builder = ProofBuilder::new(&keychain); - let max_out = global::max_block_weight() / BLOCK_OUTPUT_WEIGHT; + let max_out = global::max_block_weight() / OUTPUT_WEIGHT; let mut pks = vec![]; for n in 0..(max_out + 1) { @@ -61,7 +61,7 @@ fn too_large_block() { parts.append(&mut vec![input(500000, pks.pop().unwrap())]); let tx = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &parts, &keychain, &builder, @@ -103,7 +103,7 @@ fn block_with_nrd_kernel_pre_post_hf3() { let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee: 2, + fee_fields: 2, relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[input(7, key_id1), output(5, key_id2)], @@ -188,7 +188,7 @@ fn block_with_nrd_kernel_nrd_not_enabled() { let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee: 2, + fee_fields: 2, relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[input(7, key_id1), output(5, key_id2)], @@ -276,7 +276,7 @@ fn block_with_cut_through() { let btx1 = tx2i1o(); let btx2 = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[input(7, key_id1), output(5, key_id2.clone())], &keychain, &builder, @@ -374,7 +374,7 @@ fn remove_coinbase_kernel_flag() { let mut b = new_block(&[], &keychain, &builder, &prev, &key_id); let mut kernel = b.kernels()[0].clone(); - kernel.features = KernelFeatures::Plain { fee: 0 }; + kernel.features = KernelFeatures::Plain { fee_fields: 0 }; b.body = b.body.replace_kernel(kernel); // Flipping the coinbase flag results in kernels not summing correctly. @@ -752,7 +752,7 @@ fn same_amount_outputs_copy_range_proof() { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee: 1 }, + KernelFeatures::Plain { fee_fields: 1 }, &[input(7, key_id1), output(3, key_id2), output(3, key_id3)], &keychain, &builder, @@ -793,7 +793,7 @@ fn wrong_amount_range_proof() { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx1 = build::transaction( - KernelFeatures::Plain { fee: 1 }, + KernelFeatures::Plain { fee_fields: 1 }, &[ input(7, key_id1.clone()), output(3, key_id2.clone()), @@ -804,7 +804,7 @@ fn wrong_amount_range_proof() { ) .unwrap(); let tx2 = build::transaction( - KernelFeatures::Plain { fee: 1 }, + KernelFeatures::Plain { fee_fields: 1 }, &[input(7, key_id1), output(2, key_id2), output(4, key_id3)], &keychain, &builder, @@ -884,7 +884,7 @@ fn test_verify_cut_through_plain() -> Result<(), Error> { let builder = ProofBuilder::new(&keychain); let tx = build::transaction( - KernelFeatures::Plain { fee: 0 }, + KernelFeatures::Plain { fee_fields: 0 }, &[ build::input(10, key_id1.clone()), build::input(10, key_id2.clone()), @@ -948,7 +948,7 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { let builder = ProofBuilder::new(&keychain); let tx = build::transaction( - KernelFeatures::Plain { fee: 0 }, + KernelFeatures::Plain { fee_fields: 0 }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), build::coinbase_input(consensus::REWARD, key_id2.clone()), diff --git a/core/tests/common.rs b/core/tests/common.rs index 955f2c3359..3b3485f2a5 100644 --- a/core/tests/common.rs +++ b/core/tests/common.rs @@ -37,7 +37,7 @@ pub fn tx2i1o() -> Transaction { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[input(10, key_id1), input(11, key_id2), output(19, key_id3)], &keychain, &builder, @@ -56,7 +56,7 @@ pub fn tx1i1o() -> Transaction { let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[input(5, key_id1), output(3, key_id2)], &keychain, &builder, @@ -96,7 +96,7 @@ pub fn tx1i2o() -> Transaction { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[input(6, key_id1), output(3, key_id2), output(1, key_id3)], &keychain, &builder, @@ -140,7 +140,7 @@ where B: ProofBuild, { build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[input(v, key_id1), output(3, key_id2)], keychain, builder, diff --git a/core/tests/core.rs b/core/tests/core.rs index 220bfabb49..07e3e4d63f 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -130,7 +130,7 @@ fn test_zero_commit_fails() { // blinding should fail as signing with a zero r*G shouldn't work let res = build::transaction( - KernelFeatures::Plain { fee: 0 }, + KernelFeatures::Plain { fee_fields: 0 }, &[input(10, key_id1.clone()), output(10, key_id1)], &keychain, &builder, @@ -153,7 +153,7 @@ fn build_tx_kernel() { // first build a valid tx with corresponding blinding factor let tx = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[input(10, key_id1), output(5, key_id2), output(3, key_id3)], &keychain, &builder, @@ -169,7 +169,7 @@ fn build_tx_kernel() { let kern = &tx.kernels()[0]; kern.verify().unwrap(); - assert_eq!(kern.features, KernelFeatures::Plain { fee: 2 }); + assert_eq!(kern.features, KernelFeatures::Plain { fee_fields: 2 }); assert_eq!(2, tx.fee()); } @@ -192,7 +192,7 @@ fn build_two_half_kernels() { let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0); // build kernel with associated private excess - let mut kernel = TxKernel::with_features(KernelFeatures::Plain { fee: 2 }); + let mut kernel = TxKernel::with_features(KernelFeatures::Plain { fee_fields: 2 }); // Construct the message to be signed. let msg = kernel.msg_to_sign().unwrap(); @@ -480,7 +480,7 @@ fn hash_output() { let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee: 1 }, + KernelFeatures::Plain { fee_fields: 1 }, &[input(75, key_id1), output(42, key_id2), output(32, key_id3)], &keychain, &builder, @@ -543,8 +543,9 @@ fn tx_build_exchange() { // Alice builds her transaction, with change, which also produces the sum // of blinding factors before they're obscured. - let tx = Transaction::empty() - .with_kernel(TxKernel::with_features(KernelFeatures::Plain { fee: 2 })); + let tx = Transaction::empty().with_kernel(TxKernel::with_features(KernelFeatures::Plain { + fee_fields: 2, + })); let (tx, sum) = build::partial_transaction(tx, &[in1, in2, output(1, key_id3)], &keychain, &builder) .unwrap(); @@ -556,7 +557,7 @@ fn tx_build_exchange() { // blinding factors. He adds his output, finalizes the transaction so it's // ready for broadcast. let tx_final = build::transaction( - KernelFeatures::Plain { fee: 2 }, + KernelFeatures::Plain { fee_fields: 2 }, &[ initial_tx(tx_alice), with_excess(blind_sum), @@ -638,7 +639,7 @@ fn test_block_with_timelocked_tx() { // block height and that the resulting block is valid let tx1 = build::transaction( KernelFeatures::HeightLocked { - fee: 2, + fee_fields: 2, lock_height: 1, }, &[input(5, key_id1.clone()), output(3, key_id2.clone())], @@ -662,7 +663,7 @@ fn test_block_with_timelocked_tx() { // block height let tx1 = build::transaction( KernelFeatures::HeightLocked { - fee: 2, + fee_fields: 2, lock_height: 2, }, &[input(5, key_id1), output(3, key_id2)], diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 733ebc7236..9edac73725 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -94,7 +94,7 @@ fn test_verify_cut_through_plain() -> Result<(), Error> { let builder = proof::ProofBuilder::new(&keychain); let mut tx = build::transaction( - KernelFeatures::Plain { fee: 0 }, + KernelFeatures::Plain { fee_fields: 0 }, &[ build::input(10, key_id1.clone()), build::input(10, key_id2.clone()), @@ -153,7 +153,7 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { let builder = ProofBuilder::new(&keychain); let mut tx = build::transaction( - KernelFeatures::Plain { fee: 0 }, + KernelFeatures::Plain { fee_fields: 0 }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), build::coinbase_input(consensus::REWARD, key_id2.clone()), diff --git a/p2p/src/msg.rs b/p2p/src/msg.rs index 796817ad0c..a464dd6753 100644 --- a/p2p/src/msg.rs +++ b/p2p/src/msg.rs @@ -75,7 +75,7 @@ enum_from_primitive! { /// Max theoretical size of a block filled with outputs. fn max_block_size() -> u64 { - (global::max_block_weight() / consensus::BLOCK_OUTPUT_WEIGHT * 708) as u64 + (global::max_block_weight() / consensus::OUTPUT_WEIGHT * 708) as u64 } // Max msg size when msg type is unknown. diff --git a/pool/src/pool.rs b/pool/src/pool.rs index c3b446b0a7..144c820ec1 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -340,19 +340,19 @@ where // Use our bucket logic to identify the best transaction for eviction and evict it. // We want to avoid evicting a transaction where another transaction depends on it. - // We want to evict a transaction with low fee_to_weight. + // We want to evict a transaction with low fee_rate. pub fn evict_transaction(&mut self) { if let Some(evictable_transaction) = self.bucket_transactions(Weighting::NoLimit).last() { self.entries.retain(|x| x.tx != *evictable_transaction); }; } - /// Buckets consist of a vec of txs and track the aggregate fee_to_weight. + /// Buckets consist of a vec of txs and track the aggregate fee_rate. /// We aggregate (cut-through) dependent transactions within a bucket *unless* adding a tx - /// would reduce the aggregate fee_to_weight, in which case we start a new bucket. - /// Note this new bucket will by definition have a lower fee_to_weight than the bucket + /// would reduce the aggregate fee_rate, in which case we start a new bucket. + /// Note this new bucket will by definition have a lower fee_rate than the bucket /// containing the tx it depends on. - /// Sorting the buckets by fee_to_weight will therefore preserve dependency ordering, + /// Sorting the buckets by fee_rate will therefore preserve dependency ordering, /// maximizing both cut-through and overall fees. fn bucket_transactions(&self, weighting: Weighting) -> Vec { let mut tx_buckets: Vec = Vec::new(); @@ -413,12 +413,12 @@ where weighting, self.verifier_cache.clone(), ) { - if new_bucket.fee_to_weight >= bucket.fee_to_weight { - // Only aggregate if it would not reduce the fee_to_weight ratio. + if new_bucket.fee_rate >= bucket.fee_rate { + // Only aggregate if it would not reduce the fee_rate ratio. tx_buckets[pos] = new_bucket; } else { // Otherwise put it in its own bucket at the end. - // Note: This bucket will have a lower fee_to_weight + // Note: This bucket will have a lower fee_rate // than the bucket it depends on. tx_buckets.push(Bucket::new(entry.tx.clone(), tx_buckets.len())); } @@ -442,11 +442,11 @@ where } } - // Sort buckets by fee_to_weight (descending) and age (oldest first). - // Txs with highest fee_to_weight will be prioritied. - // Aggregation that increases the fee_to_weight of a bucket will prioritize the bucket. + // Sort buckets by fee_rate (descending) and age (oldest first). + // Txs with highest fee_rate will be prioritied. + // Aggregation that increases the fee_rate of a bucket will prioritize the bucket. // Oldest (based on pool insertion time) will then be prioritized. - tx_buckets.sort_unstable_by_key(|x| (Reverse(x.fee_to_weight), x.age_idx)); + tx_buckets.sort_unstable_by_key(|x| (Reverse(x.fee_rate), x.age_idx)); tx_buckets.into_iter().flat_map(|x| x.raw_txs).collect() } @@ -504,18 +504,18 @@ where struct Bucket { raw_txs: Vec, - fee_to_weight: u64, + fee_rate: u64, age_idx: usize, } impl Bucket { /// Construct a new bucket with the given tx. /// also specifies an "age_idx" so we can sort buckets by age - /// as well as fee_to_weight. Txs are maintainedin the pool in insert order + /// as well as fee_rate. Txs are maintainedin the pool in insert order /// so buckets with low age_idx contain oldest txs. fn new(tx: Transaction, age_idx: usize) -> Bucket { Bucket { - fee_to_weight: tx.fee_to_weight(), + fee_rate: tx.fee_rate(), raw_txs: vec![tx], age_idx, } @@ -532,7 +532,7 @@ impl Bucket { let agg_tx = transaction::aggregate(&raw_txs)?; agg_tx.validate(weighting, verifier_cache)?; Ok(Bucket { - fee_to_weight: agg_tx.fee_to_weight(), + fee_rate: agg_tx.fee_rate(), raw_txs: raw_txs, age_idx: self.age_idx, }) diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs index 29496834e8..f16a73bf6c 100644 --- a/pool/src/transaction_pool.rs +++ b/pool/src/transaction_pool.rs @@ -362,13 +362,13 @@ where return Err(PoolError::OverCapacity); } - // for a basic transaction (1 input, 2 outputs) - - // (-1 * 1) + (4 * 2) + 1 = 8 - // 8 * 10 = 80 + // for a basic transaction (2 inputs, 2 outputs, 1 kernel) - + // (2 * 1) + (2 * 21) + (1 * 3) = 47 + // 47 * 500_000 = 23_500_000 if self.config.accept_fee_base > 0 { - let threshold = (tx.tx_weight() as u64) * self.config.accept_fee_base; - if tx.fee() < threshold { - return Err(PoolError::LowFeeTransaction(threshold)); + let minfees = (tx.weight() as u64) * self.config.accept_fee_base; + if tx.shifted_fee() < minfees { + return Err(PoolError::LowFeeTransaction(minfees)); } } Ok(()) diff --git a/pool/src/types.rs b/pool/src/types.rs index 9f6eea428e..8c5dcea413 100644 --- a/pool/src/types.rs +++ b/pool/src/types.rs @@ -21,6 +21,7 @@ use self::core::core::committed; use self::core::core::hash::Hash; use self::core::core::transaction::{self, Transaction}; use self::core::core::{BlockHeader, BlockSums, Inputs, OutputIdentifier}; +use self::core::libtx::DEFAULT_BASE_FEE; use chrono::prelude::*; use failure::Fail; use grin_core as core; @@ -134,7 +135,7 @@ impl Default for PoolConfig { } fn default_accept_fee_base() -> u64 { - consensus::MILLI_GRIN + DEFAULT_BASE_FEE } fn default_max_pool_size() -> usize { 50_000 diff --git a/pool/tests/block_max_weight.rs b/pool/tests/block_max_weight.rs index 40747fbfa9..c2cb8aebbe 100644 --- a/pool/tests/block_max_weight.rs +++ b/pool/tests/block_max_weight.rs @@ -79,11 +79,11 @@ fn test_block_building_max_weight() { [250, 9, 8, 1, 7, 6] ); assert_eq!( - txs.iter().map(|x| x.tx_weight()).collect::>(), + txs.iter().map(|x| x.weight()).collect::>(), [16, 8, 8, 4, 8, 8] ); assert_eq!( - txs.iter().map(|x| x.fee_to_weight()).collect::>(), + txs.iter().map(|x| x.fee_rate()).collect::>(), [15625, 1125, 1000, 250, 875, 750] ); @@ -105,11 +105,11 @@ fn test_block_building_max_weight() { [250, 9, 8, 7] ); assert_eq!( - txs.iter().map(|x| x.tx_weight()).collect::>(), + txs.iter().map(|x| x.weight()).collect::>(), [16, 8, 8, 8] ); assert_eq!( - txs.iter().map(|x| x.fee_to_weight()).collect::>(), + txs.iter().map(|x| x.fee_rate()).collect::>(), [15625, 1125, 1000, 875] ); diff --git a/pool/tests/common.rs b/pool/tests/common.rs index 52ef16a890..84bd2e345b 100644 --- a/pool/tests/common.rs +++ b/pool/tests/common.rs @@ -206,7 +206,9 @@ where } build::transaction( - KernelFeatures::Plain { fee: fees as u64 }, + KernelFeatures::Plain { + fee_fields: fees as u64, + }, &tx_elements, keychain, &ProofBuilder::new(keychain), @@ -231,7 +233,9 @@ where keychain, input_values, output_values, - KernelFeatures::Plain { fee: fees as u64 }, + KernelFeatures::Plain { + fee_fields: fees as u64, + }, ) } diff --git a/pool/tests/nrd_kernel_relative_height.rs b/pool/tests/nrd_kernel_relative_height.rs index 0caf5f65a9..5a4f0020dc 100644 --- a/pool/tests/nrd_kernel_relative_height.rs +++ b/pool/tests/nrd_kernel_relative_height.rs @@ -73,7 +73,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let (tx1, tx2, tx3) = { let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee: 6, + fee_fields: 6, relative_height: NRDRelativeHeight::new(2)?, }); let msg = kernel.msg_to_sign().unwrap(); @@ -111,7 +111,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { // Now reuse kernel excess for tx3 but with NRD relative_height=1 (and different fee). let mut kernel_short = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee: 3, + fee_fields: 3, relative_height: NRDRelativeHeight::new(1)?, }); let msg_short = kernel_short.msg_to_sign().unwrap(); diff --git a/pool/tests/nrd_kernels_disabled.rs b/pool/tests/nrd_kernels_disabled.rs index 90d6ef0b5a..0cd96cf5cf 100644 --- a/pool/tests/nrd_kernels_disabled.rs +++ b/pool/tests/nrd_kernels_disabled.rs @@ -64,7 +64,7 @@ fn test_nrd_kernels_disabled() { vec![10, 20], vec![24], KernelFeatures::NoRecentDuplicate { - fee: 6, + fee_fields: 6, relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); diff --git a/pool/tests/nrd_kernels_enabled.rs b/pool/tests/nrd_kernels_enabled.rs index 7fd77987ae..725c6689f3 100644 --- a/pool/tests/nrd_kernels_enabled.rs +++ b/pool/tests/nrd_kernels_enabled.rs @@ -64,7 +64,7 @@ fn test_nrd_kernels_enabled() { vec![10, 20], vec![24], KernelFeatures::NoRecentDuplicate { - fee: 6, + fee_fields: 6, relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); From e6bf3f140de52b0e498d9317924aa2eb61bc70da Mon Sep 17 00:00:00 2001 From: John Tromp Date: Sat, 24 Oct 2020 13:39:41 +0200 Subject: [PATCH 02/24] use FeeFields with ::zero and try_into().unwrap() --- api/src/types.rs | 9 +++-- chain/tests/mine_nrd_kernel.rs | 5 ++- chain/tests/mine_simple_chain.rs | 13 ++++-- chain/tests/nrd_validation_rules.rs | 7 ++-- chain/tests/process_block_cut_through.rs | 6 ++- chain/tests/test_coinbase_maturity.rs | 9 ++++- core/src/core/transaction.rs | 51 ++++++++++-------------- core/src/libtx/build.rs | 15 +++++-- core/tests/block.rs | 40 ++++++++++++++----- core/tests/common.rs | 17 ++++++-- core/tests/core.rs | 34 +++++++++++----- core/tests/transaction.rs | 12 ++++-- pool/tests/common.rs | 5 ++- pool/tests/nrd_kernel_relative_height.rs | 5 ++- pool/tests/nrd_kernels_disabled.rs | 3 +- pool/tests/nrd_kernels_enabled.rs | 3 +- 16 files changed, 150 insertions(+), 84 deletions(-) diff --git a/api/src/types.rs b/api/src/types.rs index 04c3ee46f3..7d7784e2cb 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -15,7 +15,7 @@ use crate::chain; use crate::core::core::hash::Hashed; use crate::core::core::merkle_proof::MerkleProof; -use crate::core::core::{extract_fee_fields, KernelFeatures, TxKernel}; +use crate::core::core::{FeeFields, KernelFeatures, TxKernel}; use crate::core::{core, ser}; use crate::p2p; use crate::util::secp::pedersen; @@ -499,7 +499,7 @@ impl<'de> serde::de::Deserialize<'de> for OutputPrintable { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TxKernelPrintable { pub features: String, - pub fee_shift: u64, + pub fee_shift: u8, pub fee: u64, pub lock_height: u64, pub excess: String, @@ -511,7 +511,7 @@ impl TxKernelPrintable { let features = k.features.as_string(); let (fee_fields, lock_height) = match k.features { KernelFeatures::Plain { fee_fields } => (fee_fields, 0), - KernelFeatures::Coinbase => (0, 0), + KernelFeatures::Coinbase => (FeeFields::zero(), 0), KernelFeatures::HeightLocked { fee_fields, lock_height, @@ -521,7 +521,8 @@ impl TxKernelPrintable { relative_height, } => (fee_fields, relative_height.into()), }; - let (fee, fee_shift) = extract_fee_fields(fee_fields); + let fee = fee_fields.fee(); + let fee_shift: u8 = fee_fields.fee_shift(); TxKernelPrintable { features, fee_shift, diff --git a/chain/tests/mine_nrd_kernel.rs b/chain/tests/mine_nrd_kernel.rs index 29724dac02..7b34ac75ba 100644 --- a/chain/tests/mine_nrd_kernel.rs +++ b/chain/tests/mine_nrd_kernel.rs @@ -26,6 +26,7 @@ use crate::core::libtx::{build, reward, ProofBuilder}; use crate::core::{consensus, global, pow}; use crate::keychain::{ExtKeychain, ExtKeychainPath, Identifier, Keychain}; use chrono::Duration; +use std::convert::TryInto; fn build_block(chain: &Chain, keychain: &K, key_id: &Identifier, txs: Vec) -> Block where @@ -84,7 +85,7 @@ fn mine_block_with_nrd_kernel_and_nrd_feature_enabled() { let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier(); let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 20000, + fee_fields: 20000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[ @@ -131,7 +132,7 @@ fn mine_invalid_block_with_nrd_kernel_and_nrd_feature_enabled_before_hf() { let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier(); let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 20000, + fee_fields: 20000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[ diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index dd2ddfc78e..40845065aa 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -29,6 +29,7 @@ use grin_chain::{BlockStatus, ChainAdapter, Options}; use grin_core as core; use grin_keychain as keychain; use grin_util as util; +use std::convert::TryInto; use std::sync::Arc; mod chain_test_helper; @@ -569,7 +570,9 @@ fn spend_rewind_spend() { let key_id30 = ExtKeychainPath::new(1, 30, 0, 0, 0).to_identifier(); let tx1 = build::transaction( - KernelFeatures::Plain { fee_fields: 20000 }, + KernelFeatures::Plain { + fee_fields: 20000.try_into().unwrap(), + }, &[ build::coinbase_input(consensus::REWARD, key_id_coinbase.clone()), build::output(consensus::REWARD - 20000, key_id30.clone()), @@ -642,7 +645,9 @@ fn spend_in_fork_and_compact() { let key_id31 = ExtKeychainPath::new(1, 31, 0, 0, 0).to_identifier(); let tx1 = build::transaction( - KernelFeatures::Plain { fee_fields: 20000 }, + KernelFeatures::Plain { + fee_fields: 20000.try_into().unwrap(), + }, &[ build::coinbase_input(consensus::REWARD, key_id2.clone()), build::output(consensus::REWARD - 20000, key_id30.clone()), @@ -660,7 +665,9 @@ fn spend_in_fork_and_compact() { chain.validate(false).unwrap(); let tx2 = build::transaction( - KernelFeatures::Plain { fee_fields: 20000 }, + KernelFeatures::Plain { + fee_fields: 20000.try_into().unwrap(), + }, &[ build::input(consensus::REWARD - 20000, key_id30.clone()), build::output(consensus::REWARD - 40000, key_id31.clone()), diff --git a/chain/tests/nrd_validation_rules.rs b/chain/tests/nrd_validation_rules.rs index 44d693740f..62de8dc00e 100644 --- a/chain/tests/nrd_validation_rules.rs +++ b/chain/tests/nrd_validation_rules.rs @@ -28,6 +28,7 @@ use crate::core::libtx::{aggsig, build, reward, ProofBuilder}; use crate::core::{consensus, global, pow}; use crate::keychain::{BlindingFactor, ExtKeychain, ExtKeychainPath, Identifier, Keychain}; use chrono::Duration; +use std::convert::TryInto; fn build_block( chain: &Chain, @@ -100,7 +101,7 @@ fn process_block_nrd_validation() -> Result<(), Error> { assert_eq!(chain.head()?.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000, + fee_fields: 20000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(2)?, }); @@ -216,7 +217,7 @@ fn process_block_nrd_validation_relative_height_1() -> Result<(), Error> { assert_eq!(chain.head()?.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000, + fee_fields: 20000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1)?, }); @@ -315,7 +316,7 @@ fn process_block_nrd_validation_fork() -> Result<(), Error> { assert_eq!(header_8.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000, + fee_fields: 20000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(2)?, }); diff --git a/chain/tests/process_block_cut_through.rs b/chain/tests/process_block_cut_through.rs index 85aa7ffe4b..8b9c16f7d8 100644 --- a/chain/tests/process_block_cut_through.rs +++ b/chain/tests/process_block_cut_through.rs @@ -23,7 +23,7 @@ use self::chain_test_helper::{clean_output_dir, genesis_block, init_chain}; use crate::chain::{pipe, Chain, Options}; use crate::core::core::verifier_cache::LruVerifierCache; use crate::core::core::{block, pmmr, transaction}; -use crate::core::core::{Block, KernelFeatures, Transaction, Weighting}; +use crate::core::core::{Block, FeeFields, KernelFeatures, Transaction, Weighting}; use crate::core::libtx::{build, reward, ProofBuilder}; use crate::core::{consensus, global, pow}; use crate::keychain::{ExtKeychain, ExtKeychainPath, Keychain, SwitchCommitmentType}; @@ -104,7 +104,9 @@ fn process_block_cut_through() -> Result<(), chain::Error> { // Note: We reuse key_ids resulting in an input and an output sharing the same commitment. // The input is coinbase and the output is plain. let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 0 }, + KernelFeatures::Plain { + fee_fields: FeeFields::zero(), + }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), build::coinbase_input(consensus::REWARD, key_id2.clone()), diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index 997ac08797..e48c861203 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -28,6 +28,7 @@ use grin_chain as chain; use grin_core as core; use grin_keychain as keychain; use grin_util as util; +use std::convert::TryInto; use std::fs; use std::sync::Arc; @@ -100,7 +101,9 @@ fn test_coinbase_maturity() { // here we build a tx that attempts to spend the earlier coinbase output // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[ build::coinbase_input(amount, key_id1.clone()), build::output(amount - 2, key_id2.clone()), @@ -182,7 +185,9 @@ fn test_coinbase_maturity() { // here we build a tx that attempts to spend the earlier coinbase output // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[ build::coinbase_input(amount, key_id1.clone()), build::output(amount - 2, key_id2.clone()), diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 88af30e4dd..b549888313 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -95,7 +95,7 @@ impl FeeFields { /// Create a new FeeFields from the provided shift and fee /// Checks both are valid (in range) - pub fn new(fee_shift: u64, fee: u64) -> Result { + pub fn neuw(fee_shift: u64, fee: u64) -> Result { if fee == 0 { Err(Error::InvalidFeeFields) } else if fee > FeeFields::FEE_MASK { @@ -108,8 +108,8 @@ impl FeeFields { } /// Extract fee_shift field - pub fn fee_shift(&self) -> u64 { - (self.0 >> FeeFields::FEE_BITS) & FeeFields::FEE_SHIFT_MASK + pub fn fee_shift(&self) -> u8 { + ((self.0 >> FeeFields::FEE_BITS) & FeeFields::FEE_SHIFT_MASK) as u8 } /// Extract fee field @@ -124,15 +124,6 @@ impl FeeFields { let fee_shift = (self.0 >> FeeFields::FEE_BITS) & FeeFields::FEE_SHIFT_MASK; (fee, fee_shift) } - - /// Accumulate two fee_fields by adding fees and maxing fee_shift - /// THIS CAN'T WORK SINCE A TX CAN HAVE MULTIPLE MAXED-OUT FEES - /// WHICH ADDED UP DON'T FIT - pub fn accumulate(&self, other: FeeFields) -> Result { - let (fee, fee_shift) = self.as_tuple(); - let (other_fee, other_fee_shift) = other.as_tuple(); - FeeFields::new(max(fee_shift, other_fee_shift), fee + other_fee) - } } /// Relative height field on NRD kernel variant. @@ -995,7 +986,7 @@ impl TransactionBody { /// where transactions can specify a higher block-inclusion priority as a positive shift up to 15 /// but are required to overpaythe minimum required fees by a factor of 2^priority pub fn shifted_fee(&self) -> u64 { - let (fee, fee_shift) = self + let fee_shift = self .kernels .iter() .filter_map(|k| match k.features { @@ -1004,11 +995,8 @@ impl TransactionBody { KernelFeatures::HeightLocked { fee_fields, .. } => Some(fee_fields), KernelFeatures::NoRecentDuplicate { fee_fields, .. } => Some(fee_fields), }) - .fold(FeeFields::zero, |acc: FeeFields, fee_fields| { - acc.accumulate(fee_fields)? - }) - .as_tuple(); - fee >> fee_shift + .fold(0, |acc, fee_fields| max(acc, fee_fields.fee_shift())); + self.fee() >> fee_shift } fn overage(&self) -> i64 { @@ -2218,6 +2206,7 @@ mod test { use crate::core::hash::Hash; use crate::core::id::{ShortId, ShortIdentifiable}; use keychain::{ExtKeychain, Keychain, SwitchCommitmentType}; + use std::convert::TryInto; #[test] fn test_plain_kernel_ser_deser() { @@ -2232,7 +2221,7 @@ mod test { let kernel = TxKernel { features: KernelFeatures::Plain { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), }, excess: commit, excess_sig: sig.clone(), @@ -2246,7 +2235,7 @@ mod test { assert_eq!( kernel2.features, KernelFeatures::Plain { - fee_fields: FeeFields::new(0, 10) + fee_fields: 10.try_into().unwrap() } ); assert_eq!(kernel2.excess, commit); @@ -2260,7 +2249,7 @@ mod test { assert_eq!( kernel2.features, KernelFeatures::Plain { - fee_fields: FeeFields::new(0, 10) + fee_fields: 10.try_into().unwrap() } ); assert_eq!(kernel2.excess, commit); @@ -2281,7 +2270,7 @@ mod test { // now check a kernel with lock_height serialize/deserialize correctly let kernel = TxKernel { features: KernelFeatures::HeightLocked { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), lock_height: 100, }, excess: commit, @@ -2323,7 +2312,7 @@ mod test { // now check an NRD kernel will serialize/deserialize correctly let kernel = TxKernel { features: KernelFeatures::NoRecentDuplicate { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), relative_height: NRDRelativeHeight(100), }, excess: commit, @@ -2355,7 +2344,7 @@ mod test { let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), relative_height: NRDRelativeHeight(100), }); @@ -2381,27 +2370,27 @@ mod test { // Modify the fee and check signature no longer verifies. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: FeeFields::new(0, 9), + fee_fields: 9.try_into().unwrap(), relative_height: NRDRelativeHeight(100), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Modify the relative_height and check signature no longer verifies. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), relative_height: NRDRelativeHeight(101), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Swap the features out for something different and check signature no longer verifies. kernel.features = KernelFeatures::Plain { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Check signature verifies if we use the original features. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), relative_height: NRDRelativeHeight(100), }; assert_eq!(kernel.verify(), Ok(())); @@ -2465,7 +2454,7 @@ mod test { assert_eq!( features, KernelFeatures::Plain { - fee_fields: FeeFields::new(0, 10) + fee_fields: 10.try_into().unwrap() } ); @@ -2480,7 +2469,7 @@ mod test { assert_eq!( features, KernelFeatures::HeightLocked { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), lock_height: 100 } ); @@ -2510,7 +2499,7 @@ mod test { assert_eq!( features, KernelFeatures::NoRecentDuplicate { - fee_fields: FeeFields::new(0, 10), + fee_fields: 10.try_into().unwrap(), relative_height: NRDRelativeHeight(100) } ); diff --git a/core/src/libtx/build.rs b/core/src/libtx/build.rs index aa605c5768..95d63bba1a 100644 --- a/core/src/libtx/build.rs +++ b/core/src/libtx/build.rs @@ -23,7 +23,7 @@ //! //! Example: //! build::transaction( -//! KernelFeatures::Plain{ fee_fields: 2 }, +//! KernelFeatures::Plain{ fee_fields: 2.try_into().unwrap() }, //! vec![ //! input_rand(75), //! output_rand(42), @@ -262,6 +262,7 @@ mod test { use crate::global; use crate::libtx::ProofBuilder; use keychain::{ExtKeychain, ExtKeychainPath}; + use std::convert::TryInto; fn verifier_cache() -> Arc> { Arc::new(RwLock::new(LruVerifierCache::new())) @@ -279,7 +280,9 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[input(10, key_id1), input(12, key_id2), output(20, key_id3)], &keychain, &builder, @@ -301,7 +304,9 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[input(10, key_id1), input(12, key_id2), output(20, key_id3)], &keychain, &builder, @@ -322,7 +327,9 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { fee_fields: 4 }, + KernelFeatures::Plain { + fee_fields: 4.try_into().unwrap(), + }, &[input(6, key_id1), output(2, key_id2)], &keychain, &builder, diff --git a/core/tests/block.rs b/core/tests/block.rs index a1d40c64e4..d15dddf98d 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -19,7 +19,8 @@ use crate::core::core::block::{Block, BlockHeader, Error, HeaderVersion, Untrust use crate::core::core::hash::Hashed; use crate::core::core::id::ShortIdentifiable; use crate::core::core::transaction::{ - self, KernelFeatures, NRDRelativeHeight, Output, OutputFeatures, OutputIdentifier, Transaction, + self, FeeFields, KernelFeatures, NRDRelativeHeight, Output, OutputFeatures, OutputIdentifier, + Transaction, }; use crate::core::core::verifier_cache::{LruVerifierCache, VerifierCache}; use crate::core::core::{Committed, CompactBlock}; @@ -29,6 +30,7 @@ use crate::core::{global, pow, ser}; use chrono::Duration; use grin_core as core; use keychain::{BlindingFactor, ExtKeychain, Keychain}; +use std::convert::TryInto; use std::sync::Arc; use util::{secp, RwLock, ToHex}; @@ -61,7 +63,9 @@ fn too_large_block() { parts.append(&mut vec![input(500000, pks.pop().unwrap())]); let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &parts, &keychain, &builder, @@ -103,7 +107,7 @@ fn block_with_nrd_kernel_pre_post_hf3() { let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 2, + fee_fields: 2.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[input(7, key_id1), output(5, key_id2)], @@ -188,7 +192,7 @@ fn block_with_nrd_kernel_nrd_not_enabled() { let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 2, + fee_fields: 2.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[input(7, key_id1), output(5, key_id2)], @@ -276,7 +280,9 @@ fn block_with_cut_through() { let btx1 = tx2i1o(); let btx2 = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[input(7, key_id1), output(5, key_id2.clone())], &keychain, &builder, @@ -374,7 +380,9 @@ fn remove_coinbase_kernel_flag() { let mut b = new_block(&[], &keychain, &builder, &prev, &key_id); let mut kernel = b.kernels()[0].clone(); - kernel.features = KernelFeatures::Plain { fee_fields: 0 }; + kernel.features = KernelFeatures::Plain { + fee_fields: FeeFields::zero(), + }; b.body = b.body.replace_kernel(kernel); // Flipping the coinbase flag results in kernels not summing correctly. @@ -752,7 +760,9 @@ fn same_amount_outputs_copy_range_proof() { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 1 }, + KernelFeatures::Plain { + fee_fields: 1.try_into().unwrap(), + }, &[input(7, key_id1), output(3, key_id2), output(3, key_id3)], &keychain, &builder, @@ -793,7 +803,9 @@ fn wrong_amount_range_proof() { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx1 = build::transaction( - KernelFeatures::Plain { fee_fields: 1 }, + KernelFeatures::Plain { + fee_fields: 1.try_into().unwrap(), + }, &[ input(7, key_id1.clone()), output(3, key_id2.clone()), @@ -804,7 +816,9 @@ fn wrong_amount_range_proof() { ) .unwrap(); let tx2 = build::transaction( - KernelFeatures::Plain { fee_fields: 1 }, + KernelFeatures::Plain { + fee_fields: 1.try_into().unwrap(), + }, &[input(7, key_id1), output(2, key_id2), output(4, key_id3)], &keychain, &builder, @@ -884,7 +898,9 @@ fn test_verify_cut_through_plain() -> Result<(), Error> { let builder = ProofBuilder::new(&keychain); let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 0 }, + KernelFeatures::Plain { + fee_fields: FeeFields::zero(), + }, &[ build::input(10, key_id1.clone()), build::input(10, key_id2.clone()), @@ -948,7 +964,9 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { let builder = ProofBuilder::new(&keychain); let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 0 }, + KernelFeatures::Plain { + fee_fields: FeeFields::zero(), + }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), build::coinbase_input(consensus::REWARD, key_id2.clone()), diff --git a/core/tests/common.rs b/core/tests/common.rs index 3b3485f2a5..e6b36fdcb8 100644 --- a/core/tests/common.rs +++ b/core/tests/common.rs @@ -26,6 +26,7 @@ use grin_core::libtx::{ use grin_core::pow::Difficulty; use grin_core::ser::{self, PMMRable, Readable, Reader, Writeable, Writer}; use keychain::{Identifier, Keychain}; +use std::convert::TryInto; // utility producing a transaction with 2 inputs and a single outputs #[allow(dead_code)] @@ -37,7 +38,9 @@ pub fn tx2i1o() -> Transaction { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[input(10, key_id1), input(11, key_id2), output(19, key_id3)], &keychain, &builder, @@ -56,7 +59,9 @@ pub fn tx1i1o() -> Transaction { let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[input(5, key_id1), output(3, key_id2)], &keychain, &builder, @@ -96,7 +101,9 @@ pub fn tx1i2o() -> Transaction { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[input(6, key_id1), output(3, key_id2), output(1, key_id3)], &keychain, &builder, @@ -140,7 +147,9 @@ where B: ProofBuild, { build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[input(v, key_id1), output(3, key_id2)], keychain, builder, diff --git a/core/tests/core.rs b/core/tests/core.rs index 07e3e4d63f..17340492d2 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -30,6 +30,7 @@ use self::core::{global, ser}; use crate::common::{new_block, tx1i1o, tx1i2o, tx2i1o}; use grin_core as core; use keychain::{BlindingFactor, ExtKeychain, Keychain}; +use std::convert::TryInto; use std::sync::Arc; use util::static_secp_instance; use util::RwLock; @@ -130,7 +131,9 @@ fn test_zero_commit_fails() { // blinding should fail as signing with a zero r*G shouldn't work let res = build::transaction( - KernelFeatures::Plain { fee_fields: 0 }, + KernelFeatures::Plain { + fee_fields: 0.try_into().unwrap(), + }, &[input(10, key_id1.clone()), output(10, key_id1)], &keychain, &builder, @@ -153,7 +156,9 @@ fn build_tx_kernel() { // first build a valid tx with corresponding blinding factor let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[input(10, key_id1), output(5, key_id2), output(3, key_id3)], &keychain, &builder, @@ -169,7 +174,12 @@ fn build_tx_kernel() { let kern = &tx.kernels()[0]; kern.verify().unwrap(); - assert_eq!(kern.features, KernelFeatures::Plain { fee_fields: 2 }); + assert_eq!( + kern.features, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap() + } + ); assert_eq!(2, tx.fee()); } @@ -192,7 +202,9 @@ fn build_two_half_kernels() { let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0); // build kernel with associated private excess - let mut kernel = TxKernel::with_features(KernelFeatures::Plain { fee_fields: 2 }); + let mut kernel = TxKernel::with_features(KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }); // Construct the message to be signed. let msg = kernel.msg_to_sign().unwrap(); @@ -480,7 +492,9 @@ fn hash_output() { let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { fee_fields: 1 }, + KernelFeatures::Plain { + fee_fields: 1.try_into().unwrap(), + }, &[input(75, key_id1), output(42, key_id2), output(32, key_id3)], &keychain, &builder, @@ -544,7 +558,7 @@ fn tx_build_exchange() { // Alice builds her transaction, with change, which also produces the sum // of blinding factors before they're obscured. let tx = Transaction::empty().with_kernel(TxKernel::with_features(KernelFeatures::Plain { - fee_fields: 2, + fee_fields: 2.try_into().unwrap(), })); let (tx, sum) = build::partial_transaction(tx, &[in1, in2, output(1, key_id3)], &keychain, &builder) @@ -557,7 +571,9 @@ fn tx_build_exchange() { // blinding factors. He adds his output, finalizes the transaction so it's // ready for broadcast. let tx_final = build::transaction( - KernelFeatures::Plain { fee_fields: 2 }, + KernelFeatures::Plain { + fee_fields: 2.try_into().unwrap(), + }, &[ initial_tx(tx_alice), with_excess(blind_sum), @@ -639,7 +655,7 @@ fn test_block_with_timelocked_tx() { // block height and that the resulting block is valid let tx1 = build::transaction( KernelFeatures::HeightLocked { - fee_fields: 2, + fee_fields: 2.try_into().unwrap(), lock_height: 1, }, &[input(5, key_id1.clone()), output(3, key_id2.clone())], @@ -663,7 +679,7 @@ fn test_block_with_timelocked_tx() { // block height let tx1 = build::transaction( KernelFeatures::HeightLocked { - fee_fields: 2, + fee_fields: 2.try_into().unwrap(), lock_height: 2, }, &[input(5, key_id1), output(3, key_id2)], diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 9edac73725..ecfcc0fe89 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -18,7 +18,9 @@ pub mod common; use crate::common::tx1i10_v2_compatible; use crate::core::core::transaction::{self, Error}; use crate::core::core::verifier_cache::LruVerifierCache; -use crate::core::core::{KernelFeatures, Output, OutputFeatures, Transaction, Weighting}; +use crate::core::core::{ + FeeFields, KernelFeatures, Output, OutputFeatures, Transaction, Weighting, +}; use crate::core::global; use crate::core::libtx::build; use crate::core::libtx::proof::{self, ProofBuilder}; @@ -94,7 +96,9 @@ fn test_verify_cut_through_plain() -> Result<(), Error> { let builder = proof::ProofBuilder::new(&keychain); let mut tx = build::transaction( - KernelFeatures::Plain { fee_fields: 0 }, + KernelFeatures::Plain { + fee_fields: FeeFields::zero(), + }, &[ build::input(10, key_id1.clone()), build::input(10, key_id2.clone()), @@ -153,7 +157,9 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { let builder = ProofBuilder::new(&keychain); let mut tx = build::transaction( - KernelFeatures::Plain { fee_fields: 0 }, + KernelFeatures::Plain { + fee_fields: FeeFields::zero(), + }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), build::coinbase_input(consensus::REWARD, key_id2.clone()), diff --git a/pool/tests/common.rs b/pool/tests/common.rs index 84bd2e345b..21e72aa3e3 100644 --- a/pool/tests/common.rs +++ b/pool/tests/common.rs @@ -36,6 +36,7 @@ use grin_core as core; use grin_keychain as keychain; use grin_pool as pool; use grin_util as util; +use std::convert::TryInto; use std::fs; use std::sync::Arc; @@ -207,7 +208,7 @@ where build::transaction( KernelFeatures::Plain { - fee_fields: fees as u64, + fee_fields: (fees as u64).try_into().unwrap(), }, &tx_elements, keychain, @@ -234,7 +235,7 @@ where input_values, output_values, KernelFeatures::Plain { - fee_fields: fees as u64, + fee_fields: (fees as u64).try_into().unwrap(), }, ) } diff --git a/pool/tests/nrd_kernel_relative_height.rs b/pool/tests/nrd_kernel_relative_height.rs index 5a4f0020dc..50f6e7a38c 100644 --- a/pool/tests/nrd_kernel_relative_height.rs +++ b/pool/tests/nrd_kernel_relative_height.rs @@ -28,6 +28,7 @@ use grin_core as core; use grin_keychain as keychain; use grin_pool as pool; use grin_util as util; +use std::convert::TryInto; use std::sync::Arc; #[test] @@ -73,7 +74,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let (tx1, tx2, tx3) = { let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 6, + fee_fields: 6.try_into().unwrap(), relative_height: NRDRelativeHeight::new(2)?, }); let msg = kernel.msg_to_sign().unwrap(); @@ -111,7 +112,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { // Now reuse kernel excess for tx3 but with NRD relative_height=1 (and different fee). let mut kernel_short = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 3, + fee_fields: 3.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1)?, }); let msg_short = kernel_short.msg_to_sign().unwrap(); diff --git a/pool/tests/nrd_kernels_disabled.rs b/pool/tests/nrd_kernels_disabled.rs index 0cd96cf5cf..5f55bc35ec 100644 --- a/pool/tests/nrd_kernels_disabled.rs +++ b/pool/tests/nrd_kernels_disabled.rs @@ -26,6 +26,7 @@ use grin_core as core; use grin_keychain as keychain; use grin_pool as pool; use grin_util as util; +use std::convert::TryInto; use std::sync::Arc; #[test] @@ -64,7 +65,7 @@ fn test_nrd_kernels_disabled() { vec![10, 20], vec![24], KernelFeatures::NoRecentDuplicate { - fee_fields: 6, + fee_fields: 6.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); diff --git a/pool/tests/nrd_kernels_enabled.rs b/pool/tests/nrd_kernels_enabled.rs index 725c6689f3..539cda9ece 100644 --- a/pool/tests/nrd_kernels_enabled.rs +++ b/pool/tests/nrd_kernels_enabled.rs @@ -26,6 +26,7 @@ use grin_core as core; use grin_keychain as keychain; use grin_pool as pool; use grin_util as util; +use std::convert::TryInto; use std::sync::Arc; #[test] @@ -64,7 +65,7 @@ fn test_nrd_kernels_enabled() { vec![10, 20], vec![24], KernelFeatures::NoRecentDuplicate { - fee_fields: 6, + fee_fields: 6.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); From 9ef55d2c6fd3047dc4cbc2b5f6d9170ce93b7ff1 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Sat, 24 Oct 2020 18:54:09 +0200 Subject: [PATCH 03/24] fixed tests --- core/src/core/transaction.rs | 29 +++++++++-------- core/src/libtx/aggsig.rs | 6 ++-- core/tests/core.rs | 6 ++-- core/tests/transaction.rs | 50 ++++++++++++++++++++++++++++-- pool/tests/block_max_weight.rs | 35 ++++++++++++--------- pool/tests/block_reconciliation.rs | 2 +- 6 files changed, 93 insertions(+), 35 deletions(-) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index b549888313..825c2e59f4 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -81,12 +81,12 @@ impl FeeFields { /// Fees are limited to 40 bits const FEE_BITS: u32 = 40; /// Used to extract fee field - const FEE_MASK: u64 = 1u64 << FeeFields::FEE_BITS - 1; + const FEE_MASK: u64 = (1u64 << FeeFields::FEE_BITS) - 1; /// Fee shifts are limited to 4 bits pub const FEE_SHIFT_BITS: u32 = 4; /// Used to extract fee_shift field - pub const FEE_SHIFT_MASK: u64 = 1u64 << FeeFields::FEE_SHIFT_BITS - 1; + pub const FEE_SHIFT_MASK: u64 = (1u64 << FeeFields::FEE_SHIFT_BITS) - 1; /// Create a zero FeeFields with 0 fee and 0 fee_shift pub fn zero() -> Self { @@ -95,7 +95,7 @@ impl FeeFields { /// Create a new FeeFields from the provided shift and fee /// Checks both are valid (in range) - pub fn neuw(fee_shift: u64, fee: u64) -> Result { + pub fn new(fee_shift: u64, fee: u64) -> Result { if fee == 0 { Err(Error::InvalidFeeFields) } else if fee > FeeFields::FEE_MASK { @@ -103,7 +103,7 @@ impl FeeFields { } else if fee_shift > FeeFields::FEE_SHIFT_MASK { Err(Error::InvalidFeeFields) } else { - Ok(Self(fee_shift << FeeFields::FEE_BITS | fee)) + Ok(Self((fee_shift << FeeFields::FEE_BITS) | fee)) } } @@ -981,13 +981,9 @@ impl TransactionBody { .fold(0, |acc, fee_fields| acc.saturating_add(fee_fields.fee())) } - /// Shifted fee for a TransactionBody is the sum of fees shifted right by the maximum fee_shift - /// this is used to determine whether a tx can be relayed or accepted in a mempool - /// where transactions can specify a higher block-inclusion priority as a positive shift up to 15 - /// but are required to overpaythe minimum required fees by a factor of 2^priority - pub fn shifted_fee(&self) -> u64 { - let fee_shift = self - .kernels + /// fee_shift for a TransactionBody is the maximum of fee_shifts of all fee carrying kernels. + pub fn fee_shift(&self) -> u8 { + self.kernels .iter() .filter_map(|k| match k.features { KernelFeatures::Coinbase => None, @@ -995,8 +991,15 @@ impl TransactionBody { KernelFeatures::HeightLocked { fee_fields, .. } => Some(fee_fields), KernelFeatures::NoRecentDuplicate { fee_fields, .. } => Some(fee_fields), }) - .fold(0, |acc, fee_fields| max(acc, fee_fields.fee_shift())); - self.fee() >> fee_shift + .fold(0, |acc, fee_fields| max(acc, fee_fields.fee_shift())) + } + + /// Shifted fee for a TransactionBody is the sum of fees shifted right by the maximum fee_shift + /// this is used to determine whether a tx can be relayed or accepted in a mempool + /// where transactions can specify a higher block-inclusion priority as a positive shift up to 15 + /// but are required to overpaythe minimum required fees by a factor of 2^priority + pub fn shifted_fee(&self) -> u64 { + self.fee() >> self.fee_shift() } fn overage(&self) -> i64 { diff --git a/core/src/libtx/aggsig.rs b/core/src/libtx/aggsig.rs index 4d253e0bc8..147a48a04f 100644 --- a/core/src/libtx/aggsig.rs +++ b/core/src/libtx/aggsig.rs @@ -225,6 +225,7 @@ pub fn verify_partial_sig( /// use core::core::transaction::KernelFeatures; /// use core::core::{Output, OutputFeatures}; /// use keychain::{Keychain, ExtKeychain, SwitchCommitmentType}; +/// use std::convert::TryInto; /// /// let secp = Secp256k1::with_caps(ContextFlag::Commit); /// let keychain = ExtKeychain::from_random_seed(false).unwrap(); @@ -239,7 +240,7 @@ pub fn verify_partial_sig( /// let height = 20; /// let over_commit = secp.commit_value(reward(fees)).unwrap(); /// let out_commit = output.commitment(); -/// let features = KernelFeatures::HeightLocked{fee: 0, lock_height: height}; +/// let features = KernelFeatures::HeightLocked{fee_fields: 1.try_into().unwrap(), lock_height: height}; /// let msg = features.kernel_sig_msg().unwrap(); /// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap(); /// let pubkey = excess.to_pubkey(&secp).unwrap(); @@ -287,6 +288,7 @@ where /// use core::core::transaction::KernelFeatures; /// use core::core::{Output, OutputFeatures}; /// use keychain::{Keychain, ExtKeychain, SwitchCommitmentType}; +/// use std::convert::TryInto; /// /// // Create signature /// let secp = Secp256k1::with_caps(ContextFlag::Commit); @@ -302,7 +304,7 @@ where /// let height = 20; /// let over_commit = secp.commit_value(reward(fees)).unwrap(); /// let out_commit = output.commitment(); -/// let features = KernelFeatures::HeightLocked{fee: 0, lock_height: height}; +/// let features = KernelFeatures::HeightLocked{fee_fields: 1.try_into().unwrap(), lock_height: height}; /// let msg = features.kernel_sig_msg().unwrap(); /// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap(); /// let pubkey = excess.to_pubkey(&secp).unwrap(); diff --git a/core/tests/core.rs b/core/tests/core.rs index 17340492d2..ab1fe113dc 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -21,8 +21,8 @@ use self::core::core::block::Error::KernelLockHeight; use self::core::core::hash::{Hashed, ZERO_HASH}; use self::core::core::verifier_cache::{LruVerifierCache, VerifierCache}; use self::core::core::{ - aggregate, deaggregate, KernelFeatures, Output, OutputFeatures, OutputIdentifier, Transaction, - TxKernel, Weighting, + aggregate, deaggregate, FeeFields, KernelFeatures, Output, OutputFeatures, OutputIdentifier, + Transaction, TxKernel, Weighting, }; use self::core::libtx::build::{self, initial_tx, input, output, with_excess}; use self::core::libtx::{aggsig, ProofBuilder}; @@ -132,7 +132,7 @@ fn test_zero_commit_fails() { // blinding should fail as signing with a zero r*G shouldn't work let res = build::transaction( KernelFeatures::Plain { - fee_fields: 0.try_into().unwrap(), + fee_fields: FeeFields::zero(), }, &[input(10, key_id1.clone()), output(10, key_id1)], &keychain, diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index ecfcc0fe89..f08281e543 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -19,7 +19,7 @@ use crate::common::tx1i10_v2_compatible; use crate::core::core::transaction::{self, Error}; use crate::core::core::verifier_cache::LruVerifierCache; use crate::core::core::{ - FeeFields, KernelFeatures, Output, OutputFeatures, Transaction, Weighting, + FeeFields, KernelFeatures, Output, OutputFeatures, Transaction, TxKernel, Weighting, }; use crate::core::global; use crate::core::libtx::build; @@ -27,6 +27,7 @@ use crate::core::libtx::proof::{self, ProofBuilder}; use crate::core::{consensus, ser}; use grin_core as core; use keychain::{ExtKeychain, Keychain}; +use std::convert::TryInto; use std::sync::Arc; use util::RwLock; @@ -47,7 +48,10 @@ fn test_transaction_json_ser_deser() { assert!(value["body"]["outputs"][0]["proof"].is_string()); // Note: Tx kernel "features" serialize in a slightly unexpected way. - assert_eq!(value["body"]["kernels"][0]["features"]["Plain"]["fee"], 2); + assert_eq!( + value["body"]["kernels"][0]["features"]["Plain"]["fee_fields"], + 2 + ); assert!(value["body"]["kernels"][0]["excess"].is_string()); assert!(value["body"]["kernels"][0]["excess_sig"].is_string()); @@ -201,3 +205,45 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { Ok(()) } + +// Test coverage for FeeFields +#[test] +fn test_fee_fields() -> Result<(), Error> { + global::set_local_chain_type(global::ChainTypes::UserTesting); + + let keychain = ExtKeychain::from_random_seed(false)?; + + let key_id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0); + + let builder = ProofBuilder::new(&keychain); + + let mut tx = build::transaction( + KernelFeatures::Plain { + fee_fields: FeeFields::new(1, 42).unwrap(), + }, + &[ + build::coinbase_input(consensus::REWARD, key_id1.clone()), + build::output(60_000_000_000 - 84 - 42 - 21, key_id1.clone()), + ], + &keychain, + &builder, + ) + .expect("valid tx"); + + assert_eq!(tx.fee(), 42); + assert_eq!(tx.shifted_fee(), 21); + + tx.body.kernels.append(&mut vec![ + TxKernel::with_features(KernelFeatures::Plain { + fee_fields: FeeFields::new(2, 84).unwrap(), + }), + TxKernel::with_features(KernelFeatures::Plain { + fee_fields: 21.try_into().unwrap(), + }), + ]); + + assert_eq!(tx.fee(), 147); + assert_eq!(tx.shifted_fee(), 36); + + Ok(()) +} diff --git a/pool/tests/block_max_weight.rs b/pool/tests/block_max_weight.rs index c2cb8aebbe..3e3a1db692 100644 --- a/pool/tests/block_max_weight.rs +++ b/pool/tests/block_max_weight.rs @@ -54,8 +54,11 @@ fn test_block_building_max_weight() { // Now create tx to spend an early coinbase (now matured). // Provides us with some useful outputs to test with. - let initial_tx = - test_transaction_spending_coinbase(&keychain, &header_1, vec![100, 200, 300, 1000]); + let initial_tx = test_transaction_spending_coinbase( + &keychain, + &header_1, + vec![100_000, 200_000, 300_000, 1_000_000], + ); // Mine that initial tx so we can spend it with multiple txs. add_block(&chain, &[initial_tx], &keychain); @@ -65,26 +68,30 @@ fn test_block_building_max_weight() { // Build some dependent txs to add to the txpool. // We will build a block from a subset of these. let txs = vec![ - test_transaction(&keychain, vec![1000], vec![390, 130, 120, 110]), - test_transaction(&keychain, vec![100], vec![90, 1]), - test_transaction(&keychain, vec![90], vec![80, 2]), - test_transaction(&keychain, vec![200], vec![199]), - test_transaction(&keychain, vec![300], vec![290, 3]), - test_transaction(&keychain, vec![290], vec![280, 4]), + test_transaction( + &keychain, + vec![1_000_000], + vec![390_000, 130_000, 120_000, 110_000], + ), + test_transaction(&keychain, vec![100_000], vec![90_000, 1_000]), + test_transaction(&keychain, vec![90_000], vec![80_000, 2_000]), + test_transaction(&keychain, vec![200_000], vec![199_000]), + test_transaction(&keychain, vec![300_000], vec![290_000, 3_000]), + test_transaction(&keychain, vec![290_000], vec![280_000, 4_000]), ]; // Fees and weights of our original txs in insert order. assert_eq!( txs.iter().map(|x| x.fee()).collect::>(), - [250, 9, 8, 1, 7, 6] + [250_000, 9_000, 8_000, 1_000, 7_000, 6_000] ); assert_eq!( txs.iter().map(|x| x.weight()).collect::>(), - [16, 8, 8, 4, 8, 8] + [88, 46, 46, 25, 46, 46] ); assert_eq!( txs.iter().map(|x| x.fee_rate()).collect::>(), - [15625, 1125, 1000, 250, 875, 750] + [2840, 195, 173, 40, 152, 130] ); // Populate our txpool with the txs. @@ -102,15 +109,15 @@ fn test_block_building_max_weight() { // Fees and weights of the "mineable" txs. assert_eq!( txs.iter().map(|x| x.fee()).collect::>(), - [250, 9, 8, 7] + [250_000, 9_000, 8_000, 7_000] ); assert_eq!( txs.iter().map(|x| x.weight()).collect::>(), - [16, 8, 8, 8] + [88, 46, 46, 46] ); assert_eq!( txs.iter().map(|x| x.fee_rate()).collect::>(), - [15625, 1125, 1000, 875] + [2840, 195, 173, 152] ); add_block(&chain, &txs, &keychain); diff --git a/pool/tests/block_reconciliation.rs b/pool/tests/block_reconciliation.rs index 0250a55812..acc4da4faa 100644 --- a/pool/tests/block_reconciliation.rs +++ b/pool/tests/block_reconciliation.rs @@ -128,7 +128,7 @@ fn test_transaction_pool_block_reconciliation() { // - Copy of 4 let block_tx_3 = test_transaction(&keychain, vec![8], vec![5, 1]); // - Output conflict w/ 8 - let block_tx_4 = test_transaction(&keychain, vec![40], vec![9, 31]); + let block_tx_4 = test_transaction(&keychain, vec![40], vec![9, 29]); let block_txs = &[block_tx_1, block_tx_2, block_tx_3, block_tx_4]; add_block(&chain, block_txs, &keychain); From 14716f5abaeb3c41029bb9734a245bd3016d81d5 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Sun, 25 Oct 2020 14:27:12 +0100 Subject: [PATCH 04/24] avoid 0 accept_base_fee --- pool/fuzz/fuzz_targets/common.rs | 4 +- pool/src/pool.rs | 2 +- pool/src/transaction_pool.rs | 16 ++++---- pool/tests/block_building.rs | 16 +++++--- pool/tests/block_max_weight.rs | 43 ++++++++++++++------ pool/tests/block_reconciliation.rs | 51 +++++++++++++++++------- pool/tests/coinbase_maturity.rs | 2 +- pool/tests/common.rs | 4 +- pool/tests/nrd_kernel_relative_height.rs | 22 +++++----- pool/tests/nrd_kernels_disabled.rs | 12 ++++-- pool/tests/nrd_kernels_enabled.rs | 12 ++++-- pool/tests/transaction_pool.rs | 46 +++++++++++++++------ 12 files changed, 152 insertions(+), 78 deletions(-) diff --git a/pool/fuzz/fuzz_targets/common.rs b/pool/fuzz/fuzz_targets/common.rs index f5839bb152..a640562688 100644 --- a/pool/fuzz/fuzz_targets/common.rs +++ b/pool/fuzz/fuzz_targets/common.rs @@ -22,7 +22,7 @@ use self::core::core::verifier_cache::{LruVerifierCache, VerifierCache}; use self::core::core::{Block, BlockHeader, BlockSums, KernelFeatures, Transaction}; use self::core::genesis; use self::core::global; -use self::core::libtx::{build, reward, ProofBuilder}; +use self::core::libtx::{build, reward, ProofBuilder, DEFAULT_BASE_FEE}; use self::core::pow; use self::keychain::{ExtKeychain, ExtKeychainPath, Keychain}; use self::pool::types::*; @@ -262,7 +262,7 @@ impl PoolFuzzer { { TransactionPool::new( PoolConfig { - accept_fee_base: 0, + accept_fee_base: DEFAULT_BASE_FEE, max_pool_size: 50, max_stempool_size: 50, mineable_max_weight: 10_000, diff --git a/pool/src/pool.rs b/pool/src/pool.rs index 144c820ec1..a3ae3c51ad 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -511,7 +511,7 @@ struct Bucket { impl Bucket { /// Construct a new bucket with the given tx. /// also specifies an "age_idx" so we can sort buckets by age - /// as well as fee_rate. Txs are maintainedin the pool in insert order + /// as well as fee_rate. Txs are maintained in the pool in insert order /// so buckets with low age_idx contain oldest txs. fn new(tx: Transaction, age_idx: usize) -> Bucket { Bucket { diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs index f16a73bf6c..138f3c40af 100644 --- a/pool/src/transaction_pool.rs +++ b/pool/src/transaction_pool.rs @@ -182,7 +182,7 @@ where // NRD kernels only valid post HF3 and if NRD feature enabled. self.verify_kernel_variants(tx, header)?; - // Do we have the capacity to accept this transaction? + // Does this transaction pay the required fees and fit within the pool capacity? let acceptability = self.is_acceptable(tx, stem); let mut evict = false; if !stem && acceptability.as_ref().err() == Some(&PoolError::OverCapacity) { @@ -281,7 +281,7 @@ where // Evict a transaction from the txpool. // Uses bucket logic to identify the "last" transaction. - // No other tx depends on it and it has low fee_to_weight. + // No other tx depends on it and it has low fee_rate pub fn evict_from_txpool(&mut self) { self.txpool.evict_transaction() } @@ -362,14 +362,12 @@ where return Err(PoolError::OverCapacity); } - // for a basic transaction (2 inputs, 2 outputs, 1 kernel) - + // weight for a basic transaction (2 inputs, 2 outputs, 1 kernel) - // (2 * 1) + (2 * 21) + (1 * 3) = 47 - // 47 * 500_000 = 23_500_000 - if self.config.accept_fee_base > 0 { - let minfees = (tx.weight() as u64) * self.config.accept_fee_base; - if tx.shifted_fee() < minfees { - return Err(PoolError::LowFeeTransaction(minfees)); - } + // minfees = 47 * 500_000 = 23_500_000 + let minfees = (tx.weight() as u64) * self.config.accept_fee_base; + if tx.shifted_fee() < minfees { + return Err(PoolError::LowFeeTransaction(minfees)); } Ok(()) } diff --git a/pool/tests/block_building.rs b/pool/tests/block_building.rs index b9d8c70d3d..5baf169825 100644 --- a/pool/tests/block_building.rs +++ b/pool/tests/block_building.rs @@ -54,19 +54,23 @@ fn test_transaction_pool_block_building() -> Result<(), PoolError> { // Now create tx to spend an early coinbase (now matured). // Provides us with some useful outputs to test with. - let initial_tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![10, 20, 30, 40]); + let initial_tx = test_transaction_spending_coinbase( + &keychain, + &header_1, + vec![100_000_000, 200_000_000, 300_000_000, 400_000_000], + ); // Mine that initial tx so we can spend it with multiple txs. add_block(&chain, &[initial_tx], &keychain); let header = chain.head_header().unwrap(); - let root_tx_1 = test_transaction(&keychain, vec![10, 20], vec![24]); - let root_tx_2 = test_transaction(&keychain, vec![30], vec![28]); - let root_tx_3 = test_transaction(&keychain, vec![40], vec![38]); + let root_tx_1 = test_transaction(&keychain, vec![100_000_000, 200_000_000], vec![240_000_000]); + let root_tx_2 = test_transaction(&keychain, vec![300_000_000], vec![270_000_000]); + let root_tx_3 = test_transaction(&keychain, vec![400_000_000], vec![370_000_000]); - let child_tx_1 = test_transaction(&keychain, vec![24], vec![22]); - let child_tx_2 = test_transaction(&keychain, vec![38], vec![32]); + let child_tx_1 = test_transaction(&keychain, vec![240_000_000], vec![210_000_000]); + let child_tx_2 = test_transaction(&keychain, vec![370_000_000], vec![320_000_000]); { // Add the three root txs to the pool. diff --git a/pool/tests/block_max_weight.rs b/pool/tests/block_max_weight.rs index 3e3a1db692..8f2e9faf1c 100644 --- a/pool/tests/block_max_weight.rs +++ b/pool/tests/block_max_weight.rs @@ -57,7 +57,7 @@ fn test_block_building_max_weight() { let initial_tx = test_transaction_spending_coinbase( &keychain, &header_1, - vec![100_000, 200_000, 300_000, 1_000_000], + vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 10_000_000_000], ); // Mine that initial tx so we can spend it with multiple txs. @@ -70,20 +70,39 @@ fn test_block_building_max_weight() { let txs = vec![ test_transaction( &keychain, - vec![1_000_000], - vec![390_000, 130_000, 120_000, 110_000], + vec![10_000_000_000], + vec![3_900_000_000, 1_300_000_000, 1_200_000_000, 1_100_000_000], + ), + test_transaction( + &keychain, + vec![1_000_000_000], + vec![900_000_000, 10_000_000], + ), + test_transaction(&keychain, vec![900_000_000], vec![800_000_000, 20_000_000]), + test_transaction(&keychain, vec![2_000_000_000], vec![1_970_000_000]), + test_transaction( + &keychain, + vec![3_000_000_000], + vec![2_900_000_000, 30_000_000], + ), + test_transaction( + &keychain, + vec![2_900_000_000], + vec![2_800_000_000, 40_000_000], ), - test_transaction(&keychain, vec![100_000], vec![90_000, 1_000]), - test_transaction(&keychain, vec![90_000], vec![80_000, 2_000]), - test_transaction(&keychain, vec![200_000], vec![199_000]), - test_transaction(&keychain, vec![300_000], vec![290_000, 3_000]), - test_transaction(&keychain, vec![290_000], vec![280_000, 4_000]), ]; // Fees and weights of our original txs in insert order. assert_eq!( txs.iter().map(|x| x.fee()).collect::>(), - [250_000, 9_000, 8_000, 1_000, 7_000, 6_000] + [ + 2_500_000_000, + 90_000_000, + 80_000_000, + 30_000_000, + 70_000_000, + 60_000_000 + ] ); assert_eq!( txs.iter().map(|x| x.weight()).collect::>(), @@ -91,7 +110,7 @@ fn test_block_building_max_weight() { ); assert_eq!( txs.iter().map(|x| x.fee_rate()).collect::>(), - [2840, 195, 173, 40, 152, 130] + [28409090, 1956521, 1739130, 1200000, 1521739, 1304347] ); // Populate our txpool with the txs. @@ -109,7 +128,7 @@ fn test_block_building_max_weight() { // Fees and weights of the "mineable" txs. assert_eq!( txs.iter().map(|x| x.fee()).collect::>(), - [250_000, 9_000, 8_000, 7_000] + [2_500_000_000, 90_000_000, 80_000_000, 70_000_000] ); assert_eq!( txs.iter().map(|x| x.weight()).collect::>(), @@ -117,7 +136,7 @@ fn test_block_building_max_weight() { ); assert_eq!( txs.iter().map(|x| x.fee_rate()).collect::>(), - [2840, 195, 173, 152] + [28409090, 1956521, 1739130, 1521739] ); add_block(&chain, &txs, &keychain); diff --git a/pool/tests/block_reconciliation.rs b/pool/tests/block_reconciliation.rs index acc4da4faa..b7f131e6b2 100644 --- a/pool/tests/block_reconciliation.rs +++ b/pool/tests/block_reconciliation.rs @@ -53,7 +53,11 @@ fn test_transaction_pool_block_reconciliation() { // Now create tx to spend an early coinbase (now matured). // Provides us with some useful outputs to test with. - let initial_tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![10, 20, 30, 40]); + let initial_tx = test_transaction_spending_coinbase( + &keychain, + &header_1, + vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000], + ); // Mine that initial tx so we can spend it with multiple txs. add_block(&chain, &[initial_tx], &keychain); @@ -66,34 +70,47 @@ fn test_transaction_pool_block_reconciliation() { // 2. A transaction that should be invalidated because the input is // consumed in the block, although it is not exactly consumed. // 3. A transaction that should remain after block reconciliation. - let block_transaction = test_transaction(&keychain, vec![10], vec![8]); - let conflict_transaction = test_transaction(&keychain, vec![20], vec![12, 6]); - let valid_transaction = test_transaction(&keychain, vec![30], vec![13, 15]); + let block_transaction = test_transaction(&keychain, vec![1_000_000_000], vec![800_000_000]); + let conflict_transaction = test_transaction( + &keychain, + vec![2_000_000_000], + vec![1_200_000_000, 600_000_000], + ); + let valid_transaction = test_transaction( + &keychain, + vec![3_000_000_000], + vec![1_300_000_000, 1_500_000_000], + ); // We will also introduce a few children: // 4. A transaction that descends from transaction 1, that is in // turn exactly contained in the block. - let block_child = test_transaction(&keychain, vec![8], vec![5, 1]); + let block_child = + test_transaction(&keychain, vec![800_000_000], vec![500_000_000, 100_000_000]); // 5. A transaction that descends from transaction 4, that is not // contained in the block at all and should be valid after // reconciliation. - let pool_child = test_transaction(&keychain, vec![5], vec![3]); + let pool_child = test_transaction(&keychain, vec![500_000_000], vec![300_000_000]); // 6. A transaction that descends from transaction 2 that does not // conflict with anything in the block in any way, but should be // invalidated (orphaned). - let conflict_child = test_transaction(&keychain, vec![12], vec![2]); + let conflict_child = test_transaction(&keychain, vec![1_200_000_000], vec![200_000_000]); // 7. A transaction that descends from transaction 2 that should be // valid due to its inputs being satisfied by the block. - let conflict_valid_child = test_transaction(&keychain, vec![6], vec![4]); + let conflict_valid_child = test_transaction(&keychain, vec![600_000_000], vec![400_000_000]); // 8. A transaction that descends from transaction 3 that should be // invalidated due to an output conflict. - let valid_child_conflict = test_transaction(&keychain, vec![13], vec![9]); + let valid_child_conflict = test_transaction(&keychain, vec![1_300_000_000], vec![900_000_000]); // 9. A transaction that descends from transaction 3 that should remain // valid after reconciliation. - let valid_child_valid = test_transaction(&keychain, vec![15], vec![11]); + let valid_child_valid = test_transaction(&keychain, vec![1_500_000_000], vec![1_100_000_000]); // 10. A transaction that descends from both transaction 6 and // transaction 9 - let mixed_child = test_transaction(&keychain, vec![2, 11], vec![7]); + let mixed_child = test_transaction( + &keychain, + vec![200_000_000, 1_100_000_000], + vec![700_000_000], + ); let txs_to_add = vec![ block_transaction, @@ -122,13 +139,17 @@ fn test_transaction_pool_block_reconciliation() { // Now we prepare the block that will cause the above conditions to be met. // First, the transactions we want in the block: // - Copy of 1 - let block_tx_1 = test_transaction(&keychain, vec![10], vec![8]); + let block_tx_1 = test_transaction(&keychain, vec![1_000_000_000], vec![800_000_000]); // - Conflict w/ 2, satisfies 7 - let block_tx_2 = test_transaction(&keychain, vec![20], vec![6]); + let block_tx_2 = test_transaction(&keychain, vec![2_000_000_000], vec![600_000_000]); // - Copy of 4 - let block_tx_3 = test_transaction(&keychain, vec![8], vec![5, 1]); + let block_tx_3 = test_transaction(&keychain, vec![800_000_000], vec![500_000_000, 100_000_000]); // - Output conflict w/ 8 - let block_tx_4 = test_transaction(&keychain, vec![40], vec![9, 29]); + let block_tx_4 = test_transaction( + &keychain, + vec![4_000_000_000], + vec![900_000_000, 2_900_000_000], + ); let block_txs = &[block_tx_1, block_tx_2, block_tx_3, block_tx_4]; add_block(&chain, block_txs, &keychain); diff --git a/pool/tests/coinbase_maturity.rs b/pool/tests/coinbase_maturity.rs index dfc2b2db2f..40b084daed 100644 --- a/pool/tests/coinbase_maturity.rs +++ b/pool/tests/coinbase_maturity.rs @@ -52,7 +52,7 @@ fn test_coinbase_maturity() { add_block(&chain, &[], &keychain); let header_1 = chain.get_header_by_height(1).unwrap(); - let tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![100]); + let tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![100_000_000]); // Coinbase is not yet matured and cannot be spent. let header = chain.head_header().unwrap(); diff --git a/pool/tests/common.rs b/pool/tests/common.rs index 21e72aa3e3..cd57a57088 100644 --- a/pool/tests/common.rs +++ b/pool/tests/common.rs @@ -24,7 +24,7 @@ use self::core::core::{ }; use self::core::genesis; use self::core::global; -use self::core::libtx::{build, reward, ProofBuilder}; +use self::core::libtx::{build, reward, ProofBuilder, DEFAULT_BASE_FEE}; use self::core::pow; use self::keychain::{BlindingFactor, ExtKeychain, ExtKeychainPath, Keychain}; use self::pool::types::*; @@ -167,7 +167,7 @@ where { TransactionPool::new( PoolConfig { - accept_fee_base: 0, + accept_fee_base: DEFAULT_BASE_FEE, max_pool_size: 50, max_stempool_size: 50, mineable_max_weight: 10_000, diff --git a/pool/tests/nrd_kernel_relative_height.rs b/pool/tests/nrd_kernel_relative_height.rs index 50f6e7a38c..dc94a6ab56 100644 --- a/pool/tests/nrd_kernel_relative_height.rs +++ b/pool/tests/nrd_kernel_relative_height.rs @@ -60,7 +60,11 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { // Now create tx to spend an early coinbase (now matured). // Provides us with some useful outputs to test with. - let initial_tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![10, 20, 30, 40]); + let initial_tx = test_transaction_spending_coinbase( + &keychain, + &header_1, + vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000], + ); // Mine that initial tx so we can spend it with multiple txs. add_block(&chain, &[initial_tx], &keychain); @@ -74,7 +78,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let (tx1, tx2, tx3) = { let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 6.try_into().unwrap(), + fee_fields: 600_000_000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(2)?, }); let msg = kernel.msg_to_sign().unwrap(); @@ -96,23 +100,23 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let tx1 = test_transaction_with_kernel( &keychain, - vec![10, 20], - vec![24], + vec![1_000_000_000, 2_000_000_000], + vec![2_400_000_000], kernel.clone(), excess.clone(), ); let tx2 = test_transaction_with_kernel( &keychain, - vec![24], - vec![18], + vec![2_400_000_000], + vec![1_800_000_000], kernel2.clone(), excess.clone(), ); // Now reuse kernel excess for tx3 but with NRD relative_height=1 (and different fee). let mut kernel_short = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 3.try_into().unwrap(), + fee_fields: 300_000_000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1)?, }); let msg_short = kernel_short.msg_to_sign().unwrap(); @@ -124,8 +128,8 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let tx3 = test_transaction_with_kernel( &keychain, - vec![18], - vec![15], + vec![1_800_000_000], + vec![1_500_000_000], kernel_short.clone(), excess.clone(), ); diff --git a/pool/tests/nrd_kernels_disabled.rs b/pool/tests/nrd_kernels_disabled.rs index 5f55bc35ec..e2ae5a01e7 100644 --- a/pool/tests/nrd_kernels_disabled.rs +++ b/pool/tests/nrd_kernels_disabled.rs @@ -57,15 +57,19 @@ fn test_nrd_kernels_disabled() { // Spend the initial coinbase. let header_1 = chain.get_header_by_height(1).unwrap(); - let tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![10, 20, 30, 40]); + let tx = test_transaction_spending_coinbase( + &keychain, + &header_1, + vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000], + ); add_block(&chain, &[tx], &keychain); let tx_1 = test_transaction_with_kernel_features( &keychain, - vec![10, 20], - vec![24], + vec![1_000_000_000, 2_000_000_000], + vec![2_400_000_000], KernelFeatures::NoRecentDuplicate { - fee_fields: 6.try_into().unwrap(), + fee_fields: 600_000_000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); diff --git a/pool/tests/nrd_kernels_enabled.rs b/pool/tests/nrd_kernels_enabled.rs index 539cda9ece..a466e886e8 100644 --- a/pool/tests/nrd_kernels_enabled.rs +++ b/pool/tests/nrd_kernels_enabled.rs @@ -57,15 +57,19 @@ fn test_nrd_kernels_enabled() { // Spend the initial coinbase. let header_1 = chain.get_header_by_height(1).unwrap(); - let tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![10, 20, 30, 40]); + let tx = test_transaction_spending_coinbase( + &keychain, + &header_1, + vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000], + ); add_block(&chain, &[tx], &keychain); let tx_1 = test_transaction_with_kernel_features( &keychain, - vec![10, 20], - vec![24], + vec![1_000_000_000, 2_000_000_000], + vec![2_400_000_000], KernelFeatures::NoRecentDuplicate { - fee_fields: 6.try_into().unwrap(), + fee_fields: 600_000_000.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs index 5c4b5016bf..33d46a0bb9 100644 --- a/pool/tests/transaction_pool.rs +++ b/pool/tests/transaction_pool.rs @@ -56,7 +56,18 @@ fn test_the_transaction_pool() { let initial_tx = test_transaction_spending_coinbase( &keychain, &header_1, - vec![500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400], + vec![ + 500_000_000, + 600_000_000, + 700_000_000, + 800_000_000, + 900_000_000, + 1000_000_000, + 1100_000_000, + 1200_000_000, + 1300_000_000, + 1400_000_000, + ], ); // Add this tx to the pool (stem=false, direct to txpool). @@ -69,14 +80,18 @@ fn test_the_transaction_pool() { // Test adding a tx that "double spends" an output currently spent by a tx // already in the txpool. In this case we attempt to spend the original coinbase twice. { - let tx = test_transaction_spending_coinbase(&keychain, &header, vec![501]); + let tx = test_transaction_spending_coinbase(&keychain, &header, vec![501_000_000]); assert!(pool.add_to_pool(test_source(), tx, false, &header).is_err()); } // tx1 spends some outputs from the initial test tx. - let tx1 = test_transaction(&keychain, vec![500, 600], vec![499, 599]); + let tx1 = test_transaction( + &keychain, + vec![500_000_000, 600_000_000], + vec![469_000_000, 569_000_000], + ); // tx2 spends some outputs from both tx1 and the initial test tx. - let tx2 = test_transaction(&keychain, vec![499, 700], vec![498]); + let tx2 = test_transaction(&keychain, vec![469_000_000, 700_000_000], vec![498_000_000]); { // Check we have a single initial tx in the pool. @@ -105,7 +120,11 @@ fn test_the_transaction_pool() { // Test adding a duplicate tx with the same input and outputs. // Note: not the *same* tx, just same underlying inputs/outputs. { - let tx1a = test_transaction(&keychain, vec![500, 600], vec![499, 599]); + let tx1a = test_transaction( + &keychain, + vec![500_000_000, 600_000_000], + vec![469_000_000, 569_000_000], + ); assert!(pool .add_to_pool(test_source(), tx1a, false, &header) .is_err()); @@ -113,7 +132,7 @@ fn test_the_transaction_pool() { // Test adding a tx attempting to spend a non-existent output. { - let bad_tx = test_transaction(&keychain, vec![10_001], vec![10_000]); + let bad_tx = test_transaction(&keychain, vec![10_001_000_000], vec![9_900_000_000]); assert!(pool .add_to_pool(test_source(), bad_tx, false, &header) .is_err()); @@ -124,13 +143,13 @@ fn test_the_transaction_pool() { // be unique. Otherwise spending one will almost certainly cause the other // to be immediately stolen via a "replay" tx. { - let tx = test_transaction(&keychain, vec![900], vec![498]); + let tx = test_transaction(&keychain, vec![900_000_000], vec![498_000_000]); assert!(pool.add_to_pool(test_source(), tx, false, &header).is_err()); } // Confirm the tx pool correctly identifies an invalid tx (already spent). { - let tx3 = test_transaction(&keychain, vec![500], vec![497]); + let tx3 = test_transaction(&keychain, vec![500_000_000], vec![467_000_000]); assert!(pool .add_to_pool(test_source(), tx3, false, &header) .is_err()); @@ -139,9 +158,9 @@ fn test_the_transaction_pool() { // Now add a couple of txs to the stempool (stem = true). { - let tx = test_transaction(&keychain, vec![599], vec![598]); + let tx = test_transaction(&keychain, vec![569_000_000], vec![538_000_000]); pool.add_to_pool(test_source(), tx, true, &header).unwrap(); - let tx2 = test_transaction(&keychain, vec![598], vec![597]); + let tx2 = test_transaction(&keychain, vec![538_000_000], vec![507_000_000]); pool.add_to_pool(test_source(), tx2, true, &header).unwrap(); assert_eq!(pool.total_size(), 3); assert_eq!(pool.stempool.size(), 2); @@ -165,7 +184,7 @@ fn test_the_transaction_pool() { // Adding a duplicate tx to the stempool will result in it being fluffed. // This handles the case of the stem path having a cycle in it. { - let tx = test_transaction(&keychain, vec![597], vec![596]); + let tx = test_transaction(&keychain, vec![507_000_000], vec![476_000_000]); pool.add_to_pool(test_source(), tx.clone(), true, &header) .unwrap(); assert_eq!(pool.total_size(), 4); @@ -185,7 +204,7 @@ fn test_the_transaction_pool() { // We will do this be adding a new tx to the pool // that is a superset of a tx already in the pool. { - let tx4 = test_transaction(&keychain, vec![800], vec![799]); + let tx4 = test_transaction(&keychain, vec![800_000_000], vec![769_000_000]); // tx1 and tx2 are already in the txpool (in aggregated form) // tx4 is the "new" part of this aggregated tx that we care about @@ -206,7 +225,8 @@ fn test_the_transaction_pool() { // Check we cannot "double spend" an output spent in a previous block. // We use the initial coinbase output here for convenience. { - let double_spend_tx = test_transaction_spending_coinbase(&keychain, &header, vec![1000]); + let double_spend_tx = + test_transaction_spending_coinbase(&keychain, &header, vec![1000_000_000]); // check we cannot add a double spend to the stempool assert!(pool From ed46e927401705224fa5e3872fdabd163fc1e2e9 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Mon, 26 Oct 2020 13:14:40 +0100 Subject: [PATCH 05/24] add aggregate_fee_fields method for transaction --- core/src/core/transaction.rs | 10 ++++++++++ core/tests/transaction.rs | 1 + 2 files changed, 11 insertions(+) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 825c2e59f4..f44975cb67 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -1002,6 +1002,11 @@ impl TransactionBody { self.fee() >> self.fee_shift() } + /// aggregate fee_fields from all appropriate kernels in TransactionBody into one, if possible + pub fn aggregate_fee_fields(&self) -> Result { + FeeFields::new(self.fee_shift() as u64, self.fee()) + } + fn overage(&self) -> i64 { self.fee() as i64 } @@ -1376,6 +1381,11 @@ impl Transaction { self.body.shifted_fee() } + /// aggregate fee_fields from all appropriate kernels in transaction into one + pub fn aggregate_fee_fields(&self) -> Result { + self.body.aggregate_fee_fields() + } + /// Total overage across all kernels. pub fn overage(&self) -> i64 { self.body.overage() diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index f08281e543..3a545379db 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -244,6 +244,7 @@ fn test_fee_fields() -> Result<(), Error> { assert_eq!(tx.fee(), 147); assert_eq!(tx.shifted_fee(), 36); + assert_eq!(tx.aggregate_fee_fields(), FeeFields::new(2, 147)); Ok(()) } From daa200407def4fc1b40a86a5185112e514b5d4c7 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Mon, 26 Oct 2020 18:33:51 +0100 Subject: [PATCH 06/24] implement std::fmt::Display trait for FeeFields --- core/src/core/transaction.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index f44975cb67..3569f93655 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -28,6 +28,7 @@ use keychain::{self, BlindingFactor}; use std::cmp::Ordering; use std::cmp::{max, min}; use std::convert::{TryFrom, TryInto}; +use std::fmt::Display; use std::sync::Arc; use std::{error, fmt}; use util::secp; @@ -55,6 +56,12 @@ impl Readable for FeeFields { } } +impl Display for FeeFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + /// Conversion from a u64 to a valid FeeFields. /// Valid height is between 1 and WEEK_HEIGHT inclusive. impl TryFrom for FeeFields { From 29c797cc9386073dced95a7d5c6ab81ef3814bee Mon Sep 17 00:00:00 2001 From: John Tromp Date: Thu, 29 Oct 2020 19:03:48 +0100 Subject: [PATCH 07/24] make base_fee argument non-optional in libtx::mod::tx_fee --- core/src/libtx/mod.rs | 19 ++----------------- core/tests/transaction.rs | 3 ++- pool/src/types.rs | 6 +++--- pool/tests/common.rs | 4 ++-- 4 files changed, 9 insertions(+), 23 deletions(-) diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index c367b92839..48493a0381 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -34,22 +34,7 @@ use crate::core::Transaction; pub use self::proof::ProofBuilder; pub use crate::libtx::error::{Error, ErrorKind}; -/// make output (of weight 21) cost about 1 Grin-cent, keeping a round number -/// This should really be set by the `accept_fee_base` parameter in grin-server.toml -pub const DEFAULT_BASE_FEE: u64 = consensus::GRIN_BASE / 100 / 20; // 500000 - /// Transaction fee calculation -pub fn tx_fee( - input_len: usize, - output_len: usize, - kernel_len: usize, - base_fee: Option, -) -> u64 { - let use_base_fee = match base_fee { - Some(bf) => bf, - None => DEFAULT_BASE_FEE, - }; - - Transaction::weight_by_iok(input_len as u64, output_len as u64, kernel_len as u64) - * use_base_fee +pub fn tx_fee(input_len: usize, output_len: usize, kernel_len: usize, base_fee: u64) -> u64 { + Transaction::weight_by_iok(input_len as u64, output_len as u64, kernel_len as u64) * base_fee } diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 3a545379db..6acea74a64 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -22,8 +22,8 @@ use crate::core::core::{ FeeFields, KernelFeatures, Output, OutputFeatures, Transaction, TxKernel, Weighting, }; use crate::core::global; -use crate::core::libtx::build; use crate::core::libtx::proof::{self, ProofBuilder}; +use crate::core::libtx::{build, tx_fee}; use crate::core::{consensus, ser}; use grin_core as core; use keychain::{ExtKeychain, Keychain}; @@ -245,6 +245,7 @@ fn test_fee_fields() -> Result<(), Error> { assert_eq!(tx.fee(), 147); assert_eq!(tx.shifted_fee(), 36); assert_eq!(tx.aggregate_fee_fields(), FeeFields::new(2, 147)); + assert_eq!(tx_fee(1, 1, 3, 500_000), 15_500_000); Ok(()) } diff --git a/pool/src/types.rs b/pool/src/types.rs index 8c5dcea413..d550f0b440 100644 --- a/pool/src/types.rs +++ b/pool/src/types.rs @@ -21,7 +21,6 @@ use self::core::core::committed; use self::core::core::hash::Hash; use self::core::core::transaction::{self, Transaction}; use self::core::core::{BlockHeader, BlockSums, Inputs, OutputIdentifier}; -use self::core::libtx::DEFAULT_BASE_FEE; use chrono::prelude::*; use failure::Fail; use grin_core as core; @@ -134,8 +133,9 @@ impl Default for PoolConfig { } } -fn default_accept_fee_base() -> u64 { - DEFAULT_BASE_FEE +/// make output (of weight 21) cost about 1 Grin-cent by default, keeping a round number +pub fn default_accept_fee_base() -> u64 { + consensus::GRIN_BASE / 100 / 20 // 500_000 nanogrin } fn default_max_pool_size() -> usize { 50_000 diff --git a/pool/tests/common.rs b/pool/tests/common.rs index cd57a57088..12e92b5cbf 100644 --- a/pool/tests/common.rs +++ b/pool/tests/common.rs @@ -24,7 +24,7 @@ use self::core::core::{ }; use self::core::genesis; use self::core::global; -use self::core::libtx::{build, reward, ProofBuilder, DEFAULT_BASE_FEE}; +use self::core::libtx::{build, reward, ProofBuilder}; use self::core::pow; use self::keychain::{BlindingFactor, ExtKeychain, ExtKeychainPath, Keychain}; use self::pool::types::*; @@ -167,7 +167,7 @@ where { TransactionPool::new( PoolConfig { - accept_fee_base: DEFAULT_BASE_FEE, + accept_fee_base: default_accept_fee_base(), max_pool_size: 50, max_stempool_size: 50, mineable_max_weight: 10_000, From 40a03c62616c7fc8c22e2ed0c263b04bfb68b7c0 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Sun, 1 Nov 2020 21:20:05 +0100 Subject: [PATCH 08/24] add global and thread local accept_fee_base; use to simplify tests --- core/src/global.rs | 42 ++++++++++++++++++- core/src/libtx/mod.rs | 1 - pool/src/transaction_pool.rs | 2 +- pool/tests/block_building.rs | 18 ++++---- pool/tests/block_max_weight.rs | 44 ++++++-------------- pool/tests/block_reconciliation.rs | 53 ++++++++---------------- pool/tests/coinbase_maturity.rs | 3 +- pool/tests/nrd_kernel_relative_height.rs | 24 +++++------ pool/tests/nrd_kernels_disabled.rs | 13 +++--- pool/tests/nrd_kernels_enabled.rs | 14 +++---- pool/tests/transaction_pool.rs | 47 +++++++-------------- 11 files changed, 117 insertions(+), 144 deletions(-) diff --git a/core/src/global.rs b/core/src/global.rs index ed819d39be..bd38794495 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -19,8 +19,8 @@ use crate::consensus::{ graph_weight, valid_header_version, HeaderInfo, BASE_EDGE_BITS, BLOCK_TIME_SEC, COINBASE_MATURITY, CUT_THROUGH_HORIZON, DAY_HEIGHT, DEFAULT_MIN_EDGE_BITS, - DIFFICULTY_ADJUST_WINDOW, INITIAL_DIFFICULTY, KERNEL_WEIGHT, MAX_BLOCK_WEIGHT, OUTPUT_WEIGHT, - PROOFSIZE, SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD, + DIFFICULTY_ADJUST_WINDOW, GRIN_BASE, INITIAL_DIFFICULTY, KERNEL_WEIGHT, MAX_BLOCK_WEIGHT, + OUTPUT_WEIGHT, PROOFSIZE, SECOND_POW_EDGE_BITS, STATE_SYNC_THRESHOLD, }; use crate::core::block::HeaderVersion; use crate::{ @@ -144,6 +144,11 @@ lazy_static! { /// to be overridden on a per-thread basis (for testing). pub static ref GLOBAL_CHAIN_TYPE: OneTime = OneTime::new(); + /// Global acccept fee base that must be initialized once on node startup. + /// This is accessed via get_acccept_fee_base() which allows the global value + /// to be overridden on a per-thread basis (for testing). + pub static ref GLOBAL_ACCEPT_FEE_BASE: OneTime = OneTime::new(); + /// Global feature flag for NRD kernel support. /// If enabled NRD kernels are treated as valid after HF3 (based on header version). /// If disabled NRD kernels are invalid regardless of header version or block height. @@ -154,6 +159,9 @@ thread_local! { /// Mainnet|Testnet|UserTesting|AutomatedTesting pub static CHAIN_TYPE: Cell> = Cell::new(None); + /// minimum transaction fee per unit of transaction weight for mempool acceptance + pub static ACCEPT_FEE_BASE: Cell> = Cell::new(None); + /// Local feature flag for NRD kernel support. pub static NRD_FEATURE_ENABLED: Cell> = Cell::new(None); } @@ -185,6 +193,36 @@ pub fn init_global_chain_type(new_type: ChainTypes) { GLOBAL_CHAIN_TYPE.init(new_type) } +/// One time initialization of the global accept fee base +/// Will panic if we attempt to re-initialize this (via OneTime). +pub fn init_global_accept_fee_base(new_base: u64) { + GLOBAL_ACCEPT_FEE_BASE.init(new_base) +} + +/// Set the accept fee base on a per-thread basis via thread_local storage. +pub fn set_local_accept_fee_base(new_base: u64) { + ACCEPT_FEE_BASE.with(|base| base.set(Some(new_base))) +} + +/// Accept Fee Base +/// Look at thread local config first. If not set fallback to global config. +/// Default to grin-cent/20 if global config unset. +pub fn get_accept_fee_base() -> u64 { + ACCEPT_FEE_BASE.with(|base| match base.get() { + None => { + let base = if GLOBAL_ACCEPT_FEE_BASE.is_init() { + GLOBAL_ACCEPT_FEE_BASE.borrow() + } else { + // Global config unset, default to 1/20 of a Grincent + GRIN_BASE / 100 / 20 // 500_000 + }; + set_local_accept_fee_base(base); + base + } + Some(base) => base, + }) +} + /// One time initialization of the global chain_type. /// Will panic if we attempt to re-initialize this (via OneTime). pub fn init_global_nrd_enabled(enabled: bool) { diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index 48493a0381..afb30f1754 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -28,7 +28,6 @@ pub mod proof; pub mod reward; pub mod secp_ser; -use crate::consensus; use crate::core::Transaction; pub use self::proof::ProofBuilder; diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs index 138f3c40af..42661c3523 100644 --- a/pool/src/transaction_pool.rs +++ b/pool/src/transaction_pool.rs @@ -365,7 +365,7 @@ where // weight for a basic transaction (2 inputs, 2 outputs, 1 kernel) - // (2 * 1) + (2 * 21) + (1 * 3) = 47 // minfees = 47 * 500_000 = 23_500_000 - let minfees = (tx.weight() as u64) * self.config.accept_fee_base; + let minfees = (tx.weight() as u64) * global::get_accept_fee_base(); if tx.shifted_fee() < minfees { return Err(PoolError::LowFeeTransaction(minfees)); } diff --git a/pool/tests/block_building.rs b/pool/tests/block_building.rs index 5baf169825..dbdff3caa2 100644 --- a/pool/tests/block_building.rs +++ b/pool/tests/block_building.rs @@ -31,6 +31,7 @@ use std::sync::Arc; fn test_transaction_pool_block_building() -> Result<(), PoolError> { util::init_test_logger(); global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + global::set_local_accept_fee_base(1); let keychain: ExtKeychain = Keychain::from_random_seed(false).unwrap(); let db_root = "target/.block_building"; @@ -54,23 +55,20 @@ fn test_transaction_pool_block_building() -> Result<(), PoolError> { // Now create tx to spend an early coinbase (now matured). // Provides us with some useful outputs to test with. - let initial_tx = test_transaction_spending_coinbase( - &keychain, - &header_1, - vec![100_000_000, 200_000_000, 300_000_000, 400_000_000], - ); + let initial_tx = + test_transaction_spending_coinbase(&keychain, &header_1, vec![100, 200, 300, 400]); // Mine that initial tx so we can spend it with multiple txs. add_block(&chain, &[initial_tx], &keychain); let header = chain.head_header().unwrap(); - let root_tx_1 = test_transaction(&keychain, vec![100_000_000, 200_000_000], vec![240_000_000]); - let root_tx_2 = test_transaction(&keychain, vec![300_000_000], vec![270_000_000]); - let root_tx_3 = test_transaction(&keychain, vec![400_000_000], vec![370_000_000]); + let root_tx_1 = test_transaction(&keychain, vec![100, 200], vec![240]); + let root_tx_2 = test_transaction(&keychain, vec![300], vec![270]); + let root_tx_3 = test_transaction(&keychain, vec![400], vec![370]); - let child_tx_1 = test_transaction(&keychain, vec![240_000_000], vec![210_000_000]); - let child_tx_2 = test_transaction(&keychain, vec![370_000_000], vec![320_000_000]); + let child_tx_1 = test_transaction(&keychain, vec![240], vec![210]); + let child_tx_2 = test_transaction(&keychain, vec![370], vec![320]); { // Add the three root txs to the pool. diff --git a/pool/tests/block_max_weight.rs b/pool/tests/block_max_weight.rs index 8f2e9faf1c..e31d756e24 100644 --- a/pool/tests/block_max_weight.rs +++ b/pool/tests/block_max_weight.rs @@ -30,6 +30,7 @@ use std::sync::Arc; fn test_block_building_max_weight() { util::init_test_logger(); global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + global::set_local_accept_fee_base(1); let keychain: ExtKeychain = Keychain::from_random_seed(false).unwrap(); @@ -57,7 +58,7 @@ fn test_block_building_max_weight() { let initial_tx = test_transaction_spending_coinbase( &keychain, &header_1, - vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 10_000_000_000], + vec![1_000_000, 2_000_000, 3_000_000, 10_000_000], ); // Mine that initial tx so we can spend it with multiple txs. @@ -70,39 +71,20 @@ fn test_block_building_max_weight() { let txs = vec![ test_transaction( &keychain, - vec![10_000_000_000], - vec![3_900_000_000, 1_300_000_000, 1_200_000_000, 1_100_000_000], - ), - test_transaction( - &keychain, - vec![1_000_000_000], - vec![900_000_000, 10_000_000], - ), - test_transaction(&keychain, vec![900_000_000], vec![800_000_000, 20_000_000]), - test_transaction(&keychain, vec![2_000_000_000], vec![1_970_000_000]), - test_transaction( - &keychain, - vec![3_000_000_000], - vec![2_900_000_000, 30_000_000], - ), - test_transaction( - &keychain, - vec![2_900_000_000], - vec![2_800_000_000, 40_000_000], + vec![10_000_000], + vec![3_900_000, 1_300_000, 1_200_000, 1_100_000], ), + test_transaction(&keychain, vec![1_000_000], vec![900_000, 10_000]), + test_transaction(&keychain, vec![900_000], vec![800_000, 20_000]), + test_transaction(&keychain, vec![2_000_000], vec![1_970_000]), + test_transaction(&keychain, vec![3_000_000], vec![2_900_000, 30_000]), + test_transaction(&keychain, vec![2_900_000], vec![2_800_000, 40_000]), ]; // Fees and weights of our original txs in insert order. assert_eq!( txs.iter().map(|x| x.fee()).collect::>(), - [ - 2_500_000_000, - 90_000_000, - 80_000_000, - 30_000_000, - 70_000_000, - 60_000_000 - ] + [2_500_000, 90_000, 80_000, 30_000, 70_000, 60_000] ); assert_eq!( txs.iter().map(|x| x.weight()).collect::>(), @@ -110,7 +92,7 @@ fn test_block_building_max_weight() { ); assert_eq!( txs.iter().map(|x| x.fee_rate()).collect::>(), - [28409090, 1956521, 1739130, 1200000, 1521739, 1304347] + [28409, 1956, 1739, 1200, 1521, 1304] ); // Populate our txpool with the txs. @@ -128,7 +110,7 @@ fn test_block_building_max_weight() { // Fees and weights of the "mineable" txs. assert_eq!( txs.iter().map(|x| x.fee()).collect::>(), - [2_500_000_000, 90_000_000, 80_000_000, 70_000_000] + [2_500_000, 90_000, 80_000, 70_000] ); assert_eq!( txs.iter().map(|x| x.weight()).collect::>(), @@ -136,7 +118,7 @@ fn test_block_building_max_weight() { ); assert_eq!( txs.iter().map(|x| x.fee_rate()).collect::>(), - [28409090, 1956521, 1739130, 1521739] + [28409, 1956, 1739, 1521] ); add_block(&chain, &txs, &keychain); diff --git a/pool/tests/block_reconciliation.rs b/pool/tests/block_reconciliation.rs index b7f131e6b2..3e69401713 100644 --- a/pool/tests/block_reconciliation.rs +++ b/pool/tests/block_reconciliation.rs @@ -30,6 +30,7 @@ use std::sync::Arc; fn test_transaction_pool_block_reconciliation() { util::init_test_logger(); global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + global::set_local_accept_fee_base(1); let keychain: ExtKeychain = Keychain::from_random_seed(false).unwrap(); let db_root = "target/.block_reconciliation"; @@ -53,11 +54,8 @@ fn test_transaction_pool_block_reconciliation() { // Now create tx to spend an early coinbase (now matured). // Provides us with some useful outputs to test with. - let initial_tx = test_transaction_spending_coinbase( - &keychain, - &header_1, - vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000], - ); + let initial_tx = + test_transaction_spending_coinbase(&keychain, &header_1, vec![1_000, 2_000, 3_000, 4_000]); // Mine that initial tx so we can spend it with multiple txs. add_block(&chain, &[initial_tx], &keychain); @@ -70,47 +68,34 @@ fn test_transaction_pool_block_reconciliation() { // 2. A transaction that should be invalidated because the input is // consumed in the block, although it is not exactly consumed. // 3. A transaction that should remain after block reconciliation. - let block_transaction = test_transaction(&keychain, vec![1_000_000_000], vec![800_000_000]); - let conflict_transaction = test_transaction( - &keychain, - vec![2_000_000_000], - vec![1_200_000_000, 600_000_000], - ); - let valid_transaction = test_transaction( - &keychain, - vec![3_000_000_000], - vec![1_300_000_000, 1_500_000_000], - ); + let block_transaction = test_transaction(&keychain, vec![1_000], vec![800]); + let conflict_transaction = test_transaction(&keychain, vec![2_000], vec![1_200, 600]); + let valid_transaction = test_transaction(&keychain, vec![3_000], vec![1_300, 1_500]); // We will also introduce a few children: // 4. A transaction that descends from transaction 1, that is in // turn exactly contained in the block. - let block_child = - test_transaction(&keychain, vec![800_000_000], vec![500_000_000, 100_000_000]); + let block_child = test_transaction(&keychain, vec![800], vec![500, 100]); // 5. A transaction that descends from transaction 4, that is not // contained in the block at all and should be valid after // reconciliation. - let pool_child = test_transaction(&keychain, vec![500_000_000], vec![300_000_000]); + let pool_child = test_transaction(&keychain, vec![500], vec![300]); // 6. A transaction that descends from transaction 2 that does not // conflict with anything in the block in any way, but should be // invalidated (orphaned). - let conflict_child = test_transaction(&keychain, vec![1_200_000_000], vec![200_000_000]); + let conflict_child = test_transaction(&keychain, vec![1_200], vec![200]); // 7. A transaction that descends from transaction 2 that should be // valid due to its inputs being satisfied by the block. - let conflict_valid_child = test_transaction(&keychain, vec![600_000_000], vec![400_000_000]); + let conflict_valid_child = test_transaction(&keychain, vec![600], vec![400]); // 8. A transaction that descends from transaction 3 that should be // invalidated due to an output conflict. - let valid_child_conflict = test_transaction(&keychain, vec![1_300_000_000], vec![900_000_000]); + let valid_child_conflict = test_transaction(&keychain, vec![1_300], vec![900]); // 9. A transaction that descends from transaction 3 that should remain // valid after reconciliation. - let valid_child_valid = test_transaction(&keychain, vec![1_500_000_000], vec![1_100_000_000]); + let valid_child_valid = test_transaction(&keychain, vec![1_500], vec![1_100]); // 10. A transaction that descends from both transaction 6 and // transaction 9 - let mixed_child = test_transaction( - &keychain, - vec![200_000_000, 1_100_000_000], - vec![700_000_000], - ); + let mixed_child = test_transaction(&keychain, vec![200, 1_100], vec![700]); let txs_to_add = vec![ block_transaction, @@ -139,17 +124,13 @@ fn test_transaction_pool_block_reconciliation() { // Now we prepare the block that will cause the above conditions to be met. // First, the transactions we want in the block: // - Copy of 1 - let block_tx_1 = test_transaction(&keychain, vec![1_000_000_000], vec![800_000_000]); + let block_tx_1 = test_transaction(&keychain, vec![1_000], vec![800]); // - Conflict w/ 2, satisfies 7 - let block_tx_2 = test_transaction(&keychain, vec![2_000_000_000], vec![600_000_000]); + let block_tx_2 = test_transaction(&keychain, vec![2_000], vec![600]); // - Copy of 4 - let block_tx_3 = test_transaction(&keychain, vec![800_000_000], vec![500_000_000, 100_000_000]); + let block_tx_3 = test_transaction(&keychain, vec![800], vec![500, 100]); // - Output conflict w/ 8 - let block_tx_4 = test_transaction( - &keychain, - vec![4_000_000_000], - vec![900_000_000, 2_900_000_000], - ); + let block_tx_4 = test_transaction(&keychain, vec![4_000], vec![900, 2_900]); let block_txs = &[block_tx_1, block_tx_2, block_tx_3, block_tx_4]; add_block(&chain, block_txs, &keychain); diff --git a/pool/tests/coinbase_maturity.rs b/pool/tests/coinbase_maturity.rs index 40b084daed..bfc7d580ed 100644 --- a/pool/tests/coinbase_maturity.rs +++ b/pool/tests/coinbase_maturity.rs @@ -31,6 +31,7 @@ use std::sync::Arc; fn test_coinbase_maturity() { util::init_test_logger(); global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + global::set_local_accept_fee_base(00_000_000); let keychain: ExtKeychain = Keychain::from_random_seed(false).unwrap(); let db_root = "target/.coinbase_maturity"; @@ -52,7 +53,7 @@ fn test_coinbase_maturity() { add_block(&chain, &[], &keychain); let header_1 = chain.get_header_by_height(1).unwrap(); - let tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![100_000_000]); + let tx = test_transaction_spending_coinbase(&keychain, &header_1, vec![100]); // Coinbase is not yet matured and cannot be spent. let header = chain.head_header().unwrap(); diff --git a/pool/tests/nrd_kernel_relative_height.rs b/pool/tests/nrd_kernel_relative_height.rs index dc94a6ab56..d9ef65b431 100644 --- a/pool/tests/nrd_kernel_relative_height.rs +++ b/pool/tests/nrd_kernel_relative_height.rs @@ -35,6 +35,7 @@ use std::sync::Arc; fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { util::init_test_logger(); global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + global::set_local_accept_fee_base(10); global::set_local_nrd_enabled(true); let keychain: ExtKeychain = Keychain::from_random_seed(false).unwrap(); @@ -60,11 +61,8 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { // Now create tx to spend an early coinbase (now matured). // Provides us with some useful outputs to test with. - let initial_tx = test_transaction_spending_coinbase( - &keychain, - &header_1, - vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000], - ); + let initial_tx = + test_transaction_spending_coinbase(&keychain, &header_1, vec![1_000, 2_000, 3_000, 4_000]); // Mine that initial tx so we can spend it with multiple txs. add_block(&chain, &[initial_tx], &keychain); @@ -78,7 +76,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let (tx1, tx2, tx3) = { let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 600_000_000.try_into().unwrap(), + fee_fields: 600.try_into().unwrap(), relative_height: NRDRelativeHeight::new(2)?, }); let msg = kernel.msg_to_sign().unwrap(); @@ -100,23 +98,23 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let tx1 = test_transaction_with_kernel( &keychain, - vec![1_000_000_000, 2_000_000_000], - vec![2_400_000_000], + vec![1_000, 2_000], + vec![2_400], kernel.clone(), excess.clone(), ); let tx2 = test_transaction_with_kernel( &keychain, - vec![2_400_000_000], - vec![1_800_000_000], + vec![2_400], + vec![1_800], kernel2.clone(), excess.clone(), ); // Now reuse kernel excess for tx3 but with NRD relative_height=1 (and different fee). let mut kernel_short = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 300_000_000.try_into().unwrap(), + fee_fields: 300.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1)?, }); let msg_short = kernel_short.msg_to_sign().unwrap(); @@ -128,8 +126,8 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let tx3 = test_transaction_with_kernel( &keychain, - vec![1_800_000_000], - vec![1_500_000_000], + vec![1_800], + vec![1_500], kernel_short.clone(), excess.clone(), ); diff --git a/pool/tests/nrd_kernels_disabled.rs b/pool/tests/nrd_kernels_disabled.rs index e2ae5a01e7..9445c362c0 100644 --- a/pool/tests/nrd_kernels_disabled.rs +++ b/pool/tests/nrd_kernels_disabled.rs @@ -57,19 +57,16 @@ fn test_nrd_kernels_disabled() { // Spend the initial coinbase. let header_1 = chain.get_header_by_height(1).unwrap(); - let tx = test_transaction_spending_coinbase( - &keychain, - &header_1, - vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000], - ); + let tx = + test_transaction_spending_coinbase(&keychain, &header_1, vec![1_000, 2_000, 3_000, 4_000]); add_block(&chain, &[tx], &keychain); let tx_1 = test_transaction_with_kernel_features( &keychain, - vec![1_000_000_000, 2_000_000_000], - vec![2_400_000_000], + vec![1_000, 2_000], + vec![2_400], KernelFeatures::NoRecentDuplicate { - fee_fields: 600_000_000.try_into().unwrap(), + fee_fields: 600.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); diff --git a/pool/tests/nrd_kernels_enabled.rs b/pool/tests/nrd_kernels_enabled.rs index a466e886e8..03cc45400f 100644 --- a/pool/tests/nrd_kernels_enabled.rs +++ b/pool/tests/nrd_kernels_enabled.rs @@ -33,6 +33,7 @@ use std::sync::Arc; fn test_nrd_kernels_enabled() { util::init_test_logger(); global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + global::set_local_accept_fee_base(10); global::set_local_nrd_enabled(true); let keychain: ExtKeychain = Keychain::from_random_seed(false).unwrap(); @@ -57,19 +58,16 @@ fn test_nrd_kernels_enabled() { // Spend the initial coinbase. let header_1 = chain.get_header_by_height(1).unwrap(); - let tx = test_transaction_spending_coinbase( - &keychain, - &header_1, - vec![1_000_000_000, 2_000_000_000, 3_000_000_000, 4_000_000_000], - ); + let tx = + test_transaction_spending_coinbase(&keychain, &header_1, vec![1_000, 2_000, 3_000, 4_000]); add_block(&chain, &[tx], &keychain); let tx_1 = test_transaction_with_kernel_features( &keychain, - vec![1_000_000_000, 2_000_000_000], - vec![2_400_000_000], + vec![1_000, 2_000], + vec![2_400], KernelFeatures::NoRecentDuplicate { - fee_fields: 600_000_000.try_into().unwrap(), + fee_fields: 600.try_into().unwrap(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs index 33d46a0bb9..71b3858714 100644 --- a/pool/tests/transaction_pool.rs +++ b/pool/tests/transaction_pool.rs @@ -32,6 +32,7 @@ use std::sync::Arc; fn test_the_transaction_pool() { util::init_test_logger(); global::set_local_chain_type(global::ChainTypes::AutomatedTesting); + global::set_local_accept_fee_base(1); let keychain: ExtKeychain = Keychain::from_random_seed(false).unwrap(); let db_root = "target/.transaction_pool"; @@ -56,18 +57,7 @@ fn test_the_transaction_pool() { let initial_tx = test_transaction_spending_coinbase( &keychain, &header_1, - vec![ - 500_000_000, - 600_000_000, - 700_000_000, - 800_000_000, - 900_000_000, - 1000_000_000, - 1100_000_000, - 1200_000_000, - 1300_000_000, - 1400_000_000, - ], + vec![500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400], ); // Add this tx to the pool (stem=false, direct to txpool). @@ -80,18 +70,14 @@ fn test_the_transaction_pool() { // Test adding a tx that "double spends" an output currently spent by a tx // already in the txpool. In this case we attempt to spend the original coinbase twice. { - let tx = test_transaction_spending_coinbase(&keychain, &header, vec![501_000_000]); + let tx = test_transaction_spending_coinbase(&keychain, &header, vec![501]); assert!(pool.add_to_pool(test_source(), tx, false, &header).is_err()); } // tx1 spends some outputs from the initial test tx. - let tx1 = test_transaction( - &keychain, - vec![500_000_000, 600_000_000], - vec![469_000_000, 569_000_000], - ); + let tx1 = test_transaction(&keychain, vec![500, 600], vec![469, 569]); // tx2 spends some outputs from both tx1 and the initial test tx. - let tx2 = test_transaction(&keychain, vec![469_000_000, 700_000_000], vec![498_000_000]); + let tx2 = test_transaction(&keychain, vec![469, 700], vec![498]); { // Check we have a single initial tx in the pool. @@ -120,11 +106,7 @@ fn test_the_transaction_pool() { // Test adding a duplicate tx with the same input and outputs. // Note: not the *same* tx, just same underlying inputs/outputs. { - let tx1a = test_transaction( - &keychain, - vec![500_000_000, 600_000_000], - vec![469_000_000, 569_000_000], - ); + let tx1a = test_transaction(&keychain, vec![500, 600], vec![469, 569]); assert!(pool .add_to_pool(test_source(), tx1a, false, &header) .is_err()); @@ -132,7 +114,7 @@ fn test_the_transaction_pool() { // Test adding a tx attempting to spend a non-existent output. { - let bad_tx = test_transaction(&keychain, vec![10_001_000_000], vec![9_900_000_000]); + let bad_tx = test_transaction(&keychain, vec![10_001], vec![9_900]); assert!(pool .add_to_pool(test_source(), bad_tx, false, &header) .is_err()); @@ -143,13 +125,13 @@ fn test_the_transaction_pool() { // be unique. Otherwise spending one will almost certainly cause the other // to be immediately stolen via a "replay" tx. { - let tx = test_transaction(&keychain, vec![900_000_000], vec![498_000_000]); + let tx = test_transaction(&keychain, vec![900], vec![498]); assert!(pool.add_to_pool(test_source(), tx, false, &header).is_err()); } // Confirm the tx pool correctly identifies an invalid tx (already spent). { - let tx3 = test_transaction(&keychain, vec![500_000_000], vec![467_000_000]); + let tx3 = test_transaction(&keychain, vec![500], vec![467]); assert!(pool .add_to_pool(test_source(), tx3, false, &header) .is_err()); @@ -158,9 +140,9 @@ fn test_the_transaction_pool() { // Now add a couple of txs to the stempool (stem = true). { - let tx = test_transaction(&keychain, vec![569_000_000], vec![538_000_000]); + let tx = test_transaction(&keychain, vec![569], vec![538]); pool.add_to_pool(test_source(), tx, true, &header).unwrap(); - let tx2 = test_transaction(&keychain, vec![538_000_000], vec![507_000_000]); + let tx2 = test_transaction(&keychain, vec![538], vec![507]); pool.add_to_pool(test_source(), tx2, true, &header).unwrap(); assert_eq!(pool.total_size(), 3); assert_eq!(pool.stempool.size(), 2); @@ -184,7 +166,7 @@ fn test_the_transaction_pool() { // Adding a duplicate tx to the stempool will result in it being fluffed. // This handles the case of the stem path having a cycle in it. { - let tx = test_transaction(&keychain, vec![507_000_000], vec![476_000_000]); + let tx = test_transaction(&keychain, vec![507], vec![476]); pool.add_to_pool(test_source(), tx.clone(), true, &header) .unwrap(); assert_eq!(pool.total_size(), 4); @@ -204,7 +186,7 @@ fn test_the_transaction_pool() { // We will do this be adding a new tx to the pool // that is a superset of a tx already in the pool. { - let tx4 = test_transaction(&keychain, vec![800_000_000], vec![769_000_000]); + let tx4 = test_transaction(&keychain, vec![800], vec![769]); // tx1 and tx2 are already in the txpool (in aggregated form) // tx4 is the "new" part of this aggregated tx that we care about @@ -225,8 +207,7 @@ fn test_the_transaction_pool() { // Check we cannot "double spend" an output spent in a previous block. // We use the initial coinbase output here for convenience. { - let double_spend_tx = - test_transaction_spending_coinbase(&keychain, &header, vec![1000_000_000]); + let double_spend_tx = test_transaction_spending_coinbase(&keychain, &header, vec![1000]); // check we cannot add a double spend to the stempool assert!(pool From 23e563e56c11b2b233036721a553361273f1d168 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Sun, 1 Nov 2020 21:30:15 +0100 Subject: [PATCH 09/24] set unusually high fee base for a change --- pool/tests/coinbase_maturity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pool/tests/coinbase_maturity.rs b/pool/tests/coinbase_maturity.rs index bfc7d580ed..7d845f8389 100644 --- a/pool/tests/coinbase_maturity.rs +++ b/pool/tests/coinbase_maturity.rs @@ -31,7 +31,7 @@ use std::sync::Arc; fn test_coinbase_maturity() { util::init_test_logger(); global::set_local_chain_type(global::ChainTypes::AutomatedTesting); - global::set_local_accept_fee_base(00_000_000); + global::set_local_accept_fee_base(50_000_000); let keychain: ExtKeychain = Keychain::from_random_seed(false).unwrap(); let db_root = "target/.coinbase_maturity"; From e40751b200bf3efc33c1466fc7cb1968ab5d0249 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Mon, 2 Nov 2020 14:00:57 +0100 Subject: [PATCH 10/24] revert to optional base fee argument; default coming from either grin-{server,wallet}.toml --- core/src/libtx/mod.rs | 16 ++++++++++++++-- core/tests/transaction.rs | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index afb30f1754..ecfeebd5cb 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -29,11 +29,23 @@ pub mod reward; pub mod secp_ser; use crate::core::Transaction; +use crate::global::get_accept_fee_base; pub use self::proof::ProofBuilder; pub use crate::libtx::error::{Error, ErrorKind}; /// Transaction fee calculation -pub fn tx_fee(input_len: usize, output_len: usize, kernel_len: usize, base_fee: u64) -> u64 { - Transaction::weight_by_iok(input_len as u64, output_len as u64, kernel_len as u64) * base_fee +pub fn tx_fee( + input_len: usize, + output_len: usize, + kernel_len: usize, + base_fee: Option, +) -> u64 { + let use_base_fee = match base_fee { + Some(bf) => bf, + None => get_accept_fee_base(), + }; + + Transaction::weight_by_iok(input_len as u64, output_len as u64, kernel_len as u64) + * use_base_fee } diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 6acea74a64..300f6658c5 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -245,7 +245,7 @@ fn test_fee_fields() -> Result<(), Error> { assert_eq!(tx.fee(), 147); assert_eq!(tx.shifted_fee(), 36); assert_eq!(tx.aggregate_fee_fields(), FeeFields::new(2, 147)); - assert_eq!(tx_fee(1, 1, 3, 500_000), 15_500_000); + assert_eq!(tx_fee(1, 1, 3, Some(500_000)), 15_500_000); Ok(()) } From f17c816ab78b18c4e6eb391cf867ec4382f8773d Mon Sep 17 00:00:00 2001 From: John Tromp Date: Mon, 2 Nov 2020 15:34:40 +0100 Subject: [PATCH 11/24] remove optional base fee argument; can be set with global::set_local_accept_fee_base instead --- core/src/libtx/mod.rs | 14 ++------------ core/tests/transaction.rs | 3 ++- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index ecfeebd5cb..56596504c2 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -35,17 +35,7 @@ pub use self::proof::ProofBuilder; pub use crate::libtx::error::{Error, ErrorKind}; /// Transaction fee calculation -pub fn tx_fee( - input_len: usize, - output_len: usize, - kernel_len: usize, - base_fee: Option, -) -> u64 { - let use_base_fee = match base_fee { - Some(bf) => bf, - None => get_accept_fee_base(), - }; - +pub fn tx_fee(input_len: usize, output_len: usize, kernel_len: usize) -> u64 { Transaction::weight_by_iok(input_len as u64, output_len as u64, kernel_len as u64) - * use_base_fee + * get_accept_fee_base() } diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 300f6658c5..3c32872644 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -210,6 +210,7 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { #[test] fn test_fee_fields() -> Result<(), Error> { global::set_local_chain_type(global::ChainTypes::UserTesting); + global::set_local_accept_fee_base(500_000); let keychain = ExtKeychain::from_random_seed(false)?; @@ -245,7 +246,7 @@ fn test_fee_fields() -> Result<(), Error> { assert_eq!(tx.fee(), 147); assert_eq!(tx.shifted_fee(), 36); assert_eq!(tx.aggregate_fee_fields(), FeeFields::new(2, 147)); - assert_eq!(tx_fee(1, 1, 3, Some(500_000)), 15_500_000); + assert_eq!(tx_fee(1, 1, 3), 15_500_000); Ok(()) } From d83fbb1c80329ff5824542f4238edba3e0e45f19 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Mon, 2 Nov 2020 18:36:26 +0100 Subject: [PATCH 12/24] define constant global::DEFAULT_ACCEPT_FEE_BASE and set global value --- core/src/global.rs | 14 ++++++++------ pool/src/types.rs | 3 ++- src/bin/grin.rs | 6 ++++-- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/src/global.rs b/core/src/global.rs index bd38794495..dd8c3c14cb 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -83,6 +83,9 @@ pub const TESTING_INITIAL_DIFFICULTY: u64 = 1; /// Testing max_block_weight (artifically low, just enough to support a few txs). pub const TESTING_MAX_BLOCK_WEIGHT: u64 = 250; +/// Default unit of fee per tx weight, making each output cost about a Grincent +pub const DEFAULT_ACCEPT_FEE_BASE: u64 = GRIN_BASE / 100 / 20; // 500_000 + /// If a peer's last updated difficulty is 2 hours ago and its difficulty's lower than ours, /// we're sure this peer is a stuck node, and we will kick out such kind of stuck peers. pub const STUCK_PEER_KICK_TIME: i64 = 2 * 3600 * 1000; @@ -144,10 +147,10 @@ lazy_static! { /// to be overridden on a per-thread basis (for testing). pub static ref GLOBAL_CHAIN_TYPE: OneTime = OneTime::new(); - /// Global acccept fee base that must be initialized once on node startup. - /// This is accessed via get_acccept_fee_base() which allows the global value - /// to be overridden on a per-thread basis (for testing). - pub static ref GLOBAL_ACCEPT_FEE_BASE: OneTime = OneTime::new(); + /// Global acccept fee base that must be initialized once on node startup. + /// This is accessed via get_acccept_fee_base() which allows the global value + /// to be overridden on a per-thread basis (for testing). + pub static ref GLOBAL_ACCEPT_FEE_BASE: OneTime = OneTime::new(); /// Global feature flag for NRD kernel support. /// If enabled NRD kernels are treated as valid after HF3 (based on header version). @@ -213,8 +216,7 @@ pub fn get_accept_fee_base() -> u64 { let base = if GLOBAL_ACCEPT_FEE_BASE.is_init() { GLOBAL_ACCEPT_FEE_BASE.borrow() } else { - // Global config unset, default to 1/20 of a Grincent - GRIN_BASE / 100 / 20 // 500_000 + DEFAULT_ACCEPT_FEE_BASE }; set_local_accept_fee_base(base); base diff --git a/pool/src/types.rs b/pool/src/types.rs index d550f0b440..692ef01b5b 100644 --- a/pool/src/types.rs +++ b/pool/src/types.rs @@ -21,6 +21,7 @@ use self::core::core::committed; use self::core::core::hash::Hash; use self::core::core::transaction::{self, Transaction}; use self::core::core::{BlockHeader, BlockSums, Inputs, OutputIdentifier}; +use self::core::global::DEFAULT_ACCEPT_FEE_BASE; use chrono::prelude::*; use failure::Fail; use grin_core as core; @@ -135,7 +136,7 @@ impl Default for PoolConfig { /// make output (of weight 21) cost about 1 Grin-cent by default, keeping a round number pub fn default_accept_fee_base() -> u64 { - consensus::GRIN_BASE / 100 / 20 // 500_000 nanogrin + DEFAULT_ACCEPT_FEE_BASE } fn default_max_pool_size() -> usize { 50_000 diff --git a/src/bin/grin.rs b/src/bin/grin.rs index f602361c4f..4a63db7b2c 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -146,9 +146,9 @@ fn real_main() -> i32 { log_build_info(); - // Initialize our global chain_type and feature flags (NRD kernel support currently). + // Initialize our global chain_type, feature flags (NRD kernel support currently), and accept_fee_base. // These are read via global and not read from config beyond this point. - global::init_global_chain_type(config.members.unwrap().server.chain_type); + global::init_global_chain_type(config.members.as_ref().unwrap().server.chain_type); info!("Chain: {:?}", global::get_chain_type()); match global::get_chain_type() { global::ChainTypes::Mainnet => { @@ -160,6 +160,8 @@ fn real_main() -> i32 { global::init_global_nrd_enabled(true); } } + global::init_global_accept_fee_base(config.members.unwrap().server.pool_config.accept_fee_base); + info!("Accept Fee Base: {:?}", global::get_accept_fee_base()); log_feature_flags(); // Execute subcommand From 9b373ef3d7e4bdc44e4f9948f6eb06a63f4041f9 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Wed, 4 Nov 2020 14:06:36 +0100 Subject: [PATCH 13/24] add Transaction::accept_fee() method and use --- core/src/core/transaction.rs | 5 +++++ core/src/libtx/mod.rs | 7 ++++++- pool/src/transaction_pool.rs | 5 ++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 3569f93655..ba0b9e252e 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -1439,6 +1439,11 @@ impl Transaction { self.body.weight() } + /// Transaction minimum acceptable fee + pub fn accept_fee(&self) -> u64 { + self.weight() * global::get_accept_fee_base() + } + /// Calculate transaction weight from transaction details pub fn weight_by_iok(num_inputs: u64, num_outputs: u64, num_kernels: u64) -> u64 { TransactionBody::weight_by_iok(num_inputs, num_outputs, num_kernels) diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index 56596504c2..5945ec2fad 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -34,8 +34,13 @@ use crate::global::get_accept_fee_base; pub use self::proof::ProofBuilder; pub use crate::libtx::error::{Error, ErrorKind}; -/// Transaction fee calculation +/// Transaction fee calculation given numbers of inputs, outputs, and kernels pub fn tx_fee(input_len: usize, output_len: usize, kernel_len: usize) -> u64 { Transaction::weight_by_iok(input_len as u64, output_len as u64, kernel_len as u64) * get_accept_fee_base() } + +/// Transaction fee calculation given transaction +pub fn accept_fee(tx: Transaction) -> u64 { + tx.weight() * get_accept_fee_base() +} diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs index 42661c3523..61926ee276 100644 --- a/pool/src/transaction_pool.rs +++ b/pool/src/transaction_pool.rs @@ -365,9 +365,8 @@ where // weight for a basic transaction (2 inputs, 2 outputs, 1 kernel) - // (2 * 1) + (2 * 21) + (1 * 3) = 47 // minfees = 47 * 500_000 = 23_500_000 - let minfees = (tx.weight() as u64) * global::get_accept_fee_base(); - if tx.shifted_fee() < minfees { - return Err(PoolError::LowFeeTransaction(minfees)); + if tx.shifted_fee() < tx.accept_fee() { + return Err(PoolError::LowFeeTransaction(tx.shifted_fee())); } Ok(()) } From 843a6b7bb6f60c8c3e79ea17c78e5f8e89113ebc Mon Sep 17 00:00:00 2001 From: Jasper van der Maarel Date: Fri, 6 Nov 2020 14:16:06 +0100 Subject: [PATCH 14/24] Manual (de)ser impl on FeeFields --- core/src/core/transaction.rs | 63 +++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index ba0b9e252e..f932c50f10 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -25,6 +25,8 @@ use crate::ser::{ use crate::{consensus, global}; use enum_primitive::FromPrimitive; use keychain::{self, BlindingFactor}; +use serde::de; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cmp::Ordering; use std::cmp::{max, min}; use std::convert::{TryFrom, TryInto}; @@ -38,7 +40,7 @@ use util::RwLock; use util::ToHex; /// Fee fields as in fix-fees RFC: { future_use: 20, fee_shift: 4, fee: 40 } -#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct FeeFields(u64); impl DefaultHashable for FeeFields {} @@ -62,6 +64,50 @@ impl Display for FeeFields { } } +impl Serialize for FeeFields { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{}", self.0)) + } +} + +impl<'de> Deserialize<'de> for FeeFields { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct FeeFieldsVisitor; + impl<'de> de::Visitor<'de> for FeeFieldsVisitor { + type Value = FeeFields; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an 64-bit integer") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let value = value + .parse() + .map_err(|_| E::custom(format!("invalid fee field")))?; + self.visit_u64(value) + } + + fn visit_u64(self, value: u64) -> Result + where + E: de::Error, + { + FeeFields::try_from(value).map_err(|_| E::custom(format!("invalid fee field"))) + } + } + + deserializer.deserialize_str(FeeFieldsVisitor) + } +} + /// Conversion from a u64 to a valid FeeFields. /// Valid height is between 1 and WEEK_HEIGHT inclusive. impl TryFrom for FeeFields { @@ -131,6 +177,21 @@ impl FeeFields { let fee_shift = (self.0 >> FeeFields::FEE_BITS) & FeeFields::FEE_SHIFT_MASK; (fee, fee_shift) } + + /// Turn a zero `FeeField` into a `None`, any other value into a `Some`. + /// We need this because a zero `FeeField` cannot be deserialized. + pub fn as_opt(&self) -> Option { + if self.is_zero() { + None + } else { + Some(*self) + } + } + + /// Check if the `FeeFields` is set to zero + pub fn is_zero(&self) -> bool { + self.0 == 0 + } } /// Relative height field on NRD kernel variant. From c4137cc852125a2cfe9b6e3869fc7c338c816e4a Mon Sep 17 00:00:00 2001 From: John Tromp Date: Fri, 6 Nov 2020 16:28:07 +0100 Subject: [PATCH 15/24] fix comment bug --- core/src/core/transaction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index f932c50f10..da985da191 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -108,8 +108,8 @@ impl<'de> Deserialize<'de> for FeeFields { } } -/// Conversion from a u64 to a valid FeeFields. -/// Valid height is between 1 and WEEK_HEIGHT inclusive. +/// Conversion from a valid fee to a FeeFields with 0 fee_shift +/// The valid fee range is 1..FEE_MASK impl TryFrom for FeeFields { type Error = Error; From 73995da0c4489dc4df8962a288828ac96b6a46bd Mon Sep 17 00:00:00 2001 From: Jasper van der Maarel Date: Wed, 11 Nov 2020 10:40:18 +0100 Subject: [PATCH 16/24] Serialize FeeFields as int in tx --- core/src/core/transaction.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index da985da191..073b2f23f7 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -69,7 +69,7 @@ impl Serialize for FeeFields { where S: Serializer, { - serializer.serialize_str(&format!("{}", self.0)) + serializer.collect_str(&self.0) } } @@ -104,7 +104,7 @@ impl<'de> Deserialize<'de> for FeeFields { } } - deserializer.deserialize_str(FeeFieldsVisitor) + deserializer.deserialize_any(FeeFieldsVisitor) } } @@ -194,6 +194,13 @@ impl FeeFields { } } +fn fee_fields_as_int(fee_fields: &FeeFields, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_u64(fee_fields.0) +} + /// Relative height field on NRD kernel variant. /// u16 representing a height between 1 and MAX (consensus::WEEK_HEIGHT). #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] @@ -264,6 +271,7 @@ pub enum KernelFeatures { /// Plain kernel (the default for Grin txs). Plain { /// Plain kernels have fees. + #[serde(serialize_with = "fee_fields_as_int")] fee_fields: FeeFields, }, /// A coinbase kernel. @@ -271,6 +279,7 @@ pub enum KernelFeatures { /// A kernel with an explicit lock height (and fee). HeightLocked { /// Height locked kernels have fees. + #[serde(serialize_with = "fee_fields_as_int")] fee_fields: FeeFields, /// Height locked kernels have lock heights. lock_height: u64, @@ -278,6 +287,7 @@ pub enum KernelFeatures { /// "No Recent Duplicate" (NRD) kernels enforcing relative lock height between instances. NoRecentDuplicate { /// These have fees. + #[serde(serialize_with = "fee_fields_as_int")] fee_fields: FeeFields, /// Relative lock height. relative_height: NRDRelativeHeight, From d40b9c8c0cf6329b1d1f75b975dc3b1f20f5336e Mon Sep 17 00:00:00 2001 From: John Tromp Date: Fri, 13 Nov 2020 15:51:01 +0100 Subject: [PATCH 17/24] allow feefields: u32:into() for tests --- chain/tests/mine_nrd_kernel.rs | 4 +-- chain/tests/mine_simple_chain.rs | 6 ++--- chain/tests/nrd_validation_rules.rs | 6 ++--- chain/tests/test_coinbase_maturity.rs | 4 +-- core/src/core/transaction.rs | 34 +++++++++++++++--------- core/src/libtx/aggsig.rs | 4 +-- core/src/libtx/build.rs | 6 ++--- core/tests/block.rs | 14 +++++----- core/tests/common.rs | 8 +++--- core/tests/core.rs | 16 +++++------ core/tests/transaction.rs | 2 +- pool/tests/nrd_kernel_relative_height.rs | 4 +-- pool/tests/nrd_kernels_disabled.rs | 2 +- pool/tests/nrd_kernels_enabled.rs | 2 +- 14 files changed, 60 insertions(+), 52 deletions(-) diff --git a/chain/tests/mine_nrd_kernel.rs b/chain/tests/mine_nrd_kernel.rs index 7b34ac75ba..3764ab0604 100644 --- a/chain/tests/mine_nrd_kernel.rs +++ b/chain/tests/mine_nrd_kernel.rs @@ -85,7 +85,7 @@ fn mine_block_with_nrd_kernel_and_nrd_feature_enabled() { let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier(); let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.try_into().unwrap(), + fee_fields: 20000.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[ @@ -132,7 +132,7 @@ fn mine_invalid_block_with_nrd_kernel_and_nrd_feature_enabled_before_hf() { let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier(); let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.try_into().unwrap(), + fee_fields: 20000.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[ diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 40845065aa..be59545621 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -571,7 +571,7 @@ fn spend_rewind_spend() { let tx1 = build::transaction( KernelFeatures::Plain { - fee_fields: 20000.try_into().unwrap(), + fee_fields: 20000.into(), }, &[ build::coinbase_input(consensus::REWARD, key_id_coinbase.clone()), @@ -646,7 +646,7 @@ fn spend_in_fork_and_compact() { let tx1 = build::transaction( KernelFeatures::Plain { - fee_fields: 20000.try_into().unwrap(), + fee_fields: 20000.into(), }, &[ build::coinbase_input(consensus::REWARD, key_id2.clone()), @@ -666,7 +666,7 @@ fn spend_in_fork_and_compact() { let tx2 = build::transaction( KernelFeatures::Plain { - fee_fields: 20000.try_into().unwrap(), + fee_fields: 20000.into(), }, &[ build::input(consensus::REWARD - 20000, key_id30.clone()), diff --git a/chain/tests/nrd_validation_rules.rs b/chain/tests/nrd_validation_rules.rs index 62de8dc00e..8547a79378 100644 --- a/chain/tests/nrd_validation_rules.rs +++ b/chain/tests/nrd_validation_rules.rs @@ -101,7 +101,7 @@ fn process_block_nrd_validation() -> Result<(), Error> { assert_eq!(chain.head()?.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.try_into().unwrap(), + fee_fields: 20000.into(), relative_height: NRDRelativeHeight::new(2)?, }); @@ -217,7 +217,7 @@ fn process_block_nrd_validation_relative_height_1() -> Result<(), Error> { assert_eq!(chain.head()?.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.try_into().unwrap(), + fee_fields: 20000.into(), relative_height: NRDRelativeHeight::new(1)?, }); @@ -316,7 +316,7 @@ fn process_block_nrd_validation_fork() -> Result<(), Error> { assert_eq!(header_8.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.try_into().unwrap(), + fee_fields: 20000.into(), relative_height: NRDRelativeHeight::new(2)?, }); diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index e48c861203..d6636d6a78 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -102,7 +102,7 @@ fn test_coinbase_maturity() { // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[ build::coinbase_input(amount, key_id1.clone()), @@ -186,7 +186,7 @@ fn test_coinbase_maturity() { // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[ build::coinbase_input(amount, key_id1.clone()), diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 073b2f23f7..7cd7ef13aa 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -124,6 +124,14 @@ impl TryFrom for FeeFields { } } +/// Conversion from a 32-bit fee to a FeeFields with 0 fee_shift +/// For use exclusively in tests with constant fees +impl From for FeeFields { + fn from(fee: u32) -> Self { + Self(fee as u64) + } +} + impl From for u64 { fn from(fee_fields: FeeFields) -> Self { fee_fields.0 as u64 @@ -2317,7 +2325,7 @@ mod test { let kernel = TxKernel { features: KernelFeatures::Plain { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), }, excess: commit, excess_sig: sig.clone(), @@ -2331,7 +2339,7 @@ mod test { assert_eq!( kernel2.features, KernelFeatures::Plain { - fee_fields: 10.try_into().unwrap() + fee_fields: 10.into() } ); assert_eq!(kernel2.excess, commit); @@ -2345,7 +2353,7 @@ mod test { assert_eq!( kernel2.features, KernelFeatures::Plain { - fee_fields: 10.try_into().unwrap() + fee_fields: 10.into() } ); assert_eq!(kernel2.excess, commit); @@ -2366,7 +2374,7 @@ mod test { // now check a kernel with lock_height serialize/deserialize correctly let kernel = TxKernel { features: KernelFeatures::HeightLocked { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), lock_height: 100, }, excess: commit, @@ -2408,7 +2416,7 @@ mod test { // now check an NRD kernel will serialize/deserialize correctly let kernel = TxKernel { features: KernelFeatures::NoRecentDuplicate { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), relative_height: NRDRelativeHeight(100), }, excess: commit, @@ -2440,7 +2448,7 @@ mod test { let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), relative_height: NRDRelativeHeight(100), }); @@ -2466,27 +2474,27 @@ mod test { // Modify the fee and check signature no longer verifies. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: 9.try_into().unwrap(), + fee_fields: 9.into(), relative_height: NRDRelativeHeight(100), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Modify the relative_height and check signature no longer verifies. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), relative_height: NRDRelativeHeight(101), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Swap the features out for something different and check signature no longer verifies. kernel.features = KernelFeatures::Plain { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Check signature verifies if we use the original features. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), relative_height: NRDRelativeHeight(100), }; assert_eq!(kernel.verify(), Ok(())); @@ -2550,7 +2558,7 @@ mod test { assert_eq!( features, KernelFeatures::Plain { - fee_fields: 10.try_into().unwrap() + fee_fields: 10.into() } ); @@ -2565,7 +2573,7 @@ mod test { assert_eq!( features, KernelFeatures::HeightLocked { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), lock_height: 100 } ); @@ -2595,7 +2603,7 @@ mod test { assert_eq!( features, KernelFeatures::NoRecentDuplicate { - fee_fields: 10.try_into().unwrap(), + fee_fields: 10.into(), relative_height: NRDRelativeHeight(100) } ); diff --git a/core/src/libtx/aggsig.rs b/core/src/libtx/aggsig.rs index 147a48a04f..0a26125486 100644 --- a/core/src/libtx/aggsig.rs +++ b/core/src/libtx/aggsig.rs @@ -240,7 +240,7 @@ pub fn verify_partial_sig( /// let height = 20; /// let over_commit = secp.commit_value(reward(fees)).unwrap(); /// let out_commit = output.commitment(); -/// let features = KernelFeatures::HeightLocked{fee_fields: 1.try_into().unwrap(), lock_height: height}; +/// let features = KernelFeatures::HeightLocked{fee_fields: 1.into(), lock_height: height}; /// let msg = features.kernel_sig_msg().unwrap(); /// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap(); /// let pubkey = excess.to_pubkey(&secp).unwrap(); @@ -304,7 +304,7 @@ where /// let height = 20; /// let over_commit = secp.commit_value(reward(fees)).unwrap(); /// let out_commit = output.commitment(); -/// let features = KernelFeatures::HeightLocked{fee_fields: 1.try_into().unwrap(), lock_height: height}; +/// let features = KernelFeatures::HeightLocked{fee_fields: 1.into(), lock_height: height}; /// let msg = features.kernel_sig_msg().unwrap(); /// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap(); /// let pubkey = excess.to_pubkey(&secp).unwrap(); diff --git a/core/src/libtx/build.rs b/core/src/libtx/build.rs index 95d63bba1a..de717734e8 100644 --- a/core/src/libtx/build.rs +++ b/core/src/libtx/build.rs @@ -281,7 +281,7 @@ mod test { let tx = transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[input(10, key_id1), input(12, key_id2), output(20, key_id3)], &keychain, @@ -305,7 +305,7 @@ mod test { let tx = transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[input(10, key_id1), input(12, key_id2), output(20, key_id3)], &keychain, @@ -328,7 +328,7 @@ mod test { let tx = transaction( KernelFeatures::Plain { - fee_fields: 4.try_into().unwrap(), + fee_fields: 4.into(), }, &[input(6, key_id1), output(2, key_id2)], &keychain, diff --git a/core/tests/block.rs b/core/tests/block.rs index d15dddf98d..ed5d9cc825 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -64,7 +64,7 @@ fn too_large_block() { parts.append(&mut vec![input(500000, pks.pop().unwrap())]); let tx = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &parts, &keychain, @@ -107,7 +107,7 @@ fn block_with_nrd_kernel_pre_post_hf3() { let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[input(7, key_id1), output(5, key_id2)], @@ -192,7 +192,7 @@ fn block_with_nrd_kernel_nrd_not_enabled() { let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[input(7, key_id1), output(5, key_id2)], @@ -281,7 +281,7 @@ fn block_with_cut_through() { let btx1 = tx2i1o(); let btx2 = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[input(7, key_id1), output(5, key_id2.clone())], &keychain, @@ -761,7 +761,7 @@ fn same_amount_outputs_copy_range_proof() { let tx = build::transaction( KernelFeatures::Plain { - fee_fields: 1.try_into().unwrap(), + fee_fields: 1.into(), }, &[input(7, key_id1), output(3, key_id2), output(3, key_id3)], &keychain, @@ -804,7 +804,7 @@ fn wrong_amount_range_proof() { let tx1 = build::transaction( KernelFeatures::Plain { - fee_fields: 1.try_into().unwrap(), + fee_fields: 1.into(), }, &[ input(7, key_id1.clone()), @@ -817,7 +817,7 @@ fn wrong_amount_range_proof() { .unwrap(); let tx2 = build::transaction( KernelFeatures::Plain { - fee_fields: 1.try_into().unwrap(), + fee_fields: 1.into(), }, &[input(7, key_id1), output(2, key_id2), output(4, key_id3)], &keychain, diff --git a/core/tests/common.rs b/core/tests/common.rs index e6b36fdcb8..6cdab06c1c 100644 --- a/core/tests/common.rs +++ b/core/tests/common.rs @@ -39,7 +39,7 @@ pub fn tx2i1o() -> Transaction { let tx = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[input(10, key_id1), input(11, key_id2), output(19, key_id3)], &keychain, @@ -60,7 +60,7 @@ pub fn tx1i1o() -> Transaction { let tx = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[input(5, key_id1), output(3, key_id2)], &keychain, @@ -102,7 +102,7 @@ pub fn tx1i2o() -> Transaction { let tx = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[input(6, key_id1), output(3, key_id2), output(1, key_id3)], &keychain, @@ -148,7 +148,7 @@ where { build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[input(v, key_id1), output(3, key_id2)], keychain, diff --git a/core/tests/core.rs b/core/tests/core.rs index ab1fe113dc..40d31308d4 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -157,7 +157,7 @@ fn build_tx_kernel() { // first build a valid tx with corresponding blinding factor let tx = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[input(10, key_id1), output(5, key_id2), output(3, key_id3)], &keychain, @@ -177,7 +177,7 @@ fn build_tx_kernel() { assert_eq!( kern.features, KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap() + fee_fields: 2.into() } ); assert_eq!(2, tx.fee()); @@ -203,7 +203,7 @@ fn build_two_half_kernels() { // build kernel with associated private excess let mut kernel = TxKernel::with_features(KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }); // Construct the message to be signed. @@ -493,7 +493,7 @@ fn hash_output() { let tx = build::transaction( KernelFeatures::Plain { - fee_fields: 1.try_into().unwrap(), + fee_fields: 1.into(), }, &[input(75, key_id1), output(42, key_id2), output(32, key_id3)], &keychain, @@ -558,7 +558,7 @@ fn tx_build_exchange() { // Alice builds her transaction, with change, which also produces the sum // of blinding factors before they're obscured. let tx = Transaction::empty().with_kernel(TxKernel::with_features(KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), })); let (tx, sum) = build::partial_transaction(tx, &[in1, in2, output(1, key_id3)], &keychain, &builder) @@ -572,7 +572,7 @@ fn tx_build_exchange() { // ready for broadcast. let tx_final = build::transaction( KernelFeatures::Plain { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), }, &[ initial_tx(tx_alice), @@ -655,7 +655,7 @@ fn test_block_with_timelocked_tx() { // block height and that the resulting block is valid let tx1 = build::transaction( KernelFeatures::HeightLocked { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), lock_height: 1, }, &[input(5, key_id1.clone()), output(3, key_id2.clone())], @@ -679,7 +679,7 @@ fn test_block_with_timelocked_tx() { // block height let tx1 = build::transaction( KernelFeatures::HeightLocked { - fee_fields: 2.try_into().unwrap(), + fee_fields: 2.into(), lock_height: 2, }, &[input(5, key_id1), output(3, key_id2)], diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 3c32872644..095dc91573 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -239,7 +239,7 @@ fn test_fee_fields() -> Result<(), Error> { fee_fields: FeeFields::new(2, 84).unwrap(), }), TxKernel::with_features(KernelFeatures::Plain { - fee_fields: 21.try_into().unwrap(), + fee_fields: 21.into(), }), ]); diff --git a/pool/tests/nrd_kernel_relative_height.rs b/pool/tests/nrd_kernel_relative_height.rs index d9ef65b431..557626b642 100644 --- a/pool/tests/nrd_kernel_relative_height.rs +++ b/pool/tests/nrd_kernel_relative_height.rs @@ -76,7 +76,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let (tx1, tx2, tx3) = { let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 600.try_into().unwrap(), + fee_fields: 600.into(), relative_height: NRDRelativeHeight::new(2)?, }); let msg = kernel.msg_to_sign().unwrap(); @@ -114,7 +114,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { // Now reuse kernel excess for tx3 but with NRD relative_height=1 (and different fee). let mut kernel_short = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 300.try_into().unwrap(), + fee_fields: 300.into(), relative_height: NRDRelativeHeight::new(1)?, }); let msg_short = kernel_short.msg_to_sign().unwrap(); diff --git a/pool/tests/nrd_kernels_disabled.rs b/pool/tests/nrd_kernels_disabled.rs index 9445c362c0..43b068e877 100644 --- a/pool/tests/nrd_kernels_disabled.rs +++ b/pool/tests/nrd_kernels_disabled.rs @@ -66,7 +66,7 @@ fn test_nrd_kernels_disabled() { vec![1_000, 2_000], vec![2_400], KernelFeatures::NoRecentDuplicate { - fee_fields: 600.try_into().unwrap(), + fee_fields: 600.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); diff --git a/pool/tests/nrd_kernels_enabled.rs b/pool/tests/nrd_kernels_enabled.rs index 03cc45400f..a333dbc7d6 100644 --- a/pool/tests/nrd_kernels_enabled.rs +++ b/pool/tests/nrd_kernels_enabled.rs @@ -67,7 +67,7 @@ fn test_nrd_kernels_enabled() { vec![1_000, 2_000], vec![2_400], KernelFeatures::NoRecentDuplicate { - fee_fields: 600.try_into().unwrap(), + fee_fields: 600.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); From 0fe1198336052f59b177aee5d1408ff2afd7235a Mon Sep 17 00:00:00 2001 From: John Tromp Date: Sun, 22 Nov 2020 16:55:22 +0100 Subject: [PATCH 18/24] try adding height args everywhere --- core/src/core/block.rs | 2 +- core/src/core/transaction.rs | 64 +++++++++++++++++++++--------------- core/src/global.rs | 4 +-- pool/src/pool.rs | 7 ++-- 4 files changed, 45 insertions(+), 32 deletions(-) diff --git a/core/src/core/block.rs b/core/src/core/block.rs index d17e17ce45..51e89c5064 100644 --- a/core/src/core/block.rs +++ b/core/src/core/block.rs @@ -697,7 +697,7 @@ impl Block { /// Sum of all fees (inputs less outputs) in the block pub fn total_fees(&self) -> u64 { - self.body.fee() + self.body.fee(self.header.height) } /// "Lightweight" validation that we can perform quickly during read/deserialization. diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 7cd7ef13aa..326d2c9d7b 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -14,6 +14,7 @@ //! Transactions +use crate::core::block::HeaderVersion; use crate::core::hash::{DefaultHashable, Hashed}; use crate::core::verifier_cache::VerifierCache; use crate::core::{committed, Committed}; @@ -169,13 +170,21 @@ impl FeeFields { } /// Extract fee_shift field - pub fn fee_shift(&self) -> u8 { - ((self.0 >> FeeFields::FEE_BITS) & FeeFields::FEE_SHIFT_MASK) as u8 + pub fn fee_shift(&self, height: u64) -> u8 { + if consensus::header_version(height) < HeaderVersion(5) { + 0 + } else { + ((self.0 >> FeeFields::FEE_BITS) & FeeFields::FEE_SHIFT_MASK) as u8 + } } /// Extract fee field - pub fn fee(&self) -> u64 { - self.0 & FeeFields::FEE_MASK + pub fn fee(&self, height: u64) -> u64 { + if consensus::header_version(height) < HeaderVersion(5) { + self.0 + } else { + self.0 & FeeFields::FEE_MASK + } } /// Extract bitfields fee_shift and fee into tuple @@ -1055,7 +1064,7 @@ impl TransactionBody { } /// Total fee for a TransactionBody is the sum of fees of all fee carrying kernels. - pub fn fee(&self) -> u64 { + pub fn fee(&self, height: u64) -> u64 { self.kernels .iter() .filter_map(|k| match k.features { @@ -1064,11 +1073,13 @@ impl TransactionBody { KernelFeatures::HeightLocked { fee_fields, .. } => Some(fee_fields), KernelFeatures::NoRecentDuplicate { fee_fields, .. } => Some(fee_fields), }) - .fold(0, |acc, fee_fields| acc.saturating_add(fee_fields.fee())) + .fold(0, |acc, fee_fields| { + acc.saturating_add(fee_fields.fee(height)) + }) } /// fee_shift for a TransactionBody is the maximum of fee_shifts of all fee carrying kernels. - pub fn fee_shift(&self) -> u8 { + pub fn fee_shift(&self, height: u64) -> u8 { self.kernels .iter() .filter_map(|k| match k.features { @@ -1077,24 +1088,24 @@ impl TransactionBody { KernelFeatures::HeightLocked { fee_fields, .. } => Some(fee_fields), KernelFeatures::NoRecentDuplicate { fee_fields, .. } => Some(fee_fields), }) - .fold(0, |acc, fee_fields| max(acc, fee_fields.fee_shift())) + .fold(0, |acc, fee_fields| max(acc, fee_fields.fee_shift(height))) } /// Shifted fee for a TransactionBody is the sum of fees shifted right by the maximum fee_shift /// this is used to determine whether a tx can be relayed or accepted in a mempool /// where transactions can specify a higher block-inclusion priority as a positive shift up to 15 - /// but are required to overpaythe minimum required fees by a factor of 2^priority - pub fn shifted_fee(&self) -> u64 { - self.fee() >> self.fee_shift() + /// but are required to overpay the minimum required fees by a factor of 2^priority + pub fn shifted_fee(&self, height: u64) -> u64 { + self.fee(height) >> self.fee_shift(height) } /// aggregate fee_fields from all appropriate kernels in TransactionBody into one, if possible - pub fn aggregate_fee_fields(&self) -> Result { - FeeFields::new(self.fee_shift() as u64, self.fee()) + pub fn aggregate_fee_fields(&self, height: u64) -> Result { + FeeFields::new(self.fee_shift(height) as u64, self.fee(height)) } - fn overage(&self) -> i64 { - self.fee() as i64 + fn overage(&self, height: u64) -> i64 { + self.fee(height) as i64 } /// Calculate weight of transaction using block weighing @@ -1458,23 +1469,23 @@ impl Transaction { } /// Total fee for a transaction is the sum of fees of all kernels. - pub fn fee(&self) -> u64 { - self.body.fee() + pub fn fee(&self, height: u64) -> u64 { + self.body.fee(height) } /// Shifted fee for a transaction is the sum of fees of all kernels shifted right by the maximum fee shift - pub fn shifted_fee(&self) -> u64 { - self.body.shifted_fee() + pub fn shifted_fee(&self, height: u64) -> u64 { + self.body.shifted_fee(height) } /// aggregate fee_fields from all appropriate kernels in transaction into one - pub fn aggregate_fee_fields(&self) -> Result { - self.body.aggregate_fee_fields() + pub fn aggregate_fee_fields(&self, height: u64) -> Result { + self.body.aggregate_fee_fields(height) } /// Total overage across all kernels. - pub fn overage(&self) -> i64 { - self.body.overage() + pub fn overage(&self, height: u64) -> i64 { + self.body.overage(height) } /// Lock height of a transaction is the max lock height of the kernels. @@ -1500,17 +1511,18 @@ impl Transaction { &self, weighting: Weighting, verifier: Arc>, + height: u64, ) -> Result<(), Error> { self.body.verify_features()?; self.body.validate(weighting, verifier)?; - self.verify_kernel_sums(self.overage(), self.offset.clone())?; + self.verify_kernel_sums(self.overage(height), self.offset.clone())?; Ok(()) } /// Can be used to compare txs by their fee/weight ratio, aka feerate. /// Don't use these values for anything else though due to precision multiplier. - pub fn fee_rate(&self) -> u64 { - self.fee() / self.weight() as u64 + pub fn fee_rate(&self, height: u64) -> u64 { + self.fee(height) / self.weight() as u64 } /// Calculate transaction weight diff --git a/core/src/global.rs b/core/src/global.rs index dd8c3c14cb..41eb6790df 100644 --- a/core/src/global.rs +++ b/core/src/global.rs @@ -162,8 +162,8 @@ thread_local! { /// Mainnet|Testnet|UserTesting|AutomatedTesting pub static CHAIN_TYPE: Cell> = Cell::new(None); - /// minimum transaction fee per unit of transaction weight for mempool acceptance - pub static ACCEPT_FEE_BASE: Cell> = Cell::new(None); + /// minimum transaction fee per unit of transaction weight for mempool acceptance + pub static ACCEPT_FEE_BASE: Cell> = Cell::new(None); /// Local feature flag for NRD kernel support. pub static NRD_FEATURE_ENABLED: Cell> = Cell::new(None); diff --git a/pool/src/pool.rs b/pool/src/pool.rs index a3ae3c51ad..fe82f3939a 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -150,6 +150,7 @@ where pub fn all_transactions_aggregate( &self, extra_tx: Option, + height: u64, ) -> Result, PoolError> { let mut txs = self.all_transactions(); if txs.is_empty() { @@ -161,7 +162,7 @@ where let tx = transaction::aggregate(&txs)?; // Validate the single aggregate transaction "as pool", not subject to tx weight limits. - tx.validate(Weighting::NoLimit, self.verifier_cache.clone())?; + tx.validate(Weighting::NoLimit, self.verifier_cache.clone(), height)?; Ok(Some(tx)) } @@ -229,7 +230,7 @@ where ) -> Result { // Validate the tx, conditionally checking against weight limits, // based on weight verification type. - tx.validate(weighting, self.verifier_cache.clone())?; + tx.validate(weighting, self.verifier_cache.clone(), header.height)?; // Validate the tx against current chain state. // Check all inputs are in the current UTXO set. @@ -304,7 +305,7 @@ where tx: &Transaction, header: &BlockHeader, ) -> Result { - let overage = tx.overage(); + let overage = tx.overage(header.height); let offset = { let secp = static_secp_instance(); From bc5799afa11b0258d8f6c8ba1d0dd4d570b5fc0f Mon Sep 17 00:00:00 2001 From: John Tromp Date: Mon, 23 Nov 2020 17:33:09 +0100 Subject: [PATCH 19/24] make FeeFields shift/fee methods height dependent --- api/src/types.rs | 6 +- chain/tests/mine_nrd_kernel.rs | 3 +- chain/tests/mine_simple_chain.rs | 4 +- chain/tests/nrd_validation_rules.rs | 3 +- chain/tests/process_block_cut_through.rs | 5 +- chain/tests/test_coinbase_maturity.rs | 7 +- core/src/core/transaction.rs | 1 - core/src/libtx/build.rs | 13 +- core/tests/block.rs | 1 - core/tests/common.rs | 6 +- core/tests/core.rs | 187 ++++++++++++++++------- core/tests/transaction.rs | 22 +-- pool/src/pool.rs | 29 ++-- pool/src/transaction_pool.rs | 20 ++- pool/tests/block_max_weight.rs | 12 +- pool/tests/common.rs | 2 +- pool/tests/nrd_kernel_relative_height.rs | 1 - pool/tests/nrd_kernels_disabled.rs | 1 - pool/tests/nrd_kernels_enabled.rs | 1 - pool/tests/transaction_pool.rs | 3 +- servers/src/grin/dandelion_monitor.rs | 1 + servers/src/mining/mine_block.rs | 2 +- store/tests/lmdb.rs | 1 - 23 files changed, 219 insertions(+), 112 deletions(-) diff --git a/api/src/types.rs b/api/src/types.rs index 7d7784e2cb..b7d052c09d 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -13,6 +13,7 @@ // limitations under the License. use crate::chain; +use crate::core::consensus::YEAR_HEIGHT; use crate::core::core::hash::Hashed; use crate::core::core::merkle_proof::MerkleProof; use crate::core::core::{FeeFields, KernelFeatures, TxKernel}; @@ -521,8 +522,9 @@ impl TxKernelPrintable { relative_height, } => (fee_fields, relative_height.into()), }; - let fee = fee_fields.fee(); - let fee_shift: u8 = fee_fields.fee_shift(); + let height = 2 * YEAR_HEIGHT; + let fee = fee_fields.fee(height); + let fee_shift: u8 = fee_fields.fee_shift(height); TxKernelPrintable { features, fee_shift, diff --git a/chain/tests/mine_nrd_kernel.rs b/chain/tests/mine_nrd_kernel.rs index 3764ab0604..70061829a4 100644 --- a/chain/tests/mine_nrd_kernel.rs +++ b/chain/tests/mine_nrd_kernel.rs @@ -26,7 +26,6 @@ use crate::core::libtx::{build, reward, ProofBuilder}; use crate::core::{consensus, global, pow}; use crate::keychain::{ExtKeychain, ExtKeychainPath, Identifier, Keychain}; use chrono::Duration; -use std::convert::TryInto; fn build_block(chain: &Chain, keychain: &K, key_id: &Identifier, txs: Vec) -> Block where @@ -34,7 +33,7 @@ where { let prev = chain.head_header().unwrap(); let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap()); - let fee = txs.iter().map(|x| x.fee()).sum(); + let fee = txs.iter().map(|x| x.fee(prev.height + 1)).sum(); let reward = reward::output(keychain, &ProofBuilder::new(keychain), key_id, fee, false).unwrap(); diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index be59545621..259b6df6bb 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -29,7 +29,6 @@ use grin_chain::{BlockStatus, ChainAdapter, Options}; use grin_core as core; use grin_keychain as keychain; use grin_util as util; -use std::convert::TryInto; use std::sync::Arc; mod chain_test_helper; @@ -889,7 +888,8 @@ where let proof_size = global::proofsize(); let key_id = ExtKeychainPath::new(1, key_idx, 0, 0, 0).to_identifier(); - let fees = txs.iter().map(|tx| tx.fee()).sum(); + let height = prev.height + 1; + let fees = txs.iter().map(|tx| tx.fee(height)).sum(); let reward = libtx::reward::output(kc, &libtx::ProofBuilder::new(kc), &key_id, fees, false).unwrap(); let mut b = match core::core::Block::new(prev, txs, Difficulty::from_num(diff), reward) { diff --git a/chain/tests/nrd_validation_rules.rs b/chain/tests/nrd_validation_rules.rs index 8547a79378..d0e3006d3c 100644 --- a/chain/tests/nrd_validation_rules.rs +++ b/chain/tests/nrd_validation_rules.rs @@ -28,7 +28,6 @@ use crate::core::libtx::{aggsig, build, reward, ProofBuilder}; use crate::core::{consensus, global, pow}; use crate::keychain::{BlindingFactor, ExtKeychain, ExtKeychainPath, Identifier, Keychain}; use chrono::Duration; -use std::convert::TryInto; fn build_block( chain: &Chain, @@ -55,7 +54,7 @@ where { let next_header_info = consensus::next_difficulty(prev.height, chain.difficulty_iter().unwrap()); - let fee = txs.iter().map(|x| x.fee()).sum(); + let fee = txs.iter().map(|x| x.fee(prev.height + 1)).sum(); let reward = reward::output(keychain, &ProofBuilder::new(keychain), key_id, fee, false).unwrap(); diff --git a/chain/tests/process_block_cut_through.rs b/chain/tests/process_block_cut_through.rs index 8b9c16f7d8..0164fd4df3 100644 --- a/chain/tests/process_block_cut_through.rs +++ b/chain/tests/process_block_cut_through.rs @@ -43,7 +43,7 @@ where let prev = chain.head_header().unwrap(); let next_height = prev.height + 1; let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter()?); - let fee = txs.iter().map(|x| x.fee()).sum(); + let fee = txs.iter().map(|x| x.fee(next_height)).sum(); let key_id = ExtKeychainPath::new(1, next_height as u32, 0, 0, 0).to_identifier(); let reward = reward::output(keychain, &ProofBuilder::new(keychain), &key_id, fee, false).unwrap(); @@ -131,8 +131,9 @@ fn process_block_cut_through() -> Result<(), chain::Error> { let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); // Transaction is invalid due to cut-through. + let height = 7; assert_eq!( - tx.validate(Weighting::AsTransaction, verifier_cache.clone()), + tx.validate(Weighting::AsTransaction, verifier_cache.clone(), height), Err(transaction::Error::CutThrough), ); diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index d6636d6a78..dafc6773c3 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -28,7 +28,6 @@ use grin_chain as chain; use grin_core as core; use grin_keychain as keychain; use grin_util as util; -use std::convert::TryInto; use std::fs; use std::sync::Arc; @@ -114,7 +113,7 @@ fn test_coinbase_maturity() { .unwrap(); let txs = &[coinbase_txn.clone()]; - let fees = txs.iter().map(|tx| tx.fee()).sum(); + let fees = txs.iter().map(|tx| tx.fee(prev.height + 1)).sum(); let reward = libtx::reward::output(&keychain, &builder, &key_id3, fees, false).unwrap(); let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap(); let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap()); @@ -198,7 +197,7 @@ fn test_coinbase_maturity() { .unwrap(); let txs = &[coinbase_txn.clone()]; - let fees = txs.iter().map(|tx| tx.fee()).sum(); + let fees = txs.iter().map(|tx| tx.fee(prev.height + 1)).sum(); let reward = libtx::reward::output(&keychain, &builder, &key_id3, fees, false).unwrap(); let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap(); let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap()); @@ -264,7 +263,7 @@ fn test_coinbase_maturity() { .unwrap(); let txs = &[coinbase_txn]; - let fees = txs.iter().map(|tx| tx.fee()).sum(); + let fees = txs.iter().map(|tx| tx.fee(prev.height + 1)).sum(); let next_header_info = consensus::next_difficulty(1, chain.difficulty_iter().unwrap()); let reward = libtx::reward::output(&keychain, &builder, &key_id4, fees, false).unwrap(); let mut block = core::core::Block::new(&prev, txs, Difficulty::min(), reward).unwrap(); diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 326d2c9d7b..de7b3e07d4 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -2322,7 +2322,6 @@ mod test { use crate::core::hash::Hash; use crate::core::id::{ShortId, ShortIdentifiable}; use keychain::{ExtKeychain, Keychain, SwitchCommitmentType}; - use std::convert::TryInto; #[test] fn test_plain_kernel_ser_deser() { diff --git a/core/src/libtx/build.rs b/core/src/libtx/build.rs index de717734e8..a6e78fd5b4 100644 --- a/core/src/libtx/build.rs +++ b/core/src/libtx/build.rs @@ -262,7 +262,6 @@ mod test { use crate::global; use crate::libtx::ProofBuilder; use keychain::{ExtKeychain, ExtKeychainPath}; - use std::convert::TryInto; fn verifier_cache() -> Arc> { Arc::new(RwLock::new(LruVerifierCache::new())) @@ -289,7 +288,9 @@ mod test { ) .unwrap(); - tx.validate(Weighting::AsTransaction, vc.clone()).unwrap(); + let height = 42; // arbitrary + tx.validate(Weighting::AsTransaction, vc.clone(), height) + .unwrap(); } #[test] @@ -313,7 +314,9 @@ mod test { ) .unwrap(); - tx.validate(Weighting::AsTransaction, vc.clone()).unwrap(); + let height = 42; // arbitrary + tx.validate(Weighting::AsTransaction, vc.clone(), height) + .unwrap(); } #[test] @@ -336,6 +339,8 @@ mod test { ) .unwrap(); - tx.validate(Weighting::AsTransaction, vc.clone()).unwrap(); + let height = 42; // arbitrary + tx.validate(Weighting::AsTransaction, vc.clone(), height) + .unwrap(); } } diff --git a/core/tests/block.rs b/core/tests/block.rs index ed5d9cc825..3755ca7648 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -30,7 +30,6 @@ use crate::core::{global, pow, ser}; use chrono::Duration; use grin_core as core; use keychain::{BlindingFactor, ExtKeychain, Keychain}; -use std::convert::TryInto; use std::sync::Arc; use util::{secp, RwLock, ToHex}; diff --git a/core/tests/common.rs b/core/tests/common.rs index 6cdab06c1c..f67f5bbfd3 100644 --- a/core/tests/common.rs +++ b/core/tests/common.rs @@ -26,7 +26,6 @@ use grin_core::libtx::{ use grin_core::pow::Difficulty; use grin_core::ser::{self, PMMRable, Readable, Reader, Writeable, Writer}; use keychain::{Identifier, Keychain}; -use std::convert::TryInto; // utility producing a transaction with 2 inputs and a single outputs #[allow(dead_code)] @@ -127,7 +126,10 @@ where K: Keychain, B: ProofBuild, { - let fees = txs.iter().map(|tx| tx.fee()).sum(); + let fees = txs + .iter() + .map(|tx| tx.fee(previous_header.height + 1)) + .sum(); let reward_output = reward::output(keychain, builder, &key_id, fees, false).unwrap(); Block::new(&previous_header, txs, Difficulty::min(), reward_output).unwrap() } diff --git a/core/tests/core.rs b/core/tests/core.rs index 40d31308d4..a8adee682d 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -30,7 +30,6 @@ use self::core::{global, ser}; use crate::common::{new_block, tx1i1o, tx1i2o, tx2i1o}; use grin_core as core; use keychain::{BlindingFactor, ExtKeychain, Keychain}; -use std::convert::TryInto; use std::sync::Arc; use util::static_secp_instance; use util::RwLock; @@ -98,7 +97,8 @@ fn simple_tx_ser_deser() { let mut vec = Vec::new(); ser::serialize_default(&mut vec, &tx).expect("serialization failed"); let dtx: Transaction = ser::deserialize_default(&mut &vec[..]).unwrap(); - assert_eq!(dtx.fee(), 2); + let height = 42; // arbitrary + assert_eq!(dtx.fee(height), 2); assert_eq!(dtx.inputs().len(), 2); assert_eq!(dtx.outputs().len(), 1); assert_eq!(tx.hash(), dtx.hash()); @@ -166,7 +166,8 @@ fn build_tx_kernel() { .unwrap(); // check the tx is valid - tx.validate(Weighting::AsTransaction, verifier_cache()) + let height = 42; // arbitrary + tx.validate(Weighting::AsTransaction, verifier_cache(), height) .unwrap(); // check the kernel is also itself valid @@ -180,7 +181,7 @@ fn build_tx_kernel() { fee_fields: 2.into() } ); - assert_eq!(2, tx.fee()); + assert_eq!(2, tx.fee(height)); } // Proof of concept demonstrating we can build two transactions that share @@ -236,13 +237,14 @@ fn build_two_half_kernels() { ) .unwrap(); + let height = 42; // arbitrary assert_eq!( - tx1.validate(Weighting::AsTransaction, verifier_cache()), + tx1.validate(Weighting::AsTransaction, verifier_cache(), height), Ok(()), ); assert_eq!( - tx2.validate(Weighting::AsTransaction, verifier_cache()), + tx2.validate(Weighting::AsTransaction, verifier_cache(), height), Ok(()), ); @@ -268,11 +270,12 @@ fn transaction_cut_through() { let tx1 = tx1i2o(); let tx2 = tx2i1o(); + let height = 42; // arbitrary assert!(tx1 - .validate(Weighting::AsTransaction, verifier_cache()) + .validate(Weighting::AsTransaction, verifier_cache(), height) .is_ok()); assert!(tx2 - .validate(Weighting::AsTransaction, verifier_cache()) + .validate(Weighting::AsTransaction, verifier_cache(), height) .is_ok()); let vc = verifier_cache(); @@ -280,7 +283,9 @@ fn transaction_cut_through() { // now build a "cut_through" tx from tx1 and tx2 let tx3 = aggregate(&[tx1, tx2]).unwrap(); - assert!(tx3.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + assert!(tx3 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); } // Attempt to deaggregate a multi-kernel transaction in a different way @@ -294,31 +299,44 @@ fn multi_kernel_transaction_deaggregation() { let vc = verifier_cache(); - assert!(tx1.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx2.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx3.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx4.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + let height = 42; // arbitrary + assert!(tx1 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx2 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx3 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx4 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); let tx1234 = aggregate(&[tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()]).unwrap(); let tx12 = aggregate(&[tx1, tx2]).unwrap(); let tx34 = aggregate(&[tx3, tx4]).unwrap(); assert!(tx1234 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx12 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx34 + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); - assert!(tx12.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx34.validate(Weighting::AsTransaction, vc.clone()).is_ok()); let deaggregated_tx34 = deaggregate(tx1234.clone(), &[tx12.clone()]).unwrap(); assert!(deaggregated_tx34 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); assert_eq!(tx34, deaggregated_tx34); let deaggregated_tx12 = deaggregate(tx1234, &[tx34]).unwrap(); assert!(deaggregated_tx12 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); assert_eq!(tx12, deaggregated_tx12); } @@ -332,19 +350,30 @@ fn multi_kernel_transaction_deaggregation_2() { let vc = verifier_cache(); - assert!(tx1.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx2.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx3.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + let height = 42; // arbitrary + assert!(tx1 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx2 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx3 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); let tx123 = aggregate(&[tx1.clone(), tx2.clone(), tx3.clone()]).unwrap(); let tx12 = aggregate(&[tx1, tx2]).unwrap(); - assert!(tx123.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx12.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + assert!(tx123 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx12 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); let deaggregated_tx3 = deaggregate(tx123, &[tx12]).unwrap(); assert!(deaggregated_tx3 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); assert_eq!(tx3, deaggregated_tx3); } @@ -358,20 +387,31 @@ fn multi_kernel_transaction_deaggregation_3() { let vc = verifier_cache(); - assert!(tx1.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx2.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx3.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + let height = 42; // arbitrary + assert!(tx1 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx2 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx3 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); let tx123 = aggregate(&[tx1.clone(), tx2.clone(), tx3.clone()]).unwrap(); let tx13 = aggregate(&[tx1, tx3]).unwrap(); let tx2 = aggregate(&[tx2]).unwrap(); - assert!(tx123.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx2.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + assert!(tx123 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx2 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); let deaggregated_tx13 = deaggregate(tx123, &[tx2]).unwrap(); assert!(deaggregated_tx13 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); assert_eq!(tx13, deaggregated_tx13); } @@ -387,11 +427,22 @@ fn multi_kernel_transaction_deaggregation_4() { let vc = verifier_cache(); - assert!(tx1.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx2.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx3.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx4.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx5.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + let height = 42; // arbitrary + assert!(tx1 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx2 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx3 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx4 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx5 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); let tx12345 = aggregate(&[ tx1.clone(), @@ -402,12 +453,12 @@ fn multi_kernel_transaction_deaggregation_4() { ]) .unwrap(); assert!(tx12345 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); let deaggregated_tx5 = deaggregate(tx12345, &[tx1, tx2, tx3, tx4]).unwrap(); assert!(deaggregated_tx5 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); assert_eq!(tx5, deaggregated_tx5); } @@ -423,11 +474,22 @@ fn multi_kernel_transaction_deaggregation_5() { let vc = verifier_cache(); - assert!(tx1.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx2.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx3.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx4.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx5.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + let height = 42; // arbitrary + assert!(tx1 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx2 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx3 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx4 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx5 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); let tx12345 = aggregate(&[ tx1.clone(), @@ -441,12 +503,12 @@ fn multi_kernel_transaction_deaggregation_5() { let tx34 = aggregate(&[tx3, tx4]).unwrap(); assert!(tx12345 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); let deaggregated_tx5 = deaggregate(tx12345, &[tx12, tx34]).unwrap(); assert!(deaggregated_tx5 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); assert_eq!(tx5, deaggregated_tx5); } @@ -460,25 +522,32 @@ fn basic_transaction_deaggregation() { let vc = verifier_cache(); - assert!(tx1.validate(Weighting::AsTransaction, vc.clone()).is_ok()); - assert!(tx2.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + let height = 42; // arbitrary + assert!(tx1 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); + assert!(tx2 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); // now build a "cut_through" tx from tx1 and tx2 let tx3 = aggregate(&[tx1.clone(), tx2.clone()]).unwrap(); - assert!(tx3.validate(Weighting::AsTransaction, vc.clone()).is_ok()); + assert!(tx3 + .validate(Weighting::AsTransaction, vc.clone(), height) + .is_ok()); let deaggregated_tx1 = deaggregate(tx3.clone(), &[tx2.clone()]).unwrap(); assert!(deaggregated_tx1 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); assert_eq!(tx1, deaggregated_tx1); let deaggregated_tx2 = deaggregate(tx3, &[tx1]).unwrap(); assert!(deaggregated_tx2 - .validate(Weighting::AsTransaction, vc.clone()) + .validate(Weighting::AsTransaction, vc.clone(), height) .is_ok()); assert_eq!(tx2, deaggregated_tx2); } @@ -510,8 +579,9 @@ fn hash_output() { #[test] fn blind_tx() { let btx = tx2i1o(); + let height = 42; // arbitrary assert!(btx - .validate(Weighting::AsTransaction, verifier_cache()) + .validate(Weighting::AsTransaction, verifier_cache(), height) .is_ok()); // Ignored for bullet proofs, because calling range_proof_info @@ -584,8 +654,9 @@ fn tx_build_exchange() { ) .unwrap(); + let height = 42; // arbitrary tx_final - .validate(Weighting::AsTransaction, verifier_cache()) + .validate(Weighting::AsTransaction, verifier_cache(), height) .unwrap(); } @@ -614,9 +685,13 @@ fn reward_with_tx_block() { let vc = verifier_cache(); let tx1 = tx2i1o(); - tx1.validate(Weighting::AsTransaction, vc.clone()).unwrap(); - let previous_header = BlockHeader::default(); + tx1.validate( + Weighting::AsTransaction, + vc.clone(), + previous_header.height + 1, + ) + .unwrap(); let block = new_block(&[tx1], &keychain, &builder, &previous_header, &key_id); block.validate(&BlindingFactor::zero(), vc.clone()).unwrap(); @@ -703,7 +778,8 @@ fn test_block_with_timelocked_tx() { pub fn test_verify_1i1o_sig() { test_setup(); let tx = tx1i1o(); - tx.validate(Weighting::AsTransaction, verifier_cache()) + let height = 42; // arbitrary + tx.validate(Weighting::AsTransaction, verifier_cache(), height) .unwrap(); } @@ -711,6 +787,7 @@ pub fn test_verify_1i1o_sig() { pub fn test_verify_2i1o_sig() { test_setup(); let tx = tx2i1o(); - tx.validate(Weighting::AsTransaction, verifier_cache()) + let height = 42; // arbitrary + tx.validate(Weighting::AsTransaction, verifier_cache(), height) .unwrap(); } diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 095dc91573..5c79c6484d 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -27,7 +27,6 @@ use crate::core::libtx::{build, tx_fee}; use crate::core::{consensus, ser}; use grin_core as core; use keychain::{ExtKeychain, Keychain}; -use std::convert::TryInto; use std::sync::Arc; use util::RwLock; @@ -118,8 +117,9 @@ fn test_verify_cut_through_plain() -> Result<(), Error> { let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); // Transaction should fail validation due to cut-through. + let height = 42; // arbitrary assert_eq!( - tx.validate(Weighting::AsTransaction, verifier_cache.clone()), + tx.validate(Weighting::AsTransaction, verifier_cache.clone(), height), Err(Error::CutThrough), ); @@ -137,7 +137,7 @@ fn test_verify_cut_through_plain() -> Result<(), Error> { .replace_outputs(outputs); // Transaction validates successfully after applying cut-through. - tx.validate(Weighting::AsTransaction, verifier_cache.clone())?; + tx.validate(Weighting::AsTransaction, verifier_cache.clone(), height)?; // Transaction validates via lightweight "read" validation as well. tx.validate_read()?; @@ -179,8 +179,9 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { let verifier_cache = Arc::new(RwLock::new(LruVerifierCache::new())); // Transaction should fail validation due to cut-through. + let height = 42; // arbitrary assert_eq!( - tx.validate(Weighting::AsTransaction, verifier_cache.clone()), + tx.validate(Weighting::AsTransaction, verifier_cache.clone(), height), Err(Error::CutThrough), ); @@ -198,7 +199,7 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { .replace_outputs(outputs); // Transaction validates successfully after applying cut-through. - tx.validate(Weighting::AsTransaction, verifier_cache.clone())?; + tx.validate(Weighting::AsTransaction, verifier_cache.clone(), height)?; // Transaction validates via lightweight "read" validation as well. tx.validate_read()?; @@ -231,8 +232,9 @@ fn test_fee_fields() -> Result<(), Error> { ) .expect("valid tx"); - assert_eq!(tx.fee(), 42); - assert_eq!(tx.shifted_fee(), 21); + let height = 2 * consensus::YEAR_HEIGHT; + assert_eq!(tx.fee(height), 42); + assert_eq!(tx.shifted_fee(height), 21); tx.body.kernels.append(&mut vec![ TxKernel::with_features(KernelFeatures::Plain { @@ -243,9 +245,9 @@ fn test_fee_fields() -> Result<(), Error> { }), ]); - assert_eq!(tx.fee(), 147); - assert_eq!(tx.shifted_fee(), 36); - assert_eq!(tx.aggregate_fee_fields(), FeeFields::new(2, 147)); + assert_eq!(tx.fee(height), 147); + assert_eq!(tx.shifted_fee(height), 36); + assert_eq!(tx.aggregate_fee_fields(height), FeeFields::new(2, 147)); assert_eq!(tx_fee(1, 1, 3), 15_500_000); Ok(()) diff --git a/pool/src/pool.rs b/pool/src/pool.rs index fe82f3939a..5127b09471 100644 --- a/pool/src/pool.rs +++ b/pool/src/pool.rs @@ -128,13 +128,13 @@ where // * maintain dependency ordering // * maximize cut-through // * maximize overall fees + let header = self.blockchain.chain_head()?; let txs = self.bucket_transactions(weighting); // Iteratively apply the txs to the current chain state, // rejecting any that do not result in a valid state. // Verify these txs produce an aggregated tx below max_weight. // Return a vec of all the valid txs. - let header = self.blockchain.chain_head()?; let valid_txs = self.validate_raw_txs(&txs, None, &header, weighting)?; Ok(valid_txs) } @@ -150,7 +150,6 @@ where pub fn all_transactions_aggregate( &self, extra_tx: Option, - height: u64, ) -> Result, PoolError> { let mut txs = self.all_transactions(); if txs.is_empty() { @@ -162,7 +161,12 @@ where let tx = transaction::aggregate(&txs)?; // Validate the single aggregate transaction "as pool", not subject to tx weight limits. - tx.validate(Weighting::NoLimit, self.verifier_cache.clone(), height)?; + let header = self.blockchain.chain_head()?; + tx.validate( + Weighting::NoLimit, + self.verifier_cache.clone(), + header.height, + )?; Ok(Some(tx)) } @@ -395,13 +399,14 @@ where continue; } + let height = self.blockchain.chain_head().map(|x| x.height).unwrap_or(0); match insert_pos { None => { // No parent tx, just add to the end in its own bucket. // This is the common case for non 0-conf txs in the txpool. // We assume the tx is valid here as we validated it on the way into the txpool. insert_pos = Some(tx_buckets.len()); - tx_buckets.push(Bucket::new(entry.tx.clone(), tx_buckets.len())); + tx_buckets.push(Bucket::new(entry.tx.clone(), tx_buckets.len(), height)); } Some(pos) => { // We found a single parent tx, so aggregate in the bucket @@ -413,6 +418,7 @@ where entry.tx.clone(), weighting, self.verifier_cache.clone(), + height, ) { if new_bucket.fee_rate >= bucket.fee_rate { // Only aggregate if it would not reduce the fee_rate ratio. @@ -421,7 +427,11 @@ where // Otherwise put it in its own bucket at the end. // Note: This bucket will have a lower fee_rate // than the bucket it depends on. - tx_buckets.push(Bucket::new(entry.tx.clone(), tx_buckets.len())); + tx_buckets.push(Bucket::new( + entry.tx.clone(), + tx_buckets.len(), + height, + )); } } else { // Aggregation failed so discard this new tx. @@ -514,9 +524,9 @@ impl Bucket { /// also specifies an "age_idx" so we can sort buckets by age /// as well as fee_rate. Txs are maintained in the pool in insert order /// so buckets with low age_idx contain oldest txs. - fn new(tx: Transaction, age_idx: usize) -> Bucket { + fn new(tx: Transaction, age_idx: usize, height: u64) -> Bucket { Bucket { - fee_rate: tx.fee_rate(), + fee_rate: tx.fee_rate(height), raw_txs: vec![tx], age_idx, } @@ -527,13 +537,14 @@ impl Bucket { new_tx: Transaction, weighting: Weighting, verifier_cache: Arc>, + height: u64, ) -> Result { let mut raw_txs = self.raw_txs.clone(); raw_txs.push(new_tx); let agg_tx = transaction::aggregate(&raw_txs)?; - agg_tx.validate(weighting, verifier_cache)?; + agg_tx.validate(weighting, verifier_cache, height)?; Ok(Bucket { - fee_rate: agg_tx.fee_rate(), + fee_rate: agg_tx.fee_rate(height), raw_txs: raw_txs, age_idx: self.age_idx, }) diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs index 61926ee276..040ecbddf6 100644 --- a/pool/src/transaction_pool.rs +++ b/pool/src/transaction_pool.rs @@ -193,8 +193,12 @@ where // Make sure the transaction is valid before anything else. // Validate tx accounting for max tx weight. - tx.validate(Weighting::AsTransaction, self.verifier_cache.clone()) - .map_err(PoolError::InvalidTx)?; + tx.validate( + Weighting::AsTransaction, + self.verifier_cache.clone(), + header.height, + ) + .map_err(PoolError::InvalidTx)?; // Check the tx lock_time is valid based on current chain state. self.blockchain.verify_tx_lock_height(tx)?; @@ -274,7 +278,12 @@ where }; // Validate the tx to ensure our converted inputs are correct. - tx.validate(Weighting::AsTransaction, self.verifier_cache.clone())?; + let header = self.chain_head()?; + tx.validate( + Weighting::AsTransaction, + self.verifier_cache.clone(), + header.height, + )?; Ok(PoolEntry::new(tx, entry.src)) } @@ -365,8 +374,9 @@ where // weight for a basic transaction (2 inputs, 2 outputs, 1 kernel) - // (2 * 1) + (2 * 21) + (1 * 3) = 47 // minfees = 47 * 500_000 = 23_500_000 - if tx.shifted_fee() < tx.accept_fee() { - return Err(PoolError::LowFeeTransaction(tx.shifted_fee())); + let header = self.chain_head()?; + if tx.shifted_fee(header.height) < tx.accept_fee() { + return Err(PoolError::LowFeeTransaction(tx.shifted_fee(header.height))); } Ok(()) } diff --git a/pool/tests/block_max_weight.rs b/pool/tests/block_max_weight.rs index e31d756e24..a0ab7816a2 100644 --- a/pool/tests/block_max_weight.rs +++ b/pool/tests/block_max_weight.rs @@ -83,7 +83,7 @@ fn test_block_building_max_weight() { // Fees and weights of our original txs in insert order. assert_eq!( - txs.iter().map(|x| x.fee()).collect::>(), + txs.iter().map(|x| x.fee(header.height)).collect::>(), [2_500_000, 90_000, 80_000, 30_000, 70_000, 60_000] ); assert_eq!( @@ -91,7 +91,9 @@ fn test_block_building_max_weight() { [88, 46, 46, 25, 46, 46] ); assert_eq!( - txs.iter().map(|x| x.fee_rate()).collect::>(), + txs.iter() + .map(|x| x.fee_rate(header.height)) + .collect::>(), [28409, 1956, 1739, 1200, 1521, 1304] ); @@ -109,7 +111,7 @@ fn test_block_building_max_weight() { // Fees and weights of the "mineable" txs. assert_eq!( - txs.iter().map(|x| x.fee()).collect::>(), + txs.iter().map(|x| x.fee(header.height)).collect::>(), [2_500_000, 90_000, 80_000, 70_000] ); assert_eq!( @@ -117,7 +119,9 @@ fn test_block_building_max_weight() { [88, 46, 46, 46] ); assert_eq!( - txs.iter().map(|x| x.fee_rate()).collect::>(), + txs.iter() + .map(|x| x.fee_rate(header.height)) + .collect::>(), [28409, 1956, 1739, 1521] ); diff --git a/pool/tests/common.rs b/pool/tests/common.rs index 12e92b5cbf..4628644f13 100644 --- a/pool/tests/common.rs +++ b/pool/tests/common.rs @@ -80,7 +80,7 @@ where let prev = chain.head_header().unwrap(); let height = prev.height + 1; let next_header_info = consensus::next_difficulty(height, chain.difficulty_iter().unwrap()); - let fee = txs.iter().map(|x| x.fee()).sum(); + let fee = txs.iter().map(|x| x.fee(height)).sum(); let key_id = ExtKeychainPath::new(1, height as u32, 0, 0, 0).to_identifier(); let reward = reward::output(keychain, &ProofBuilder::new(keychain), &key_id, fee, false).unwrap(); diff --git a/pool/tests/nrd_kernel_relative_height.rs b/pool/tests/nrd_kernel_relative_height.rs index 557626b642..3096098ab1 100644 --- a/pool/tests/nrd_kernel_relative_height.rs +++ b/pool/tests/nrd_kernel_relative_height.rs @@ -28,7 +28,6 @@ use grin_core as core; use grin_keychain as keychain; use grin_pool as pool; use grin_util as util; -use std::convert::TryInto; use std::sync::Arc; #[test] diff --git a/pool/tests/nrd_kernels_disabled.rs b/pool/tests/nrd_kernels_disabled.rs index 43b068e877..689cde2761 100644 --- a/pool/tests/nrd_kernels_disabled.rs +++ b/pool/tests/nrd_kernels_disabled.rs @@ -26,7 +26,6 @@ use grin_core as core; use grin_keychain as keychain; use grin_pool as pool; use grin_util as util; -use std::convert::TryInto; use std::sync::Arc; #[test] diff --git a/pool/tests/nrd_kernels_enabled.rs b/pool/tests/nrd_kernels_enabled.rs index a333dbc7d6..10290a7b07 100644 --- a/pool/tests/nrd_kernels_enabled.rs +++ b/pool/tests/nrd_kernels_enabled.rs @@ -26,7 +26,6 @@ use grin_core as core; use grin_keychain as keychain; use grin_pool as pool; use grin_util as util; -use std::convert::TryInto; use std::sync::Arc; #[test] diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs index 71b3858714..2f97239598 100644 --- a/pool/tests/transaction_pool.rs +++ b/pool/tests/transaction_pool.rs @@ -192,8 +192,9 @@ fn test_the_transaction_pool() { // tx4 is the "new" part of this aggregated tx that we care about let agg_tx = transaction::aggregate(&[tx1.clone(), tx2.clone(), tx4]).unwrap(); + let height = 4; agg_tx - .validate(Weighting::AsTransaction, verifier_cache.clone()) + .validate(Weighting::AsTransaction, verifier_cache.clone(), height) .unwrap(); pool.add_to_pool(test_source(), agg_tx, false, &header) diff --git a/servers/src/grin/dandelion_monitor.rs b/servers/src/grin/dandelion_monitor.rs index 37c84004e0..5b99591fd9 100644 --- a/servers/src/grin/dandelion_monitor.rs +++ b/servers/src/grin/dandelion_monitor.rs @@ -150,6 +150,7 @@ fn process_fluff_phase( agg_tx.validate( transaction::Weighting::AsTransaction, verifier_cache.clone(), + header.height, )?; tx_pool.add_to_pool(TxSource::Fluff, agg_tx, false, &header)?; diff --git a/servers/src/mining/mine_block.rs b/servers/src/mining/mine_block.rs index fa7cec221c..064f40885e 100644 --- a/servers/src/mining/mine_block.rs +++ b/servers/src/mining/mine_block.rs @@ -166,7 +166,7 @@ fn build_block( }; // build the coinbase and the block itself - let fees = txs.iter().map(|tx| tx.fee()).sum(); + let fees = txs.iter().map(|tx| tx.fee(head.height)).sum(); let height = head.height + 1; let block_fees = BlockFees { fees, diff --git a/store/tests/lmdb.rs b/store/tests/lmdb.rs index 6dfebc16b5..0bbf3c4c4e 100644 --- a/store/tests/lmdb.rs +++ b/store/tests/lmdb.rs @@ -15,7 +15,6 @@ use grin_core as core; use grin_store as store; use grin_util as util; -use store::PrefixIterator; use crate::core::global; use crate::core::ser::{self, Readable, Reader, Writeable, Writer}; From 65aec68d0ecc84315a37b7ce5b875109831cb184 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Mon, 23 Nov 2020 23:49:24 +0100 Subject: [PATCH 20/24] prior to hf4 feefield testing --- core/tests/transaction.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 5c79c6484d..ec1cbd2c58 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -232,9 +232,11 @@ fn test_fee_fields() -> Result<(), Error> { ) .expect("valid tx"); - let height = 2 * consensus::YEAR_HEIGHT; - assert_eq!(tx.fee(height), 42); - assert_eq!(tx.shifted_fee(height), 21); + let hf4_height = 2 * consensus::YEAR_HEIGHT; + assert_eq!(tx.fee(hf4_height), 42); + assert_eq!(tx.shifted_fee(hf4_height), 21); + assert_eq!(tx.fee(hf4_height - 1), 42 + (1u64 << 40)); + assert_eq!(tx.shifted_fee(hf4_height - 1), 42 + (1u64 << 40)); tx.body.kernels.append(&mut vec![ TxKernel::with_features(KernelFeatures::Plain { @@ -245,9 +247,9 @@ fn test_fee_fields() -> Result<(), Error> { }), ]); - assert_eq!(tx.fee(height), 147); - assert_eq!(tx.shifted_fee(height), 36); - assert_eq!(tx.aggregate_fee_fields(height), FeeFields::new(2, 147)); + assert_eq!(tx.fee(hf4_height), 147); + assert_eq!(tx.shifted_fee(hf4_height), 36); + assert_eq!(tx.aggregate_fee_fields(hf4_height), FeeFields::new(2, 147)); assert_eq!(tx_fee(1, 1, 3), 15_500_000); Ok(()) From 3cfcb4d8eb87ffc4a2e7b5cbb2c64481bedb2e78 Mon Sep 17 00:00:00 2001 From: John Tromp Date: Tue, 24 Nov 2020 17:35:32 +0100 Subject: [PATCH 21/24] rename selected fee_fields back to fee for serialization compatibility --- api/src/types.rs | 13 +-- chain/tests/mine_nrd_kernel.rs | 4 +- chain/tests/mine_simple_chain.rs | 12 +- chain/tests/nrd_validation_rules.rs | 6 +- chain/tests/process_block_cut_through.rs | 2 +- chain/tests/test_coinbase_maturity.rs | 8 +- core/src/core/transaction.rs | 136 +++++++++-------------- core/src/libtx/build.rs | 14 +-- core/tests/block.rs | 30 ++--- core/tests/common.rs | 16 +-- core/tests/core.rs | 31 ++---- core/tests/transaction.rs | 17 +-- pool/tests/common.rs | 4 +- pool/tests/nrd_kernel_relative_height.rs | 4 +- pool/tests/nrd_kernels_disabled.rs | 2 +- pool/tests/nrd_kernels_enabled.rs | 2 +- 16 files changed, 106 insertions(+), 195 deletions(-) diff --git a/api/src/types.rs b/api/src/types.rs index b7d052c09d..422a90e37d 100644 --- a/api/src/types.rs +++ b/api/src/types.rs @@ -511,18 +511,15 @@ impl TxKernelPrintable { pub fn from_txkernel(k: &core::TxKernel) -> TxKernelPrintable { let features = k.features.as_string(); let (fee_fields, lock_height) = match k.features { - KernelFeatures::Plain { fee_fields } => (fee_fields, 0), + KernelFeatures::Plain { fee } => (fee, 0), KernelFeatures::Coinbase => (FeeFields::zero(), 0), - KernelFeatures::HeightLocked { - fee_fields, - lock_height, - } => (fee_fields, lock_height), + KernelFeatures::HeightLocked { fee, lock_height } => (fee, lock_height), KernelFeatures::NoRecentDuplicate { - fee_fields, + fee, relative_height, - } => (fee_fields, relative_height.into()), + } => (fee, relative_height.into()), }; - let height = 2 * YEAR_HEIGHT; + let height = 2 * YEAR_HEIGHT; // print as if post-HF4 let fee = fee_fields.fee(height); let fee_shift: u8 = fee_fields.fee_shift(height); TxKernelPrintable { diff --git a/chain/tests/mine_nrd_kernel.rs b/chain/tests/mine_nrd_kernel.rs index 70061829a4..fb04292501 100644 --- a/chain/tests/mine_nrd_kernel.rs +++ b/chain/tests/mine_nrd_kernel.rs @@ -84,7 +84,7 @@ fn mine_block_with_nrd_kernel_and_nrd_feature_enabled() { let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier(); let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.into(), + fee: 20000.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[ @@ -131,7 +131,7 @@ fn mine_invalid_block_with_nrd_kernel_and_nrd_feature_enabled_before_hf() { let key_id2 = ExtKeychainPath::new(1, 2, 0, 0, 0).to_identifier(); let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.into(), + fee: 20000.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[ diff --git a/chain/tests/mine_simple_chain.rs b/chain/tests/mine_simple_chain.rs index 259b6df6bb..fff548914a 100644 --- a/chain/tests/mine_simple_chain.rs +++ b/chain/tests/mine_simple_chain.rs @@ -569,9 +569,7 @@ fn spend_rewind_spend() { let key_id30 = ExtKeychainPath::new(1, 30, 0, 0, 0).to_identifier(); let tx1 = build::transaction( - KernelFeatures::Plain { - fee_fields: 20000.into(), - }, + KernelFeatures::Plain { fee: 20000.into() }, &[ build::coinbase_input(consensus::REWARD, key_id_coinbase.clone()), build::output(consensus::REWARD - 20000, key_id30.clone()), @@ -644,9 +642,7 @@ fn spend_in_fork_and_compact() { let key_id31 = ExtKeychainPath::new(1, 31, 0, 0, 0).to_identifier(); let tx1 = build::transaction( - KernelFeatures::Plain { - fee_fields: 20000.into(), - }, + KernelFeatures::Plain { fee: 20000.into() }, &[ build::coinbase_input(consensus::REWARD, key_id2.clone()), build::output(consensus::REWARD - 20000, key_id30.clone()), @@ -664,9 +660,7 @@ fn spend_in_fork_and_compact() { chain.validate(false).unwrap(); let tx2 = build::transaction( - KernelFeatures::Plain { - fee_fields: 20000.into(), - }, + KernelFeatures::Plain { fee: 20000.into() }, &[ build::input(consensus::REWARD - 20000, key_id30.clone()), build::output(consensus::REWARD - 40000, key_id31.clone()), diff --git a/chain/tests/nrd_validation_rules.rs b/chain/tests/nrd_validation_rules.rs index d0e3006d3c..7cce9027ae 100644 --- a/chain/tests/nrd_validation_rules.rs +++ b/chain/tests/nrd_validation_rules.rs @@ -100,7 +100,7 @@ fn process_block_nrd_validation() -> Result<(), Error> { assert_eq!(chain.head()?.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.into(), + fee: 20000.into(), relative_height: NRDRelativeHeight::new(2)?, }); @@ -216,7 +216,7 @@ fn process_block_nrd_validation_relative_height_1() -> Result<(), Error> { assert_eq!(chain.head()?.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.into(), + fee: 20000.into(), relative_height: NRDRelativeHeight::new(1)?, }); @@ -315,7 +315,7 @@ fn process_block_nrd_validation_fork() -> Result<(), Error> { assert_eq!(header_8.height, 8); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 20000.into(), + fee: 20000.into(), relative_height: NRDRelativeHeight::new(2)?, }); diff --git a/chain/tests/process_block_cut_through.rs b/chain/tests/process_block_cut_through.rs index 0164fd4df3..89c0759389 100644 --- a/chain/tests/process_block_cut_through.rs +++ b/chain/tests/process_block_cut_through.rs @@ -105,7 +105,7 @@ fn process_block_cut_through() -> Result<(), chain::Error> { // The input is coinbase and the output is plain. let tx = build::transaction( KernelFeatures::Plain { - fee_fields: FeeFields::zero(), + fee: FeeFields::zero(), }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), diff --git a/chain/tests/test_coinbase_maturity.rs b/chain/tests/test_coinbase_maturity.rs index dafc6773c3..7f7a525f5b 100644 --- a/chain/tests/test_coinbase_maturity.rs +++ b/chain/tests/test_coinbase_maturity.rs @@ -100,9 +100,7 @@ fn test_coinbase_maturity() { // here we build a tx that attempts to spend the earlier coinbase output // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[ build::coinbase_input(amount, key_id1.clone()), build::output(amount - 2, key_id2.clone()), @@ -184,9 +182,7 @@ fn test_coinbase_maturity() { // here we build a tx that attempts to spend the earlier coinbase output // this is not a valid tx as the coinbase output cannot be spent yet let coinbase_txn = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[ build::coinbase_input(amount, key_id1.clone()), build::output(amount - 2, key_id2.clone()), diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index de7b3e07d4..7a56c05c43 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -289,7 +289,7 @@ pub enum KernelFeatures { Plain { /// Plain kernels have fees. #[serde(serialize_with = "fee_fields_as_int")] - fee_fields: FeeFields, + fee: FeeFields, }, /// A coinbase kernel. Coinbase, @@ -297,7 +297,7 @@ pub enum KernelFeatures { HeightLocked { /// Height locked kernels have fees. #[serde(serialize_with = "fee_fields_as_int")] - fee_fields: FeeFields, + fee: FeeFields, /// Height locked kernels have lock heights. lock_height: u64, }, @@ -305,7 +305,7 @@ pub enum KernelFeatures { NoRecentDuplicate { /// These have fees. #[serde(serialize_with = "fee_fields_as_int")] - fee_fields: FeeFields, + fee: FeeFields, /// Relative lock height. relative_height: NRDRelativeHeight, }, @@ -345,16 +345,13 @@ impl KernelFeatures { pub fn kernel_sig_msg(&self) -> Result { let x = self.as_u8(); let hash = match self { - KernelFeatures::Plain { fee_fields } => (x, fee_fields).hash(), + KernelFeatures::Plain { fee } => (x, fee).hash(), KernelFeatures::Coinbase => x.hash(), - KernelFeatures::HeightLocked { - fee_fields, - lock_height, - } => (x, fee_fields, lock_height).hash(), + KernelFeatures::HeightLocked { fee, lock_height } => (x, fee, lock_height).hash(), KernelFeatures::NoRecentDuplicate { - fee_fields, + fee, relative_height, - } => (x, fee_fields, relative_height).hash(), + } => (x, fee, relative_height).hash(), }; let msg = secp::Message::from_slice(&hash.as_bytes())?; @@ -366,8 +363,8 @@ impl KernelFeatures { fn write_v1(&self, writer: &mut W) -> Result<(), ser::Error> { writer.write_u8(self.as_u8())?; match self { - KernelFeatures::Plain { fee_fields } => { - fee_fields.write(writer)?; + KernelFeatures::Plain { fee } => { + fee.write(writer)?; // Write "empty" bytes for feature specific data (8 bytes). writer.write_empty_bytes(8)?; } @@ -375,19 +372,16 @@ impl KernelFeatures { // Write "empty" bytes for fee_fields (8 bytes) and feature specific data (8 bytes). writer.write_empty_bytes(16)?; } - KernelFeatures::HeightLocked { - fee_fields, - lock_height, - } => { - fee_fields.write(writer)?; + KernelFeatures::HeightLocked { fee, lock_height } => { + fee.write(writer)?; // 8 bytes of feature specific data containing the lock height as big-endian u64. writer.write_u64(*lock_height)?; } KernelFeatures::NoRecentDuplicate { - fee_fields, + fee, relative_height, } => { - fee_fields.write(writer)?; + fee.write(writer)?; // 8 bytes of feature specific data. First 6 bytes are empty. // Last 2 bytes contain the relative lock height as big-endian u16. @@ -407,26 +401,23 @@ impl KernelFeatures { fn write_v2(&self, writer: &mut W) -> Result<(), ser::Error> { writer.write_u8(self.as_u8())?; match self { - KernelFeatures::Plain { fee_fields } => { + KernelFeatures::Plain { fee } => { // Fee only, no additional data on plain kernels. - fee_fields.write(writer)?; + fee.write(writer)?; } KernelFeatures::Coinbase => { // No additional data. } - KernelFeatures::HeightLocked { - fee_fields, - lock_height, - } => { - fee_fields.write(writer)?; + KernelFeatures::HeightLocked { fee, lock_height } => { + fee.write(writer)?; // V2 height locked kernels use 8 bytes for the lock height. writer.write_u64(*lock_height)?; } KernelFeatures::NoRecentDuplicate { - fee_fields, + fee, relative_height, } => { - fee_fields.write(writer)?; + fee.write(writer)?; // V2 NRD kernels use 2 bytes for the relative lock height. relative_height.write(writer)?; } @@ -442,10 +433,10 @@ impl KernelFeatures { let feature_byte = reader.read_u8()?; let features = match feature_byte { KernelFeatures::PLAIN_U8 => { - let fee_fields = FeeFields::read(reader)?; + let fee = FeeFields::read(reader)?; // 8 "empty" bytes as additional data is not used. reader.read_empty_bytes(8)?; - KernelFeatures::Plain { fee_fields } + KernelFeatures::Plain { fee } } KernelFeatures::COINBASE_U8 => { // 8 "empty" bytes as fee_fields is not used. @@ -454,13 +445,10 @@ impl KernelFeatures { KernelFeatures::Coinbase } KernelFeatures::HEIGHT_LOCKED_U8 => { - let fee_fields = FeeFields::read(reader)?; + let fee = FeeFields::read(reader)?; // 8 bytes of feature specific data, lock height as big-endian u64. let lock_height = reader.read_u64()?; - KernelFeatures::HeightLocked { - fee_fields, - lock_height, - } + KernelFeatures::HeightLocked { fee, lock_height } } KernelFeatures::NO_RECENT_DUPLICATE_U8 => { // NRD kernels are invalid if NRD feature flag is not enabled. @@ -468,7 +456,7 @@ impl KernelFeatures { return Err(ser::Error::CorruptedData); } - let fee_fields = FeeFields::read(reader)?; + let fee = FeeFields::read(reader)?; // 8 bytes of feature specific data. // The first 6 bytes must be "empty". @@ -476,7 +464,7 @@ impl KernelFeatures { reader.read_empty_bytes(6)?; let relative_height = NRDRelativeHeight::read(reader)?; KernelFeatures::NoRecentDuplicate { - fee_fields, + fee, relative_height, } } @@ -492,17 +480,14 @@ impl KernelFeatures { fn read_v2(reader: &mut R) -> Result { let features = match reader.read_u8()? { KernelFeatures::PLAIN_U8 => { - let fee_fields = FeeFields::read(reader)?; - KernelFeatures::Plain { fee_fields } + let fee = FeeFields::read(reader)?; + KernelFeatures::Plain { fee } } KernelFeatures::COINBASE_U8 => KernelFeatures::Coinbase, KernelFeatures::HEIGHT_LOCKED_U8 => { - let fee_fields = FeeFields::read(reader)?; + let fee = FeeFields::read(reader)?; let lock_height = reader.read_u64()?; - KernelFeatures::HeightLocked { - fee_fields, - lock_height, - } + KernelFeatures::HeightLocked { fee, lock_height } } KernelFeatures::NO_RECENT_DUPLICATE_U8 => { // NRD kernels are invalid if NRD feature flag is not enabled. @@ -510,10 +495,10 @@ impl KernelFeatures { return Err(ser::Error::CorruptedData); } - let fee_fields = FeeFields::read(reader)?; + let fee = FeeFields::read(reader)?; let relative_height = NRDRelativeHeight::read(reader)?; KernelFeatures::NoRecentDuplicate { - fee_fields, + fee, relative_height, } } @@ -819,7 +804,7 @@ impl TxKernel { /// Build an empty tx kernel with zero values. pub fn empty() -> TxKernel { TxKernel::with_features(KernelFeatures::Plain { - fee_fields: FeeFields::zero(), + fee: FeeFields::zero(), }) } @@ -1069,9 +1054,9 @@ impl TransactionBody { .iter() .filter_map(|k| match k.features { KernelFeatures::Coinbase => None, - KernelFeatures::Plain { fee_fields } => Some(fee_fields), - KernelFeatures::HeightLocked { fee_fields, .. } => Some(fee_fields), - KernelFeatures::NoRecentDuplicate { fee_fields, .. } => Some(fee_fields), + KernelFeatures::Plain { fee } => Some(fee), + KernelFeatures::HeightLocked { fee, .. } => Some(fee), + KernelFeatures::NoRecentDuplicate { fee, .. } => Some(fee), }) .fold(0, |acc, fee_fields| { acc.saturating_add(fee_fields.fee(height)) @@ -1084,9 +1069,9 @@ impl TransactionBody { .iter() .filter_map(|k| match k.features { KernelFeatures::Coinbase => None, - KernelFeatures::Plain { fee_fields } => Some(fee_fields), - KernelFeatures::HeightLocked { fee_fields, .. } => Some(fee_fields), - KernelFeatures::NoRecentDuplicate { fee_fields, .. } => Some(fee_fields), + KernelFeatures::Plain { fee } => Some(fee), + KernelFeatures::HeightLocked { fee, .. } => Some(fee), + KernelFeatures::NoRecentDuplicate { fee, .. } => Some(fee), }) .fold(0, |acc, fee_fields| max(acc, fee_fields.fee_shift(height))) } @@ -2335,9 +2320,7 @@ mod test { let sig = secp::Signature::from_raw_data(&[0; 64]).unwrap(); let kernel = TxKernel { - features: KernelFeatures::Plain { - fee_fields: 10.into(), - }, + features: KernelFeatures::Plain { fee: 10.into() }, excess: commit, excess_sig: sig.clone(), }; @@ -2347,12 +2330,7 @@ mod test { let mut vec = vec![]; ser::serialize(&mut vec, version, &kernel).expect("serialized failed"); let kernel2: TxKernel = ser::deserialize(&mut &vec[..], version).unwrap(); - assert_eq!( - kernel2.features, - KernelFeatures::Plain { - fee_fields: 10.into() - } - ); + assert_eq!(kernel2.features, KernelFeatures::Plain { fee: 10.into() }); assert_eq!(kernel2.excess, commit); assert_eq!(kernel2.excess_sig, sig.clone()); } @@ -2361,12 +2339,7 @@ mod test { let mut vec = vec![]; ser::serialize_default(&mut vec, &kernel).expect("serialized failed"); let kernel2: TxKernel = ser::deserialize_default(&mut &vec[..]).unwrap(); - assert_eq!( - kernel2.features, - KernelFeatures::Plain { - fee_fields: 10.into() - } - ); + assert_eq!(kernel2.features, KernelFeatures::Plain { fee: 10.into() }); assert_eq!(kernel2.excess, commit); assert_eq!(kernel2.excess_sig, sig.clone()); } @@ -2385,7 +2358,7 @@ mod test { // now check a kernel with lock_height serialize/deserialize correctly let kernel = TxKernel { features: KernelFeatures::HeightLocked { - fee_fields: 10.into(), + fee: 10.into(), lock_height: 100, }, excess: commit, @@ -2427,7 +2400,7 @@ mod test { // now check an NRD kernel will serialize/deserialize correctly let kernel = TxKernel { features: KernelFeatures::NoRecentDuplicate { - fee_fields: 10.into(), + fee: 10.into(), relative_height: NRDRelativeHeight(100), }, excess: commit, @@ -2459,7 +2432,7 @@ mod test { let key_id = ExtKeychain::derive_key_id(1, 1, 0, 0, 0); let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 10.into(), + fee: 10.into(), relative_height: NRDRelativeHeight(100), }); @@ -2485,27 +2458,25 @@ mod test { // Modify the fee and check signature no longer verifies. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: 9.into(), + fee: 9.into(), relative_height: NRDRelativeHeight(100), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Modify the relative_height and check signature no longer verifies. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: 10.into(), + fee: 10.into(), relative_height: NRDRelativeHeight(101), }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Swap the features out for something different and check signature no longer verifies. - kernel.features = KernelFeatures::Plain { - fee_fields: 10.into(), - }; + kernel.features = KernelFeatures::Plain { fee: 10.into() }; assert_eq!(kernel.verify(), Err(Error::IncorrectSignature)); // Check signature verifies if we use the original features. kernel.features = KernelFeatures::NoRecentDuplicate { - fee_fields: 10.into(), + fee: 10.into(), relative_height: NRDRelativeHeight(100), }; assert_eq!(kernel.verify(), Ok(())); @@ -2566,12 +2537,7 @@ mod test { let mut vec = vec![]; ser::serialize_default(&mut vec, &(0u8, 10u64, 0u64))?; let features: KernelFeatures = ser::deserialize_default(&mut &vec[..])?; - assert_eq!( - features, - KernelFeatures::Plain { - fee_fields: 10.into() - } - ); + assert_eq!(features, KernelFeatures::Plain { fee: 10.into() }); let mut vec = vec![]; ser::serialize_default(&mut vec, &(1u8, 0u64, 0u64))?; @@ -2584,7 +2550,7 @@ mod test { assert_eq!( features, KernelFeatures::HeightLocked { - fee_fields: 10.into(), + fee: 10.into(), lock_height: 100 } ); @@ -2614,7 +2580,7 @@ mod test { assert_eq!( features, KernelFeatures::NoRecentDuplicate { - fee_fields: 10.into(), + fee: 10.into(), relative_height: NRDRelativeHeight(100) } ); diff --git a/core/src/libtx/build.rs b/core/src/libtx/build.rs index a6e78fd5b4..0693bd0f61 100644 --- a/core/src/libtx/build.rs +++ b/core/src/libtx/build.rs @@ -23,7 +23,7 @@ //! //! Example: //! build::transaction( -//! KernelFeatures::Plain{ fee_fields: 2.try_into().unwrap() }, +//! KernelFeatures::Plain{ fee: 2.try_into().unwrap() }, //! vec![ //! input_rand(75), //! output_rand(42), @@ -279,9 +279,7 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[input(10, key_id1), input(12, key_id2), output(20, key_id3)], &keychain, &builder, @@ -305,9 +303,7 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[input(10, key_id1), input(12, key_id2), output(20, key_id3)], &keychain, &builder, @@ -330,9 +326,7 @@ mod test { let vc = verifier_cache(); let tx = transaction( - KernelFeatures::Plain { - fee_fields: 4.into(), - }, + KernelFeatures::Plain { fee: 4.into() }, &[input(6, key_id1), output(2, key_id2)], &keychain, &builder, diff --git a/core/tests/block.rs b/core/tests/block.rs index 3755ca7648..114a26da00 100644 --- a/core/tests/block.rs +++ b/core/tests/block.rs @@ -62,9 +62,7 @@ fn too_large_block() { parts.append(&mut vec![input(500000, pks.pop().unwrap())]); let tx = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &parts, &keychain, &builder, @@ -106,7 +104,7 @@ fn block_with_nrd_kernel_pre_post_hf3() { let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 2.into(), + fee: 2.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[input(7, key_id1), output(5, key_id2)], @@ -191,7 +189,7 @@ fn block_with_nrd_kernel_nrd_not_enabled() { let tx = build::transaction( KernelFeatures::NoRecentDuplicate { - fee_fields: 2.into(), + fee: 2.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, &[input(7, key_id1), output(5, key_id2)], @@ -279,9 +277,7 @@ fn block_with_cut_through() { let btx1 = tx2i1o(); let btx2 = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[input(7, key_id1), output(5, key_id2.clone())], &keychain, &builder, @@ -380,7 +376,7 @@ fn remove_coinbase_kernel_flag() { let mut kernel = b.kernels()[0].clone(); kernel.features = KernelFeatures::Plain { - fee_fields: FeeFields::zero(), + fee: FeeFields::zero(), }; b.body = b.body.replace_kernel(kernel); @@ -759,9 +755,7 @@ fn same_amount_outputs_copy_range_proof() { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { - fee_fields: 1.into(), - }, + KernelFeatures::Plain { fee: 1.into() }, &[input(7, key_id1), output(3, key_id2), output(3, key_id3)], &keychain, &builder, @@ -802,9 +796,7 @@ fn wrong_amount_range_proof() { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx1 = build::transaction( - KernelFeatures::Plain { - fee_fields: 1.into(), - }, + KernelFeatures::Plain { fee: 1.into() }, &[ input(7, key_id1.clone()), output(3, key_id2.clone()), @@ -815,9 +807,7 @@ fn wrong_amount_range_proof() { ) .unwrap(); let tx2 = build::transaction( - KernelFeatures::Plain { - fee_fields: 1.into(), - }, + KernelFeatures::Plain { fee: 1.into() }, &[input(7, key_id1), output(2, key_id2), output(4, key_id3)], &keychain, &builder, @@ -898,7 +888,7 @@ fn test_verify_cut_through_plain() -> Result<(), Error> { let tx = build::transaction( KernelFeatures::Plain { - fee_fields: FeeFields::zero(), + fee: FeeFields::zero(), }, &[ build::input(10, key_id1.clone()), @@ -964,7 +954,7 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { let tx = build::transaction( KernelFeatures::Plain { - fee_fields: FeeFields::zero(), + fee: FeeFields::zero(), }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), diff --git a/core/tests/common.rs b/core/tests/common.rs index f67f5bbfd3..5e1f63f6a3 100644 --- a/core/tests/common.rs +++ b/core/tests/common.rs @@ -37,9 +37,7 @@ pub fn tx2i1o() -> Transaction { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[input(10, key_id1), input(11, key_id2), output(19, key_id3)], &keychain, &builder, @@ -58,9 +56,7 @@ pub fn tx1i1o() -> Transaction { let key_id2 = keychain::ExtKeychain::derive_key_id(1, 2, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[input(5, key_id1), output(3, key_id2)], &keychain, &builder, @@ -100,9 +96,7 @@ pub fn tx1i2o() -> Transaction { let key_id3 = keychain::ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[input(6, key_id1), output(3, key_id2), output(1, key_id3)], &keychain, &builder, @@ -149,9 +143,7 @@ where B: ProofBuild, { build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[input(v, key_id1), output(3, key_id2)], keychain, builder, diff --git a/core/tests/core.rs b/core/tests/core.rs index a8adee682d..3df78de43a 100644 --- a/core/tests/core.rs +++ b/core/tests/core.rs @@ -132,7 +132,7 @@ fn test_zero_commit_fails() { // blinding should fail as signing with a zero r*G shouldn't work let res = build::transaction( KernelFeatures::Plain { - fee_fields: FeeFields::zero(), + fee: FeeFields::zero(), }, &[input(10, key_id1.clone()), output(10, key_id1)], &keychain, @@ -156,9 +156,7 @@ fn build_tx_kernel() { // first build a valid tx with corresponding blinding factor let tx = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[input(10, key_id1), output(5, key_id2), output(3, key_id3)], &keychain, &builder, @@ -175,12 +173,7 @@ fn build_tx_kernel() { let kern = &tx.kernels()[0]; kern.verify().unwrap(); - assert_eq!( - kern.features, - KernelFeatures::Plain { - fee_fields: 2.into() - } - ); + assert_eq!(kern.features, KernelFeatures::Plain { fee: 2.into() }); assert_eq!(2, tx.fee(height)); } @@ -203,9 +196,7 @@ fn build_two_half_kernels() { let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0); // build kernel with associated private excess - let mut kernel = TxKernel::with_features(KernelFeatures::Plain { - fee_fields: 2.into(), - }); + let mut kernel = TxKernel::with_features(KernelFeatures::Plain { fee: 2.into() }); // Construct the message to be signed. let msg = kernel.msg_to_sign().unwrap(); @@ -561,9 +552,7 @@ fn hash_output() { let key_id3 = ExtKeychain::derive_key_id(1, 3, 0, 0, 0); let tx = build::transaction( - KernelFeatures::Plain { - fee_fields: 1.into(), - }, + KernelFeatures::Plain { fee: 1.into() }, &[input(75, key_id1), output(42, key_id2), output(32, key_id3)], &keychain, &builder, @@ -628,7 +617,7 @@ fn tx_build_exchange() { // Alice builds her transaction, with change, which also produces the sum // of blinding factors before they're obscured. let tx = Transaction::empty().with_kernel(TxKernel::with_features(KernelFeatures::Plain { - fee_fields: 2.into(), + fee: 2.into(), })); let (tx, sum) = build::partial_transaction(tx, &[in1, in2, output(1, key_id3)], &keychain, &builder) @@ -641,9 +630,7 @@ fn tx_build_exchange() { // blinding factors. He adds his output, finalizes the transaction so it's // ready for broadcast. let tx_final = build::transaction( - KernelFeatures::Plain { - fee_fields: 2.into(), - }, + KernelFeatures::Plain { fee: 2.into() }, &[ initial_tx(tx_alice), with_excess(blind_sum), @@ -730,7 +717,7 @@ fn test_block_with_timelocked_tx() { // block height and that the resulting block is valid let tx1 = build::transaction( KernelFeatures::HeightLocked { - fee_fields: 2.into(), + fee: 2.into(), lock_height: 1, }, &[input(5, key_id1.clone()), output(3, key_id2.clone())], @@ -754,7 +741,7 @@ fn test_block_with_timelocked_tx() { // block height let tx1 = build::transaction( KernelFeatures::HeightLocked { - fee_fields: 2.into(), + fee: 2.into(), lock_height: 2, }, &[input(5, key_id1), output(3, key_id2)], diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index ec1cbd2c58..aafbde6b93 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -47,10 +47,7 @@ fn test_transaction_json_ser_deser() { assert!(value["body"]["outputs"][0]["proof"].is_string()); // Note: Tx kernel "features" serialize in a slightly unexpected way. - assert_eq!( - value["body"]["kernels"][0]["features"]["Plain"]["fee_fields"], - 2 - ); + assert_eq!(value["body"]["kernels"][0]["features"]["Plain"]["fee"], 2); assert!(value["body"]["kernels"][0]["excess"].is_string()); assert!(value["body"]["kernels"][0]["excess_sig"].is_string()); @@ -100,7 +97,7 @@ fn test_verify_cut_through_plain() -> Result<(), Error> { let mut tx = build::transaction( KernelFeatures::Plain { - fee_fields: FeeFields::zero(), + fee: FeeFields::zero(), }, &[ build::input(10, key_id1.clone()), @@ -162,7 +159,7 @@ fn test_verify_cut_through_coinbase() -> Result<(), Error> { let mut tx = build::transaction( KernelFeatures::Plain { - fee_fields: FeeFields::zero(), + fee: FeeFields::zero(), }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), @@ -221,7 +218,7 @@ fn test_fee_fields() -> Result<(), Error> { let mut tx = build::transaction( KernelFeatures::Plain { - fee_fields: FeeFields::new(1, 42).unwrap(), + fee: FeeFields::new(1, 42).unwrap(), }, &[ build::coinbase_input(consensus::REWARD, key_id1.clone()), @@ -240,11 +237,9 @@ fn test_fee_fields() -> Result<(), Error> { tx.body.kernels.append(&mut vec![ TxKernel::with_features(KernelFeatures::Plain { - fee_fields: FeeFields::new(2, 84).unwrap(), - }), - TxKernel::with_features(KernelFeatures::Plain { - fee_fields: 21.into(), + fee: FeeFields::new(2, 84).unwrap(), }), + TxKernel::with_features(KernelFeatures::Plain { fee: 21.into() }), ]); assert_eq!(tx.fee(hf4_height), 147); diff --git a/pool/tests/common.rs b/pool/tests/common.rs index 4628644f13..ad24a22e1d 100644 --- a/pool/tests/common.rs +++ b/pool/tests/common.rs @@ -208,7 +208,7 @@ where build::transaction( KernelFeatures::Plain { - fee_fields: (fees as u64).try_into().unwrap(), + fee: (fees as u64).try_into().unwrap(), }, &tx_elements, keychain, @@ -235,7 +235,7 @@ where input_values, output_values, KernelFeatures::Plain { - fee_fields: (fees as u64).try_into().unwrap(), + fee: (fees as u64).try_into().unwrap(), }, ) } diff --git a/pool/tests/nrd_kernel_relative_height.rs b/pool/tests/nrd_kernel_relative_height.rs index 3096098ab1..64cf7dfcbe 100644 --- a/pool/tests/nrd_kernel_relative_height.rs +++ b/pool/tests/nrd_kernel_relative_height.rs @@ -75,7 +75,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { let (tx1, tx2, tx3) = { let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 600.into(), + fee: 600.into(), relative_height: NRDRelativeHeight::new(2)?, }); let msg = kernel.msg_to_sign().unwrap(); @@ -113,7 +113,7 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { // Now reuse kernel excess for tx3 but with NRD relative_height=1 (and different fee). let mut kernel_short = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { - fee_fields: 300.into(), + fee: 300.into(), relative_height: NRDRelativeHeight::new(1)?, }); let msg_short = kernel_short.msg_to_sign().unwrap(); diff --git a/pool/tests/nrd_kernels_disabled.rs b/pool/tests/nrd_kernels_disabled.rs index 689cde2761..d96e68edd6 100644 --- a/pool/tests/nrd_kernels_disabled.rs +++ b/pool/tests/nrd_kernels_disabled.rs @@ -65,7 +65,7 @@ fn test_nrd_kernels_disabled() { vec![1_000, 2_000], vec![2_400], KernelFeatures::NoRecentDuplicate { - fee_fields: 600.into(), + fee: 600.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); diff --git a/pool/tests/nrd_kernels_enabled.rs b/pool/tests/nrd_kernels_enabled.rs index 10290a7b07..33a404f2ee 100644 --- a/pool/tests/nrd_kernels_enabled.rs +++ b/pool/tests/nrd_kernels_enabled.rs @@ -66,7 +66,7 @@ fn test_nrd_kernels_enabled() { vec![1_000, 2_000], vec![2_400], KernelFeatures::NoRecentDuplicate { - fee_fields: 600.into(), + fee: 600.into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); From 0d59394d733209e4a3c98e607a4fb72f92cfb34d Mon Sep 17 00:00:00 2001 From: John Tromp Date: Wed, 25 Nov 2020 18:34:35 +0100 Subject: [PATCH 22/24] fix test_fee_fields test, merge conflict, and doctest use of obsolete fee_fields --- core/src/libtx/aggsig.rs | 4 ++-- core/tests/transaction.rs | 2 +- src/bin/grin.rs | 10 +++++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/core/src/libtx/aggsig.rs b/core/src/libtx/aggsig.rs index 0a26125486..551bcbc2bc 100644 --- a/core/src/libtx/aggsig.rs +++ b/core/src/libtx/aggsig.rs @@ -240,7 +240,7 @@ pub fn verify_partial_sig( /// let height = 20; /// let over_commit = secp.commit_value(reward(fees)).unwrap(); /// let out_commit = output.commitment(); -/// let features = KernelFeatures::HeightLocked{fee_fields: 1.into(), lock_height: height}; +/// let features = KernelFeatures::HeightLocked{fee: 1.into(), lock_height: height}; /// let msg = features.kernel_sig_msg().unwrap(); /// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap(); /// let pubkey = excess.to_pubkey(&secp).unwrap(); @@ -304,7 +304,7 @@ where /// let height = 20; /// let over_commit = secp.commit_value(reward(fees)).unwrap(); /// let out_commit = output.commitment(); -/// let features = KernelFeatures::HeightLocked{fee_fields: 1.into(), lock_height: height}; +/// let features = KernelFeatures::HeightLocked{fee: 1.into(), lock_height: height}; /// let msg = features.kernel_sig_msg().unwrap(); /// let excess = secp.commit_sum(vec![out_commit], vec![over_commit]).unwrap(); /// let pubkey = excess.to_pubkey(&secp).unwrap(); diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index aafbde6b93..789cfa1760 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -229,7 +229,7 @@ fn test_fee_fields() -> Result<(), Error> { ) .expect("valid tx"); - let hf4_height = 2 * consensus::YEAR_HEIGHT; + let hf4_height = 4 * consensus::TESTING_HARD_FORK_INTERVAL; assert_eq!(tx.fee(hf4_height), 42); assert_eq!(tx.shifted_fee(hf4_height), 21); assert_eq!(tx.fee(hf4_height - 1), 42 + (1u64 << 40)); diff --git a/src/bin/grin.rs b/src/bin/grin.rs index b9f6eddabf..5cd07aa509 100644 --- a/src/bin/grin.rs +++ b/src/bin/grin.rs @@ -160,7 +160,15 @@ fn real_main() -> i32 { global::init_global_nrd_enabled(true); } } - global::init_global_accept_fee_base(config.members.unwrap().server.pool_config.accept_fee_base); + global::init_global_accept_fee_base( + config + .members + .as_ref() + .unwrap() + .server + .pool_config + .accept_fee_base, + ); info!("Accept Fee Base: {:?}", global::get_accept_fee_base()); global::init_global_future_time_limit(config.members.unwrap().server.future_time_limit); info!("Future Time Limit: {:?}", global::get_future_time_limit()); From f4b7c308449a4f06346cb7f0abe8125ee3dd3dfb Mon Sep 17 00:00:00 2001 From: John Tromp Date: Thu, 26 Nov 2020 13:32:03 +0100 Subject: [PATCH 23/24] make accept_fee height dependent --- core/src/core/transaction.rs | 21 +++++++++++++++++++-- core/src/libtx/mod.rs | 4 ++-- core/tests/transaction.rs | 9 +++++++++ pool/src/transaction_pool.rs | 2 +- pool/tests/block_building.rs | 3 ++- pool/tests/block_max_weight.rs | 3 ++- pool/tests/block_reconciliation.rs | 3 ++- pool/tests/nrd_kernel_relative_height.rs | 7 ++++--- pool/tests/nrd_kernels_enabled.rs | 16 ++++++++++------ pool/tests/transaction_pool.rs | 5 +++-- 10 files changed, 54 insertions(+), 19 deletions(-) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 7a56c05c43..41f08448b0 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -1516,8 +1516,25 @@ impl Transaction { } /// Transaction minimum acceptable fee - pub fn accept_fee(&self) -> u64 { - self.weight() * global::get_accept_fee_base() + pub fn accept_fee(&self, height: u64) -> u64 { + if consensus::header_version(height) < HeaderVersion(5) { + Transaction::old_weight_by_iok( + self.body.inputs.len() as u64, + self.body.outputs.len() as u64, + self.body.kernels.len() as u64, + ) * consensus::MILLI_GRIN + } else { + self.weight() * global::get_accept_fee_base() + } + } + + /// Old weight definition for pool acceptance + pub fn old_weight_by_iok(num_inputs: u64, num_outputs: u64, num_kernels: u64) -> u64 { + let body_weight = num_outputs + .saturating_mul(4) + .saturating_add(num_kernels) + .saturating_sub(num_inputs); + max(body_weight, 1) } /// Calculate transaction weight from transaction details diff --git a/core/src/libtx/mod.rs b/core/src/libtx/mod.rs index 5945ec2fad..c7080d3064 100644 --- a/core/src/libtx/mod.rs +++ b/core/src/libtx/mod.rs @@ -41,6 +41,6 @@ pub fn tx_fee(input_len: usize, output_len: usize, kernel_len: usize) -> u64 { } /// Transaction fee calculation given transaction -pub fn accept_fee(tx: Transaction) -> u64 { - tx.weight() * get_accept_fee_base() +pub fn accept_fee(tx: Transaction, height: u64) -> u64 { + tx.accept_fee(height) } diff --git a/core/tests/transaction.rs b/core/tests/transaction.rs index 789cfa1760..423a67c585 100644 --- a/core/tests/transaction.rs +++ b/core/tests/transaction.rs @@ -230,8 +230,17 @@ fn test_fee_fields() -> Result<(), Error> { .expect("valid tx"); let hf4_height = 4 * consensus::TESTING_HARD_FORK_INTERVAL; + assert_eq!( + tx.accept_fee(hf4_height), + (1 * 1 + 1 * 21 + 1 * 3) * 500_000 + ); + assert_eq!(tx.fee(hf4_height), 42); assert_eq!(tx.fee(hf4_height), 42); assert_eq!(tx.shifted_fee(hf4_height), 21); + assert_eq!( + tx.accept_fee(hf4_height - 1), + (1 * 4 + 1 * 1 - 1 * 1) * 1_000_000 + ); assert_eq!(tx.fee(hf4_height - 1), 42 + (1u64 << 40)); assert_eq!(tx.shifted_fee(hf4_height - 1), 42 + (1u64 << 40)); diff --git a/pool/src/transaction_pool.rs b/pool/src/transaction_pool.rs index 040ecbddf6..8d4a46e2b0 100644 --- a/pool/src/transaction_pool.rs +++ b/pool/src/transaction_pool.rs @@ -375,7 +375,7 @@ where // (2 * 1) + (2 * 21) + (1 * 3) = 47 // minfees = 47 * 500_000 = 23_500_000 let header = self.chain_head()?; - if tx.shifted_fee(header.height) < tx.accept_fee() { + if tx.shifted_fee(header.height) < tx.accept_fee(header.height) { return Err(PoolError::LowFeeTransaction(tx.shifted_fee(header.height))); } Ok(()) diff --git a/pool/tests/block_building.rs b/pool/tests/block_building.rs index dbdff3caa2..d7e7843415 100644 --- a/pool/tests/block_building.rs +++ b/pool/tests/block_building.rs @@ -49,7 +49,8 @@ fn test_transaction_pool_block_building() -> Result<(), PoolError> { verifier_cache, ); - add_some_blocks(&chain, 3, &keychain); + // mine enough blocks to get past HF4 + add_some_blocks(&chain, 4 * 3, &keychain); let header_1 = chain.get_header_by_height(1).unwrap(); diff --git a/pool/tests/block_max_weight.rs b/pool/tests/block_max_weight.rs index a0ab7816a2..9928f990b7 100644 --- a/pool/tests/block_max_weight.rs +++ b/pool/tests/block_max_weight.rs @@ -49,7 +49,8 @@ fn test_block_building_max_weight() { verifier_cache, ); - add_some_blocks(&chain, 3, &keychain); + // mine past HF4 to see effect of set_local_accept_fee_base + add_some_blocks(&chain, 4 * 3, &keychain); let header_1 = chain.get_header_by_height(1).unwrap(); diff --git a/pool/tests/block_reconciliation.rs b/pool/tests/block_reconciliation.rs index 3e69401713..9ce20104ae 100644 --- a/pool/tests/block_reconciliation.rs +++ b/pool/tests/block_reconciliation.rs @@ -48,7 +48,8 @@ fn test_transaction_pool_block_reconciliation() { verifier_cache, ); - add_some_blocks(&chain, 3, &keychain); + // mine past HF4 to see effect of set_local_accept_fee_base + add_some_blocks(&chain, 4 * 3, &keychain); let header_1 = chain.get_header_by_height(1).unwrap(); diff --git a/pool/tests/nrd_kernel_relative_height.rs b/pool/tests/nrd_kernel_relative_height.rs index f446c305e0..6ceb570ced 100644 --- a/pool/tests/nrd_kernel_relative_height.rs +++ b/pool/tests/nrd_kernel_relative_height.rs @@ -66,12 +66,13 @@ fn test_nrd_kernel_relative_height() -> Result<(), PoolError> { // Mine that initial tx so we can spend it with multiple txs. add_block(&chain, &[initial_tx], &keychain); - add_some_blocks(&chain, 5, &keychain); + // mine past HF4 to see effect of set_local_accept_fee_base + add_some_blocks(&chain, 8, &keychain); let header = chain.head_header().unwrap(); - assert_eq!(header.height, 3 * consensus::TESTING_HARD_FORK_INTERVAL); - assert_eq!(header.version, HeaderVersion(4)); + assert_eq!(header.height, 4 * consensus::TESTING_HARD_FORK_INTERVAL); + assert_eq!(header.version, HeaderVersion(5)); let (tx1, tx2, tx3) = { let mut kernel = TxKernel::with_features(KernelFeatures::NoRecentDuplicate { diff --git a/pool/tests/nrd_kernels_enabled.rs b/pool/tests/nrd_kernels_enabled.rs index cac0ffa9a2..8479a4f225 100644 --- a/pool/tests/nrd_kernels_enabled.rs +++ b/pool/tests/nrd_kernels_enabled.rs @@ -57,16 +57,20 @@ fn test_nrd_kernels_enabled() { // Spend the initial coinbase. let header_1 = chain.get_header_by_height(1).unwrap(); - let tx = - test_transaction_spending_coinbase(&keychain, &header_1, vec![1_000, 2_000, 3_000, 4_000]); + let mg = consensus::MILLI_GRIN; + let tx = test_transaction_spending_coinbase( + &keychain, + &header_1, + vec![1_000 * mg, 2_000 * mg, 3_000 * mg, 4_000 * mg], + ); add_block(&chain, &[tx], &keychain); let tx_1 = test_transaction_with_kernel_features( &keychain, - vec![1_000, 2_000], - vec![2_400], + vec![1_000 * mg, 2_000 * mg], + vec![2_400 * mg], KernelFeatures::NoRecentDuplicate { - fee: 600.into(), + fee: (600 * mg as u32).into(), relative_height: NRDRelativeHeight::new(1440).unwrap(), }, ); @@ -85,7 +89,7 @@ fn test_nrd_kernels_enabled() { assert_eq!(header.height, 3 * consensus::TESTING_HARD_FORK_INTERVAL); assert_eq!(header.version, HeaderVersion(4)); - // NRD kernel support not enabled via feature flag, so not valid. + // NRD kernel support enabled via feature flag, so valid. assert_eq!( pool.add_to_pool(test_source(), tx_1.clone(), false, &header), Ok(()) diff --git a/pool/tests/transaction_pool.rs b/pool/tests/transaction_pool.rs index 2f97239598..61f4a4876b 100644 --- a/pool/tests/transaction_pool.rs +++ b/pool/tests/transaction_pool.rs @@ -50,7 +50,8 @@ fn test_the_transaction_pool() { verifier_cache.clone(), ); - add_some_blocks(&chain, 3, &keychain); + // mine past HF4 to see effect of set_local_accept_fee_base + add_some_blocks(&chain, 4 * 3, &keychain); let header = chain.head_header().unwrap(); let header_1 = chain.get_header_by_height(1).unwrap(); @@ -192,7 +193,7 @@ fn test_the_transaction_pool() { // tx4 is the "new" part of this aggregated tx that we care about let agg_tx = transaction::aggregate(&[tx1.clone(), tx2.clone(), tx4]).unwrap(); - let height = 4; + let height = 12 + 1; agg_tx .validate(Weighting::AsTransaction, verifier_cache.clone(), height) .unwrap(); From 2e5a3dca9d93991a5608955be9e439fa554f7cac Mon Sep 17 00:00:00 2001 From: Jasper van der Maarel Date: Thu, 26 Nov 2020 16:59:56 +0100 Subject: [PATCH 24/24] Accept any u64 in FeeFields deser --- core/src/core/transaction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index 41f08448b0..e7a3a43fa4 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -101,7 +101,7 @@ impl<'de> Deserialize<'de> for FeeFields { where E: de::Error, { - FeeFields::try_from(value).map_err(|_| E::custom(format!("invalid fee field"))) + Ok(FeeFields(value)) } }