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
137impl From<Message> for Message_v2 {
138    fn from(other: Message) -> Self {
139        Self {
140            version: other.version as i64,
141            from: other.from.into(),
142            to: other.to.into(),
143            sequence: other.sequence,
144            value: other.value.into(),
145            method_num: other.method_num,
146            params: other.params,
147            gas_limit: other.gas_limit as i64,
148            gas_fee_cap: other.gas_fee_cap.into(),
149            gas_premium: other.gas_premium.into(),
150        }
151    }
152}
153
154impl From<&Message> for Message_v2 {
155    fn from(other: &Message) -> Self {
156        other.clone().into()
157    }
158}
159
160impl Message {
161    /// Does some basic checks on the Message to see if the fields are valid.
162    pub fn check(self: &Message) -> anyhow::Result<()> {
163        if self.gas_limit == 0 {
164            return Err(anyhow!("Message has no gas limit set"));
165        }
166        if self.gas_limit > i64::MAX as u64 {
167            return Err(anyhow!("Message gas exceeds i64 max"));
168        }
169        Ok(())
170    }
171
172    /// Creates a new Message to transfer an amount of FIL specified in the `value` field.
173    pub fn transfer(from: Address, to: Address, value: TokenAmount) -> Self {
174        Message {
175            from,
176            to,
177            value,
178            method_num: METHOD_SEND,
179            ..Default::default()
180        }
181    }
182
183    pub fn cid(&self) -> cid::Cid {
184        use crate::utils::cid::CidCborExt;
185        cid::Cid::from_cbor_blake2b256(self).expect("message serialization is infallible")
186    }
187
188    /// Tests if a message is equivalent to another replacing message.
189    /// A replacing message is a message with a different CID,
190    /// any of Gas values, and different signature, but with all
191    /// other parameters matching (source/destination, nonce, parameters, etc.)
192    /// See <https://github.com/filecoin-project/lotus/blob/813d133c24295629ef442fc3aa60e6e6b2101226/chain/types/message.go#L138>
193    pub fn equal_call(&self, other: &Self) -> bool {
194        self.version == other.version
195            && self.from == other.from
196            && self.to == other.to
197            && self.sequence == other.sequence
198            && self.value == other.value
199            && self.method_num == other.method_num
200            && self.params == other.params
201    }
202}
203
204impl Serialize for Message {
205    fn serialize<S>(&self, s: S) -> std::result::Result<S::Ok, S::Error>
206    where
207        S: Serializer,
208    {
209        (
210            &self.version,
211            &self.to,
212            &self.from,
213            &self.sequence,
214            &self.value,
215            &self.gas_limit,
216            &self.gas_fee_cap,
217            &self.gas_premium,
218            &self.method_num,
219            &self.params,
220        )
221            .serialize(s)
222    }
223}
224
225impl<'de> Deserialize<'de> for Message {
226    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
227    where
228        D: Deserializer<'de>,
229    {
230        let (
231            version,
232            to,
233            from,
234            sequence,
235            value,
236            gas_limit,
237            gas_fee_cap,
238            gas_premium,
239            method_num,
240            params,
241        ) = Deserialize::deserialize(deserializer)?;
242        Ok(Self {
243            version,
244            from,
245            to,
246            sequence,
247            value,
248            method_num,
249            params,
250            gas_limit,
251            gas_fee_cap,
252            gas_premium,
253        })
254    }
255}