From 57f9aac662e054965cdfa6d3d7414515a098419e Mon Sep 17 00:00:00 2001 From: Santiago Carmuega Date: Sat, 13 Aug 2022 15:45:16 -0300 Subject: [PATCH] chore: Move fee logic out of primitives (#174) --- pallas-primitives/src/byron/fees.rs | 73 ----------------------------- pallas-primitives/src/byron/mod.rs | 1 - pallas-traverse/Cargo.toml | 3 ++ pallas-traverse/src/fees.rs | 63 +++++++++++++++++++++++++ pallas-traverse/src/lib.rs | 1 + pallas-traverse/src/tx.rs | 28 ++++++++++- 6 files changed, 93 insertions(+), 76 deletions(-) delete mode 100644 pallas-primitives/src/byron/fees.rs create mode 100644 pallas-traverse/src/fees.rs diff --git a/pallas-primitives/src/byron/fees.rs b/pallas-primitives/src/byron/fees.rs deleted file mode 100644 index 9069149..0000000 --- a/pallas-primitives/src/byron/fees.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::Error; - -use super::TxPayload; -use pallas_codec::minicbor::to_vec; - -pub struct PolicyParams { - constant: u64, - size_coeficient: u64, -} - -impl Default for PolicyParams { - fn default() -> Self { - Self { - constant: 155_381_000_000_000u64, - size_coeficient: 43_946_000_000u64, - } - } -} - -fn compute_linear_fee_policy(tx_size: u64, params: &PolicyParams) -> u64 { - println!("tx size: {}", tx_size); - let nanos = params.constant + (tx_size * params.size_coeficient); - - let loves = nanos / 1_000_000_000; - - let rem = match nanos % 1_000_000_000 { - 0 => 0u64, - _ => 1u64, - }; - - loves + rem -} - -impl TxPayload { - pub fn compute_fee(&self, params: &PolicyParams) -> Result { - let tx_size = to_vec(&self)?.len(); - let fee = compute_linear_fee_policy(tx_size as u64, params); - - Ok(fee) - } - - pub fn compute_fee_with_defaults(&self) -> Result { - self.compute_fee(&PolicyParams::default()) - } -} - -#[cfg(test)] -mod tests { - use pallas_codec::minicbor; - - use crate::byron::Block; - - type BlockWrapper = (u16, Block); - - #[test] - fn known_fee_matches() { - // TODO: expand this test to include more test blocks - let block_idx = 1; - let block_str = include_str!("../../../test_data/byron4.block"); - - let block_bytes = hex::decode(block_str).expect(&format!("bad block file {}", block_idx)); - let (_, block): BlockWrapper = minicbor::decode(&block_bytes[..]) - .expect(&format!("error decoding cbor for file {}", block_idx)); - - // don't want to pass if we don't have tx in the block - assert!(block.body.tx_payload.len() > 0); - - for tx in block.body.tx_payload.iter().take(1) { - let fee = tx.compute_fee_with_defaults().unwrap(); - assert_eq!(fee, 171070); - } - } -} diff --git a/pallas-primitives/src/byron/mod.rs b/pallas-primitives/src/byron/mod.rs index 8e5f65b..7c075fa 100644 --- a/pallas-primitives/src/byron/mod.rs +++ b/pallas-primitives/src/byron/mod.rs @@ -1,6 +1,5 @@ //! Ledger primitives and cbor codec for the Byron era -mod fees; mod model; pub use model::*; diff --git a/pallas-traverse/Cargo.toml b/pallas-traverse/Cargo.toml index 2f35108..cd45cca 100644 --- a/pallas-traverse/Cargo.toml +++ b/pallas-traverse/Cargo.toml @@ -17,3 +17,6 @@ pallas-crypto = { version = "0.13.0", path = "../pallas-crypto" } pallas-codec = { version = "0.13.0", path = "../pallas-codec" } hex = "0.4.3" thiserror = "1.0.31" + +[features] +unstable = [] diff --git a/pallas-traverse/src/fees.rs b/pallas-traverse/src/fees.rs new file mode 100644 index 0000000..0f8f0ec --- /dev/null +++ b/pallas-traverse/src/fees.rs @@ -0,0 +1,63 @@ +use pallas_codec::minicbor::to_vec; +use pallas_primitives::byron; + +pub struct PolicyParams { + constant: u64, + size_coeficient: u64, +} + +impl Default for PolicyParams { + fn default() -> Self { + Self { + constant: 155_381_000_000_000u64, + size_coeficient: 43_946_000_000u64, + } + } +} + +pub fn compute_linear_fee_policy(tx_size: u64, params: &PolicyParams) -> u64 { + let nanos = params.constant + (tx_size * params.size_coeficient); + + let loves = nanos / 1_000_000_000; + + let rem = match nanos % 1_000_000_000 { + 0 => 0u64, + _ => 1u64, + }; + + loves + rem +} + +pub fn compute_byron_fee(tx: &byron::MintedTxPayload, params: Option<&PolicyParams>) -> u64 { + let tx_size = to_vec(&tx).unwrap().len(); + + match params { + Some(params) => compute_linear_fee_policy(tx_size as u64, params), + None => compute_linear_fee_policy(tx_size as u64, &PolicyParams::default()), + } +} + +#[cfg(test)] +mod tests { + use super::compute_byron_fee; + + #[test] + fn known_fee_matches() { + // TODO: expand this test to include more test blocks + let block_idx = 1; + let block_str = include_str!("../../test_data/byron4.block"); + + let block_bytes = hex::decode(block_str).expect(&format!("bad block file {}", block_idx)); + let block = crate::MultiEraBlock::decode_byron(&block_bytes).unwrap(); + let txs = block.txs(); + + // don't want to pass if we don't have tx in the block + assert!(txs.len() > 0); + + for tx in txs.iter().take(1) { + let byron = tx.as_byron().unwrap(); + let fee = compute_byron_fee(&byron, None); + assert_eq!(fee, 171070); + } + } +} diff --git a/pallas-traverse/src/lib.rs b/pallas-traverse/src/lib.rs index 86188ef..c519f5c 100644 --- a/pallas-traverse/src/lib.rs +++ b/pallas-traverse/src/lib.rs @@ -12,6 +12,7 @@ use pallas_primitives::{alonzo, babbage, byron}; pub mod block; pub mod cert; pub mod era; +pub mod fees; pub mod hashes; pub mod header; pub mod input; diff --git a/pallas-traverse/src/tx.rs b/pallas-traverse/src/tx.rs index 94a4edc..2600393 100644 --- a/pallas-traverse/src/tx.rs +++ b/pallas-traverse/src/tx.rs @@ -1,8 +1,10 @@ -use crate::hashes::ToHash; +use std::{borrow::Cow, ops::Deref}; + use pallas_codec::{minicbor, utils::KeepRaw}; use pallas_crypto::hash::Hash; use pallas_primitives::{alonzo, babbage, byron}; -use std::{borrow::Cow, ops::Deref}; + +use crate::hashes::ToHash; use crate::{ Era, MultiEraCert, MultiEraInput, MultiEraMeta, MultiEraMint, MultiEraOutput, MultiEraSigners, @@ -232,6 +234,28 @@ impl<'b> MultiEraTx<'b> { } } + pub fn fee(&self) -> Option { + match self { + MultiEraTx::AlonzoCompatible(x, _) => Some(x.transaction_body.fee), + MultiEraTx::Babbage(x) => Some(x.transaction_body.fee), + MultiEraTx::Byron(_) => None, + } + } + + /// Returns the fee or attempts to compute it + /// + /// If the fee is available as part of the tx data (post-byron), this + /// function will return the existing value. For byron txs, this method + /// attempts to compute the value by using the linear fee policy. + #[cfg(feature = "unstable")] + pub fn fee_or_compute(&self) -> u64 { + match self { + MultiEraTx::AlonzoCompatible(x, _) => x.transaction_body.fee, + MultiEraTx::Babbage(x) => x.transaction_body.fee, + MultiEraTx::Byron(x) => crate::fees::compute_byron_fee(x, None), + } + } + fn aux_data(&self) -> Option<&KeepRaw<'_, alonzo::AuxiliaryData>> { match self { MultiEraTx::AlonzoCompatible(x, _) => match &x.auxiliary_data {