fuel_core/schema/
message.rs

1use super::{
2    ReadViewProvider,
3    block::Header,
4    scalars::{
5        Address,
6        Bytes32,
7        HexString,
8        Nonce,
9        TransactionId,
10        U64,
11    },
12};
13use crate::{
14    fuel_core_graphql_api::query_costs,
15    graphql_api::IntoApiResult,
16    schema::scalars::{
17        BlockId,
18        U32,
19    },
20};
21use anyhow::anyhow;
22use async_graphql::{
23    Context,
24    Enum,
25    Object,
26    connection::{
27        Connection,
28        EmptyFields,
29    },
30};
31use fuel_core_services::stream::IntoBoxStream;
32use fuel_core_types::entities;
33use futures::StreamExt;
34
35pub struct Message(pub(crate) entities::relayer::message::Message);
36
37#[Object]
38impl Message {
39    async fn amount(&self) -> U64 {
40        self.0.amount().into()
41    }
42
43    async fn sender(&self) -> Address {
44        (*self.0.sender()).into()
45    }
46
47    async fn recipient(&self) -> Address {
48        (*self.0.recipient()).into()
49    }
50
51    async fn nonce(&self) -> Nonce {
52        (*self.0.nonce()).into()
53    }
54
55    async fn data(&self) -> HexString {
56        self.0.data().clone().into()
57    }
58
59    async fn da_height(&self) -> U64 {
60        self.0.da_height().as_u64().into()
61    }
62}
63
64#[derive(Default)]
65pub struct MessageQuery {}
66
67#[Object]
68impl MessageQuery {
69    #[graphql(complexity = "query_costs().storage_read + child_complexity")]
70    async fn message(
71        &self,
72        ctx: &Context<'_>,
73        #[graphql(desc = "The Nonce of the message")] nonce: Nonce,
74    ) -> async_graphql::Result<Option<Message>> {
75        let query = ctx.read_view()?;
76        let nonce = nonce.0;
77        query.message(&nonce).into_api_result()
78    }
79
80    #[graphql(complexity = "{\
81        query_costs().storage_iterator\
82        + first.unwrap_or_default() as usize * (child_complexity + query_costs().storage_read) \
83        + last.unwrap_or_default() as usize * (child_complexity + query_costs().storage_read) \
84    }")]
85    async fn messages(
86        &self,
87        ctx: &Context<'_>,
88        #[graphql(desc = "address of the owner")] owner: Option<Address>,
89        first: Option<i32>,
90        after: Option<String>,
91        last: Option<i32>,
92        before: Option<String>,
93    ) -> async_graphql::Result<Connection<HexString, Message, EmptyFields, EmptyFields>>
94    {
95        let query = ctx.read_view()?;
96        let owner = owner.map(|owner| owner.0);
97        let owner_ref = owner.as_ref();
98        crate::schema::query_pagination(
99            after,
100            before,
101            first,
102            last,
103            |start: &Option<HexString>, direction| {
104                let start = if let Some(start) = start.clone() {
105                    Some(start.try_into().map_err(|err| anyhow!("{}", err))?)
106                } else {
107                    None
108                };
109
110                let messages = if let Some(owner) = owner_ref {
111                    query
112                        .owned_messages(owner, start, direction)
113                        .into_boxed_ref()
114                } else {
115                    query.all_messages(start, direction).into_boxed_ref()
116                };
117
118                let messages = messages.map(|result| {
119                    result.map(|message| ((*message.nonce()).into(), message.into()))
120                });
121
122                Ok(messages)
123            },
124        )
125        .await
126    }
127
128    // 256 * QUERY_COSTS.storage_read because the depth of the Merkle tree in the worst case is 256
129    #[graphql(complexity = "256 * query_costs().storage_read + child_complexity")]
130    async fn message_proof(
131        &self,
132        ctx: &Context<'_>,
133        transaction_id: TransactionId,
134        nonce: Nonce,
135        commit_block_id: Option<BlockId>,
136        commit_block_height: Option<U32>,
137    ) -> async_graphql::Result<MessageProof> {
138        let query = ctx.read_view()?;
139        let height = match (commit_block_id, commit_block_height) {
140            (Some(commit_block_id), None) => {
141                query.block_height(&commit_block_id.0.into())?
142            }
143            (None, Some(commit_block_height)) => commit_block_height.0.into(),
144            _ => Err(anyhow::anyhow!(
145                "Either `commit_block_id` or `commit_block_height` must be provided exclusively"
146            ))?,
147        };
148
149        let proof = crate::query::message_proof(
150            query.as_ref(),
151            transaction_id.into(),
152            nonce.into(),
153            height,
154        )?;
155
156        Ok(MessageProof(proof))
157    }
158
159    #[graphql(complexity = "query_costs().storage_read + child_complexity")]
160    async fn message_status(
161        &self,
162        ctx: &Context<'_>,
163        nonce: Nonce,
164    ) -> async_graphql::Result<MessageStatus> {
165        let query = ctx.read_view()?;
166        let status = crate::query::message_status(query.as_ref(), nonce.into())?;
167        Ok(status.into())
168    }
169}
170pub struct MerkleProof(pub(crate) entities::relayer::message::MerkleProof);
171
172#[Object]
173impl MerkleProof {
174    async fn proof_set(&self) -> Vec<Bytes32> {
175        self.0
176            .proof_set
177            .iter()
178            .cloned()
179            .map(|array| Bytes32::from(fuel_core_types::fuel_types::Bytes32::from(array)))
180            .collect()
181    }
182
183    async fn proof_index(&self) -> U64 {
184        self.0.proof_index.into()
185    }
186}
187
188pub struct MessageProof(pub(crate) entities::relayer::message::MessageProof);
189
190#[Object]
191impl MessageProof {
192    async fn message_proof(&self) -> MerkleProof {
193        self.0.message_proof.clone().into()
194    }
195
196    async fn block_proof(&self) -> MerkleProof {
197        self.0.block_proof.clone().into()
198    }
199
200    async fn message_block_header(&self) -> Header {
201        self.0.message_block_header.clone().into()
202    }
203
204    async fn commit_block_header(&self) -> Header {
205        self.0.commit_block_header.clone().into()
206    }
207
208    async fn sender(&self) -> Address {
209        self.0.sender.into()
210    }
211
212    async fn recipient(&self) -> Address {
213        self.0.recipient.into()
214    }
215
216    async fn nonce(&self) -> Nonce {
217        self.0.nonce.into()
218    }
219
220    async fn amount(&self) -> U64 {
221        self.0.amount.into()
222    }
223
224    async fn data(&self) -> HexString {
225        self.0.data.clone().into()
226    }
227}
228
229impl From<entities::relayer::message::Message> for Message {
230    fn from(message: entities::relayer::message::Message) -> Self {
231        Message(message)
232    }
233}
234
235impl From<entities::relayer::message::MerkleProof> for MerkleProof {
236    fn from(proof: entities::relayer::message::MerkleProof) -> Self {
237        MerkleProof(proof)
238    }
239}
240
241pub struct MessageStatus(pub(crate) entities::relayer::message::MessageStatus);
242
243#[derive(Enum, Copy, Clone, Eq, PartialEq)]
244enum MessageState {
245    Unspent,
246    Spent,
247    NotFound,
248}
249
250#[Object]
251impl MessageStatus {
252    async fn state(&self) -> MessageState {
253        match self.0.state {
254            entities::relayer::message::MessageState::Unspent => MessageState::Unspent,
255            entities::relayer::message::MessageState::Spent => MessageState::Spent,
256            entities::relayer::message::MessageState::NotFound => MessageState::NotFound,
257        }
258    }
259}
260
261impl From<entities::relayer::message::MessageStatus> for MessageStatus {
262    fn from(status: entities::relayer::message::MessageStatus) -> Self {
263        MessageStatus(status)
264    }
265}