massa_api/
lib.rs

1//! Copyright (c) 2022 MASSA LABS <info@massa.net>
2//! Json RPC API for a massa-node
3
4#![warn(missing_docs)]
5#![warn(unused_crate_dependencies)]
6
7use api_trait::MassaApiServer;
8use http::Method;
9use jsonrpsee::core::{client::Error as JsonRpseeError, RpcResult};
10use jsonrpsee::proc_macros::rpc;
11use jsonrpsee::server::middleware::http::HostFilterLayer;
12use jsonrpsee::server::{BatchRequestConfig, PingConfig, ServerBuilder, ServerHandle};
13use jsonrpsee::RpcModule;
14use massa_api_exports::address::GetAddressDatastoreKeysResponse;
15use massa_api_exports::execution::{
16    DeferredCallResponse, DeferredCallsQuoteRequest, DeferredCallsQuoteResponse,
17    DeferredCallsSlotResponse, Transfer,
18};
19use massa_api_exports::{
20    address::{AddressFilter, AddressInfo, GetAddressDatastoreKeysRequest},
21    block::{BlockInfo, BlockSummary},
22    config::APIConfig,
23    datastore::{DatastoreEntryInput, DatastoreEntryOutput},
24    endorsement::EndorsementInfo,
25    error::ApiError::WrongAPI,
26    execution::{ExecuteReadOnlyResponse, ReadOnlyBytecodeExecution, ReadOnlyCall},
27    node::NodeStatus,
28    operation::{OperationInfo, OperationInput},
29    page::{PageRequest, PagedVec},
30    TimeInterval,
31};
32use massa_consensus_exports::{ConsensusBroadcasts, ConsensusController};
33use massa_execution_exports::ExecutionController;
34use massa_models::clique::Clique;
35use massa_models::composite::PubkeySig;
36use massa_models::node::NodeId;
37use massa_models::operation::OperationId;
38use massa_models::output_event::SCOutputEvent;
39use massa_models::prehash::PreHashSet;
40use massa_models::{
41    address::Address, block::Block, block_id::BlockId, endorsement::EndorsementId,
42    execution::EventFilter, slot::Slot, version::Version,
43};
44use massa_pool_exports::{PoolBroadcasts, PoolController};
45use massa_pos_exports::SelectorController;
46use massa_protocol_exports::{ProtocolConfig, ProtocolController};
47use massa_storage::Storage;
48use massa_versioning::keypair_factory::KeyPairFactory;
49use massa_wallet::Wallet;
50use parking_lot::RwLock;
51use serde_json::Value;
52use std::net::{IpAddr, SocketAddr};
53use std::sync::{Arc, Condvar, Mutex};
54use tower_http::cors::{Any, CorsLayer};
55use tracing::{info, warn};
56
57#[cfg(feature = "test-exports")]
58use massa_channel as _;
59#[cfg(feature = "test-exports")]
60use massa_grpc as _;
61
62mod api;
63mod api_trait;
64mod private;
65mod public;
66
67#[cfg(test)]
68mod tests;
69
70/// Public API component
71pub struct Public {
72    /// link to the consensus component
73    pub consensus_controller: Box<dyn ConsensusController>,
74    /// link to the execution component
75    pub execution_controller: Box<dyn ExecutionController>,
76    /// link to the selector component
77    pub selector_controller: Box<dyn SelectorController>,
78    /// link to the pool component
79    pub pool_command_sender: Box<dyn PoolController>,
80    /// link to the protocol component
81    pub protocol_controller: Box<dyn ProtocolController>,
82    /// Massa storage
83    pub storage: Storage,
84    /// API settings
85    pub api_settings: APIConfig,
86    /// Protocol config
87    pub protocol_config: ProtocolConfig,
88    /// node version (TODO remove, can be retrieved via an endpoint)
89    pub version: Version,
90    /// our node id
91    pub node_id: NodeId,
92    /// keypair factory
93    pub keypair_factory: KeyPairFactory,
94}
95
96/// Private API content
97pub struct Private {
98    /// link to the protocol component
99    pub protocol_controller: Box<dyn ProtocolController>,
100    /// link to the execution component
101    pub execution_controller: Box<dyn ExecutionController>,
102    /// API settings
103    pub api_settings: APIConfig,
104    /// Mechanism by which to gracefully shut down.
105    /// To be a clone of the same pair provided to the ctrlc handler.
106    pub stop_cv: Arc<(Mutex<bool>, Condvar)>,
107    /// User wallet
108    pub node_wallet: Arc<RwLock<Wallet>>,
109}
110
111/// API v2 content
112pub struct ApiV2 {
113    /// link to the consensus component
114    pub consensus_controller: Box<dyn ConsensusController>,
115    /// channels with information broadcasted by the consensus
116    pub consensus_broadcasts: ConsensusBroadcasts,
117    /// link to the execution component
118    pub execution_controller: Box<dyn ExecutionController>,
119    /// channels with information broadcasted by the pool
120    pub pool_broadcasts: PoolBroadcasts,
121    /// API settings
122    pub api_settings: APIConfig,
123    /// node version
124    pub version: Version,
125}
126
127/// The API wrapper
128pub struct API<T>(T);
129
130/// Used to manage the API
131#[async_trait::async_trait]
132pub trait RpcServer: MassaRpcServer {
133    /// Start the API
134    async fn serve(
135        self,
136        url: &SocketAddr,
137        api_config: &APIConfig,
138    ) -> Result<StopHandle, JsonRpseeError>;
139}
140
141/// Used to manage the API
142#[async_trait::async_trait]
143pub trait ApiServer: MassaApiServer {
144    /// Start the API
145    async fn serve(
146        self,
147        url: &SocketAddr,
148        api_config: &APIConfig,
149    ) -> Result<StopHandle, JsonRpseeError>;
150}
151
152async fn serve<T>(
153    api: RpcModule<T>,
154    url: &SocketAddr,
155    api_config: &APIConfig,
156) -> Result<StopHandle, JsonRpseeError> {
157    let ping_config = PingConfig::new().ping_interval(api_config.ping_interval.to_duration());
158    let mut server_builder = ServerBuilder::new()
159        .max_request_body_size(api_config.max_request_body_size)
160        .max_response_body_size(api_config.max_response_body_size)
161        .max_connections(api_config.max_connections)
162        .set_batch_request_config(if api_config.batch_request_limit > 0 {
163            BatchRequestConfig::Limit(api_config.batch_request_limit)
164        } else {
165            BatchRequestConfig::Disabled
166        })
167        .enable_ws_ping(ping_config);
168
169    if api_config.enable_http && !api_config.enable_ws {
170        server_builder = server_builder.http_only();
171    } else if api_config.enable_ws && !api_config.enable_http {
172        server_builder = server_builder.ws_only()
173    } else if !api_config.enable_http && !api_config.enable_ws {
174        panic!("wrong server configuration, you can't disable both http and ws");
175    }
176
177    let cors = CorsLayer::new()
178        // Allow `POST` and `OPTIONS` when accessing the resource
179        .allow_methods([Method::POST, Method::OPTIONS])
180        // Allow requests from any origin
181        .allow_origin(Any)
182        .allow_headers([http::header::CONTENT_TYPE]);
183
184    let hosts = if api_config.allow_hosts.is_empty() {
185        vec!["*:*"]
186    } else {
187        api_config
188            .allow_hosts
189            .iter()
190            .map(|hostname| hostname.as_str())
191            .collect()
192    };
193
194    let allowed_hosts = HostFilterLayer::new(hosts).expect("failed to build allowed hosts filter");
195
196    let middleware = tower::ServiceBuilder::new()
197        .layer(cors)
198        .layer(allowed_hosts);
199
200    let server = server_builder
201        .set_http_middleware(middleware)
202        .build(url)
203        .await
204        .expect("failed to build server");
205
206    let server_handler = server.start(api);
207    let stop_handler = StopHandle { server_handler };
208
209    Ok(stop_handler)
210}
211
212/// Used to be able to stop the API
213pub struct StopHandle {
214    server_handler: ServerHandle,
215}
216
217impl StopHandle {
218    /// stop the API gracefully
219    pub async fn stop(self) {
220        match self.server_handler.stop() {
221            Ok(_) => {
222                info!("API stop signal sent successfully");
223            }
224            Err(err) => warn!("API thread panicked: {:?}", err),
225        }
226        self.server_handler.stopped().await;
227    }
228}
229
230/// Exposed API methods
231#[rpc(server)]
232pub trait MassaRpc {
233    /// Gracefully stop the node.
234    #[method(name = "stop_node")]
235    fn stop_node(&self) -> RpcResult<()>;
236
237    /// Sign message with node's key.
238    /// Returns the public key that signed the message and the signature.
239    #[method(name = "node_sign_message")]
240    async fn node_sign_message(&self, arg: Vec<u8>) -> RpcResult<PubkeySig>;
241
242    /// Add a vector of new secret(private) keys for the node to use to stake.
243    /// No confirmation to expect.
244    #[method(name = "add_staking_secret_keys")]
245    async fn add_staking_secret_keys(&self, arg: Vec<String>) -> RpcResult<()>;
246
247    /// Execute bytecode in read-only mode.
248    #[method(name = "execute_read_only_bytecode")]
249    async fn execute_read_only_bytecode(
250        &self,
251        arg: Vec<ReadOnlyBytecodeExecution>,
252    ) -> RpcResult<Vec<ExecuteReadOnlyResponse>>;
253
254    /// Execute an SC function in read-only mode.
255    #[method(name = "execute_read_only_call")]
256    async fn execute_read_only_call(
257        &self,
258        arg: Vec<ReadOnlyCall>,
259    ) -> RpcResult<Vec<ExecuteReadOnlyResponse>>;
260
261    /// Remove a vector of addresses used to stake.
262    /// No confirmation to expect.
263    #[method(name = "remove_staking_addresses")]
264    async fn remove_staking_addresses(&self, arg: Vec<Address>) -> RpcResult<()>;
265
266    /// Return hash set of staking addresses.
267    #[method(name = "get_staking_addresses")]
268    async fn get_staking_addresses(&self) -> RpcResult<PreHashSet<Address>>;
269
270    /// Bans given IP address(es).
271    /// No confirmation to expect.
272    #[method(name = "node_ban_by_ip")]
273    async fn node_ban_by_ip(&self, arg: Vec<IpAddr>) -> RpcResult<()>;
274
275    /// Bans given node id.
276    /// No confirmation to expect.
277    #[method(name = "node_ban_by_id")]
278    async fn node_ban_by_id(&self, arg: Vec<NodeId>) -> RpcResult<()>;
279
280    /// Returns node peers whitelist IP address(es).
281    #[method(name = "node_peers_whitelist")]
282    async fn node_peers_whitelist(&self) -> RpcResult<Vec<IpAddr>>;
283
284    /// Add IP address(es) to node peers whitelist.
285    /// No confirmation to expect.
286    /// Note: If the ip was unknown it adds it to the known peers, otherwise it updates the peer type
287    #[method(name = "node_add_to_peers_whitelist")]
288    async fn node_add_to_peers_whitelist(&self, arg: Vec<IpAddr>) -> RpcResult<()>;
289
290    /// Remove from peers whitelist given IP address(es).
291    /// keep it as standard
292    /// No confirmation to expect.
293    #[method(name = "node_remove_from_peers_whitelist")]
294    async fn node_remove_from_peers_whitelist(&self, arg: Vec<IpAddr>) -> RpcResult<()>;
295
296    /// Returns node bootstrap whitelist IP address(es).
297    #[method(name = "node_bootstrap_whitelist")]
298    async fn node_bootstrap_whitelist(&self) -> RpcResult<Vec<IpAddr>>;
299
300    /// Allow everyone to bootstrap from the node.
301    /// remove bootstrap whitelist configuration file.
302    #[method(name = "node_bootstrap_whitelist_allow_all")]
303    async fn node_bootstrap_whitelist_allow_all(&self) -> RpcResult<()>;
304
305    /// Add IP address(es) to node bootstrap whitelist.
306    #[method(name = "node_add_to_bootstrap_whitelist")]
307    async fn node_add_to_bootstrap_whitelist(&self, arg: Vec<IpAddr>) -> RpcResult<()>;
308
309    /// Remove IP address(es) to bootstrap whitelist.
310    #[method(name = "node_remove_from_bootstrap_whitelist")]
311    async fn node_remove_from_bootstrap_whitelist(&self, arg: Vec<IpAddr>) -> RpcResult<()>;
312
313    /// Returns node bootstrap blacklist IP address(es).
314    #[method(name = "node_bootstrap_blacklist")]
315    async fn node_bootstrap_blacklist(&self) -> RpcResult<Vec<IpAddr>>;
316
317    /// Add IP address(es) to node bootstrap blacklist.
318    #[method(name = "node_add_to_bootstrap_blacklist")]
319    async fn node_add_to_bootstrap_blacklist(&self, arg: Vec<IpAddr>) -> RpcResult<()>;
320
321    /// Remove IP address(es) to bootstrap blacklist.
322    #[method(name = "node_remove_from_bootstrap_blacklist")]
323    async fn node_remove_from_bootstrap_blacklist(&self, arg: Vec<IpAddr>) -> RpcResult<()>;
324
325    /// Unban given IP address(es).
326    /// No confirmation to expect.
327    #[method(name = "node_unban_by_ip")]
328    async fn node_unban_by_ip(&self, arg: Vec<IpAddr>) -> RpcResult<()>;
329
330    /// Unban given node id.
331    /// No confirmation to expect.
332    #[method(name = "node_unban_by_id")]
333    async fn node_unban_by_id(&self, arg: Vec<NodeId>) -> RpcResult<()>;
334
335    /// Summary of the current state: time, last final blocks (hash, thread, slot, timestamp), clique count, connected nodes count.
336    #[method(name = "get_status")]
337    async fn get_status(&self) -> RpcResult<NodeStatus>;
338
339    /// Get cliques.
340    #[method(name = "get_cliques")]
341    async fn get_cliques(&self) -> RpcResult<Vec<Clique>>;
342
343    /// Returns the active stakers and their active roll counts for the current cycle.
344    #[method(name = "get_stakers")]
345    async fn get_stakers(
346        &self,
347        page_request: Option<PageRequest>,
348    ) -> RpcResult<PagedVec<(Address, u64)>>;
349
350    /// Returns operation(s) information associated to a given list of operation(s) ID(s).
351    #[method(name = "get_operations")]
352    async fn get_operations(&self, arg: Vec<OperationId>) -> RpcResult<Vec<OperationInfo>>;
353
354    /// Returns endorsement(s) information associated to a given list of endorsement(s) ID(s)
355    #[method(name = "get_endorsements")]
356    async fn get_endorsements(&self, arg: Vec<EndorsementId>) -> RpcResult<Vec<EndorsementInfo>>;
357
358    /// Returns block(s) information associated to a given list of block(s) ID(s)
359    #[method(name = "get_blocks")]
360    async fn get_blocks(&self, arg: Vec<BlockId>) -> RpcResult<Vec<BlockInfo>>;
361
362    /// Get information on the block at a slot in the blockclique.
363    /// If there is no block at this slot a `None` is returned.
364    #[method(name = "get_blockclique_block_by_slot")]
365    async fn get_blockclique_block_by_slot(&self, arg: Slot) -> RpcResult<Option<Block>>;
366
367    /// Get the block graph within the specified time interval.
368    /// Optional parameters: from `<time_start>` (included) and to `<time_end>` (excluded) millisecond timestamp
369    #[method(name = "get_graph_interval")]
370    async fn get_graph_interval(&self, arg: TimeInterval) -> RpcResult<Vec<BlockSummary>>;
371
372    /// Get multiple datastore entries.
373    #[method(name = "get_datastore_entries")]
374    async fn get_datastore_entries(
375        &self,
376        arg: Vec<DatastoreEntryInput>,
377    ) -> RpcResult<Vec<DatastoreEntryOutput>>;
378
379    /// Get addresses.
380    #[method(name = "get_addresses")]
381    async fn get_addresses(&self, arg: Vec<Address>) -> RpcResult<Vec<AddressInfo>>;
382
383    /// Get addresses bytecode.
384    #[method(name = "get_addresses_bytecode")]
385    async fn get_addresses_bytecode(&self, args: Vec<AddressFilter>) -> RpcResult<Vec<Vec<u8>>>;
386
387    /// Get all the transfers for a slot
388    #[method(name = "get_slots_transfers")]
389    async fn get_slots_transfers(&self, arg: Vec<Slot>) -> RpcResult<Vec<Vec<Transfer>>>;
390
391    /// Adds operations to pool. Returns operations that were ok and sent to pool.
392    #[method(name = "send_operations")]
393    async fn send_operations(&self, arg: Vec<OperationInput>) -> RpcResult<Vec<OperationId>>;
394
395    /// Get events optionally filtered by:
396    /// * start slot
397    /// * end slot
398    /// * emitter address
399    /// * original caller address
400    /// * operation id
401    #[method(name = "get_filtered_sc_output_event")]
402    async fn get_filtered_sc_output_event(&self, arg: EventFilter)
403        -> RpcResult<Vec<SCOutputEvent>>;
404
405    /// Get OpenRPC specification.
406    #[method(name = "rpc.discover")]
407    async fn get_openrpc_spec(&self) -> RpcResult<Value>;
408
409    /// DeferredCall quote
410    #[method(name = "get_deferred_call_quote")]
411    async fn get_deferred_call_quote(
412        &self,
413        arg: Vec<DeferredCallsQuoteRequest>,
414    ) -> RpcResult<Vec<DeferredCallsQuoteResponse>>;
415
416    /// DeferredCall get info
417    #[method(name = "get_deferred_call_info")]
418    async fn get_deferred_call_info(
419        &self,
420        arg: Vec<String>,
421    ) -> RpcResult<Vec<DeferredCallResponse>>;
422
423    /// List deferred calls for given slot
424    #[method(name = "get_deferred_call_ids_by_slot")]
425    async fn get_deferred_call_ids_by_slot(
426        &self,
427        arg: Vec<Slot>,
428    ) -> RpcResult<Vec<DeferredCallsSlotResponse>>;
429
430    /// Get keys gor given address
431    #[method(name = "get_addresses_datastore_keys")]
432    async fn get_addresses_datastore_keys(
433        &self,
434        arg: Vec<GetAddressDatastoreKeysRequest>,
435    ) -> RpcResult<Vec<GetAddressDatastoreKeysResponse>>;
436}
437
438fn wrong_api<T>() -> RpcResult<T> {
439    Err((WrongAPI).into())
440}