Skip to main content

forest/shim/
message.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use anyhow::anyhow;
5use fvm_ipld_encoding::{RawBytes, de::Deserializer, ser::Serializer};
6use fvm_shared2::message::Message as Message_v2;
7pub use fvm_shared3::METHOD_SEND;
8pub use fvm_shared3::message::Message as Message_v3;
9use fvm_shared4::message::Message as Message_v4;
10use get_size2::GetSize;
11use serde::{Deserialize, Serialize};
12
13use crate::shim::{address::Address, econ::TokenAmount};
14
15/// Method number indicator for calling actor methods.
16pub type MethodNum = u64;
17
18#[derive(Clone, Default, PartialEq, Eq, Debug, Hash, GetSize)]
19#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
20pub struct Message {
21    pub version: u64,
22    pub from: Address,
23    pub to: Address,
24    pub sequence: u64,
25    pub value: TokenAmount,
26    pub method_num: MethodNum,
27    #[cfg_attr(test, arbitrary(gen(
28        |g| RawBytes::new(Vec::arbitrary(g))
29    )))]
30    #[get_size(size_fn = raw_bytes_heap_size)]
31    pub params: RawBytes,
32    pub gas_limit: u64,
33    pub gas_fee_cap: TokenAmount,
34    pub gas_premium: TokenAmount,
35}
36
37fn raw_bytes_heap_size(b: &RawBytes) -> usize {
38    // Note: this is a cheap but inaccurate estimation,
39    // the correct implementation should be `Vec<u8>.from(b.clone()).get_heap_size()`,
40    // or `b.bytes.get_heap_size()` if `bytes` is made public.
41    b.bytes().get_heap_size()
42}
43
44impl From<Message_v4> for Message {
45    fn from(other: Message_v4) -> Self {
46        Self {
47            version: other.version,
48            from: other.from.into(),
49            to: other.to.into(),
50            sequence: other.sequence,
51            value: other.value.into(),
52            method_num: other.method_num,
53            params: other.params,
54            gas_limit: other.gas_limit,
55            gas_fee_cap: other.gas_fee_cap.into(),
56            gas_premium: other.gas_premium.into(),
57        }
58    }
59}
60
61impl From<Message> for Message_v4 {
62    fn from(other: Message) -> Self {
63        (&other).into()
64    }
65}
66
67impl From<&Message> for Message_v4 {
68    fn from(other: &Message) -> Self {
69        let other: Message = other.clone();
70        Self {
71            version: other.version,
72            from: other.from.into(),
73            to: other.to.into(),
74            sequence: other.sequence,
75            value: other.value.into(),
76            method_num: other.method_num,
77            params: other.params,
78            gas_limit: other.gas_limit,
79            gas_fee_cap: other.gas_fee_cap.into(),
80            gas_premium: other.gas_premium.into(),
81        }
82    }
83}
84
85impl From<Message_v3> for Message {
86    fn from(other: Message_v3) -> Self {
87        Self {
88            version: other.version,
89            from: other.from.into(),
90            to: other.to.into(),
91            sequence: other.sequence,
92            value: other.value.into(),
93            method_num: other.method_num,
94            params: other.params,
95            gas_limit: other.gas_limit,
96            gas_fee_cap: other.gas_fee_cap.into(),
97            gas_premium: other.gas_premium.into(),
98        }
99    }
100}
101
102impl From<Message> for Message_v3 {
103    fn from(other: Message) -> Self {
104        (&other).into()
105    }
106}
107
108impl From<&Message> for Message_v3 {
109    fn from(other: &Message) -> Self {
110        let other: Message = other.clone();
111        Self {
112            version: other.version,
113            from: other.from.into(),
114            to: other.to.into(),
115            sequence: other.sequence,
116            value: other.value.into(),
117            method_num: other.method_num,
118            params: other.params,
119            gas_limit: other.gas_limit,
120            gas_fee_cap: other.gas_fee_cap.into(),
121            gas_premium: other.gas_premium.into(),
122        }
123    }
124}
125
126impl From<Message_v2> for Message {
127    fn from(other: Message_v2) -> Self {
128        Self {
129            version: other.version as u64,
130            from: other.from.into(),
131            to: other.to.into(),
132            sequence: other.sequence,
133            value: other.value.into(),
134            method_num: other.method_num,
135            params: other.params,
136            gas_limit: other.gas_limit as u64,
137            gas_fee_cap: other.gas_fee_cap.into(),
138            gas_premium: other.gas_premium.into(),
139        }
140    }
141}
142
143impl From<Message> for Message_v2 {
144    fn from(other: Message) -> Self {
145        Self {
146            version: other.version as i64,
147            from: other.from.into(),
148            to: other.to.into(),
149            sequence: other.sequence,
150            value: other.value.into(),
151            method_num: other.method_num,
152            params: other.params,
153            gas_limit: other.gas_limit as i64,
154            gas_fee_cap: other.gas_fee_cap.into(),
155            gas_premium: other.gas_premium.into(),
156        }
157    }
158}
159
160impl From<&Message> for Message_v2 {
161    fn from(other: &Message) -> Self {
162        other.clone().into()
163    }
164}
165
166impl Message {
167    /// Does some basic checks on the Message to see if the fields are valid.
168    pub fn check(self: &Message) -> anyhow::Result<()> {
169        if self.gas_limit == 0 {
170            return Err(anyhow!("Message has no gas limit set"));
171        }
172        if self.gas_limit > i64::MAX as u64 {
173            return Err(anyhow!("Message gas exceeds i64 max"));
174        }
175        Ok(())
176    }
177
178    /// Creates a new Message to transfer an amount of FIL specified in the `value` field.
179    pub fn transfer(from: Address, to: Address, value: TokenAmount) -> Self {
180        Message {
181            from,
182            to,
183            value,
184            method_num: METHOD_SEND,
185            ..Default::default()
186        }
187    }
188
189    pub fn cid(&self) -> cid::Cid {
190        use crate::utils::cid::CidCborExt;
191        cid::Cid::from_cbor_blake2b256(self).expect("message serialization is infallible")
192    }
193
194    /// Tests if a message is equivalent to another replacing message.
195    /// A replacing message is a message with a different CID,
196    /// any of Gas values, and different signature, but with all
197    /// other parameters matching (source/destination, nonce, parameters, etc.)
198    /// See <https://github.com/filecoin-project/lotus/blob/813d133c24295629ef442fc3aa60e6e6b2101226/chain/types/message.go#L138>
199    pub fn equal_call(&self, other: &Self) -> bool {
200        self.version == other.version
201            && self.from == other.from
202            && self.to == other.to
203            && self.sequence == other.sequence
204            && self.value == other.value
205            && self.method_num == other.method_num
206            && self.params == other.params
207    }
208}
209
210impl Serialize for Message {
211    fn serialize<S>(&self, s: S) -> std::result::Result<S::Ok, S::Error>
212    where
213        S: Serializer,
214    {
215        (
216            &self.version,
217            &self.to,
218            &self.from,
219            &self.sequence,
220            &self.value,
221            &self.gas_limit,
222            &self.gas_fee_cap,
223            &self.gas_premium,
224            &self.method_num,
225            &self.params,
226        )
227            .serialize(s)
228    }
229}
230
231impl<'de> Deserialize<'de> for Message {
232    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
233    where
234        D: Deserializer<'de>,
235    {
236        let (
237            version,
238            to,
239            from,
240            sequence,
241            value,
242            gas_limit,
243            gas_fee_cap,
244            gas_premium,
245            method_num,
246            params,
247        ) = Deserialize::deserialize(deserializer)?;
248        Ok(Self {
249            version,
250            from,
251            to,
252            sequence,
253            value,
254            method_num,
255            params,
256            gas_limit,
257            gas_fee_cap,
258            gas_premium,
259        })
260    }
261}