1pub mod chain_message;
5pub mod signed_message;
6
7use crate::shim::message::MethodNum;
8use crate::shim::{address::Address, econ::TokenAmount, message::Message};
9use crate::shim::{gas::Gas, version::NetworkVersion};
10use ambassador::delegatable_trait;
11pub use chain_message::ChainMessage;
12use fvm_ipld_encoding::RawBytes;
13use num::Zero;
14pub use signed_message::SignedMessage;
15
16#[auto_impl::auto_impl(&, Arc)]
19#[delegatable_trait]
20pub trait MessageRead {
21 fn from(&self) -> Address;
23 fn to(&self) -> Address;
25 fn sequence(&self) -> u64;
27 fn value(&self) -> TokenAmount;
29 fn method_num(&self) -> MethodNum;
31 fn params(&self) -> &RawBytes;
33 fn gas_limit(&self) -> u64;
35 fn required_funds(&self) -> TokenAmount;
37 fn gas_fee_cap(&self) -> TokenAmount;
39 fn gas_premium(&self) -> TokenAmount;
41 fn effective_gas_premium(&self, base_fee: &TokenAmount) -> TokenAmount {
47 let available = self.gas_fee_cap() - base_fee;
48 available.clamp(TokenAmount::zero(), self.gas_premium())
51 }
52}
53
54pub trait MessageReadWrite: MessageRead {
57 fn set_gas_limit(&mut self, amount: u64);
59 fn set_sequence(&mut self, sequence: u64);
61 fn set_gas_fee_cap(&mut self, cap: TokenAmount);
63 fn set_gas_premium(&mut self, prem: TokenAmount);
65}
66
67impl MessageRead for Message {
68 fn from(&self) -> Address {
69 self.from
70 }
71 fn to(&self) -> Address {
72 self.to
73 }
74 fn sequence(&self) -> u64 {
75 self.sequence
76 }
77 fn value(&self) -> TokenAmount {
78 self.value.clone()
79 }
80 fn method_num(&self) -> MethodNum {
81 self.method_num
82 }
83 fn params(&self) -> &RawBytes {
84 &self.params
85 }
86 fn gas_limit(&self) -> u64 {
87 self.gas_limit
88 }
89 fn required_funds(&self) -> TokenAmount {
90 &self.gas_fee_cap * self.gas_limit
91 }
92 fn gas_fee_cap(&self) -> TokenAmount {
93 self.gas_fee_cap.clone()
94 }
95 fn gas_premium(&self) -> TokenAmount {
96 self.gas_premium.clone()
97 }
98}
99
100impl MessageReadWrite for Message {
101 fn set_gas_limit(&mut self, token_amount: u64) {
102 self.gas_limit = token_amount;
103 }
104 fn set_sequence(&mut self, new_sequence: u64) {
105 self.sequence = new_sequence;
106 }
107 fn set_gas_fee_cap(&mut self, cap: TokenAmount) {
108 self.gas_fee_cap = cap;
109 }
110 fn set_gas_premium(&mut self, prem: TokenAmount) {
111 self.gas_premium = prem;
112 }
113}
114
115pub fn valid_for_block_inclusion(
117 msg: &Message,
118 min_gas: Gas,
119 version: NetworkVersion,
120) -> anyhow::Result<()> {
121 use crate::shim::address::ZERO_ADDRESS;
122 use crate::shim::econ::{BLOCK_GAS_LIMIT, TOTAL_FILECOIN};
123 if msg.version != 0 {
124 anyhow::bail!("Message version: {} not supported", msg.version);
125 }
126 if msg.to == *ZERO_ADDRESS && version >= NetworkVersion::V7 {
127 anyhow::bail!("invalid 'to' address");
128 }
129 if msg.value.is_negative() {
130 anyhow::bail!("message value cannot be negative");
131 }
132 if msg.value > *TOTAL_FILECOIN {
133 anyhow::bail!("message value cannot be greater than total FIL supply");
134 }
135 if msg.gas_fee_cap.is_negative() {
136 anyhow::bail!("gas_fee_cap cannot be negative");
137 }
138 if msg.gas_premium.is_negative() {
139 anyhow::bail!("gas_premium cannot be negative");
140 }
141 if msg.gas_premium > msg.gas_fee_cap {
142 anyhow::bail!("gas_fee_cap less than gas_premium");
143 }
144 if msg.gas_limit > BLOCK_GAS_LIMIT {
145 anyhow::bail!(
146 "gas_limit {} cannot be greater than block gas limit",
147 msg.gas_limit
148 );
149 }
150
151 if Gas::new(msg.gas_limit) < min_gas {
152 anyhow::bail!(
153 "gas_limit {} cannot be less than cost {} of storing a message on chain",
154 msg.gas_limit,
155 min_gas
156 );
157 }
158
159 Ok(())
160}
161
162#[cfg(test)]
163mod tests {
164 mod builder_test;
165
166 use itertools::Itertools;
167
168 use super::*;
169
170 #[test]
171 fn test_effective_gas_premium() {
172 let test_cases = vec![
175 (8, 8, 8, 0),
177 (8, 16, 7, 7),
178 (8, 19, 10, 10),
179 (123456, 123455, 123455, 0),
180 (123456, 1234567, 1111112, 1111111),
181 ]
182 .into_iter()
183 .map(|(base_fee, gas_fee_cap, gas_premium, expected)| {
184 (
185 TokenAmount::from_atto(base_fee),
186 TokenAmount::from_atto(gas_fee_cap),
187 TokenAmount::from_atto(gas_premium),
188 TokenAmount::from_atto(expected),
189 )
190 })
191 .collect_vec();
192
193 for (base_fee, gas_fee_cap, gas_premium, expected) in test_cases.into_iter() {
194 let msg = Message {
195 gas_fee_cap: gas_fee_cap.clone(),
196 gas_premium: gas_premium.clone(),
197 ..Default::default()
198 };
199
200 let result = msg.effective_gas_premium(&base_fee);
201 assert_eq!(
202 result, expected,
203 "base_fee={} gas_fee_cap={} gas_premium={} expected={} got={}",
204 base_fee, gas_fee_cap, gas_premium, expected, result
205 );
206 }
207 }
208}