forest/message/
mod.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4pub mod chain_message;
5pub mod signed_message;
6
7use crate::shim::message::MethodNum;
8use crate::shim::{address::Address, econ::TokenAmount, message::Message as ShimMessage};
9use crate::shim::{gas::Gas, version::NetworkVersion};
10pub use chain_message::ChainMessage;
11use fvm_ipld_encoding::RawBytes;
12use num::Zero;
13pub use signed_message::SignedMessage;
14
15/// Message interface to interact with Signed and unsigned messages in a generic
16/// context.
17pub trait Message {
18    /// Returns the from address of the message.
19    fn from(&self) -> Address;
20    /// Returns the destination address of the message.
21    fn to(&self) -> Address;
22    /// Returns the message sequence or nonce.
23    fn sequence(&self) -> u64;
24    /// Returns the amount sent in message.
25    fn value(&self) -> TokenAmount;
26    /// Returns the method number to be called.
27    fn method_num(&self) -> MethodNum;
28    /// Returns the encoded parameters for the method call.
29    fn params(&self) -> &RawBytes;
30    /// sets the gas limit for the message.
31    fn set_gas_limit(&mut self, amount: u64);
32    /// sets a new sequence to the message.
33    fn set_sequence(&mut self, sequence: u64);
34    /// Returns the gas limit for the message.
35    fn gas_limit(&self) -> u64;
36    /// Returns the required funds for the message.
37    fn required_funds(&self) -> TokenAmount;
38    /// gets gas fee cap for the message.
39    fn gas_fee_cap(&self) -> TokenAmount;
40    /// gets gas premium for the message.
41    fn gas_premium(&self) -> TokenAmount;
42    /// sets the gas fee cap.
43    fn set_gas_fee_cap(&mut self, cap: TokenAmount);
44    /// sets the gas premium.
45    fn set_gas_premium(&mut self, prem: TokenAmount);
46    /// This method returns the effective gas premium claimable by the miner
47    /// given the supplied base fee. This method is not used anywhere except the `Eth` API.
48    ///
49    /// Filecoin clamps the gas premium at `gas_fee_cap` - `base_fee`, if lower than the
50    /// specified premium. Returns 0 if `gas_fee_cap` is less than `base_fee`.
51    fn effective_gas_premium(&self, base_fee: &TokenAmount) -> TokenAmount {
52        let available = self.gas_fee_cap() - base_fee;
53        // It's possible that storage providers may include messages with gasFeeCap less than the baseFee
54        // In such cases, their reward should be viewed as zero
55        available.clamp(TokenAmount::zero(), self.gas_premium())
56    }
57}
58
59impl Message for ShimMessage {
60    fn from(&self) -> Address {
61        self.from
62    }
63    fn to(&self) -> Address {
64        self.to
65    }
66    fn sequence(&self) -> u64 {
67        self.sequence
68    }
69    fn value(&self) -> TokenAmount {
70        self.value.clone()
71    }
72    fn method_num(&self) -> MethodNum {
73        self.method_num
74    }
75    fn params(&self) -> &RawBytes {
76        &self.params
77    }
78    fn gas_limit(&self) -> u64 {
79        self.gas_limit
80    }
81    fn set_gas_limit(&mut self, token_amount: u64) {
82        self.gas_limit = token_amount;
83    }
84    fn set_sequence(&mut self, new_sequence: u64) {
85        self.sequence = new_sequence;
86    }
87    fn required_funds(&self) -> TokenAmount {
88        &self.gas_fee_cap * self.gas_limit
89    }
90    fn gas_fee_cap(&self) -> TokenAmount {
91        self.gas_fee_cap.clone()
92    }
93    fn gas_premium(&self) -> TokenAmount {
94        self.gas_premium.clone()
95    }
96
97    fn set_gas_fee_cap(&mut self, cap: TokenAmount) {
98        self.gas_fee_cap = cap;
99    }
100
101    fn set_gas_premium(&mut self, prem: TokenAmount) {
102        self.gas_premium = prem;
103    }
104}
105
106/// Semantic validation and validates the message has enough gas.
107pub fn valid_for_block_inclusion(
108    msg: &ShimMessage,
109    min_gas: Gas,
110    version: NetworkVersion,
111) -> Result<(), anyhow::Error> {
112    use crate::shim::address::ZERO_ADDRESS;
113    use crate::shim::econ::{BLOCK_GAS_LIMIT, TOTAL_FILECOIN};
114    if msg.version != 0 {
115        anyhow::bail!("Message version: {} not supported", msg.version);
116    }
117    if msg.to == *ZERO_ADDRESS && version >= NetworkVersion::V7 {
118        anyhow::bail!("invalid 'to' address");
119    }
120    if msg.value.is_negative() {
121        anyhow::bail!("message value cannot be negative");
122    }
123    if msg.value > *TOTAL_FILECOIN {
124        anyhow::bail!("message value cannot be greater than total FIL supply");
125    }
126    if msg.gas_fee_cap.is_negative() {
127        anyhow::bail!("gas_fee_cap cannot be negative");
128    }
129    if msg.gas_premium.is_negative() {
130        anyhow::bail!("gas_premium cannot be negative");
131    }
132    if msg.gas_premium > msg.gas_fee_cap {
133        anyhow::bail!("gas_fee_cap less than gas_premium");
134    }
135    if msg.gas_limit > BLOCK_GAS_LIMIT {
136        anyhow::bail!(
137            "gas_limit {} cannot be greater than block gas limit",
138            msg.gas_limit
139        );
140    }
141
142    if Gas::new(msg.gas_limit) < min_gas {
143        anyhow::bail!(
144            "gas_limit {} cannot be less than cost {} of storing a message on chain",
145            msg.gas_limit,
146            min_gas
147        );
148    }
149
150    Ok(())
151}
152
153#[cfg(test)]
154mod tests {
155    mod builder_test;
156}