1use std::str::FromStr;
4
5use crate::execution_info::ExecutionInfoForSlot;
6
7#[cfg(feature = "execution-info")]
8use crate::execution_info::TransferContext;
9use crate::{
10 ExecutionOutput, ExecutionQueryCycleInfos, ExecutionQueryError, ExecutionQueryExecutionStatus,
11 ExecutionQueryRequestItem, ExecutionQueryResponseItem, ExecutionQueryStakerInfo,
12 SlotExecutionOutput,
13};
14use grpc_api::execution_query_request_item as exec;
15use massa_models::address::Address;
16use massa_models::datastore::cleanup_datastore_key_range_query;
17use massa_models::deferred_calls::DeferredCallId;
18use massa_models::error::ModelsError;
19use massa_models::execution::EventFilter;
20use massa_models::mapping_grpc::to_denunciation_index;
21use massa_models::operation::OperationId;
22use massa_models::prehash::{CapacityAllocator, PreHashSet};
23use massa_proto_rs::massa::api::v1 as grpc_api;
24#[cfg(feature = "execution-info")]
25use massa_proto_rs::massa::model::v1::CoinOrigin;
26use massa_proto_rs::massa::model::v1::{self as grpc_model};
27
28pub fn to_querystate_filter(
30 query: grpc_api::ExecutionQueryRequestItem,
31 max_datastore_query_config: Option<u32>,
32 max_datastore_key_length: u8,
33) -> Result<ExecutionQueryRequestItem, ModelsError> {
34 if let Some(item) = query.request_item {
35 match item {
36 exec::RequestItem::AddressExistsCandidate(value) => {
37 Ok(ExecutionQueryRequestItem::AddressExistsCandidate(
38 Address::from_str(&value.address)?,
39 ))
40 }
41 exec::RequestItem::AddressExistsFinal(value) => Ok(
42 ExecutionQueryRequestItem::AddressExistsFinal(Address::from_str(&value.address)?),
43 ),
44 exec::RequestItem::AddressBalanceCandidate(value) => {
45 Ok(ExecutionQueryRequestItem::AddressBalanceCandidate(
46 Address::from_str(&value.address)?,
47 ))
48 }
49 exec::RequestItem::AddressBalanceFinal(value) => Ok(
50 ExecutionQueryRequestItem::AddressBalanceFinal(Address::from_str(&value.address)?),
51 ),
52 exec::RequestItem::AddressBytecodeCandidate(value) => {
53 Ok(ExecutionQueryRequestItem::AddressBytecodeCandidate(
54 Address::from_str(&value.address)?,
55 ))
56 }
57 exec::RequestItem::AddressBytecodeFinal(value) => Ok(
58 ExecutionQueryRequestItem::AddressBytecodeFinal(Address::from_str(&value.address)?),
59 ),
60 exec::RequestItem::AddressDatastoreKeysCandidate(value) => {
61 let address = Address::from_str(&value.address)?;
62
63 let start_key = match (value.start_key, value.inclusive_start_key.unwrap_or(true)) {
64 (None, _) => std::ops::Bound::Unbounded,
65 (Some(k), true) => std::ops::Bound::Included(k),
66 (Some(k), false) => std::ops::Bound::Excluded(k),
67 };
68 let end_key = match (value.end_key, value.inclusive_end_key.unwrap_or(true)) {
69 (None, _) => std::ops::Bound::Unbounded,
70 (Some(k), true) => std::ops::Bound::Included(k),
71 (Some(k), false) => std::ops::Bound::Excluded(k),
72 };
73
74 let (prefix, start_key, end_key, count) = cleanup_datastore_key_range_query(
75 &value.prefix,
76 start_key,
77 end_key,
78 value.limit,
79 max_datastore_key_length,
80 max_datastore_query_config,
81 )?;
82
83 Ok(ExecutionQueryRequestItem::AddressDatastoreKeysCandidate {
84 address,
85 prefix,
86 start_key,
87 end_key,
88 count,
89 })
90 }
91 exec::RequestItem::AddressDatastoreKeysFinal(value) => {
92 let address = Address::from_str(&value.address)?;
93
94 let start_key = match (value.start_key, value.inclusive_start_key.unwrap_or(true)) {
95 (None, _) => std::ops::Bound::Unbounded,
96 (Some(k), true) => std::ops::Bound::Included(k),
97 (Some(k), false) => std::ops::Bound::Excluded(k),
98 };
99 let end_key = match (value.end_key, value.inclusive_end_key.unwrap_or(true)) {
100 (None, _) => std::ops::Bound::Unbounded,
101 (Some(k), true) => std::ops::Bound::Included(k),
102 (Some(k), false) => std::ops::Bound::Excluded(k),
103 };
104
105 let (prefix, start_key, end_key, count) = cleanup_datastore_key_range_query(
106 &value.prefix,
107 start_key,
108 end_key,
109 value.limit,
110 max_datastore_key_length,
111 max_datastore_query_config,
112 )?;
113
114 Ok(ExecutionQueryRequestItem::AddressDatastoreKeysFinal {
115 address,
116 prefix,
117 start_key,
118 end_key,
119 count,
120 })
121 }
122 exec::RequestItem::AddressDatastoreValueCandidate(value) => {
123 Ok(ExecutionQueryRequestItem::AddressDatastoreValueCandidate {
124 addr: Address::from_str(&value.address)?,
125 key: value.key,
126 })
127 }
128 exec::RequestItem::AddressDatastoreValueFinal(value) => {
129 Ok(ExecutionQueryRequestItem::AddressDatastoreValueFinal {
130 addr: Address::from_str(&value.address)?,
131 key: value.key,
132 })
133 }
134 exec::RequestItem::OpExecutionStatusCandidate(value) => {
135 Ok(ExecutionQueryRequestItem::OpExecutionStatusCandidate(
136 OperationId::from_str(&value.operation_id)?,
137 ))
138 }
139 exec::RequestItem::OpExecutionStatusFinal(value) => {
140 Ok(ExecutionQueryRequestItem::OpExecutionStatusFinal(
141 OperationId::from_str(&value.operation_id)?,
142 ))
143 }
144 exec::RequestItem::DenunciationExecutionStatusCandidate(value) => Ok(
146 ExecutionQueryRequestItem::DenunciationExecutionStatusCandidate(
147 to_denunciation_index(value.denunciation_index.ok_or_else(|| {
148 ModelsError::ErrorRaised("no denounciation index found".to_string())
149 })?)?,
150 ),
151 ),
152 exec::RequestItem::DenunciationExecutionStatusFinal(value) => {
154 Ok(ExecutionQueryRequestItem::DenunciationExecutionStatusFinal(
155 to_denunciation_index(value.denunciation_index.ok_or_else(|| {
156 ModelsError::ErrorRaised("no denounciation index found".to_string())
157 })?)?,
158 ))
159 }
160 exec::RequestItem::AddressRollsCandidate(value) => {
161 Ok(ExecutionQueryRequestItem::AddressRollsCandidate(
162 Address::from_str(&value.address)?,
163 ))
164 }
165 exec::RequestItem::AddressRollsFinal(value) => Ok(
166 ExecutionQueryRequestItem::AddressRollsFinal(Address::from_str(&value.address)?),
167 ),
168 exec::RequestItem::AddressDeferredCreditsCandidate(value) => {
169 Ok(ExecutionQueryRequestItem::AddressDeferredCreditsCandidate(
170 Address::from_str(&value.address)?,
171 ))
172 }
173 exec::RequestItem::AddressDeferredCreditsFinal(value) => {
174 Ok(ExecutionQueryRequestItem::AddressDeferredCreditsFinal(
175 Address::from_str(&value.address)?,
176 ))
177 }
178 exec::RequestItem::CycleInfos(value) => {
180 let addresses = value
181 .restrict_to_addresses
182 .into_iter()
183 .map(|address| Address::from_str(&address))
184 .collect::<Result<Vec<_>, _>>()?;
185 let mut addresses_set = PreHashSet::with_capacity(addresses.len());
186 addresses_set.extend(addresses);
187 Ok(ExecutionQueryRequestItem::CycleInfos {
188 cycle: value.cycle,
189 restrict_to_addresses: Some(addresses_set),
190 })
191 }
192 exec::RequestItem::Events(value) => {
193 let event_filter = to_event_filter(value.filters)?;
194 Ok(ExecutionQueryRequestItem::Events(event_filter))
195 }
196 exec::RequestItem::DeferredCallQuote(value) => {
197 Ok(ExecutionQueryRequestItem::DeferredCallQuote {
198 target_slot: value
199 .target_slot
200 .ok_or(ModelsError::ErrorRaised(
201 "target slot is required".to_string(),
202 ))?
203 .into(),
204 max_gas_request: value.max_gas,
205 params_size: value.params_size,
206 })
207 }
208 exec::RequestItem::DeferredCallInfo(info) => {
209 let id = DeferredCallId::from_str(&info.call_id)?;
210 Ok(ExecutionQueryRequestItem::DeferredCallInfo(id))
211 }
212 exec::RequestItem::DeferredCallsBySlot(value) => {
213 Ok(ExecutionQueryRequestItem::DeferredCallsBySlot(
214 value
215 .slot
216 .ok_or(ModelsError::ErrorRaised("slot is required".to_string()))?
217 .into(),
218 ))
219 }
220 }
221 } else {
222 Err(ModelsError::ErrorRaised("no filter provided".to_string()))
223 }
224}
225
226pub fn to_event_filter(
228 sce_filters: Vec<grpc_api::ScExecutionEventsFilter>,
229) -> Result<EventFilter, ModelsError> {
230 let mut event_filter = EventFilter::default();
231 for query in sce_filters {
232 if let Some(filter) = query.filter {
233 match filter {
234 grpc_api::sc_execution_events_filter::Filter::SlotRange(slot_range) => {
235 event_filter.start = slot_range.start_slot.map(|slot| slot.into());
236 event_filter.end = slot_range.end_slot.map(|slot| slot.into());
237 }
238 grpc_api::sc_execution_events_filter::Filter::CallerAddress(caller_address) => {
239 event_filter.original_caller_address =
240 Some(Address::from_str(&caller_address)?);
241 }
242 grpc_api::sc_execution_events_filter::Filter::EmitterAddress(emitter_address) => {
243 event_filter.emitter_address = Some(Address::from_str(&emitter_address)?);
244 }
245 grpc_api::sc_execution_events_filter::Filter::OriginalOperationId(operation_id) => {
246 event_filter.original_operation_id =
247 Some(OperationId::from_str(&operation_id)?);
248 }
249 grpc_api::sc_execution_events_filter::Filter::IsFailure(is_failure) => {
250 event_filter.is_error = Some(is_failure);
251 }
252 grpc_api::sc_execution_events_filter::Filter::Status(status) => {
253 match grpc_model::ScExecutionEventStatus::try_from(status) {
255 Ok(grpc_model::ScExecutionEventStatus::Final) => {
256 event_filter.is_final = Some(true)
257 }
258 Ok(grpc_model::ScExecutionEventStatus::Candidate) => {
259 event_filter.is_final = Some(false)
260 }
261 _ => {}
262 }
263 }
264 }
265 }
266 }
267
268 Ok(event_filter)
269}
270
271pub fn to_execution_query_response(
273 value: Result<ExecutionQueryResponseItem, ExecutionQueryError>,
274) -> grpc_api::ExecutionQueryResponse {
275 match value {
276 Ok(item) => grpc_api::ExecutionQueryResponse {
277 response: Some(grpc_api::execution_query_response::Response::Result(
278 to_execution_query_result(item),
279 )),
280 },
281 Err(err) => grpc_api::ExecutionQueryResponse {
282 response: Some(grpc_api::execution_query_response::Response::Error(
283 err.into(),
284 )),
285 },
286 }
287}
288
289fn to_execution_query_result(
291 value: ExecutionQueryResponseItem,
292) -> grpc_api::ExecutionQueryResponseItem {
293 let response_item = match value {
294 ExecutionQueryResponseItem::Boolean(result) => {
295 grpc_api::execution_query_response_item::ResponseItem::Boolean(result)
296 }
297 ExecutionQueryResponseItem::RollCount(result) => {
298 grpc_api::execution_query_response_item::ResponseItem::RollCount(result)
299 }
300 ExecutionQueryResponseItem::Amount(result) => {
301 grpc_api::execution_query_response_item::ResponseItem::Amount(result.into())
302 }
303 ExecutionQueryResponseItem::Bytecode(result) => {
304 grpc_api::execution_query_response_item::ResponseItem::Bytes(result.0)
305 }
306 ExecutionQueryResponseItem::DatastoreValue(result) => {
307 grpc_api::execution_query_response_item::ResponseItem::Bytes(result)
308 }
309 ExecutionQueryResponseItem::AddressDatastoreKeys(result, _address, _is_final) => {
310 grpc_api::execution_query_response_item::ResponseItem::VecBytes(
311 grpc_model::ArrayOfBytesWrapper {
312 items: result.into_iter().collect(),
313 },
314 )
315 }
316 ExecutionQueryResponseItem::DeferredCredits(result) => {
317 grpc_api::execution_query_response_item::ResponseItem::DeferredCredits(
318 grpc_api::DeferredCreditsEntryWrapper {
319 entries: result
320 .into_iter()
321 .map(|(slot, amount)| grpc_api::DeferredCreditsEntry {
322 slot: Some(slot.into()),
323 amount: Some(amount.into()),
324 })
325 .collect(),
326 },
327 )
328 }
329 ExecutionQueryResponseItem::ExecutionStatus(result) => match result {
330 ExecutionQueryExecutionStatus::AlreadyExecutedWithSuccess => {
331 grpc_api::execution_query_response_item::ResponseItem::ExecutionStatus(
332 grpc_api::ExecutionQueryExecutionStatus::AlreadyExecutedWithSuccess as i32,
333 )
334 }
335 ExecutionQueryExecutionStatus::AlreadyExecutedWithFailure => {
336 grpc_api::execution_query_response_item::ResponseItem::ExecutionStatus(
337 grpc_api::ExecutionQueryExecutionStatus::AlreadyExecutedWithFailure as i32,
338 )
339 }
340 ExecutionQueryExecutionStatus::ExecutableOrExpired => {
341 grpc_api::execution_query_response_item::ResponseItem::ExecutionStatus(
342 grpc_api::ExecutionQueryExecutionStatus::ExecutableOrExpired as i32,
343 )
344 }
345 },
346 ExecutionQueryResponseItem::CycleInfos(result) => {
347 grpc_api::execution_query_response_item::ResponseItem::CycleInfos(to_cycle_info(result))
348 }
349 ExecutionQueryResponseItem::Events(result) => {
350 grpc_api::execution_query_response_item::ResponseItem::Events(
351 grpc_api::ScOutputEventsWrapper {
352 events: result.into_iter().map(|event| event.into()).collect(),
353 },
354 )
355 }
356 ExecutionQueryResponseItem::DeferredCallQuote(
357 target_slot,
358 max_gas_request,
359 available,
360 price,
361 ) => grpc_api::execution_query_response_item::ResponseItem::DeferredCallQuote(
362 grpc_api::DeferredCallQuoteResponse {
363 target_slot: Some(target_slot.into()),
364 max_gas_request,
365 available,
366 price: Some(price.into()),
367 },
368 ),
369 ExecutionQueryResponseItem::DeferredCallInfo(call_id, call) => {
370 grpc_api::execution_query_response_item::ResponseItem::DeferredCallInfo(
371 grpc_api::DeferredCallInfoResponse {
372 call_id: call_id.to_string(),
373 call: Some(call.into()),
374 },
375 )
376 }
377 ExecutionQueryResponseItem::DeferredCallsBySlot(slot, ids) => {
378 let arr = ids.into_iter().map(|id| id.to_string()).collect();
379 grpc_api::execution_query_response_item::ResponseItem::DeferredCallsBySlot(
380 grpc_api::DeferredCallsBySlotResponse {
381 slot: Some(slot.into()),
382 call_ids: arr,
383 },
384 )
385 }
386 };
387
388 grpc_api::ExecutionQueryResponseItem {
389 response_item: Some(response_item),
390 }
391}
392
393fn to_cycle_info(value: ExecutionQueryCycleInfos) -> grpc_api::ExecutionQueryCycleInfos {
395 grpc_api::ExecutionQueryCycleInfos {
396 cycle: value.cycle,
397 is_final: value.is_final,
398 staker_infos: value
399 .staker_infos
400 .into_iter()
401 .map(|(address, info)| to_execution_query_staker_info(address, info))
402 .collect(),
403 }
404}
405
406fn to_execution_query_staker_info(
408 address: Address,
409 info: ExecutionQueryStakerInfo,
410) -> grpc_api::ExecutionQueryStakerInfoEntry {
411 grpc_api::ExecutionQueryStakerInfoEntry {
412 address: address.to_string(),
413 info: Some(grpc_api::ExecutionQueryStakerInfo {
414 active_rolls: info.active_rolls,
415 production_stats: Some(grpc_api::ExecutionQueryStakerInfoProductionStatsEntry {
416 address: address.to_string(),
417 stats: Some(grpc_api::ExecutionQueryStakerInfoProductionStats {
418 block_success_count: info.production_stats.block_success_count,
419 block_failure_count: info.production_stats.block_failure_count,
420 }),
421 }),
422 }),
423 }
424}
425
426impl From<SlotExecutionOutput> for grpc_model::SlotExecutionOutput {
427 fn from(value: SlotExecutionOutput) -> Self {
428 match value {
429 SlotExecutionOutput::ExecutedSlot(execution_output) => {
430 grpc_model::SlotExecutionOutput {
431 status: grpc_model::ExecutionOutputStatus::Candidate as i32,
432 execution_output: Some(execution_output.into()),
433 }
434 }
435 SlotExecutionOutput::FinalizedSlot(execution_output) => {
436 grpc_model::SlotExecutionOutput {
437 status: grpc_model::ExecutionOutputStatus::Final as i32,
438 execution_output: Some(execution_output.into()),
439 }
440 }
441 }
442 }
443}
444
445impl From<ExecutionOutput> for grpc_model::ExecutionOutput {
446 fn from(value: ExecutionOutput) -> Self {
447 grpc_model::ExecutionOutput {
448 slot: Some(value.slot.into()),
449 block_id: value.block_info.map(|i| i.block_id.to_string()),
450 events: value
451 .events
452 .0
453 .into_iter()
454 .map(|event| event.into())
455 .collect(),
456 state_changes: Some(value.state_changes.into()),
457 }
458 }
459}
460
461impl From<ExecutionQueryError> for grpc_model::Error {
462 fn from(value: ExecutionQueryError) -> Self {
463 match value {
464 ExecutionQueryError::NotFound(error) => grpc_model::Error {
465 code: 404,
467 message: error,
468 },
469 ExecutionQueryError::TooLargeResponse(error) => grpc_model::Error {
470 code: 413,
471 message: error,
472 },
473 }
474 }
475}
476
477impl From<ExecutionInfoForSlot> for grpc_api::NewTransfersInfoServerResponse {
478 fn from(value: ExecutionInfoForSlot) -> Self {
479 #[cfg(feature = "execution-info")]
480 let transfers_info: Vec<grpc_model::ExecTransferInfo> = value
481 .transfers
482 .into_iter()
483 .map(|transfer| {
484 let id = transfer.id.unwrap_or("Unknown id".to_string());
485
486 let from_address = transfer.from.map(|a| a.to_string());
487 let to_address = transfer.to.map(|a| a.to_string());
488
489 let value = match transfer.value {
490 crate::execution_info::TransferValue::Rolls(roll_count) => {
491 grpc_model::TransferValue {
492 value: Some(grpc_model::transfer_value::Value::Rolls(
493 roll_count.into(),
494 )),
495 }
496 }
497 crate::execution_info::TransferValue::DeferredCredits(amount) => {
498 grpc_model::TransferValue {
499 value: Some(grpc_model::transfer_value::Value::DeferredCredits(
500 amount.into(),
501 )),
502 }
503 }
504 crate::execution_info::TransferValue::Coins(amount) => {
505 grpc_model::TransferValue {
506 value: Some(grpc_model::transfer_value::Value::Coins(amount.into())),
507 }
508 }
509 };
510
511 let (origin, ctx) = match transfer.context {
512 TransferContext::TransactionCoins(ctx) => {
513 (CoinOrigin::OpTransactionCoins as i32, Some(ctx))
514 }
515 TransferContext::AyncMsgCancel(ctx) => {
516 (CoinOrigin::AsyncMsgCancel as i32, Some(ctx))
517 }
518 TransferContext::DeferredCredits(ctx) => {
519 (CoinOrigin::DeferredCredit as i32, Some(ctx))
520 }
521 TransferContext::DeferredCallFail(ctx) => {
522 (CoinOrigin::DeferredCallFail as i32, Some(ctx))
523 }
524 TransferContext::DeferredCallCancel(ctx) => {
525 (CoinOrigin::DeferredCallCancel as i32, Some(ctx))
526 }
527 TransferContext::DeferredCallCoins(ctx) => {
528 (CoinOrigin::DeferredCallCoins as i32, Some(ctx))
529 }
530 TransferContext::DeferredCallRegister(ctx) => {
531 (CoinOrigin::DeferredCallRegister as i32, Some(ctx))
532 }
533 TransferContext::DeferredCallStorageRefund(ctx) => {
534 (CoinOrigin::DeferredCallStorageRefund as i32, Some(ctx))
535 }
536 TransferContext::OperationFee(ctx) => {
537 (CoinOrigin::OpTransactionFees as i32, Some(ctx))
538 }
539 TransferContext::RollBuy(ctx) => (CoinOrigin::OpRollBuy as i32, Some(ctx)),
540 TransferContext::RollSell(ctx) => (CoinOrigin::OpRollSell as i32, Some(ctx)),
541 TransferContext::RollSlash => (CoinOrigin::Slash as i32, None),
542 TransferContext::CreateSCStorage => (CoinOrigin::CreateScStorage as i32, None),
543 TransferContext::DatastoreStorage => {
544 (CoinOrigin::DatastoreStorage as i32, None)
545 }
546 TransferContext::CallSCCoins(ctx) => {
547 (CoinOrigin::OpCallscCoins as i32, Some(ctx))
548 }
549 TransferContext::AsyncMsgCoins(ctx) => {
550 (CoinOrigin::AsyncMsgCoins as i32, Some(ctx))
551 }
552 TransferContext::EndorsementCreatorReward => {
553 (CoinOrigin::EndorsementReward as i32, None)
554 }
555 TransferContext::EndorsementTargetReward => {
556 (CoinOrigin::EndorsedReward as i32, None)
557 }
558 TransferContext::BlockCreatorReward => (CoinOrigin::BlockReward as i32, None),
559 TransferContext::ReadOnlyBytecodeExecutionFee => {
560 (CoinOrigin::ReadOnlyBytecodeExecFees as i32, None)
561 }
562 TransferContext::ReadOnlyFunctionCallFee => {
563 (CoinOrigin::ReadOnlyFnCallFees as i32, None)
564 }
565 TransferContext::ReadOnlyFunctionCallCoins => {
566 (CoinOrigin::ReadOnlyFnCallCoins as i32, None)
567 }
568 TransferContext::SetBytecodeStorage => {
569 (CoinOrigin::SetBytecodeStorage as i32, None)
570 }
571 TransferContext::AbiCallCoins => (CoinOrigin::AbiCallCoins as i32, None),
572 TransferContext::AbiTransferCoins => {
573 (CoinOrigin::AbiTransferCoins as i32, None)
574 }
575 TransferContext::AbiTransferForCoins => {
576 (CoinOrigin::AbiTransferForCoins as i32, None)
577 }
578 TransferContext::AbiSendMsgCoins => (CoinOrigin::AbiSendMsgCoins as i32, None),
579 TransferContext::AbiSendMsgFee => (CoinOrigin::AbiSendMsgFees as i32, None),
580 };
581
582 let (operation_id, async_msg_id, deferred_call_id, denunciation_index) = match ctx {
583 Some(ctx) => (
584 ctx.operation_id.map(|id| id.to_string()),
585 ctx.async_message_id_str,
586 ctx.deferred_call_id.map(|id| id.to_string()),
587 ctx.denunciation_index.map(|idx| idx.into()),
588 ),
589 None => (None, None, None, None),
590 };
591
592 grpc_model::ExecTransferInfo {
593 id,
594 from_address,
595 to_address,
596 origin,
597 value: Some(value),
598 operation_id,
599 async_msg_id,
600 deferred_call_id,
601 denunciation_index,
602 }
603 })
604 .collect();
605
606 grpc_api::NewTransfersInfoServerResponse {
607 slot: Some(value.slot.into()),
608 timestamp: value.timestamp.as_millis() as i64,
609 block_id: value.opt_block_id.map(|b| b.to_string()),
610 #[cfg(feature = "execution-info")]
611 transfers_info,
612 #[cfg(not(feature = "execution-info"))]
613 transfers_info: vec![],
614 }
615 }
616}