diff --git a/fendermint/app/src/app.rs b/fendermint/app/src/app.rs index 599afcdb6..427c70641 100644 --- a/fendermint/app/src/app.rs +++ b/fendermint/app/src/app.rs @@ -646,9 +646,6 @@ where .await .context("error running check")?; - // Update the check state. - *guard = Some(state); - let mut mpool_received_trace = MpoolReceived::default(); let response = match result { @@ -658,11 +655,16 @@ where Ok(Err(InvalidSignature(d))) => invalid_check_tx(AppError::InvalidSignature, d), Ok(Ok(ret)) => { mpool_received_trace.message = Some(Message::from(&ret.message)); - to_check_tx(ret) + + let priority = state.txn_priority_calculator().priority(&ret.message); + to_check_tx(ret, priority) } }, }; + // Update the check state. + *guard = Some(state); + mpool_received_trace.accept = response.code.is_ok(); if !mpool_received_trace.accept { mpool_received_trace.reason = Some(format!("{:?} - {}", response.code, response.info)); diff --git a/fendermint/app/src/tmconv.rs b/fendermint/app/src/tmconv.rs index 029270e47..407c9f4f9 100644 --- a/fendermint/app/src/tmconv.rs +++ b/fendermint/app/src/tmconv.rs @@ -125,7 +125,7 @@ pub fn to_deliver_tx( } } -pub fn to_check_tx(ret: FvmCheckRet) -> response::CheckTx { +pub fn to_check_tx(ret: FvmCheckRet, priority: i64) -> response::CheckTx { // Putting the message `log` because only `log` appears in the `tx_sync` JSON-RPC response. let message = ret .info @@ -144,6 +144,7 @@ pub fn to_check_tx(ret: FvmCheckRet) -> response::CheckTx { data, gas_wanted: ret.gas_limit.try_into().unwrap_or(i64::MAX), sender: ret.sender.to_string(), + priority, ..Default::default() } } diff --git a/fendermint/vm/interpreter/src/fvm/gas.rs b/fendermint/vm/interpreter/src/fvm/gas.rs index a0c5289b4..6b59f42d6 100644 --- a/fendermint/vm/interpreter/src/fvm/gas.rs +++ b/fendermint/vm/interpreter/src/fvm/gas.rs @@ -26,6 +26,10 @@ pub struct BlockGasTracker { } impl BlockGasTracker { + pub fn base_fee(&self) -> &TokenAmount { + &self.base_fee + } + pub fn create(executor: &mut E) -> anyhow::Result { let mut ret = Self { base_fee: Zero::zero(), diff --git a/fendermint/vm/interpreter/src/fvm/state/exec.rs b/fendermint/vm/interpreter/src/fvm/state/exec.rs index 49666634c..4d2c3985b 100644 --- a/fendermint/vm/interpreter/src/fvm/state/exec.rs +++ b/fendermint/vm/interpreter/src/fvm/state/exec.rs @@ -6,6 +6,7 @@ use std::collections::{HashMap, HashSet}; use crate::fvm::activity::actor::ActorActivityTracker; use crate::fvm::externs::FendermintExterns; use crate::fvm::gas::BlockGasTracker; +use crate::fvm::state::priority::TxnPriorityCalculator; use anyhow::Ok; use cid::Cid; use fendermint_actors_api::gas_market::Reading; @@ -149,6 +150,8 @@ where params: FvmUpdatableParams, /// Indicate whether the parameters have been updated. params_dirty: bool, + + txn_priority: TxnPriorityCalculator, } impl FvmExecState @@ -185,6 +188,7 @@ where let mut executor = DefaultExecutor::new(engine.clone(), machine)?; let block_gas_tracker = BlockGasTracker::create(&mut executor)?; + let base_fee = block_gas_tracker.base_fee().clone(); Ok(Self { executor, @@ -198,6 +202,7 @@ where power_scale: params.power_scale, }, params_dirty: false, + txn_priority: TxnPriorityCalculator::new(base_fee), }) } @@ -312,6 +317,10 @@ where self.params.power_scale } + pub fn txn_priority_calculator(&self) -> &TxnPriorityCalculator { + &self.txn_priority + } + pub fn app_version(&self) -> u64 { self.params.app_version } diff --git a/fendermint/vm/interpreter/src/fvm/state/mod.rs b/fendermint/vm/interpreter/src/fvm/state/mod.rs index ab3d9e7f6..ba601f0a2 100644 --- a/fendermint/vm/interpreter/src/fvm/state/mod.rs +++ b/fendermint/vm/interpreter/src/fvm/state/mod.rs @@ -8,6 +8,7 @@ pub mod snapshot; mod check; mod exec; mod genesis; +mod priority; mod query; use std::sync::Arc; diff --git a/fendermint/vm/interpreter/src/fvm/state/priority.rs b/fendermint/vm/interpreter/src/fvm/state/priority.rs new file mode 100644 index 000000000..f17799f68 --- /dev/null +++ b/fendermint/vm/interpreter/src/fvm/state/priority.rs @@ -0,0 +1,80 @@ +// Copyright 2022-2024 Protocol Labs +// SPDX-License-Identifier: Apache-2.0, MIT + +use crate::fvm::FvmMessage; +use fvm_shared::econ::TokenAmount; +use num_traits::ToPrimitive; + +/// The transaction priority calculator. The priority calculated is used to determine the ordering +/// in the mempool. +pub struct TxnPriorityCalculator { + base_fee: TokenAmount, +} + +impl TxnPriorityCalculator { + pub fn new(base_fee: TokenAmount) -> Self { + Self { base_fee } + } + + pub fn priority(&self, msg: &FvmMessage) -> i64 { + if msg.gas_fee_cap < self.base_fee { + return i64::MIN; + } + + let effective_premium = msg + .gas_premium + .clone() + .min(&msg.gas_fee_cap - &self.base_fee); + effective_premium.atto().to_i64().unwrap_or(i64::MAX) + } +} + +#[cfg(test)] +mod tests { + use crate::fvm::state::priority::TxnPriorityCalculator; + use crate::fvm::FvmMessage; + use fvm_shared::address::Address; + use fvm_shared::bigint::BigInt; + use fvm_shared::econ::TokenAmount; + + fn create_msg(fee_cap: TokenAmount, premium: TokenAmount) -> FvmMessage { + FvmMessage { + version: 0, + from: Address::new_id(10), + to: Address::new_id(12), + sequence: 0, + value: Default::default(), + method_num: 0, + params: Default::default(), + gas_limit: 0, + gas_fee_cap: fee_cap, + gas_premium: premium, + } + } + + #[test] + fn priority_calculation() { + let cal = TxnPriorityCalculator::new(TokenAmount::from_atto(30)); + + let msg = create_msg(TokenAmount::from_atto(1), TokenAmount::from_atto(20)); + assert_eq!(cal.priority(&msg), i64::MIN); + + let msg = create_msg(TokenAmount::from_atto(10), TokenAmount::from_atto(20)); + assert_eq!(cal.priority(&msg), i64::MIN); + + let msg = create_msg(TokenAmount::from_atto(35), TokenAmount::from_atto(20)); + assert_eq!(cal.priority(&msg), 5); + + let msg = create_msg(TokenAmount::from_atto(50), TokenAmount::from_atto(20)); + assert_eq!(cal.priority(&msg), 20); + + let msg = create_msg(TokenAmount::from_atto(50), TokenAmount::from_atto(10)); + assert_eq!(cal.priority(&msg), 10); + + let msg = create_msg( + TokenAmount::from_atto(BigInt::from(i128::MAX)), + TokenAmount::from_atto(BigInt::from(i128::MAX)), + ); + assert_eq!(cal.priority(&msg), i64::MAX); + } +}