use anyhow::anyhow;
use fvm_ipld_encoding::{RawBytes, de::Deserializer, ser::Serializer};
use fvm_shared2::message::Message as Message_v2;
pub use fvm_shared3::METHOD_SEND;
pub use fvm_shared3::message::Message as Message_v3;
use fvm_shared4::message::Message as Message_v4;
use get_size2::GetSize;
use serde::{Deserialize, Serialize};
use crate::shim::{address::Address, econ::TokenAmount};
use crate::utils::get_size::raw_bytes_heap_size_helper;
pub type MethodNum = u64;
#[derive(Clone, Default, PartialEq, Eq, Debug, Hash, GetSize)]
#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
pub struct Message {
pub version: u64,
pub from: Address,
pub to: Address,
pub sequence: u64,
pub value: TokenAmount,
pub method_num: MethodNum,
#[cfg_attr(test, arbitrary(gen(
|g| RawBytes::new(Vec::arbitrary(g))
)))]
#[get_size(size_fn = raw_bytes_heap_size_helper)]
pub params: RawBytes,
pub gas_limit: u64,
pub gas_fee_cap: TokenAmount,
pub gas_premium: TokenAmount,
}
macro_rules! message_conversion {
($($version:ty),+ $(,)?) => {
$(
impl From<$version> for Message {
fn from(other: $version) -> Self {
Self {
version: other.version,
from: other.from.into(),
to: other.to.into(),
sequence: other.sequence,
value: other.value.into(),
method_num: other.method_num,
params: other.params,
gas_limit: other.gas_limit,
gas_fee_cap: other.gas_fee_cap.into(),
gas_premium: other.gas_premium.into(),
}
}
}
impl From<Message> for $version {
fn from(other: Message) -> Self {
Self {
version: other.version,
from: other.from.into(),
to: other.to.into(),
sequence: other.sequence,
value: other.value.into(),
method_num: other.method_num,
params: other.params,
gas_limit: other.gas_limit,
gas_fee_cap: other.gas_fee_cap.into(),
gas_premium: other.gas_premium.into(),
}
}
}
impl From<&Message> for $version {
fn from(other: &Message) -> Self {
other.clone().into()
}
}
)+
};
}
message_conversion!(Message_v3, Message_v4);
impl From<Message_v2> for Message {
fn from(other: Message_v2) -> Self {
Self {
version: other.version as u64,
from: other.from.into(),
to: other.to.into(),
sequence: other.sequence,
value: other.value.into(),
method_num: other.method_num,
params: other.params,
gas_limit: other.gas_limit as u64,
gas_fee_cap: other.gas_fee_cap.into(),
gas_premium: other.gas_premium.into(),
}
}
}
impl TryFrom<Message> for Message_v2 {
type Error = anyhow::Error;
fn try_from(other: Message) -> Result<Self, Self::Error> {
Ok(Self {
version: other.version as i64,
from: other.from.try_to_v2()?,
to: other.to.try_to_v2()?,
sequence: other.sequence,
value: other.value.into(),
method_num: other.method_num,
params: other.params,
gas_limit: other.gas_limit as i64,
gas_fee_cap: other.gas_fee_cap.into(),
gas_premium: other.gas_premium.into(),
})
}
}
impl TryFrom<&Message> for Message_v2 {
type Error = anyhow::Error;
fn try_from(other: &Message) -> Result<Self, Self::Error> {
other.clone().try_into()
}
}
impl Message {
pub fn check(self: &Message) -> anyhow::Result<()> {
if self.gas_limit == 0 {
return Err(anyhow!("Message has no gas limit set"));
}
if self.gas_limit > i64::MAX as u64 {
return Err(anyhow!("Message gas exceeds i64 max"));
}
Ok(())
}
pub fn transfer(from: Address, to: Address, value: TokenAmount) -> Self {
Message {
from,
to,
value,
method_num: METHOD_SEND,
..Default::default()
}
}
pub fn cid(&self) -> cid::Cid {
use crate::utils::cid::CidCborExt;
cid::Cid::from_cbor_blake2b256(self).expect("message serialization is infallible")
}
pub fn equal_call(&self, other: &Self) -> bool {
self.version == other.version
&& self.from == other.from
&& self.to == other.to
&& self.sequence == other.sequence
&& self.value == other.value
&& self.method_num == other.method_num
&& self.params == other.params
}
}
impl Serialize for Message {
fn serialize<S>(&self, s: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
(
&self.version,
&self.to,
&self.from,
&self.sequence,
&self.value,
&self.gas_limit,
&self.gas_fee_cap,
&self.gas_premium,
&self.method_num,
&self.params,
)
.serialize(s)
}
}
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let (
version,
to,
from,
sequence,
value,
gas_limit,
gas_fee_cap,
gas_premium,
method_num,
params,
) = Deserialize::deserialize(deserializer)?;
Ok(Self {
version,
from,
to,
sequence,
value,
method_num,
params,
gas_limit,
gas_fee_cap,
gas_premium,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn message_v4_roundtrip(msg: Message) {
assert_eq!(Message::from(Message_v4::from(msg.clone())), msg);
}
#[quickcheck]
fn message_v3_roundtrip(msg: Message) {
assert_eq!(Message::from(Message_v3::from(msg.clone())), msg);
}
#[quickcheck]
fn message_v2_roundtrip(msg: Message) {
use crate::shim::address::Protocol;
let representable =
msg.from.protocol() != Protocol::Delegated && msg.to.protocol() != Protocol::Delegated;
match Message_v2::try_from(msg.clone()) {
Ok(v2) => {
assert!(representable);
assert_eq!(Message::from(v2), msg);
}
Err(_) => assert!(!representable),
}
}
}