massa_api/
public.rs

1//! Copyright (c) 2022 MASSA LABS <info@massa.net>
2#![allow(clippy::too_many_arguments)]
3
4use crate::{MassaRpcServer, Public, RpcServer, StopHandle, Value, API};
5use async_trait::async_trait;
6use itertools::{izip, Itertools};
7use jsonrpsee::core::{client::Error as JsonRpseeError, RpcResult};
8use massa_api_exports::{
9    address::{
10        AddressFilter, AddressInfo, GetAddressDatastoreKeysRequest, GetAddressDatastoreKeysResponse,
11    },
12    block::{BlockInfo, BlockInfoContent, BlockSummary},
13    config::APIConfig,
14    datastore::{DatastoreEntryInput, DatastoreEntryOutput},
15    endorsement::EndorsementInfo,
16    error::ApiError,
17    execution::{
18        DeferredCallResponse, DeferredCallsQuoteRequest, DeferredCallsQuoteResponse,
19        DeferredCallsSlotResponse, ExecuteReadOnlyResponse, ReadOnlyBytecodeExecution,
20        ReadOnlyCall, ReadOnlyResult, Transfer,
21    },
22    node::NodeStatus,
23    operation::{OperationInfo, OperationInput},
24    page::{PageRequest, PagedVec},
25    slot::SlotAmount,
26    TimeInterval,
27};
28use massa_consensus_exports::block_status::DiscardReason;
29use massa_consensus_exports::ConsensusController;
30use massa_execution_exports::{
31    ExecutionController, ExecutionQueryRequest, ExecutionQueryRequestItem, ExecutionQueryResponse,
32    ExecutionQueryResponseItem, ExecutionStackElement, ReadOnlyExecutionRequest,
33    ReadOnlyExecutionTarget,
34};
35use massa_models::{
36    address::Address,
37    amount::Amount,
38    block::{Block, BlockGraphStatus},
39    block_id::BlockId,
40    clique::Clique,
41    composite::PubkeySig,
42    config::CompactConfig,
43    datastore::{cleanup_datastore_key_range_query, DatastoreDeserializer},
44    deferred_calls::DeferredCallId,
45    endorsement::{EndorsementId, SecureShareEndorsement},
46    error::ModelsError,
47    execution::EventFilter,
48    node::NodeId,
49    operation::{OperationDeserializer, OperationId, SecureShareOperation},
50    output_event::SCOutputEvent,
51    prehash::{PreHashMap, PreHashSet},
52    secure_share::SecureShareDeserializer,
53    slot::{IndexedSlot, Slot},
54    timeslots::{self, get_latest_block_slot_at_timestamp, time_range_to_slot_range},
55    version::Version,
56};
57use massa_pool_exports::PoolController;
58use massa_pos_exports::SelectorController;
59use massa_protocol_exports::{PeerConnectionType, ProtocolConfig, ProtocolController};
60use massa_serialization::{DeserializeError, Deserializer};
61use massa_storage::Storage;
62use massa_time::MassaTime;
63use massa_versioning::versioning_factory::FactoryStrategy;
64use massa_versioning::{
65    keypair_factory::KeyPairFactory, versioning::MipStore, versioning_factory::VersioningFactory,
66};
67use std::net::{IpAddr, SocketAddr};
68use std::{collections::BTreeMap, str::FromStr};
69use tracing::warn;
70
71impl API<Public> {
72    /// generate a new public API
73    pub fn new(
74        consensus_controller: Box<dyn ConsensusController>,
75        execution_controller: Box<dyn ExecutionController>,
76        api_settings: APIConfig,
77        selector_controller: Box<dyn SelectorController>,
78        pool_command_sender: Box<dyn PoolController>,
79        protocol_controller: Box<dyn ProtocolController>,
80        protocol_config: ProtocolConfig,
81        version: Version,
82        node_id: NodeId,
83        storage: Storage,
84        mip_store: MipStore,
85    ) -> Self {
86        API(Public {
87            consensus_controller,
88            api_settings,
89            pool_command_sender,
90            version,
91            protocol_controller,
92            node_id,
93            execution_controller,
94            selector_controller,
95            protocol_config,
96            storage,
97            keypair_factory: KeyPairFactory { mip_store },
98        })
99    }
100
101    fn exec_query(&self, requests: Vec<ExecutionQueryRequestItem>) -> ExecutionQueryResponse {
102        self.0
103            .execution_controller
104            .query_state(ExecutionQueryRequest {
105                requests,
106                max_response_size: self.0.api_settings.max_response_body_size as usize,
107                max_event_count: Some(self.0.api_settings.max_event_per_query as usize),
108                query_state_deadline_ms: self.0.api_settings.query_state_deadline_ms,
109            })
110    }
111}
112
113#[async_trait]
114impl RpcServer for API<Public> {
115    async fn serve(
116        self,
117        url: &SocketAddr,
118        api_config: &APIConfig,
119    ) -> Result<StopHandle, JsonRpseeError> {
120        crate::serve(self.into_rpc(), url, api_config).await
121    }
122}
123
124#[doc(hidden)]
125#[async_trait]
126impl MassaRpcServer for API<Public> {
127    fn stop_node(&self) -> RpcResult<()> {
128        crate::wrong_api::<()>()
129    }
130
131    async fn node_sign_message(&self, _: Vec<u8>) -> RpcResult<PubkeySig> {
132        crate::wrong_api::<PubkeySig>()
133    }
134
135    async fn add_staking_secret_keys(&self, _: Vec<String>) -> RpcResult<()> {
136        crate::wrong_api::<()>()
137    }
138
139    #[cfg(feature = "execution-trace")]
140    async fn get_slots_transfers(&self, slots: Vec<Slot>) -> RpcResult<Vec<Vec<Transfer>>> {
141        use massa_api_exports::execution::TransferContext;
142        use std::str::FromStr;
143
144        let mut res: Vec<Vec<Transfer>> = Vec::with_capacity(slots.len());
145        for slot in slots {
146            let Some(block_id) = self
147                .0
148                .consensus_controller
149                .get_blockclique_block_at_slot(slot)
150            else {
151                // Every `Transfer` returned here carries the block ID of the slot, so a slot
152                // with no block (`get_blockclique_block_at_slot` also covers final blocks) has
153                // nothing to report. Keep `res` positionally aligned with the input `slots`
154                // anyway: it must still occupy its position, otherwise every later entry would
155                // be misattributed to the wrong slot.
156                res.push(Vec::new());
157                continue;
158            };
159            let mut transfers = Vec::new();
160            // Read the ABI call stack and the direct transfers for this slot from a single
161            // consistent snapshot, so the combined response cannot mix data from two
162            // different executions of the same slot (a re-execution between two reads).
163            let (abi_calls, slot_transfers) = self
164                .0
165                .execution_controller
166                .get_slot_abi_call_stack_and_transfers(slot);
167            if let Some(abi_calls) = abi_calls {
168                // flatten & filter transfer trace in asc_call_stacks
169
170                let abi_transfer_1 = "assembly_script_transfer_coins".to_string();
171                let abi_transfer_2 = "assembly_script_transfer_coins_for".to_string();
172                let abi_transfer_3 = "abi_transfer_coins".to_string();
173                let transfer_abi_names = vec![abi_transfer_1, abi_transfer_2, abi_transfer_3];
174                for (i, asc_call_stack) in abi_calls.asc_call_stacks.iter().enumerate() {
175                    for abi_trace in asc_call_stack {
176                        let only_transfer = abi_trace.flatten_filter(&transfer_abi_names);
177                        for transfer in only_transfer {
178                            let (t_from, t_to, t_amount) = transfer.parse_transfer();
179                            if let (Ok(addr_from), Ok(addr_to)) =
180                                (Address::from_str(&t_from), Address::from_str(&t_to))
181                            {
182                                transfers.push(Transfer {
183                                    from: addr_from,
184                                    to: addr_to,
185                                    amount: Amount::from_raw(t_amount),
186                                    effective_amount_received: Amount::from_raw(t_amount),
187                                    context: TransferContext::ASC(i as u64),
188                                    succeed: true,
189                                    fee: Amount::from_raw(0),
190                                    block_id,
191                                });
192                            }
193                        }
194                    }
195                }
196
197                for (call_id, deferred_call_call_stack) in abi_calls.deferred_call_stacks {
198                    for abi_trace in deferred_call_call_stack {
199                        let only_transfer = abi_trace.flatten_filter(&transfer_abi_names);
200                        for transfer in only_transfer {
201                            let (t_from, t_to, t_amount) = transfer.parse_transfer();
202                            if let (Ok(addr_from), Ok(addr_to)) =
203                                (Address::from_str(&t_from), Address::from_str(&t_to))
204                            {
205                                transfers.push(Transfer {
206                                    from: addr_from,
207                                    to: addr_to,
208                                    amount: Amount::from_raw(t_amount),
209                                    effective_amount_received: Amount::from_raw(t_amount),
210                                    context: TransferContext::DeferredCall(call_id.clone()),
211                                    succeed: true,
212                                    fee: Amount::from_raw(0),
213                                    block_id,
214                                });
215                            }
216                        }
217                    }
218                }
219
220                for op_call_stack in abi_calls.operation_call_stacks {
221                    let op_id = op_call_stack.0;
222                    let op_call_stack = op_call_stack.1;
223                    for abi_trace in op_call_stack {
224                        let only_transfer = abi_trace.flatten_filter(&transfer_abi_names);
225                        for transfer in only_transfer {
226                            let (t_from, t_to, t_amount) = transfer.parse_transfer();
227                            if let (Ok(addr_from), Ok(addr_to)) =
228                                (Address::from_str(&t_from), Address::from_str(&t_to))
229                            {
230                                transfers.push(Transfer {
231                                    from: addr_from,
232                                    to: addr_to,
233                                    amount: Amount::from_raw(t_amount),
234                                    effective_amount_received: Amount::from_raw(t_amount),
235                                    context: TransferContext::Operation(op_id),
236                                    succeed: true,
237                                    fee: Amount::from_raw(0),
238                                    block_id,
239                                });
240                            }
241                        }
242                    }
243                }
244            }
245            let transfers_op: Vec<Transfer> = slot_transfers
246                .unwrap_or_default()
247                .iter()
248                .map(|t| Transfer {
249                    from: t.from,
250                    to: t.to,
251                    amount: t.amount,
252                    effective_amount_received: t.effective_received_amount,
253                    context: TransferContext::Operation(t.op_id),
254                    succeed: t.succeed,
255                    fee: t.fee,
256                    block_id,
257                })
258                .collect();
259            transfers.extend(transfers_op);
260            res.push(transfers);
261        }
262        Ok(res)
263    }
264
265    #[cfg(not(feature = "execution-trace"))]
266    async fn get_slots_transfers(&self, _: Vec<Slot>) -> RpcResult<Vec<Vec<Transfer>>> {
267        RpcResult::Err(ApiError::BadRequest("feature execution-trace is not enabled".into()).into())
268    }
269
270    async fn execute_read_only_bytecode(
271        &self,
272        reqs: Vec<ReadOnlyBytecodeExecution>,
273    ) -> RpcResult<Vec<ExecuteReadOnlyResponse>> {
274        if reqs.len() as u64 > self.0.api_settings.max_arguments {
275            return Err(ApiError::BadRequest("too many arguments".into()).into());
276        }
277
278        let mut res: Vec<ExecuteReadOnlyResponse> = Vec::with_capacity(reqs.len());
279        for ReadOnlyBytecodeExecution {
280            max_gas,
281            address,
282            bytecode,
283            operation_datastore,
284            fee,
285        } in reqs
286        {
287            let address = if let Some(addr) = address {
288                addr
289            } else {
290                let now = MassaTime::now();
291                let keypair = self
292                    .0
293                    .keypair_factory
294                    .create(&(), FactoryStrategy::At(now))
295                    .map_err(ApiError::from)?;
296                Address::from_public_key(&keypair.get_public_key())
297            };
298
299            let op_datastore = match operation_datastore {
300                Some(v) => {
301                    let deserializer = DatastoreDeserializer::new(
302                        self.0.api_settings.max_op_datastore_entry_count,
303                        self.0.api_settings.max_op_datastore_key_length,
304                        self.0.api_settings.max_op_datastore_value_length,
305                    );
306                    match deserializer.deserialize::<DeserializeError>(&v) {
307                        Ok((_, deserialized)) => Some(deserialized),
308                        Err(e) => {
309                            return Err(ApiError::InconsistencyError(format!(
310                                "Operation datastore error: {}",
311                                e
312                            ))
313                            .into())
314                        }
315                    }
316                }
317                None => None,
318            };
319
320            // translate request
321            let req = ReadOnlyExecutionRequest {
322                max_gas,
323                target: ReadOnlyExecutionTarget::BytecodeExecution(bytecode),
324                call_stack: vec![ExecutionStackElement {
325                    address,
326                    coins: Default::default(),
327                    owned_addresses: vec![address],
328                    operation_datastore: op_datastore,
329                }],
330                coins: None,
331                fee,
332            };
333
334            // check if fee is enough
335            if let Some(fee) = fee {
336                if fee.checked_sub(self.0.api_settings.minimal_fees).is_none() {
337                    let result = ExecuteReadOnlyResponse {
338                        executed_at: Slot::new(0, 0),
339                        result: ReadOnlyResult::Error(format!(
340                            "fee is too low provided: {} , minimal_fees required: {}",
341                            fee, self.0.api_settings.minimal_fees
342                        )),
343                        gas_cost: 0,
344                        output_events: Default::default(),
345                        state_changes: Default::default(),
346                    };
347                    res.push(result);
348                    continue;
349                }
350            }
351
352            // run
353            let result = self.0.execution_controller.execute_readonly_request(req);
354
355            // map result
356            let result = ExecuteReadOnlyResponse {
357                executed_at: result
358                    .as_ref()
359                    .map_or_else(|_| Slot::new(0, 0), |v| v.out.slot),
360                result: result.as_ref().map_or_else(
361                    |err| ReadOnlyResult::Error(format!("readonly call failed: {}", err)),
362                    |res| ReadOnlyResult::Ok(res.call_result.clone()),
363                ),
364                gas_cost: result.as_ref().map_or_else(|_| 0, |v| v.gas_cost),
365                output_events: result
366                    .as_ref()
367                    .map_or_else(|_| Default::default(), |v| v.out.events.clone().0),
368                state_changes: result.map_or_else(|_| Default::default(), |v| v.out.state_changes),
369            };
370
371            res.push(result);
372        }
373
374        // return result
375        Ok(res)
376    }
377
378    /// execute read-only calls
379    async fn execute_read_only_call(
380        &self,
381        reqs: Vec<ReadOnlyCall>,
382    ) -> RpcResult<Vec<ExecuteReadOnlyResponse>> {
383        if reqs.len() as u64 > self.0.api_settings.max_arguments {
384            return Err(ApiError::BadRequest("too many arguments".into()).into());
385        }
386
387        let mut res: Vec<ExecuteReadOnlyResponse> = Vec::with_capacity(reqs.len());
388        for ReadOnlyCall {
389            max_gas,
390            target_address,
391            target_function,
392            parameter,
393            caller_address,
394            coins,
395            fee,
396        } in reqs
397        {
398            let caller_address = if let Some(addr) = caller_address {
399                addr
400            } else {
401                let now = MassaTime::now();
402                let keypair = self
403                    .0
404                    .keypair_factory
405                    .create(&(), FactoryStrategy::At(now))
406                    .map_err(ApiError::from)?;
407                Address::from_public_key(&keypair.get_public_key())
408            };
409
410            // translate request
411            let req = ReadOnlyExecutionRequest {
412                max_gas,
413                target: ReadOnlyExecutionTarget::FunctionCall {
414                    target_func: target_function,
415                    target_addr: target_address,
416                    parameter,
417                },
418                call_stack: vec![
419                    ExecutionStackElement {
420                        address: caller_address,
421                        coins: Default::default(),
422                        owned_addresses: vec![caller_address],
423                        operation_datastore: None, // should always be None
424                    },
425                    ExecutionStackElement {
426                        address: target_address,
427                        coins: coins.unwrap_or(Amount::default()),
428                        owned_addresses: vec![target_address],
429                        operation_datastore: None, // should always be None
430                    },
431                ],
432                coins,
433                fee,
434            };
435
436            if let Some(fee) = fee {
437                if fee.checked_sub(self.0.api_settings.minimal_fees).is_none() {
438                    let result = ExecuteReadOnlyResponse {
439                        executed_at: Slot::new(0, 0),
440                        result: ReadOnlyResult::Error(format!(
441                            "fee is too low provided: {} , minimal_fees required: {}",
442                            fee, self.0.api_settings.minimal_fees
443                        )),
444                        gas_cost: 0,
445                        output_events: Default::default(),
446                        state_changes: Default::default(),
447                    };
448                    res.push(result);
449                    continue;
450                }
451            }
452
453            // run
454            let result = self.0.execution_controller.execute_readonly_request(req);
455
456            // map result
457            let result = ExecuteReadOnlyResponse {
458                executed_at: result
459                    .as_ref()
460                    .map_or_else(|_| Slot::new(0, 0), |v| v.out.slot),
461                result: result.as_ref().map_or_else(
462                    |err| ReadOnlyResult::Error(format!("readonly call failed: {}", err)),
463                    |res| ReadOnlyResult::Ok(res.call_result.clone()),
464                ),
465                gas_cost: result.as_ref().map_or_else(|_| 0, |v| v.gas_cost),
466                output_events: result
467                    .as_ref()
468                    .map_or_else(|_| Default::default(), |v| v.out.events.clone().0),
469                state_changes: result.map_or_else(|_| Default::default(), |v| v.out.state_changes),
470            };
471
472            res.push(result);
473        }
474
475        // return result
476        Ok(res)
477    }
478
479    async fn remove_staking_addresses(&self, _: Vec<Address>) -> RpcResult<()> {
480        crate::wrong_api::<()>()
481    }
482
483    async fn get_staking_addresses(&self) -> RpcResult<PreHashSet<Address>> {
484        crate::wrong_api::<PreHashSet<Address>>()
485    }
486
487    async fn node_ban_by_ip(&self, _: Vec<IpAddr>) -> RpcResult<()> {
488        crate::wrong_api::<()>()
489    }
490
491    async fn node_ban_by_id(&self, _: Vec<NodeId>) -> RpcResult<()> {
492        crate::wrong_api::<()>()
493    }
494
495    async fn node_unban_by_ip(&self, _: Vec<IpAddr>) -> RpcResult<()> {
496        crate::wrong_api::<()>()
497    }
498
499    async fn node_unban_by_id(&self, _: Vec<NodeId>) -> RpcResult<()> {
500        crate::wrong_api::<()>()
501    }
502
503    /// get status
504    async fn get_status(&self) -> RpcResult<NodeStatus> {
505        let version = self.0.version;
506        let api_settings = self.0.api_settings.clone();
507        let protocol_config = self.0.protocol_config.clone();
508        let node_id = self.0.node_id;
509        let config = CompactConfig::default();
510        let now = MassaTime::now();
511
512        let current_mip_version = self
513            .0
514            .keypair_factory
515            .mip_store
516            .get_network_version_current();
517
518        let last_slot_result = get_latest_block_slot_at_timestamp(
519            api_settings.thread_count,
520            api_settings.t0,
521            api_settings.genesis_timestamp,
522            now,
523        );
524        let last_slot = match last_slot_result {
525            Ok(last_slot) => last_slot,
526            Err(e) => return Err(ApiError::ModelsError(e).into()),
527        };
528
529        let execution_stats = self.0.execution_controller.get_stats();
530        let consensus_stats_result = self.0.consensus_controller.get_stats();
531        let consensus_stats = match consensus_stats_result {
532            Ok(consensus_stats) => consensus_stats,
533            Err(e) => return Err(ApiError::ConsensusError(e.to_string()).into()),
534        };
535
536        let (network_stats, peers) = match self.0.protocol_controller.get_stats() {
537            Ok((stats, peers)) => (stats, peers),
538            Err(e) => return Err(ApiError::ProtocolError(e.to_string()).into()),
539        };
540
541        let pool_stats = (
542            match self
543                .0
544                .pool_command_sender
545                .get_operation_count(Some(self.0.api_settings.pool_api_timeout))
546            {
547                Ok(count) => count,
548                Err(e) => return Err(ApiError::PoolError(e).into()),
549            },
550            match self
551                .0
552                .pool_command_sender
553                .get_endorsement_count(Some(self.0.api_settings.pool_api_timeout))
554            {
555                Ok(count) => count,
556                Err(e) => return Err(ApiError::PoolError(e).into()),
557            },
558        );
559
560        let next_slot_result = last_slot
561            .unwrap_or_else(|| Slot::new(0, 0))
562            .get_next_slot(api_settings.thread_count);
563
564        let next_slot = match next_slot_result {
565            Ok(next_slot) => next_slot,
566            Err(e) => return Err(ApiError::ModelsError(e).into()),
567        };
568
569        let connected_nodes = peers
570            .iter()
571            .map(|(id, peer)| {
572                let is_outgoing = match peer.1 {
573                    PeerConnectionType::IN => false,
574                    PeerConnectionType::OUT => true,
575                };
576                (NodeId::new(id.get_public_key()), (peer.0.ip(), is_outgoing))
577            })
578            .collect::<BTreeMap<_, _>>();
579
580        let current_cycle = last_slot
581            .unwrap_or_else(|| Slot::new(0, 0))
582            .get_cycle(api_settings.periods_per_cycle);
583
584        let cycle_duration = match api_settings.t0.checked_mul(api_settings.periods_per_cycle) {
585            Ok(cycle_duration) => cycle_duration,
586            Err(e) => return Err(ApiError::TimeError(e).into()),
587        };
588
589        let current_cycle_time_result = if current_cycle == 0 {
590            Ok(api_settings.genesis_timestamp)
591        } else {
592            cycle_duration.checked_mul(current_cycle).and_then(
593                |elapsed_time_before_current_cycle| {
594                    api_settings
595                        .genesis_timestamp
596                        .checked_add(elapsed_time_before_current_cycle)
597                },
598            )
599        };
600
601        let current_cycle_time = match current_cycle_time_result {
602            Ok(current_cycle_time) => current_cycle_time,
603            Err(e) => return Err(ApiError::TimeError(e).into()),
604        };
605
606        let next_cycle_time = match current_cycle_time.checked_add(cycle_duration) {
607            Ok(next_cycle_time) => next_cycle_time,
608            Err(e) => return Err(ApiError::TimeError(e).into()),
609        };
610
611        Ok(NodeStatus {
612            node_id,
613            node_ip: protocol_config.routable_ip,
614            version,
615            current_time: now,
616            current_cycle_time,
617            next_cycle_time,
618            connected_nodes,
619            last_slot,
620            next_slot,
621            execution_stats,
622            consensus_stats,
623            network_stats,
624            pool_stats,
625            config,
626            current_cycle,
627            chain_id: self.0.api_settings.chain_id,
628            minimal_fees: self.0.api_settings.minimal_fees,
629            current_mip_version,
630            max_datastore_keys_query: self.0.api_settings.max_datastore_keys_queries,
631        })
632    }
633
634    /// get cliques
635    async fn get_cliques(&self) -> RpcResult<Vec<Clique>> {
636        Ok(self.0.consensus_controller.get_cliques())
637    }
638
639    /// get stakers
640    async fn get_stakers(
641        &self,
642        page_request: Option<PageRequest>,
643    ) -> RpcResult<PagedVec<(Address, u64)>> {
644        let cfg = self.0.api_settings.clone();
645
646        let now = MassaTime::now();
647
648        let latest_block_slot_at_timestamp_result = get_latest_block_slot_at_timestamp(
649            cfg.thread_count,
650            cfg.t0,
651            cfg.genesis_timestamp,
652            now,
653        );
654
655        let curr_cycle = match latest_block_slot_at_timestamp_result {
656            Ok(Some(cur_slot)) if cur_slot.period <= self.0.api_settings.last_start_period => {
657                Slot::new(self.0.api_settings.last_start_period, 0).get_cycle(cfg.periods_per_cycle)
658            }
659            Ok(Some(cur_slot)) => cur_slot.get_cycle(cfg.periods_per_cycle),
660            Ok(None) => 0,
661            Err(e) => return Err(ApiError::ModelsError(e).into()),
662        };
663
664        let mut staker_vec = self
665            .0
666            .execution_controller
667            .get_cycle_active_rolls(curr_cycle)
668            .into_iter()
669            .collect::<Vec<(Address, u64)>>();
670
671        staker_vec
672            .sort_by(|&(_, roll_counts_a), &(_, roll_counts_b)| roll_counts_b.cmp(&roll_counts_a));
673
674        let paged_vec = PagedVec::new(staker_vec, page_request);
675
676        Ok(paged_vec)
677    }
678
679    /// get operations
680    async fn get_operations(
681        &self,
682        operations_ids: Vec<OperationId>,
683    ) -> RpcResult<Vec<OperationInfo>> {
684        let api_cfg = self.0.api_settings.clone();
685        // bound the request size on the ids provided by the caller, before any storage lookup
686        if operations_ids.len() as u64 > api_cfg.max_arguments {
687            return Err(ApiError::BadRequest("too many arguments".into()).into());
688        }
689
690        // get the operations and the list of blocks that contain them from storage
691        let secure_share_operations: Vec<SecureShareOperation> = {
692            let read_ops = self.0.storage.read_operations();
693            operations_ids
694                .iter()
695                .filter_map(|id| read_ops.get(id).cloned())
696                .collect()
697        };
698
699        let storage_info: Vec<(SecureShareOperation, PreHashSet<BlockId>)> = {
700            let read_blocks = self.0.storage.read_blocks();
701            secure_share_operations
702                .into_iter()
703                .map(|secure_share_operation| {
704                    let op_id = secure_share_operation.id;
705                    (
706                        secure_share_operation,
707                        read_blocks
708                            .get_blocks_by_operation(&op_id)
709                            .cloned()
710                            .unwrap_or_default(),
711                    )
712                })
713                .collect()
714        };
715
716        // keep only the ops id (found in storage)
717        let ops: Vec<OperationId> = storage_info.iter().map(|(op, _)| op.id).collect();
718
719        // ask pool whether it carries the operations
720        // this is auxiliary metadata: on pool failure (e.g. lock timeout) we still return the
721        // operations found in storage and report `in_pool: false`, like we do for
722        // get_endorsements on both the JSON-RPC and gRPC endpoints
723        let in_pool = self
724            .0
725            .pool_command_sender
726            .contains_operations(&ops, Some(api_cfg.pool_api_timeout))
727            .unwrap_or_else(|e| {
728                warn!(
729                    "could not read operations from pool, reporting in_pool=false: {}",
730                    e
731                );
732                vec![false; ops.len()]
733            });
734
735        let op_exec_statuses = self.0.execution_controller.get_ops_exec_status(&ops);
736
737        // compute operation finality and operation execution status from *_op_exec_statuses
738        let (is_operation_final, statuses): (Vec<Option<bool>>, Vec<Option<bool>>) =
739            op_exec_statuses
740                .into_iter()
741                .map(|(spec_exec, final_exec)| match (spec_exec, final_exec) {
742                    (Some(true), Some(true)) => (Some(true), Some(true)),
743                    (Some(false), Some(false)) => (Some(true), Some(false)),
744                    (Some(true), None) => (Some(false), Some(true)),
745                    (Some(false), None) => (Some(false), Some(false)),
746                    _ => (None, None),
747                })
748                .collect::<Vec<(Option<bool>, Option<bool>)>>()
749                .into_iter()
750                .unzip();
751
752        // gather all values into a vector of OperationInfo instances
753        let mut res: Vec<OperationInfo> = Vec::with_capacity(ops.len());
754        let zipped_iterator = izip!(
755            ops.into_iter(),
756            storage_info.into_iter(),
757            in_pool.into_iter(),
758            is_operation_final.into_iter(),
759            statuses.into_iter(),
760        );
761        for (id, (operation, in_blocks), in_pool, is_operation_final, op_exec_status) in
762            zipped_iterator
763        {
764            #[cfg(feature = "execution-trace")]
765            {
766                let mut transfer = None;
767                if is_operation_final.is_none() || op_exec_status.is_none() {
768                    transfer = self.0.execution_controller.get_transfer_for_op(&id);
769                }
770                let is_operation_final = is_operation_final.or(Some(transfer.is_some()));
771                let op_exec_status = op_exec_status.or(transfer.map(|t| t.succeed));
772                res.push(OperationInfo {
773                    id,
774                    in_pool,
775                    is_operation_final,
776                    thread: operation
777                        .content_creator_address
778                        .get_thread(api_cfg.thread_count),
779                    operation,
780                    in_blocks: in_blocks.into_iter().collect(),
781                    op_exec_status,
782                });
783            }
784            #[cfg(not(feature = "execution-trace"))]
785            {
786                res.push(OperationInfo {
787                    id,
788                    in_pool,
789                    is_operation_final,
790                    thread: operation
791                        .content_creator_address
792                        .get_thread(api_cfg.thread_count),
793                    operation,
794                    in_blocks: in_blocks.into_iter().collect(),
795                    op_exec_status,
796                });
797            }
798        }
799
800        // return values in the right order
801        Ok(res)
802    }
803
804    /// get endorsements
805    async fn get_endorsements(
806        &self,
807        mut endorsement_ids: Vec<EndorsementId>,
808    ) -> RpcResult<Vec<EndorsementInfo>> {
809        if endorsement_ids.len() as u64 > self.0.api_settings.max_arguments {
810            return Err(ApiError::BadRequest("too many arguments".into()).into());
811        }
812
813        let mut secure_share_endorsements: Vec<SecureShareEndorsement> =
814            Vec::with_capacity(endorsement_ids.len());
815        {
816            let endorsement_storage_lock = self.0.storage.read_endorsements();
817            endorsement_ids.retain(|id| {
818                if let Some(wrapped_endorsement) = endorsement_storage_lock.get(id) {
819                    secure_share_endorsements.push(wrapped_endorsement.clone());
820                    return true;
821                };
822                false
823            });
824        }
825
826        let storage_info: Vec<(SecureShareEndorsement, PreHashSet<BlockId>)> = {
827            let read_blocks = self.0.storage.read_blocks();
828            secure_share_endorsements
829                .into_iter()
830                .map(|secure_share_operation| {
831                    let ed_id = secure_share_operation.id;
832                    (
833                        secure_share_operation,
834                        read_blocks
835                            .get_blocks_by_endorsement(&ed_id)
836                            .cloned()
837                            .unwrap_or_default(),
838                    )
839                })
840                .collect()
841        };
842
843        // ask pool whether it carries the endorsements
844        // this is auxiliary metadata: on pool failure (e.g. lock timeout) we still return the
845        // endorsements found in storage and report `in_pool: false`, like the gRPC
846        // get_endorsements endpoint already does
847        let in_pool = self
848            .0
849            .pool_command_sender
850            .contains_endorsements(&endorsement_ids, Some(self.0.api_settings.pool_api_timeout))
851            .unwrap_or_else(|e| {
852                warn!(
853                    "could not read endorsements from pool, reporting in_pool=false: {}",
854                    e
855                );
856                vec![false; endorsement_ids.len()]
857            });
858
859        // check finality by cross-referencing Consensus and looking for final blocks that contain the endorsement
860        let is_final: Vec<bool> = {
861            let involved_blocks: Vec<BlockId> = storage_info
862                .iter()
863                .flat_map(|(_ed, bs)| bs.iter())
864                .unique()
865                .cloned()
866                .collect();
867
868            let involved_block_statuses = self
869                .0
870                .consensus_controller
871                .get_block_statuses(&involved_blocks);
872
873            let block_statuses: PreHashMap<BlockId, BlockGraphStatus> = involved_blocks
874                .into_iter()
875                .zip(involved_block_statuses)
876                .collect();
877            storage_info
878                .iter()
879                .map(|(_ed, bs)| {
880                    bs.iter()
881                        .any(|b| block_statuses.get(b) == Some(&BlockGraphStatus::Final))
882                })
883                .collect()
884        };
885
886        // gather all values into a vector of EndorsementInfo instances
887        let mut res: Vec<EndorsementInfo> = Vec::with_capacity(endorsement_ids.len());
888        let zipped_iterator = izip!(
889            endorsement_ids.into_iter(),
890            storage_info.into_iter(),
891            in_pool.into_iter(),
892            is_final.into_iter()
893        );
894        for (id, (endorsement, in_blocks), in_pool, is_final) in zipped_iterator {
895            res.push(EndorsementInfo {
896                id,
897                endorsement,
898                in_pool,
899                is_final,
900                in_blocks: in_blocks.into_iter().collect(),
901            });
902        }
903
904        // return values in the right order
905        Ok(res)
906    }
907
908    /// get blocks
909    /// Returns only active blocks are returned
910    async fn get_blocks(&self, mut ids: Vec<BlockId>) -> RpcResult<Vec<BlockInfo>> {
911        let mut blocks: Vec<Block> = Vec::with_capacity(ids.len());
912        {
913            let block_storage_lock = self.0.storage.read_blocks();
914            ids.retain(|id| {
915                if let Some(wrapped_block) = block_storage_lock.get(id) {
916                    blocks.push(wrapped_block.content.clone());
917                    return true;
918                };
919                false
920            });
921        }
922        let block_statuses = self.0.consensus_controller.get_block_statuses(&ids);
923        let res = ids
924            .into_iter()
925            .zip(blocks)
926            .zip(block_statuses)
927            .map(|((id, content), graph_status)| BlockInfo {
928                id,
929                content: Some(BlockInfoContent {
930                    is_final: graph_status == BlockGraphStatus::Final,
931                    is_in_blockclique: graph_status == BlockGraphStatus::ActiveInBlockclique,
932                    is_candidate: graph_status == BlockGraphStatus::ActiveInBlockclique
933                        || graph_status == BlockGraphStatus::ActiveInAlternativeCliques,
934                    is_discarded: graph_status == BlockGraphStatus::Discarded,
935                    block: content,
936                }),
937            })
938            .collect();
939        Ok(res)
940    }
941
942    async fn get_blockclique_block_by_slot(&self, slot: Slot) -> RpcResult<Option<Block>> {
943        let block_id_option = self
944            .0
945            .consensus_controller
946            .get_blockclique_block_at_slot(slot);
947
948        let block_id = match block_id_option {
949            Some(id) => id,
950            None => return Ok(None),
951        };
952
953        let res = self
954            .0
955            .storage
956            .read_blocks()
957            .get(&block_id)
958            .map(|b| b.content.clone());
959        Ok(res)
960    }
961
962    /// gets an interval of the block graph from consensus, with time filtering
963    /// time filtering is done consensus-side to prevent communication overhead
964    async fn get_graph_interval(&self, time: TimeInterval) -> RpcResult<Vec<BlockSummary>> {
965        let api_settings = self.0.api_settings.clone();
966
967        // filter blocks from graph_export
968        let time_range_to_slot_range_result = time_range_to_slot_range(
969            api_settings.thread_count,
970            api_settings.t0,
971            api_settings.genesis_timestamp,
972            time.start,
973            time.end,
974        );
975
976        let (start_slot, end_slot) = match time_range_to_slot_range_result {
977            Ok(time_range_to_slot_range) => time_range_to_slot_range,
978            Err(e) => return Err(ApiError::ModelsError(e).into()),
979        };
980
981        let graph = match self
982            .0
983            .consensus_controller
984            .get_block_graph_status(start_slot, end_slot)
985        {
986            Ok(graph) => graph,
987            Err(e) => return Err(ApiError::ConsensusError(e.to_string()).into()),
988        };
989
990        let mut res = Vec::with_capacity(graph.active_blocks.len());
991        let blockclique = graph
992            .max_cliques
993            .iter()
994            .find(|clique| clique.is_blockclique)
995            .ok_or_else(|| ApiError::InconsistencyError("missing blockclique".to_string()))?;
996        for (id, exported_block) in graph.active_blocks.into_iter() {
997            res.push(BlockSummary {
998                id,
999                is_final: exported_block.is_final,
1000                is_stale: false,
1001                is_in_blockclique: blockclique.block_ids.contains(&id),
1002                slot: exported_block.header.content.slot,
1003                creator: exported_block.header.content_creator_address,
1004                parents: exported_block.header.content.parents,
1005            });
1006        }
1007        for (id, (reason, (slot, creator, parents))) in graph.discarded_blocks.into_iter() {
1008            if reason == DiscardReason::Stale {
1009                res.push(BlockSummary {
1010                    id,
1011                    is_final: false,
1012                    is_stale: true,
1013                    is_in_blockclique: false,
1014                    slot,
1015                    creator,
1016                    parents,
1017                });
1018            }
1019        }
1020        Ok(res)
1021    }
1022
1023    /// get datastore entries
1024    async fn get_datastore_entries(
1025        &self,
1026        entries: Vec<DatastoreEntryInput>,
1027    ) -> RpcResult<Vec<DatastoreEntryOutput>> {
1028        Ok(self
1029            .0
1030            .execution_controller
1031            .get_final_and_active_data_entry(
1032                entries
1033                    .into_iter()
1034                    .map(|input| (input.address, input.key))
1035                    .collect::<Vec<_>>(),
1036            )
1037            .into_iter()
1038            .map(|output| DatastoreEntryOutput {
1039                final_value: output.0,
1040                candidate_value: output.1,
1041            })
1042            .collect())
1043    }
1044
1045    async fn get_addresses_datastore_keys(
1046        &self,
1047        arg: Vec<GetAddressDatastoreKeysRequest>,
1048    ) -> RpcResult<Vec<GetAddressDatastoreKeysResponse>> {
1049        if let Some(conf_max) = self.0.api_settings.max_addresses_datastore_keys_query {
1050            if arg.len() > conf_max as usize {
1051                return Err(ApiError::BadRequest(format!("too many arguments received. Only a maximum of {} arguments are accepted per request", conf_max)).into());
1052            }
1053        }
1054
1055        let requests: Vec<ExecutionQueryRequestItem> = arg
1056            .into_iter()
1057            .map(|request| {
1058                get_address_datastore_keys_to_state_query_item(
1059                    request,
1060                    self.0.api_settings.max_datastore_keys_queries,
1061                    self.0.api_settings.max_datastore_key_length,
1062                )
1063            })
1064            .collect::<Result<_, _>>()?;
1065
1066        let mut result: Vec<GetAddressDatastoreKeysResponse> = Vec::with_capacity(requests.len());
1067
1068        let response = self.exec_query(requests);
1069
1070        for response_item in response.responses {
1071            match response_item {
1072                Ok(item) => match item {
1073                    ExecutionQueryResponseItem::AddressDatastoreKeys(keys, address, is_final) => {
1074                        result.push(GetAddressDatastoreKeysResponse {
1075                            address,
1076                            is_final,
1077                            keys: keys.into_iter().collect(),
1078                        });
1079                    }
1080                    _ => {
1081                        return Err(
1082                            ApiError::ExecutionError("unexpected response".to_string()).into()
1083                        );
1084                    }
1085                },
1086                Err(e) => {
1087                    return Err(ApiError::ExecutionError(e.to_string()).into());
1088                }
1089            }
1090        }
1091
1092        Ok(result)
1093    }
1094
1095    /// get addresses
1096    async fn get_addresses(&self, addresses: Vec<Address>) -> RpcResult<Vec<AddressInfo>> {
1097        // get info from storage about which blocks the addresses have created
1098        let created_blocks: Vec<PreHashSet<BlockId>> = {
1099            let lck = self.0.storage.read_blocks();
1100            addresses
1101                .iter()
1102                .map(|address| {
1103                    lck.get_blocks_created_by(address)
1104                        .cloned()
1105                        .unwrap_or_default()
1106                })
1107                .collect()
1108        };
1109
1110        // get info from storage about which operations the addresses have created
1111        let created_operations: Vec<PreHashSet<OperationId>> = {
1112            let lck = self.0.storage.read_operations();
1113            addresses
1114                .iter()
1115                .map(|address| {
1116                    lck.get_operations_created_by(address)
1117                        .cloned()
1118                        .unwrap_or_default()
1119                })
1120                .collect()
1121        };
1122
1123        // get info from storage about which endorsements the addresses have created
1124        let created_endorsements: Vec<PreHashSet<EndorsementId>> = {
1125            let lck = self.0.storage.read_endorsements();
1126            addresses
1127                .iter()
1128                .map(|address| {
1129                    lck.get_endorsements_created_by(address)
1130                        .cloned()
1131                        .unwrap_or_default()
1132                })
1133                .collect()
1134        };
1135
1136        // Compute a limit (as a slot) for deferred credits as it can be quite huge
1137        let bound_ts = MassaTime::now().saturating_add(self.0.api_settings.deferred_credits_delta);
1138
1139        let deferred_credit_max_slot = timeslots::get_closest_slot_to_timestamp(
1140            self.0.api_settings.thread_count,
1141            self.0.api_settings.t0,
1142            self.0.api_settings.genesis_timestamp,
1143            bound_ts,
1144        );
1145
1146        // get execution info
1147        let execution_infos = self.0.execution_controller.get_addresses_infos(
1148            &addresses,
1149            std::ops::Bound::Included(deferred_credit_max_slot),
1150            self.0.api_settings.max_datastore_keys_queries,
1151        );
1152
1153        // get future draws from selector
1154        let selection_draws = {
1155            let cur_slot = timeslots::get_current_latest_block_slot(
1156                self.0.api_settings.thread_count,
1157                self.0.api_settings.t0,
1158                self.0.api_settings.genesis_timestamp,
1159            )
1160            .expect("could not get latest current slot")
1161            .unwrap_or_else(|| Slot::new(0, 0));
1162            let slot_end = Slot::new(
1163                cur_slot
1164                    .period
1165                    .saturating_add(self.0.api_settings.draw_lookahead_period_count),
1166                cur_slot.thread,
1167            );
1168            let selections = self
1169                .0
1170                .selector_controller
1171                .get_available_selections_in_range(
1172                    cur_slot..=slot_end,
1173                    Some(&addresses.iter().copied().collect()),
1174                )
1175                .unwrap_or_default();
1176
1177            addresses
1178                .iter()
1179                .map(|addr| {
1180                    let mut producer_slots = Vec::new();
1181                    let mut endorser_slots = Vec::new();
1182                    for (selection_slot, selection) in &selections {
1183                        if selection.producer == *addr {
1184                            producer_slots.push(*selection_slot);
1185                        }
1186                        for (index, endorser) in selection.endorsements.iter().enumerate() {
1187                            if endorser == addr {
1188                                endorser_slots.push(IndexedSlot {
1189                                    slot: *selection_slot,
1190                                    index,
1191                                });
1192                            }
1193                        }
1194                    }
1195                    (producer_slots, endorser_slots)
1196                })
1197                .collect::<Vec<_>>()
1198        };
1199
1200        // compile results
1201        let mut res = Vec::with_capacity(addresses.len());
1202        let iterator = izip!(
1203            addresses.into_iter(),
1204            created_blocks.into_iter(),
1205            created_operations.into_iter(),
1206            created_endorsements.into_iter(),
1207            execution_infos.into_iter(),
1208            selection_draws.into_iter(),
1209        );
1210        for (
1211            address,
1212            created_blocks,
1213            created_operations,
1214            created_endorsements,
1215            execution_infos,
1216            (next_block_draws, next_endorsement_draws),
1217        ) in iterator
1218        {
1219            res.push(AddressInfo {
1220                // general address info
1221                address,
1222                thread: address.get_thread(self.0.api_settings.thread_count),
1223
1224                // final execution info
1225                final_balance: execution_infos.final_balance,
1226                final_roll_count: execution_infos.final_roll_count,
1227                final_datastore_keys: execution_infos
1228                    .final_datastore_keys
1229                    .into_iter()
1230                    .collect::<Vec<_>>(),
1231
1232                // candidate execution info
1233                candidate_balance: execution_infos.candidate_balance,
1234                candidate_roll_count: execution_infos.candidate_roll_count,
1235                candidate_datastore_keys: execution_infos
1236                    .candidate_datastore_keys
1237                    .into_iter()
1238                    .collect::<Vec<_>>(),
1239
1240                // deferred credits
1241                deferred_credits: execution_infos
1242                    .future_deferred_credits
1243                    .into_iter()
1244                    .map(|(slot, amount)| SlotAmount { slot, amount })
1245                    .collect::<Vec<_>>(),
1246
1247                // selector info
1248                next_block_draws,
1249                next_endorsement_draws,
1250
1251                // created objects
1252                created_blocks: created_blocks.into_iter().collect::<Vec<_>>(),
1253                created_endorsements: created_endorsements.into_iter().collect::<Vec<_>>(),
1254                created_operations: created_operations.into_iter().collect::<Vec<_>>(),
1255
1256                // cycle infos
1257                cycle_infos: execution_infos.cycle_infos,
1258            });
1259        }
1260
1261        Ok(res)
1262    }
1263
1264    /// get addresses bytecode
1265    async fn get_addresses_bytecode(&self, args: Vec<AddressFilter>) -> RpcResult<Vec<Vec<u8>>> {
1266        let queries = args
1267            .into_iter()
1268            .map(|arg| {
1269                if arg.is_final {
1270                    ExecutionQueryRequestItem::AddressBytecodeFinal(arg.address)
1271                } else {
1272                    ExecutionQueryRequestItem::AddressBytecodeCandidate(arg.address)
1273                }
1274            })
1275            .collect::<Vec<_>>();
1276
1277        if queries.is_empty() {
1278            return Err(ApiError::BadRequest("no arguments specified".to_string()).into());
1279        }
1280
1281        if queries.len() as u64 > self.0.api_settings.max_arguments {
1282            return Err(ApiError::BadRequest(format!("too many arguments received. Only a maximum of {} arguments are accepted per request", self.0.api_settings.max_arguments)).into());
1283        }
1284
1285        let responses = self.exec_query(queries).responses;
1286
1287        let res: Result<Vec<Vec<u8>>, ApiError> = responses
1288            .into_iter()
1289            .map(|value| match value {
1290                Ok(item) => match item {
1291                    ExecutionQueryResponseItem::Bytecode(bytecode) => Ok(bytecode.0),
1292                    _ => Err(ApiError::InternalServerError(
1293                        "unexpected response type".to_string(),
1294                    )),
1295                },
1296                Err(err) => Err(ApiError::InternalServerError(err.to_string())),
1297            })
1298            .collect();
1299
1300        Ok(res?)
1301    }
1302
1303    async fn get_deferred_call_quote(
1304        &self,
1305        req: Vec<DeferredCallsQuoteRequest>,
1306    ) -> RpcResult<Vec<DeferredCallsQuoteResponse>> {
1307        if req.len() as u64 > self.0.api_settings.max_arguments {
1308            return Err(ApiError::BadRequest("too many arguments".into()).into());
1309        }
1310
1311        let queries: Vec<ExecutionQueryRequestItem> = req
1312            .into_iter()
1313            .map(|call| ExecutionQueryRequestItem::DeferredCallQuote {
1314                target_slot: call.target_slot,
1315                max_gas_request: call.max_gas_request,
1316                params_size: call.params_size,
1317            })
1318            .collect();
1319
1320        let result = self
1321            .exec_query(queries)
1322            .responses
1323            .into_iter()
1324            .map(|response| match response {
1325                Ok(ExecutionQueryResponseItem::DeferredCallQuote(
1326                    target_slot,
1327                    max_gas_request,
1328                    available,
1329                    price,
1330                )) => Ok(DeferredCallsQuoteResponse {
1331                    target_slot,
1332                    max_gas_request,
1333                    available,
1334                    price,
1335                }),
1336                Ok(_) => Err(ApiError::InternalServerError(
1337                    "unexpected response type".to_string(),
1338                )),
1339                Err(err) => Err(ApiError::InternalServerError(err.to_string())),
1340            })
1341            .collect::<Result<Vec<DeferredCallsQuoteResponse>, ApiError>>()?;
1342
1343        Ok(result)
1344    }
1345
1346    async fn get_deferred_call_info(
1347        &self,
1348        arg: Vec<String>,
1349    ) -> RpcResult<Vec<DeferredCallResponse>> {
1350        if arg.len() as u64 > self.0.api_settings.max_arguments {
1351            return Err(ApiError::BadRequest("too many arguments".into()).into());
1352        }
1353
1354        let requests: Vec<ExecutionQueryRequestItem> = arg
1355            .into_iter()
1356            .map(|id_str| {
1357                DeferredCallId::from_str(&id_str)
1358                    .map_err(|e| ApiError::BadRequest(e.to_string()))
1359                    .map(ExecutionQueryRequestItem::DeferredCallInfo)
1360            })
1361            .collect::<Result<_, _>>()?;
1362
1363        let result: Vec<DeferredCallResponse> = self
1364            .exec_query(requests)
1365            .responses
1366            .into_iter()
1367            .map(|exec| match exec {
1368                Ok(ExecutionQueryResponseItem::DeferredCallInfo(id, call)) => {
1369                    Ok(DeferredCallResponse {
1370                        call_id: id.to_string(),
1371                        call,
1372                    })
1373                }
1374                Ok(_) => Err(ApiError::InternalServerError(
1375                    "unexpected response type".to_string(),
1376                )),
1377                Err(err) => Err(ApiError::InternalServerError(err.to_string())),
1378            })
1379            .collect::<Result<_, _>>()?;
1380
1381        Ok(result)
1382    }
1383
1384    async fn get_deferred_call_ids_by_slot(
1385        &self,
1386        slots: Vec<Slot>,
1387    ) -> RpcResult<Vec<DeferredCallsSlotResponse>> {
1388        if slots.len() as u64 > self.0.api_settings.max_arguments {
1389            return Err(ApiError::BadRequest("too many arguments".into()).into());
1390        }
1391
1392        let requests: Vec<ExecutionQueryRequestItem> = slots
1393            .into_iter()
1394            .map(ExecutionQueryRequestItem::DeferredCallsBySlot)
1395            .collect();
1396
1397        let mut slot_calls = Vec::new();
1398
1399        for exec in self.exec_query(requests).responses.into_iter() {
1400            match exec {
1401                Ok(ExecutionQueryResponseItem::DeferredCallsBySlot(slot, result)) => {
1402                    let call_ids = result.into_iter().map(|id| id.to_string()).collect();
1403
1404                    slot_calls.push(DeferredCallsSlotResponse { slot, call_ids });
1405                }
1406                Ok(_) => {
1407                    return Err(ApiError::InternalServerError(
1408                        "unexpected response type".to_string(),
1409                    )
1410                    .into())
1411                }
1412                Err(err) => return Err(ApiError::InternalServerError(err.to_string()).into()),
1413            }
1414        }
1415
1416        Ok(slot_calls)
1417    }
1418
1419    /// send operations
1420    async fn send_operations(&self, ops: Vec<OperationInput>) -> RpcResult<Vec<OperationId>> {
1421        let mut cmd_sender = self.0.pool_command_sender.clone();
1422        let protocol_sender = self.0.protocol_controller.clone();
1423        let api_cfg = &self.0.api_settings;
1424        let mut to_send = self.0.storage.clone_without_refs();
1425
1426        if ops.len() as u64 > api_cfg.max_arguments {
1427            return Err(ApiError::BadRequest("too many arguments".into()).into());
1428        }
1429        let now = MassaTime::now();
1430        let last_slot = get_latest_block_slot_at_timestamp(
1431            api_cfg.thread_count,
1432            api_cfg.t0,
1433            api_cfg.genesis_timestamp,
1434            now,
1435        )
1436        .map_err(ApiError::ModelsError)?;
1437
1438        let verified_ops = ops
1439            .into_iter()
1440            .map(|op_input| check_input_operation(op_input, api_cfg, last_slot))
1441            .map(|op| match op {
1442                Ok(operation) => {
1443                    if operation
1444                        .content
1445                        .fee
1446                        .checked_sub(api_cfg.minimal_fees)
1447                        .is_none()
1448                    {
1449                        return Err(ApiError::BadRequest(format!(
1450                            "fee is too low provided: {} , minimal_fees required: {}",
1451                            operation.content.fee, self.0.api_settings.minimal_fees
1452                        ))
1453                        .into());
1454                    }
1455
1456                    let _verify_signature = match operation.verify_signature(None) {
1457                        Ok(()) => (),
1458                        Err(e) => return Err(ApiError::ModelsError(e).into()),
1459                    };
1460                    Ok(operation)
1461                }
1462                Err(e) => Err(e),
1463            })
1464            .collect::<RpcResult<Vec<SecureShareOperation>>>()?;
1465
1466        to_send.store_operations(verified_ops.clone());
1467        let ids: Vec<OperationId> = verified_ops.iter().map(|op| op.id).collect();
1468
1469        // propagate the operations before admitting them into the local pool,
1470        // so that an error returned by this method means the operations were
1471        // neither propagated nor queued locally
1472        let to_propagate = to_send.clone();
1473        tokio::task::spawn_blocking(move || protocol_sender.propagate_operations(to_propagate))
1474            .await
1475            .map_err(|err| ApiError::InternalServerError(err.to_string()))?
1476            .map_err(|err| {
1477                ApiError::InternalServerError(format!("Failed to propagate operations: {}", err))
1478            })?;
1479
1480        cmd_sender.add_operations(to_send).map_err(|err| {
1481            ApiError::InternalServerError(format!("Failed to add operations to pool: {}", err))
1482        })?;
1483
1484        Ok(ids)
1485    }
1486
1487    /// Get events optionally filtered by:
1488    /// * start slot
1489    /// * end slot
1490    /// * emitter address
1491    /// * original caller address
1492    /// * operation id
1493    async fn get_filtered_sc_output_event(
1494        &self,
1495        filter: EventFilter,
1496    ) -> RpcResult<Vec<SCOutputEvent>> {
1497        let events = self
1498            .0
1499            .execution_controller
1500            .get_filtered_sc_output_event(filter);
1501
1502        // TODO: get rid of the async part
1503        Ok(events)
1504    }
1505
1506    async fn node_peers_whitelist(&self) -> RpcResult<Vec<IpAddr>> {
1507        crate::wrong_api::<Vec<IpAddr>>()
1508    }
1509
1510    async fn node_add_to_peers_whitelist(&self, _: Vec<IpAddr>) -> RpcResult<()> {
1511        crate::wrong_api::<()>()
1512    }
1513
1514    async fn node_remove_from_peers_whitelist(&self, _: Vec<IpAddr>) -> RpcResult<()> {
1515        crate::wrong_api::<()>()
1516    }
1517
1518    async fn node_bootstrap_whitelist(&self) -> RpcResult<Vec<IpAddr>> {
1519        crate::wrong_api::<Vec<IpAddr>>()
1520    }
1521
1522    async fn node_bootstrap_whitelist_allow_all(&self) -> RpcResult<()> {
1523        crate::wrong_api::<()>()
1524    }
1525
1526    async fn node_add_to_bootstrap_whitelist(&self, _: Vec<IpAddr>) -> RpcResult<()> {
1527        crate::wrong_api::<()>()
1528    }
1529
1530    async fn node_remove_from_bootstrap_whitelist(&self, _: Vec<IpAddr>) -> RpcResult<()> {
1531        crate::wrong_api::<()>()
1532    }
1533
1534    async fn node_bootstrap_blacklist(&self) -> RpcResult<Vec<IpAddr>> {
1535        crate::wrong_api::<Vec<IpAddr>>()
1536    }
1537
1538    async fn node_add_to_bootstrap_blacklist(&self, _: Vec<IpAddr>) -> RpcResult<()> {
1539        crate::wrong_api::<()>()
1540    }
1541
1542    async fn node_remove_from_bootstrap_blacklist(&self, _: Vec<IpAddr>) -> RpcResult<()> {
1543        crate::wrong_api::<()>()
1544    }
1545
1546    /// Get the OpenRPC specification of the node
1547    async fn get_openrpc_spec(&self) -> RpcResult<Value> {
1548        let openrpc_spec_path = self.0.api_settings.openrpc_spec_path.clone();
1549        let openrpc: RpcResult<Value> = std::fs::read_to_string(openrpc_spec_path)
1550            .map_err(|e| {
1551                ApiError::InternalServerError(format!(
1552                    "failed to read OpenRPC specification: {}",
1553                    e
1554                ))
1555                .into()
1556            })
1557            .and_then(|openrpc_str| {
1558                serde_json::from_str(&openrpc_str).map_err(|e| {
1559                    ApiError::InternalServerError(format!(
1560                        "failed to parse OpenRPC specification: {}",
1561                        e
1562                    ))
1563                    .into()
1564                })
1565            });
1566
1567        openrpc
1568    }
1569}
1570
1571/// Checks the validity of an input operation.
1572///
1573/// This function takes an `OperationInput`, an `APIConfig`, and an optional `Slot` as input parameters.
1574/// It performs various checks on the input operation and returns a `SecureShareOperation` if the checks pass.
1575/// Otherwise, it returns an `RpcResult` with an appropriate error.
1576///
1577/// # Arguments
1578///
1579/// * `op_input` - The input operation to be checked.
1580/// * `api_cfg` - The API configuration used for checking the operation.
1581/// * `last_slot` - An optional `Slot` representing the last slot used.
1582///
1583/// # Returns
1584///
1585/// Returns a `RpcResult` containing a `SecureShareOperation` if the input operation is valid.
1586/// Otherwise, returns an `RpcResult` with an appropriate error.
1587fn check_input_operation(
1588    op_input: OperationInput,
1589    api_cfg: &APIConfig,
1590    last_slot: Option<Slot>,
1591) -> RpcResult<SecureShareOperation> {
1592    let operation_deserializer = SecureShareDeserializer::new(
1593        OperationDeserializer::new(
1594            api_cfg.max_bytecode_size,
1595            api_cfg.max_function_name_length,
1596            api_cfg.max_parameter_size,
1597            api_cfg.max_op_datastore_entry_count,
1598            api_cfg.max_op_datastore_key_length,
1599            api_cfg.max_op_datastore_value_length,
1600        ),
1601        api_cfg.chain_id,
1602    );
1603
1604    let mut op_serialized = Vec::new();
1605    op_serialized.extend(op_input.signature.to_bytes());
1606    op_serialized.extend(op_input.creator_public_key.to_bytes());
1607    op_serialized.extend(op_input.serialized_content);
1608    let (rest, op): (&[u8], SecureShareOperation) = operation_deserializer
1609        .deserialize::<DeserializeError>(&op_serialized)
1610        .map_err(|err| ApiError::ModelsError(ModelsError::DeserializeError(err.to_string())))?;
1611    // an operation bigger than the whole operations payload of a block can
1612    // never be propagated by the protocol nor included in a block
1613    let op_size = op.serialized_size();
1614    if op_size > api_cfg.max_serialized_operation_size {
1615        return Err(ApiError::BadRequest(format!(
1616            "serialized operation size {} exceeds the maximum authorized size of {} bytes. Your operation will never be included in a block.",
1617            op_size, api_cfg.max_serialized_operation_size
1618        ))
1619        .into());
1620    }
1621    if let Err(err_msg) = op.check_gas_usage(
1622        api_cfg.max_gas_per_block,
1623        api_cfg.base_operation_gas_cost,
1624        api_cfg.sp_compilation_cost,
1625    ) {
1626        return Err(ApiError::InconsistencyError(err_msg).into());
1627    }
1628    if let Some(slot) = last_slot {
1629        if op.content.expire_period < slot.period {
1630            return Err(
1631                ApiError::InconsistencyError(
1632                    "Operation expire_period is lower than the current period of this node. Your operation will never be included in a block.".into()
1633                ).into()
1634            );
1635        }
1636    }
1637    if rest.is_empty() {
1638        Ok(op)
1639    } else {
1640        Err(ApiError::ModelsError(ModelsError::DeserializeError(
1641            "There is data left after operation deserialization".to_owned(),
1642        ))
1643        .into())
1644    }
1645}
1646
1647/// Convert GetAddressDatastoreKeys to ExecutionQueryRequestItem
1648fn get_address_datastore_keys_to_state_query_item(
1649    value: GetAddressDatastoreKeysRequest,
1650    max_datastore_query_config: Option<u32>,
1651    max_datastore_key_length: u8,
1652) -> RpcResult<ExecutionQueryRequestItem> {
1653    let start_key = match (value.start_key, value.inclusive_start_key.unwrap_or(true)) {
1654        (None, _) => std::ops::Bound::Unbounded,
1655        (Some(k), true) => std::ops::Bound::Included(k),
1656        (Some(k), false) => std::ops::Bound::Excluded(k),
1657    };
1658    let end_key = match (value.end_key, value.inclusive_end_key.unwrap_or(true)) {
1659        (None, _) => std::ops::Bound::Unbounded,
1660        (Some(k), true) => std::ops::Bound::Included(k),
1661        (Some(k), false) => std::ops::Bound::Excluded(k),
1662    };
1663
1664    let (prefix, start_key, end_key, count) = cleanup_datastore_key_range_query(
1665        &value.prefix,
1666        start_key,
1667        end_key,
1668        value.count,
1669        max_datastore_key_length,
1670        max_datastore_query_config,
1671    )
1672    .map_err(|e| {
1673        ApiError::BadRequest(format!(
1674            "error querying datastore keys for address {}: {}",
1675            value.address, e
1676        ))
1677    })?;
1678
1679    if value.is_final {
1680        Ok(ExecutionQueryRequestItem::AddressDatastoreKeysFinal {
1681            address: value.address,
1682            prefix,
1683            start_key,
1684            end_key,
1685            count,
1686        })
1687    } else {
1688        Ok(ExecutionQueryRequestItem::AddressDatastoreKeysCandidate {
1689            address: value.address,
1690            prefix,
1691            start_key,
1692            end_key,
1693            count,
1694        })
1695    }
1696}