1use graphql_client::GraphQLQuery;
5use linera_base::{
6 crypto::CryptoHash,
7 data_types::{Amount, Blob, BlockHeight, ChainDescription, OracleResponse, Round, Timestamp},
8 identifiers::{Account, AccountOwner, BlobId, ChainId, GenericApplicationId, StreamName},
9};
10use thiserror::Error;
11
12pub type JSONObject = serde_json::Value;
13
14#[cfg(target_arch = "wasm32")]
15mod types {
16 use linera_base::data_types::Round;
17 use serde::{Deserialize, Serialize};
18 use serde_json::Value;
19
20 use super::{BlockHeight, ChainId, CryptoHash};
21
22 pub type ChainManager = Value;
23 pub type ChainOwnership = Value;
24 pub type Epoch = Value;
25 pub type MessageBundle = Value;
26 pub type MessageKind = Value;
27 pub type Message = Value;
28 pub type MessageAction = Value;
29 pub type Operation = Value;
30 pub type Origin = Value;
31 pub type ApplicationDescription = Value;
32 pub type OperationResult = Value;
33
34 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
35 pub struct Notification {
36 pub chain_id: ChainId,
37 pub reason: Reason,
38 }
39
40 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
41 #[expect(clippy::enum_variant_names)]
42 pub enum Reason {
43 NewBlock {
44 height: BlockHeight,
45 hash: CryptoHash,
46 },
47 NewIncomingBundle {
48 origin: Origin,
49 height: BlockHeight,
50 },
51 NewRound {
52 height: BlockHeight,
53 round: Round,
54 },
55 }
56}
57
58#[cfg(not(target_arch = "wasm32"))]
59mod types {
60 pub use linera_base::{
61 data_types::{ApplicationDescription, Epoch},
62 ownership::ChainOwnership,
63 };
64 pub use linera_chain::{
65 data_types::{IncomingBundle, MessageAction, MessageBundle, OperationResult, Transaction},
66 manager::ChainManager,
67 };
68 pub use linera_core::worker::{Notification, Reason};
69 pub use linera_execution::{Message, MessageKind, Operation, SystemOperation};
70}
71
72pub use types::*;
73pub type ApplicationId = String;
74
75#[derive(GraphQLQuery)]
76#[graphql(
77 schema_path = "gql/service_schema.graphql",
78 query_path = "gql/service_requests.graphql",
79 response_derives = "Debug, Serialize, Clone"
80)]
81pub struct Chain;
82
83#[derive(GraphQLQuery)]
84#[graphql(
85 schema_path = "gql/service_schema.graphql",
86 query_path = "gql/service_requests.graphql",
87 response_derives = "Debug, Serialize, Clone"
88)]
89pub struct Chains;
90
91#[derive(GraphQLQuery)]
92#[graphql(
93 schema_path = "gql/service_schema.graphql",
94 query_path = "gql/service_requests.graphql",
95 response_derives = "Debug, Serialize, Clone, PartialEq"
96)]
97pub struct Applications;
98
99#[derive(GraphQLQuery)]
100#[graphql(
101 schema_path = "gql/service_schema.graphql",
102 query_path = "gql/service_requests.graphql",
103 response_derives = "Debug, Serialize, Clone, PartialEq"
104)]
105pub struct Blocks;
106
107#[derive(GraphQLQuery)]
108#[graphql(
109 schema_path = "gql/service_schema.graphql",
110 query_path = "gql/service_requests.graphql",
111 response_derives = "Debug, Serialize, Clone, PartialEq"
112)]
113pub struct Block;
114
115#[derive(GraphQLQuery)]
116#[graphql(
117 schema_path = "gql/service_schema.graphql",
118 query_path = "gql/service_requests.graphql",
119 response_derives = "Debug, Serialize, Clone, PartialEq"
120)]
121pub struct Notifications;
122
123#[derive(GraphQLQuery)]
124#[graphql(
125 schema_path = "gql/service_schema.graphql",
126 query_path = "gql/service_requests.graphql",
127 response_derives = "Debug, Serialize, Clone"
128)]
129pub struct Transfer;
130
131#[derive(Error, Debug)]
132pub enum ConversionError {
133 #[error(transparent)]
134 Serde(#[from] serde_json::Error),
135 #[error("Unexpected certificate type: {0}")]
136 UnexpectedCertificateType(String),
137}
138
139#[cfg(not(target_arch = "wasm32"))]
140mod from {
141 use linera_base::{data_types::Event, identifiers::StreamId};
142 use linera_chain::{
143 block::{Block, BlockBody, BlockHeader},
144 types::ConfirmedBlock,
145 };
146 use linera_execution::OutgoingMessage;
147
148 use super::*;
149
150 fn convert_transaction_metadata(
152 metadata: block::BlockBlockBlockBodyTransactionMetadata,
153 ) -> Result<Transaction, ConversionError> {
154 match metadata.transaction_type.as_str() {
155 "ReceiveMessages" => {
156 let incoming_bundle = metadata.incoming_bundle.ok_or_else(|| {
157 ConversionError::UnexpectedCertificateType(
158 "Missing incoming_bundle for ReceiveMessages transaction".to_string(),
159 )
160 })?;
161
162 let bundle = IncomingBundle {
163 origin: incoming_bundle.origin,
164 bundle: MessageBundle {
165 height: incoming_bundle.bundle.height,
166 timestamp: incoming_bundle.bundle.timestamp,
167 certificate_hash: incoming_bundle.bundle.certificate_hash,
168 transaction_index: incoming_bundle.bundle.transaction_index as u32,
169 messages: incoming_bundle
170 .bundle
171 .messages
172 .into_iter()
173 .map(|msg| linera_chain::data_types::PostedMessage {
174 authenticated_signer: msg.authenticated_signer,
175 grant: msg.grant,
176 refund_grant_to: msg.refund_grant_to,
177 kind: msg.kind,
178 index: msg.index as u32,
179 message: msg.message,
180 })
181 .collect(),
182 },
183 action: incoming_bundle.action,
184 };
185
186 Ok(Transaction::ReceiveMessages(bundle))
187 }
188 "ExecuteOperation" => {
189 let graphql_operation = metadata.operation.ok_or_else(|| {
190 ConversionError::UnexpectedCertificateType(
191 "Missing operation for ExecuteOperation transaction".to_string(),
192 )
193 })?;
194
195 let operation = match graphql_operation.operation_type.as_str() {
196 "System" => {
197 let bytes_hex = graphql_operation.system_bytes_hex.ok_or_else(|| {
198 ConversionError::UnexpectedCertificateType(
199 "Missing system_bytes_hex for System operation".to_string(),
200 )
201 })?;
202
203 let bytes = hex::decode(bytes_hex).map_err(|_| {
205 ConversionError::UnexpectedCertificateType(
206 "Invalid hex in system_bytes_hex".to_string(),
207 )
208 })?;
209
210 let system_operation: SystemOperation =
212 linera_base::bcs::from_bytes(&bytes).map_err(|_| {
213 ConversionError::UnexpectedCertificateType(
214 "Failed to deserialize system operation from BCS bytes"
215 .to_string(),
216 )
217 })?;
218
219 Operation::System(Box::new(system_operation))
220 }
221 "User" => {
222 let application_id = graphql_operation.application_id.ok_or_else(|| {
223 ConversionError::UnexpectedCertificateType(
224 "Missing application_id for User operation".to_string(),
225 )
226 })?;
227
228 let bytes_hex = graphql_operation.user_bytes_hex.ok_or_else(|| {
229 ConversionError::UnexpectedCertificateType(
230 "Missing user_bytes_hex for User operation".to_string(),
231 )
232 })?;
233
234 let bytes = hex::decode(bytes_hex).map_err(|_| {
236 ConversionError::UnexpectedCertificateType(
237 "Invalid hex in user_bytes_hex".to_string(),
238 )
239 })?;
240
241 Operation::User {
242 application_id: application_id.parse().map_err(|_| {
243 ConversionError::UnexpectedCertificateType(
244 "Invalid application_id format".to_string(),
245 )
246 })?,
247 bytes,
248 }
249 }
250 _ => {
251 return Err(ConversionError::UnexpectedCertificateType(format!(
252 "Unknown operation type: {}",
253 graphql_operation.operation_type
254 )));
255 }
256 };
257
258 Ok(Transaction::ExecuteOperation(operation))
259 }
260 _ => Err(ConversionError::UnexpectedCertificateType(format!(
261 "Unknown transaction type: {}",
262 metadata.transaction_type
263 ))),
264 }
265 }
266
267 impl From<block::BlockBlockBlockBodyMessages> for OutgoingMessage {
268 fn from(val: block::BlockBlockBlockBodyMessages) -> Self {
269 let block::BlockBlockBlockBodyMessages {
270 destination,
271 authenticated_signer,
272 grant,
273 refund_grant_to,
274 kind,
275 message,
276 } = val;
277 OutgoingMessage {
278 destination,
279 authenticated_signer,
280 grant,
281 refund_grant_to,
282 kind,
283 message,
284 }
285 }
286 }
287
288 impl TryFrom<block::BlockBlockBlock> for Block {
289 type Error = ConversionError;
290
291 fn try_from(val: block::BlockBlockBlock) -> Result<Self, Self::Error> {
292 let block::BlockBlockBlock { header, body } = val;
293 let block::BlockBlockBlockHeader {
294 chain_id,
295 epoch,
296 height,
297 timestamp,
298 authenticated_signer,
299 previous_block_hash,
300 state_hash,
301 transactions_hash,
302 messages_hash,
303 previous_message_blocks_hash,
304 previous_event_blocks_hash,
305 oracle_responses_hash,
306 events_hash,
307 blobs_hash,
308 operation_results_hash,
309 } = header;
310 let block::BlockBlockBlockBody {
311 messages,
312 previous_message_blocks,
313 previous_event_blocks,
314 oracle_responses,
315 events,
316 blobs,
317 operation_results,
318 transaction_metadata,
319 } = body;
320
321 let block_header = BlockHeader {
322 chain_id,
323 epoch,
324 height,
325 timestamp,
326 authenticated_signer,
327 previous_block_hash,
328 state_hash,
329 transactions_hash,
330 messages_hash,
331 previous_message_blocks_hash,
332 previous_event_blocks_hash,
333 oracle_responses_hash,
334 events_hash,
335 blobs_hash,
336 operation_results_hash,
337 };
338
339 let transactions = transaction_metadata
341 .into_iter()
342 .map(convert_transaction_metadata)
343 .collect::<Result<Vec<_>, _>>()?;
344
345 let block_body = BlockBody {
346 transactions,
347 messages: messages
348 .into_iter()
349 .map(|messages| messages.into_iter().map(Into::into).collect())
350 .collect::<Vec<Vec<_>>>(),
351 previous_message_blocks: serde_json::from_value(previous_message_blocks)
352 .map_err(ConversionError::Serde)?,
353 previous_event_blocks: serde_json::from_value(previous_event_blocks)
354 .map_err(ConversionError::Serde)?,
355 oracle_responses: oracle_responses.into_iter().collect(),
356 events: events
357 .into_iter()
358 .map(|events| events.into_iter().map(Into::into).collect())
359 .collect(),
360 blobs: blobs
361 .into_iter()
362 .map(|blobs| blobs.into_iter().collect())
363 .collect(),
364 operation_results,
365 };
366
367 Ok(Block {
368 header: block_header,
369 body: block_body,
370 })
371 }
372 }
373
374 impl From<block::BlockBlockBlockBodyEvents> for Event {
375 fn from(event: block::BlockBlockBlockBodyEvents) -> Self {
376 Event {
377 stream_id: event.stream_id.into(),
378 index: event.index as u32,
379 value: event.value.into_iter().map(|byte| byte as u8).collect(),
380 }
381 }
382 }
383
384 impl From<block::BlockBlockBlockBodyEventsStreamId> for StreamId {
385 fn from(stream_id: block::BlockBlockBlockBodyEventsStreamId) -> Self {
386 StreamId {
387 application_id: stream_id.application_id,
388 stream_name: stream_id.stream_name,
389 }
390 }
391 }
392
393 impl TryFrom<block::BlockBlock> for ConfirmedBlock {
394 type Error = ConversionError;
395
396 fn try_from(val: block::BlockBlock) -> Result<Self, Self::Error> {
397 match (val.status.as_str(), val.block) {
398 ("confirmed", block) => Ok(ConfirmedBlock::new(block.try_into()?)),
399 _ => Err(ConversionError::UnexpectedCertificateType(val.status)),
400 }
401 }
402 }
403}