pub mod chain_message;
pub mod signed_message;
use crate::shim::message::MethodNum;
use crate::shim::{address::Address, econ::TokenAmount, message::Message};
use crate::shim::{gas::Gas, version::NetworkVersion};
use ambassador::delegatable_trait;
pub use chain_message::ChainMessage;
use fvm_ipld_encoding::RawBytes;
use num::Zero;
pub use signed_message::SignedMessage;
#[auto_impl::auto_impl(&, Arc)]
#[delegatable_trait]
pub trait MessageRead {
fn from(&self) -> Address;
fn to(&self) -> Address;
fn sequence(&self) -> u64;
fn value(&self) -> TokenAmount;
fn method_num(&self) -> MethodNum;
fn params(&self) -> &RawBytes;
fn gas_limit(&self) -> u64;
fn required_funds(&self) -> TokenAmount;
fn gas_fee_cap(&self) -> TokenAmount;
fn gas_premium(&self) -> TokenAmount;
fn effective_gas_premium(&self, base_fee: &TokenAmount) -> TokenAmount {
let available = self.gas_fee_cap() - base_fee;
available.clamp(TokenAmount::zero(), self.gas_premium())
}
}
pub trait MessageReadWrite: MessageRead {
fn set_gas_limit(&mut self, amount: u64);
fn set_sequence(&mut self, sequence: u64);
fn set_gas_fee_cap(&mut self, cap: TokenAmount);
fn set_gas_premium(&mut self, prem: TokenAmount);
}
impl MessageRead for Message {
fn from(&self) -> Address {
self.from
}
fn to(&self) -> Address {
self.to
}
fn sequence(&self) -> u64 {
self.sequence
}
fn value(&self) -> TokenAmount {
self.value.clone()
}
fn method_num(&self) -> MethodNum {
self.method_num
}
fn params(&self) -> &RawBytes {
&self.params
}
fn gas_limit(&self) -> u64 {
self.gas_limit
}
fn required_funds(&self) -> TokenAmount {
&self.gas_fee_cap * self.gas_limit
}
fn gas_fee_cap(&self) -> TokenAmount {
self.gas_fee_cap.clone()
}
fn gas_premium(&self) -> TokenAmount {
self.gas_premium.clone()
}
}
impl MessageReadWrite for Message {
fn set_gas_limit(&mut self, token_amount: u64) {
self.gas_limit = token_amount;
}
fn set_sequence(&mut self, new_sequence: u64) {
self.sequence = new_sequence;
}
fn set_gas_fee_cap(&mut self, cap: TokenAmount) {
self.gas_fee_cap = cap;
}
fn set_gas_premium(&mut self, prem: TokenAmount) {
self.gas_premium = prem;
}
}
pub fn valid_for_block_inclusion(
msg: &Message,
min_gas: Gas,
version: NetworkVersion,
) -> anyhow::Result<()> {
use crate::shim::address::ZERO_ADDRESS;
use crate::shim::econ::{BLOCK_GAS_LIMIT, TOTAL_FILECOIN};
if msg.version != 0 {
anyhow::bail!("Message version: {} not supported", msg.version);
}
if msg.to == *ZERO_ADDRESS && version >= NetworkVersion::V7 {
anyhow::bail!("invalid 'to' address");
}
if msg.value.is_negative() {
anyhow::bail!("message value cannot be negative");
}
if msg.value > *TOTAL_FILECOIN {
anyhow::bail!("message value cannot be greater than total FIL supply");
}
if msg.gas_fee_cap.is_negative() {
anyhow::bail!("gas_fee_cap cannot be negative");
}
if msg.gas_premium.is_negative() {
anyhow::bail!("gas_premium cannot be negative");
}
if msg.gas_premium > msg.gas_fee_cap {
anyhow::bail!("gas_fee_cap less than gas_premium");
}
if msg.gas_limit > BLOCK_GAS_LIMIT {
anyhow::bail!(
"gas_limit {} cannot be greater than block gas limit",
msg.gas_limit
);
}
if Gas::new(msg.gas_limit) < min_gas {
anyhow::bail!(
"gas_limit {} cannot be less than cost {} of storing a message on chain",
msg.gas_limit,
min_gas
);
}
Ok(())
}
#[cfg(test)]
mod tests {
mod builder_test;
use itertools::Itertools;
use super::*;
#[test]
fn test_effective_gas_premium() {
let test_cases = vec![
(8, 8, 8, 0),
(8, 16, 7, 7),
(8, 19, 10, 10),
(123456, 123455, 123455, 0),
(123456, 1234567, 1111112, 1111111),
]
.into_iter()
.map(|(base_fee, gas_fee_cap, gas_premium, expected)| {
(
TokenAmount::from_atto(base_fee),
TokenAmount::from_atto(gas_fee_cap),
TokenAmount::from_atto(gas_premium),
TokenAmount::from_atto(expected),
)
})
.collect_vec();
for (base_fee, gas_fee_cap, gas_premium, expected) in test_cases.into_iter() {
let msg = Message {
gas_fee_cap: gas_fee_cap.clone(),
gas_premium: gas_premium.clone(),
..Default::default()
};
let result = msg.effective_gas_premium(&base_fee);
assert_eq!(
result, expected,
"base_fee={} gas_fee_cap={} gas_premium={} expected={} got={}",
base_fee, gas_fee_cap, gas_premium, expected, result
);
}
}
}