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