Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add query and handling for MessageStatus #1371

Merged
merged 9 commits into from
Sep 20, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Description of the upcoming release here.

### Added

- [#1371](https://github.com/FuelLabs/fuel-core/pull/1371): Add new client function for querying the `MessageStatus` for a specific message (by `Nonce`)
- [#1356](https://github.com/FuelLabs/fuel-core/pull/1356): Add peer reputation reporting to heartbeat code
- [#1355](https://github.com/FuelLabs/fuel-core/pull/1355): Added new metrics related to block importing, such as tps, sync delays etc
- [#1339](https://github.com/FuelLabs/fuel-core/pull/1339): Adds `baseAssetId` to `FeeParameters` in the GraphQL API.
Expand Down
2 changes: 2 additions & 0 deletions crates/chain-config/src/config/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub struct MessageConfig {
pub da_height: DaBlockHeight,
}

pub struct SpentMessageConfig {}

MitchTurner marked this conversation as resolved.
Show resolved Hide resolved
impl From<MessageConfig> for Message {
fn from(msg: MessageConfig) -> Self {
Message {
Expand Down
8 changes: 7 additions & 1 deletion crates/chain-config/src/config/state.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use crate::serialization::HexNumber;

use fuel_core_storage::Result as StorageResult;
use fuel_core_types::fuel_types::BlockHeight;
use fuel_core_types::fuel_types::{
BlockHeight,
Nonce,
};

use serde::{
Deserialize,
Expand Down Expand Up @@ -29,6 +32,8 @@ pub struct StateConfig {
pub contracts: Option<Vec<ContractConfig>>,
/// Messages from Layer 1
pub messages: Option<Vec<MessageConfig>>,
/// Spent Messages
pub spent_messages: Option<Vec<Nonce>>,
/// Starting block height (useful for flattened fork networks)
#[serde_as(as = "Option<HexNumber>")]
#[serde(default)]
Expand All @@ -44,6 +49,7 @@ impl StateConfig {
coins: db.get_coin_config()?,
contracts: db.get_contract_config()?,
messages: db.get_message_config()?,
spent_messages: None,
MitchTurner marked this conversation as resolved.
Show resolved Hide resolved
height: Some(db.get_block_height()?),
})
}
Expand Down
11 changes: 11 additions & 0 deletions crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,16 @@ type MessageProof {
data: HexString!
}

enum MessageState {
UNSPENT
SPENT
NOT_FOUND
}

type MessageStatus {
state: MessageState!
}

type Mutation {
startSession: ID!
endSession(id: ID!): Boolean!
Expand Down Expand Up @@ -696,6 +706,7 @@ type Query {
nodeInfo: NodeInfo!
messages(owner: Address, first: Int, after: String, last: Int, before: String): MessageConnection!
messageProof(transactionId: TransactionId!, nonce: Nonce!, commitBlockId: BlockId, commitBlockHeight: U32): MessageProof
messageStatus(nonce: Nonce!): MessageStatus!
}

type Receipt {
Expand Down
25 changes: 19 additions & 6 deletions crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@ use crate::client::{
SpendQueryElementInput,
},
contract::ContractBalanceQueryArgs,
message::MessageStatusArgs,
tx::DryRunArg,
Tai64Timestamp,
TransactionId,
},
types::primitives::{
Address,
AssetId,
BlockId,
ContractId,
UtxoId,
types::{
message::MessageStatus,
primitives::{
Address,
AssetId,
BlockId,
ContractId,
UtxoId,
},
},
};
use anyhow::Context;
Expand Down Expand Up @@ -874,6 +878,15 @@ impl FuelClient {
Ok(messages)
}

pub async fn message_status(&self, nonce: &Nonce) -> io::Result<MessageStatus> {
let query = schema::message::MessageStatusQuery::build(MessageStatusArgs {
nonce: (*nonce).into(),
});
let status = self.query(query).await?.message_status.into();

Ok(status)
}

/// Request a merkle proof of an output message.
pub async fn message_proof(
&self,
Expand Down
33 changes: 32 additions & 1 deletion crates/client/src/client/schema/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ pub struct Message {
pub da_height: U64,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(schema_path = "./assets/schema.sdl")]
pub struct MessageStatus {
pub(crate) state: MessageState,
}

#[derive(cynic::Enum, Debug)]
#[cynic(schema_path = "./assets/schema.sdl")]
pub enum MessageState {
Unspent,
Spent,
NotFound,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
schema_path = "./assets/schema.sdl",
Expand Down Expand Up @@ -126,7 +140,7 @@ pub struct MessageProof {
pub struct MessageProofArgs {
/// Transaction id that contains the output message.
pub transaction_id: TransactionId,
/// Message id of the output message that requires a proof.
/// Nonce of the output message that requires a proof.
pub nonce: Nonce,

/// The query supports either `commit_block_id`, or `commit_block_height` set on, not both.
Expand All @@ -139,6 +153,23 @@ pub struct MessageProofArgs {
pub commit_block_height: Option<U32>,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(
schema_path = "./assets/schema.sdl",
graphql_type = "Query",
variables = "MessageStatusArgs"
)]
pub struct MessageStatusQuery {
#[arguments(nonce: $nonce)]
pub message_status: MessageStatus,
}

#[derive(cynic::QueryVariables, Debug)]
pub struct MessageStatusArgs {
/// Nonce of the output message that requires a proof.
pub nonce: Nonce,
}

impl From<(Option<Address>, PaginationRequest<String>)> for OwnedMessagesConnectionArgs {
fn from(r: (Option<Address>, PaginationRequest<String>)) -> Self {
match r.1.direction {
Expand Down
17 changes: 17 additions & 0 deletions crates/client/src/client/types/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ pub struct MessageProof {
pub data: Bytes,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MessageStatus {
Unspent,
Spent,
NotFound,
}

impl From<schema::message::MessageStatus> for MessageStatus {
fn from(value: schema::message::MessageStatus) -> Self {
match value.state {
schema::message::MessageState::Unspent => Self::Unspent,
schema::message::MessageState::Spent => Self::Spent,
schema::message::MessageState::NotFound => Self::NotFound,
}
}
}

// GraphQL Translation

impl From<schema::message::Message> for Message {
Expand Down
4 changes: 4 additions & 0 deletions crates/fuel-core/src/database/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ impl Database {
pub fn is_message_spent(&self, id: &Nonce) -> StorageResult<bool> {
fuel_core_storage::StorageAsRef::storage::<SpentMessages>(&self).contains_key(id)
}

pub fn message_exists(&self, id: &Nonce) -> StorageResult<bool> {
fuel_core_storage::StorageAsRef::storage::<Messages>(&self).contains_key(id)
}
}

// TODO: Reuse `fuel_vm::storage::double_key` macro.
Expand Down
4 changes: 4 additions & 0 deletions crates/fuel-core/src/graphql_api/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ pub trait DatabaseMessages:
start_message_id: Option<Nonce>,
direction: IterDirection,
) -> BoxedIter<'_, StorageResult<Message>>;

fn message_is_spent(&self, nonce: &Nonce) -> StorageResult<bool>;

fn message_exists(&self, nonce: &Nonce) -> StorageResult<bool>;
}

/// Trait that specifies all the getters required for coins.
Expand Down
15 changes: 15 additions & 0 deletions crates/fuel-core/src/query/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
fuel_core_graphql_api::{
ports::{
DatabaseMessageProof,
DatabaseMessages,
DatabasePort,
},
IntoApiResult,
Expand Down Expand Up @@ -33,6 +34,7 @@ use fuel_core_types::{
MerkleProof,
Message,
MessageProof,
MessageStatus,
},
fuel_merkle::binary::in_memory::MerkleTree,
fuel_tx::{
Expand Down Expand Up @@ -270,3 +272,16 @@ fn message_receipts_proof<T: MessageProofData + ?Sized>(
None => Ok(None),
}
}

pub fn message_status<T: DatabaseMessages + ?Sized>(
database: &T,
message_nonce: Nonce,
) -> StorageResult<MessageStatus> {
if database.message_is_spent(&message_nonce)? {
Ok(MessageStatus::spent())
} else if database.message_exists(&message_nonce)? {
Ok(MessageStatus::unspent())
} else {
Ok(MessageStatus::unknown())
}
}
37 changes: 37 additions & 0 deletions crates/fuel-core/src/schema/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use async_graphql::{
EmptyFields,
},
Context,
Enum,
Object,
};
use fuel_core_types::entities;
Expand Down Expand Up @@ -141,6 +142,16 @@ impl MessageQuery {
)?
.map(MessageProof))
}

async fn message_status(
&self,
ctx: &Context<'_>,
nonce: Nonce,
) -> async_graphql::Result<MessageStatus> {
let data: &Database = ctx.data_unchecked();
let status = crate::query::message_status(data.deref(), nonce.into())?;
Ok(status.into())
}
}
pub struct MerkleProof(pub(crate) entities::message::MerkleProof);

Expand Down Expand Up @@ -212,3 +223,29 @@ impl From<entities::message::MerkleProof> for MerkleProof {
MerkleProof(proof)
}
}

pub struct MessageStatus(pub(crate) entities::message::MessageStatus);

#[derive(Enum, Copy, Clone, Eq, PartialEq)]
enum MessageState {
Unspent,
Spent,
NotFound,
}

#[Object]
impl MessageStatus {
async fn state(&self) -> MessageState {
match self.0.state {
entities::message::MessageState::Unspent => MessageState::Unspent,
entities::message::MessageState::Spent => MessageState::Spent,
entities::message::MessageState::NotFound => MessageState::NotFound,
}
}
}

impl From<entities::message::MessageStatus> for MessageStatus {
fn from(status: entities::message::MessageStatus) -> Self {
MessageStatus(status)
}
}
8 changes: 8 additions & 0 deletions crates/fuel-core/src/service/adapters/graphql_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ impl DatabaseMessages for Database {
.map(|result| result.map_err(StorageError::from))
.into_boxed()
}

fn message_is_spent(&self, nonce: &Nonce) -> StorageResult<bool> {
MitchTurner marked this conversation as resolved.
Show resolved Hide resolved
self.is_message_spent(nonce)
}

fn message_exists(&self, nonce: &Nonce) -> StorageResult<bool> {
self.message_exists(nonce)
}
}

impl DatabaseCoins for Database {
Expand Down
9 changes: 9 additions & 0 deletions crates/fuel-core/src/service/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use fuel_core_storage::{
ContractsRawCode,
FuelBlocks,
Messages,
SpentMessages,
},
transactional::Transactional,
MerkleRoot,
Expand Down Expand Up @@ -339,6 +340,14 @@ fn init_da_messages(
message_tree.push(message.root()?.as_slice());
}
}
if let Some(spent_message_state) = &state.spent_messages {
for nonce in spent_message_state {
if db.storage::<SpentMessages>().insert(nonce, &())?.is_some() {
return Err(anyhow!("Spent Message should not exist"))
}
// message_tree.push(message.root()?.as_slice());
MitchTurner marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

Ok(message_tree.root())
Expand Down
39 changes: 39 additions & 0 deletions crates/types/src/entities/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,42 @@ impl MessageProof {
)
}
}

/// Represents the status of a message
pub struct MessageStatus {
/// The message state
pub state: MessageState,
}

impl MessageStatus {
/// Constructor for `MessageStatus` that fills with `Unspent` state
pub fn unspent() -> Self {
Self {
state: MessageState::Unspent,
}
}

/// Constructor for `MessageStatus` that fills with `Spent` state
pub fn spent() -> Self {
Self {
state: MessageState::Spent,
}
}

/// Constructor for `MessageStatus` that fills with `Unknown` state
pub fn unknown() -> Self {
MitchTurner marked this conversation as resolved.
Show resolved Hide resolved
Self {
state: MessageState::NotFound,
}
}
}

/// The possible states a Message can be in
pub enum MessageState {
/// Message is still unspent
Unspent,
/// Message has already been spent
Spent,
/// There is no record of this Message
NotFound,
}
2 changes: 2 additions & 0 deletions tests/tests/balances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ async fn balance() {
})
.collect(),
),
spent_messages: None,
});

// setup server & client
Expand Down Expand Up @@ -207,6 +208,7 @@ async fn first_5_balances() {
contracts: None,
coins: Some(coins),
messages: Some(messages),
spent_messages: None,
});

// setup server & client
Expand Down
Loading
Loading