diff --git a/Cargo.toml b/Cargo.toml index 76256e14..97aa17cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,3 +108,6 @@ opt-level = 1 [package.metadata.docs.rs] features = ["base64", "serde", "models", "sync", "stats", "abi"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } diff --git a/src/models/message/mod.rs b/src/models/message/mod.rs index 3fcb57a4..1a1959db 100644 --- a/src/models/message/mod.rs +++ b/src/models/message/mod.rs @@ -1,5 +1,7 @@ //! Message models. +use std::borrow::Borrow; + use crate::cell::*; use crate::error::Error; use crate::num::*; @@ -131,6 +133,13 @@ where } } +impl, B> BaseMessage { + /// Returns the type of this message. + pub fn ty(&self) -> MsgType { + self.info.borrow().ty() + } +} + impl BaseMessage { /// Computes the most optimal layout of the message parts. pub fn compute_layout(info: &I, init: Option<&StateInit>, body: &B) -> MessageLayout { @@ -576,6 +585,56 @@ impl<'a> Load<'a> for RelaxedMsgInfo { } } +/// Message type. +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +pub enum MsgType { + /// Internal message. + Int, + /// External incoming message. + ExtIn, + /// External outgoing message. + ExtOut, +} + +impl MsgType { + /// Returns whether this message is internal. + pub const fn is_internal(&self) -> bool { + matches!(self, Self::Int) + } + + /// Returns whether this message is external incoming. + pub const fn is_external_in(&self) -> bool { + matches!(self, Self::ExtIn) + } + + /// Returns whether this message is external outgoing. + pub const fn is_external_out(&self) -> bool { + matches!(self, Self::ExtOut) + } +} + +impl Store for MsgType { + fn store_into(&self, b: &mut CellBuilder, _: &mut dyn CellContext) -> Result<(), Error> { + match self { + Self::Int => b.store_bit_zero(), + Self::ExtIn => b.store_small_uint(0b10, 2), + Self::ExtOut => b.store_small_uint(0b11, 2), + } + } +} + +impl<'a> Load<'a> for MsgType { + fn load_from(slice: &mut CellSlice<'a>) -> Result { + Ok(if !ok!(slice.load_bit()) { + Self::Int + } else if !ok!(slice.load_bit()) { + Self::ExtIn + } else { + Self::ExtOut + }) + } +} + /// Message info. #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -590,6 +649,30 @@ pub enum MsgInfo { } impl MsgInfo { + /// Returns the type of this message info. + pub const fn ty(&self) -> MsgType { + match self { + Self::Int(_) => MsgType::Int, + Self::ExtIn(_) => MsgType::ExtIn, + Self::ExtOut(_) => MsgType::ExtOut, + } + } + + /// Returns whether this message is internal. + pub const fn is_internal(&self) -> bool { + matches!(self, Self::Int(_)) + } + + /// Returns whether this message is external incoming. + pub const fn is_external_in(&self) -> bool { + matches!(self, Self::ExtIn(_)) + } + + /// Returns whether this message is external outgoing. + pub const fn is_external_out(&self) -> bool { + matches!(self, Self::ExtOut(_)) + } + /// Exact size of this value when it is stored in slice. pub const fn exact_size_const(&self) -> Size { Size { diff --git a/src/models/message/tests/mod.rs b/src/models/message/tests/mod.rs index a1795a45..575039d1 100644 --- a/src/models/message/tests/mod.rs +++ b/src/models/message/tests/mod.rs @@ -37,6 +37,8 @@ fn check_message(boc: &[u8]) -> Cell { #[test] fn external_message() -> anyhow::Result<()> { let boc = check_message(include_bytes!("external_message.boc")); + assert_eq!(boc.parse::()?, MsgType::ExtIn); + let body = Boc::decode(include_bytes!("external_message_body.boc")).unwrap(); let serialized = serialize_message(Message { info: MsgInfo::ExtIn(ExtInMsgInfo { @@ -57,6 +59,8 @@ fn external_message() -> anyhow::Result<()> { #[test] fn external_outgoing() { let boc = check_message(include_bytes!("external_out_message.boc")); + assert_eq!(boc.parse::().unwrap(), MsgType::ExtOut); + let body = Boc::decode_base64("te6ccgEBAQEADgAAGJMdgs1k/wsgCERmwQ==").unwrap(); let serialized = serialize_message(Message { info: MsgInfo::ExtOut(ExtOutMsgInfo { @@ -77,6 +81,7 @@ fn external_outgoing() { #[test] fn internal_message_empty() { let boc = check_message(include_bytes!("empty_internal_message.boc")); + assert_eq!(boc.parse::().unwrap(), MsgType::Int); let serialized = serialize_message(Message { info: MsgInfo::Int(IntMsgInfo { @@ -103,6 +108,8 @@ fn internal_message_empty() { #[test] fn internal_message_with_body() -> anyhow::Result<()> { let boc = check_message(include_bytes!("internal_message_with_body.boc")); + assert_eq!(boc.parse::().unwrap(), MsgType::Int); + let body = Boc::decode(include_bytes!("internal_message_body.boc")).unwrap(); let serialized = serialize_message(Message { @@ -132,6 +139,7 @@ fn internal_message_with_body() -> anyhow::Result<()> { #[test] fn internal_message_with_deploy() -> anyhow::Result<()> { let boc = check_message(include_bytes!("internal_message_with_deploy.boc")); + assert_eq!(boc.parse::().unwrap(), MsgType::Int); let init = Boc::decode(include_bytes!( "internal_message_with_deploy_state_init.boc" @@ -170,6 +178,7 @@ fn internal_message_with_deploy_special() -> anyhow::Result<()> { use crate::models::account::*; let boc = check_message(include_bytes!("internal_message_with_deploy_special.boc")); + assert_eq!(boc.parse::().unwrap(), MsgType::Int); let init = StateInit { split_depth: None,