diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fa53c8ef11..159667a2e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Description of the upcoming release here. - [#1601](https://github.com/FuelLabs/fuel-core/pull/1601): Fix formatting in docs and check that `cargo doc` passes in the CI. #### Breaking +- [#16232](https://github.com/FuelLabs/fuel-core/pull/1632): Make `Message` type a version-able enum - [#1628](https://github.com/FuelLabs/fuel-core/pull/1628): Make `CompressedCoin` type a version-able enum - [#1616](https://github.com/FuelLabs/fuel-core/pull/1616): Make `BlockHeader` type a version-able enum - [#1614](https://github.com/FuelLabs/fuel-core/pull/1614): Use the default consensus key regardless of trigger mode. The change is breaking because it removes the `--dev-keys` argument. If the `debug` flag is set, the default consensus key will be used, regardless of the trigger mode. diff --git a/crates/chain-config/src/config/message.rs b/crates/chain-config/src/config/message.rs index d51349d022c..968c0325137 100644 --- a/crates/chain-config/src/config/message.rs +++ b/crates/chain-config/src/config/message.rs @@ -8,7 +8,10 @@ use crate::{ use fuel_core_storage::MerkleRoot; use fuel_core_types::{ blockchain::primitives::DaBlockHeight, - entities::message::Message, + entities::message::{ + Message, + MessageV1, + }, fuel_asm::Word, fuel_crypto::Hasher, fuel_types::{ @@ -42,7 +45,7 @@ pub struct MessageConfig { impl From for Message { fn from(msg: MessageConfig) -> Self { - Message { + MessageV1 { sender: msg.sender, recipient: msg.recipient, nonce: msg.nonce, @@ -50,19 +53,18 @@ impl From for Message { data: msg.data, da_height: msg.da_height, } + .into() } } impl GenesisCommitment for Message { fn root(&self) -> anyhow::Result { - let Self { - sender, - recipient, - nonce, - amount, - data, - da_height, - } = self; + let sender = self.sender(); + let recipient = self.recipient(); + let nonce = self.nonce(); + let amount = self.amount(); + let data = self.data(); + let da_height = self.da_height(); let message_hash = *Hasher::default() .chain(sender) diff --git a/crates/fuel-core/src/coins_query.rs b/crates/fuel-core/src/coins_query.rs index 9cb6f24e938..a1b537a5c1e 100644 --- a/crates/fuel-core/src/coins_query.rs +++ b/crates/fuel-core/src/coins_query.rs @@ -251,7 +251,10 @@ mod tests { Coin, CompressedCoin, }, - message::Message, + message::{ + Message, + MessageV1, + }, }, fuel_asm::Word, fuel_tx::*, @@ -783,7 +786,7 @@ mod tests { let excluded_ids = db .owned_messages(&owner) .into_iter() - .filter(|message| message.amount == 5) + .filter(|message| message.amount() == 5) .map(|message| CoinId::Message(*message.id())) .collect_vec(); @@ -799,7 +802,7 @@ mod tests { let excluded_ids = db .owned_messages(&owner) .into_iter() - .filter(|message| message.amount == 5) + .filter(|message| message.amount() == 5) .map(|message| CoinId::Message(*message.id())) .collect_vec(); @@ -965,14 +968,15 @@ mod tests { let nonce = self.last_message_index.into(); self.last_message_index += 1; - let message = Message { + let message: Message = MessageV1 { sender: Default::default(), recipient: owner, nonce, amount, data: vec![], da_height: DaBlockHeight::from(1u64), - }; + } + .into(); let db = &mut self.database; StorageMutate::::insert(db, message.id(), &message).unwrap(); diff --git a/crates/fuel-core/src/database/message.rs b/crates/fuel-core/src/database/message.rs index 21bdcac862e..c797942ed8c 100644 --- a/crates/fuel-core/src/database/message.rs +++ b/crates/fuel-core/src/database/message.rs @@ -92,7 +92,7 @@ impl StorageMutate for Database { // insert secondary record by owner self.storage_as_mut::() - .insert(&OwnedMessageKey::new(&value.recipient, key), &())?; + .insert(&OwnedMessageKey::new(value.recipient(), key), &())?; Ok(result) } @@ -103,7 +103,7 @@ impl StorageMutate for Database { if let Some(message) = &result { self.storage_as_mut::() - .remove(&OwnedMessageKey::new(&message.recipient, key))?; + .remove(&OwnedMessageKey::new(message.recipient(), key))?; } Ok(result) @@ -155,12 +155,12 @@ impl Database { let msg = msg?; Ok(MessageConfig { - sender: msg.sender, - recipient: msg.recipient, - nonce: msg.nonce, - amount: msg.amount, - data: msg.data, - da_height: msg.da_height, + sender: *msg.sender(), + recipient: *msg.recipient(), + nonce: *msg.nonce(), + amount: msg.amount(), + data: msg.data().clone(), + da_height: msg.da_height(), }) }) .collect::>>()?; @@ -201,7 +201,7 @@ mod tests { .unwrap(); // verify that 2 message IDs are associated with a single Owner/Recipient - let owned_msg_ids = db.owned_message_ids(&message.recipient, None, None); + let owned_msg_ids = db.owned_message_ids(message.recipient(), None, None); assert_eq!(owned_msg_ids.count(), 2); // remove the first message with its given id @@ -209,14 +209,14 @@ mod tests { // verify that only second ID is left let owned_msg_ids: Vec<_> = db - .owned_message_ids(&message.recipient, None, None) + .owned_message_ids(message.recipient(), None, None) .collect(); assert_eq!(owned_msg_ids.first().unwrap().as_ref().unwrap(), &second_id); assert_eq!(owned_msg_ids.len(), 1); // remove the second message with its given id let _ = db.storage_as_mut::().remove(&second_id).unwrap(); - let owned_msg_ids = db.owned_message_ids(&message.recipient, None, None); + let owned_msg_ids = db.owned_message_ids(message.recipient(), None, None); assert_eq!(owned_msg_ids.count(), 0); } } diff --git a/crates/fuel-core/src/executor.rs b/crates/fuel-core/src/executor.rs index 846dcd271fc..2e0fa11160e 100644 --- a/crates/fuel-core/src/executor.rs +++ b/crates/fuel-core/src/executor.rs @@ -40,7 +40,10 @@ mod tests { }, entities::{ coins::coin::CompressedCoin, - message::Message, + message::{ + Message, + MessageV1, + }, }, fuel_asm::{ op, @@ -2225,7 +2228,7 @@ mod tests { } fn message_from_input(input: &Input, da_height: u64) -> Message { - Message { + MessageV1 { sender: *input.sender().unwrap(), recipient: *input.recipient().unwrap(), nonce: *input.nonce().unwrap(), @@ -2236,6 +2239,7 @@ mod tests { .unwrap_or_default(), da_height: DaBlockHeight(da_height), } + .into() } /// Helper to build transactions and a message in it for some of the message tests @@ -2331,8 +2335,8 @@ mod tests { let exec = make_executor(&messages); let view = exec.database_view_provider.latest_view(); - assert!(!view.message_is_spent(&message_coin.nonce).unwrap()); - assert!(!view.message_is_spent(&message_data.nonce).unwrap()); + assert!(!view.message_is_spent(message_coin.nonce()).unwrap()); + assert!(!view.message_is_spent(message_data.nonce()).unwrap()); let ExecutionResult { skipped_transactions, @@ -2349,8 +2353,8 @@ mod tests { // Successful execution consumes `message_coin` and `message_data`. let view = exec.database_view_provider.latest_view(); - assert!(view.message_is_spent(&message_coin.nonce).unwrap()); - assert!(view.message_is_spent(&message_data.nonce).unwrap()); + assert!(view.message_is_spent(message_coin.nonce()).unwrap()); + assert!(view.message_is_spent(message_data.nonce()).unwrap()); assert_eq!( *view.coin(&UtxoId::new(tx_id, 0)).unwrap().amount(), amount + amount @@ -2385,8 +2389,8 @@ mod tests { let exec = make_executor(&messages); let view = exec.database_view_provider.latest_view(); - assert!(!view.message_is_spent(&message_coin.nonce).unwrap()); - assert!(!view.message_is_spent(&message_data.nonce).unwrap()); + assert!(!view.message_is_spent(message_coin.nonce()).unwrap()); + assert!(!view.message_is_spent(message_data.nonce()).unwrap()); let ExecutionResult { skipped_transactions, @@ -2403,8 +2407,8 @@ mod tests { // We should spend only `message_coin`. The `message_data` should be unspent. let view = exec.database_view_provider.latest_view(); - assert!(view.message_is_spent(&message_coin.nonce).unwrap()); - assert!(!view.message_is_spent(&message_data.nonce).unwrap()); + assert!(view.message_is_spent(message_coin.nonce()).unwrap()); + assert!(!view.message_is_spent(message_data.nonce()).unwrap()); assert_eq!(*view.coin(&UtxoId::new(tx_id, 0)).unwrap().amount(), amount); } @@ -2528,7 +2532,7 @@ mod tests { let (tx, mut message) = make_tx_and_message(&mut rng, 0); // Modifying the message to make it mismatch - message.amount = 123; + message.set_amount(123); let mut block = Block::default(); *block.transactions_mut() = vec![tx.clone()]; diff --git a/crates/fuel-core/src/query/balance/asset_query.rs b/crates/fuel-core/src/query/balance/asset_query.rs index ee0266b1245..aca50068b71 100644 --- a/crates/fuel-core/src/query/balance/asset_query.rs +++ b/crates/fuel-core/src/query/balance/asset_query.rs @@ -133,7 +133,7 @@ impl<'a> AssetsQuery<'a> { Ok(message) }) }) - .filter_ok(|message| message.data.is_empty()) + .filter_ok(|message| message.data().is_empty()) .map(|result| { result.map(|message| { CoinType::MessageCoin( diff --git a/crates/fuel-core/src/schema/message.rs b/crates/fuel-core/src/schema/message.rs index c8c0c3f6dbe..cc36502c980 100644 --- a/crates/fuel-core/src/schema/message.rs +++ b/crates/fuel-core/src/schema/message.rs @@ -38,27 +38,27 @@ pub struct Message(pub(crate) entities::message::Message); #[Object] impl Message { async fn amount(&self) -> U64 { - self.0.amount.into() + self.0.amount().into() } async fn sender(&self) -> Address { - self.0.sender.into() + (*self.0.sender()).into() } async fn recipient(&self) -> Address { - self.0.recipient.into() + (*self.0.recipient()).into() } async fn nonce(&self) -> Nonce { - self.0.nonce.into() + (*self.0.nonce()).into() } async fn data(&self) -> HexString { - self.0.data.clone().into() + self.0.data().clone().into() } async fn da_height(&self) -> U64 { - self.0.da_height.as_u64().into() + self.0.da_height().as_u64().into() } } @@ -108,7 +108,7 @@ impl MessageQuery { let messages = messages.map(|result| { result - .map(|message| (message.nonce.into(), message.into())) + .map(|message| ((*message.nonce()).into(), message.into())) .map_err(Into::into) }); diff --git a/crates/fuel-core/src/service/genesis.rs b/crates/fuel-core/src/service/genesis.rs index 36d99165105..fe642cc7fb6 100644 --- a/crates/fuel-core/src/service/genesis.rs +++ b/crates/fuel-core/src/service/genesis.rs @@ -45,7 +45,10 @@ use fuel_core_types::{ CompressedCoinV1, }, contract::ContractUtxoInfo, - message::Message, + message::{ + Message, + MessageV1, + }, }, fuel_merkle::binary, fuel_tx::{ @@ -331,14 +334,15 @@ fn init_da_messages( if let Some(state) = &state { if let Some(message_state) = &state.messages { for msg in message_state { - let message = Message { + let message: Message = MessageV1 { sender: msg.sender, recipient: msg.recipient, nonce: msg.nonce, amount: msg.amount, data: msg.data.clone(), da_height: msg.da_height, - }; + } + .into(); if db .storage::() diff --git a/crates/services/executor/src/executor.rs b/crates/services/executor/src/executor.rs index b56e28285ef..f77b7e7af2e 100644 --- a/crates/services/executor/src/executor.rs +++ b/crates/services/executor/src/executor.rs @@ -1125,7 +1125,7 @@ where .get_message(nonce, &block_da_height) .map_err(|e| ExecutorError::RelayerError(e.into()))? { - if message.da_height > block_da_height { + if message.da_height() > block_da_height { return Err(TransactionValidityError::MessageSpendTooEarly( *nonce, ) diff --git a/crates/services/relayer/src/log.rs b/crates/services/relayer/src/log.rs index 723459f7ac3..95dfc0cf4d7 100644 --- a/crates/services/relayer/src/log.rs +++ b/crates/services/relayer/src/log.rs @@ -10,7 +10,10 @@ use ethers_core::{ }; use fuel_core_types::{ blockchain::primitives::DaBlockHeight, - entities::message::Message, + entities::message::{ + Message, + MessageV1, + }, fuel_types::{ Address, Nonce, @@ -31,7 +34,7 @@ pub struct MessageLog { impl From<&MessageLog> for Message { fn from(message: &MessageLog) -> Self { - Self { + MessageV1 { sender: message.sender, recipient: message.recipient, nonce: message.nonce, @@ -39,6 +42,7 @@ impl From<&MessageLog> for Message { data: message.data.clone(), da_height: message.da_height, } + .into() } } diff --git a/crates/services/relayer/src/mock_db.rs b/crates/services/relayer/src/mock_db.rs index c4f4e46eed7..1a87a48b9f0 100644 --- a/crates/services/relayer/src/mock_db.rs +++ b/crates/services/relayer/src/mock_db.rs @@ -56,7 +56,7 @@ impl RelayerDb for MockDb { let mut m = self.data.lock().unwrap(); for message in messages { m.messages - .entry(message.da_height) + .entry(message.da_height()) .or_default() .insert(*message.id(), message.clone()); } diff --git a/crates/services/relayer/src/ports.rs b/crates/services/relayer/src/ports.rs index 2dbd210678c..6a4cbe747ea 100644 --- a/crates/services/relayer/src/ports.rs +++ b/crates/services/relayer/src/ports.rs @@ -73,7 +73,7 @@ where for message in messages { db.storage::().insert(message.id(), message)?; let max = max_height.get_or_insert(0u64); - *max = (*max).max(message.da_height.0); + *max = (*max).max(message.da_height().0); } if let Some(height) = max_height { if **da_height < height { diff --git a/crates/services/relayer/src/ports/tests.rs b/crates/services/relayer/src/ports/tests.rs index bb2f46221b1..c49ac9bbe66 100644 --- a/crates/services/relayer/src/ports/tests.rs +++ b/crates/services/relayer/src/ports/tests.rs @@ -22,13 +22,11 @@ fn test_insert_messages() { .returning(|_| Ok(Some(std::borrow::Cow::Owned(9u64.into())))); let mut db = db.into_transactional(); - let m = Message { - amount: 10, - da_height: 12u64.into(), - ..Default::default() - }; + let mut m = Message::default(); + m.set_amount(10); + m.set_da_height(12u64.into()); let mut m2 = m.clone(); - m2.nonce = 1.into(); + m2.set_nonce(1.into()); assert_ne!(m.id(), m2.id()); let messages = [m, m2]; db.insert_messages(&12u64.into(), &messages[..]).unwrap(); @@ -37,11 +35,13 @@ fn test_insert_messages() { #[test] fn insert_always_raises_da_height_monotonically() { let messages: Vec<_> = (0..10) - .map(|i| Message { - amount: i, - da_height: i.into(), - ..Default::default() + .map(|i| { + let mut message = Message::default(); + message.set_amount(i); + message.set_da_height(i.into()); + message }) + .map(Into::into) .collect(); let mut db = MockStorage::default(); diff --git a/crates/services/relayer/src/service.rs b/crates/services/relayer/src/service.rs index dea48770420..ffebf694c9e 100644 --- a/crates/services/relayer/src/service.rs +++ b/crates/services/relayer/src/service.rs @@ -295,7 +295,7 @@ impl SharedState { .storage::() .get(id)? .map(Cow::into_owned) - .filter(|message| message.da_height <= *da_height)) + .filter(|message| message.da_height() <= *da_height)) } /// Get finalized da height that represents last block from da layer that got finalized. diff --git a/crates/services/txpool/src/txpool/test_helpers.rs b/crates/services/txpool/src/txpool/test_helpers.rs index 24a4a85a0e0..05499981a3b 100644 --- a/crates/services/txpool/src/txpool/test_helpers.rs +++ b/crates/services/txpool/src/txpool/test_helpers.rs @@ -1,6 +1,9 @@ use crate::test_helpers::IntoEstimated; use fuel_core_types::{ - entities::message::Message, + entities::message::{ + Message, + MessageV1, + }, fuel_asm::op, fuel_tx::{ Contract, @@ -18,7 +21,7 @@ pub(crate) fn create_message_predicate_from_message( nonce: u64, ) -> (Message, Input) { let predicate = vec![op::ret(1)].into_iter().collect::>(); - let message = Message { + let message = MessageV1 { sender: Default::default(), recipient: Input::predicate_owner(&predicate), nonce: nonce.into(), @@ -28,7 +31,7 @@ pub(crate) fn create_message_predicate_from_message( }; ( - message.clone(), + message.clone().into(), Input::message_coin_predicate( message.sender, Input::predicate_owner(&predicate), diff --git a/crates/types/src/entities.rs b/crates/types/src/entities.rs index 7e6afb3dc96..90328f77c39 100644 --- a/crates/types/src/entities.rs +++ b/crates/types/src/entities.rs @@ -1,5 +1,6 @@ //! Higher level domain types +use crate::entities::message::MessageV1; use coins::message_coin::MessageCoin; use message::Message; @@ -11,14 +12,12 @@ impl TryFrom for MessageCoin { type Error = anyhow::Error; fn try_from(message: Message) -> Result { - let Message { - sender, - recipient, - nonce, - amount, - data, - da_height, - } = message; + let sender = *message.sender(); + let recipient = *message.recipient(); + let nonce = *message.nonce(); + let amount = message.amount(); + let data = message.data(); + let da_height = message.da_height(); if !data.is_empty() { return Err(anyhow::anyhow!( @@ -48,7 +47,7 @@ impl From for Message { da_height, } = coin; - Message { + MessageV1 { sender, recipient, nonce, @@ -56,5 +55,6 @@ impl From for Message { data: vec![], da_height, } + .into() } } diff --git a/crates/types/src/entities/message.rs b/crates/types/src/entities/message.rs index e68b036028c..fee7440136d 100644 --- a/crates/types/src/entities/message.rs +++ b/crates/types/src/entities/message.rs @@ -24,10 +24,26 @@ use crate::{ }, }; -/// Message send from Da layer to fuel by bridge +/// Message sent from DA layer to fuel by relayer bridge. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum Message { + /// Message Version 1 + V1(MessageV1), +} + +#[cfg(any(test, feature = "test-helpers"))] +impl Default for Message { + fn default() -> Self { + Self::V1(Default::default()) + } +} + +/// The V1 version of the message from the DA layer. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct Message { +pub struct MessageV1 { /// Account that sent the message from the da layer pub sender: Address, /// Fuel account receiving the message @@ -42,27 +58,125 @@ pub struct Message { pub da_height: DaBlockHeight, } +impl From for Message { + fn from(value: MessageV1) -> Self { + Self::V1(value) + } +} + impl Message { + /// Get the message sender + pub fn sender(&self) -> &Address { + match self { + Message::V1(message) => &message.sender, + } + } + + /// Set the message sender + #[cfg(any(test, feature = "test-helpers"))] + pub fn set_sender(&mut self, sender: Address) { + match self { + Message::V1(message) => message.sender = sender, + } + } + + /// Get the message recipient + pub fn recipient(&self) -> &Address { + match self { + Message::V1(message) => &message.recipient, + } + } + + /// Set the message recipient + #[cfg(any(test, feature = "test-helpers"))] + pub fn set_recipient(&mut self, recipient: Address) { + match self { + Message::V1(message) => message.recipient = recipient, + } + } + + /// Get the message nonce + pub fn nonce(&self) -> &Nonce { + match self { + Message::V1(message) => &message.nonce, + } + } + + /// Set the message nonce + #[cfg(any(test, feature = "test-helpers"))] + pub fn set_nonce(&mut self, nonce: Nonce) { + match self { + Message::V1(message) => message.nonce = nonce, + } + } + + /// Get the message amount + pub fn amount(&self) -> Word { + match self { + Message::V1(message) => message.amount, + } + } + + /// Set the message amount + #[cfg(any(test, feature = "test-helpers"))] + pub fn set_amount(&mut self, amount: Word) { + match self { + Message::V1(message) => message.amount = amount, + } + } + + /// Get the message data + pub fn data(&self) -> &Vec { + match self { + Message::V1(message) => &message.data, + } + } + + /// Set the message data + #[cfg(any(test, feature = "test-helpers"))] + pub fn set_data(&mut self, data: Vec) { + match self { + Message::V1(message) => message.data = data, + } + } + + /// Get the message DA height + pub fn da_height(&self) -> DaBlockHeight { + match self { + Message::V1(message) => message.da_height, + } + } + + /// Set the message DA height + #[cfg(any(test, feature = "test-helpers"))] + pub fn set_da_height(&mut self, da_height: DaBlockHeight) { + match self { + Message::V1(message) => message.da_height = da_height, + } + } + /// Returns the id of the message pub fn id(&self) -> &Nonce { - &self.nonce + match self { + Message::V1(message) => &message.nonce, + } } /// Computed message id pub fn message_id(&self) -> MessageId { compute_message_id( - &self.sender, - &self.recipient, - &self.nonce, - self.amount, - &self.data, + self.sender(), + self.recipient(), + self.nonce(), + self.amount(), + self.data(), ) } /// Verifies the integrity of the message. /// /// Returns `None`, if the `input` is not a message. - /// Otherwise returns the result of the field comparison. + /// Otherwise, returns the result of the field comparison. pub fn matches_input(&self, input: &Input) -> Option { match input { Input::MessageDataSigned(MessageDataSigned { @@ -93,16 +207,16 @@ impl Message { amount, .. }) => { - let expected_data = if self.data.is_empty() { + let expected_data = if self.data().is_empty() { None } else { - Some(self.data.as_slice()) + Some(self.data().as_slice()) }; Some( - &self.sender == sender - && &self.recipient == recipient - && &self.nonce == nonce - && &self.amount == amount + self.sender() == sender + && self.recipient() == recipient + && self.nonce() == nonce + && &self.amount() == amount && expected_data == input.input_data(), ) }