massa_grpc/
private.rs

1// Copyright (c) 2023 MASSA LABS <info@massa.net>
2
3use std::net::IpAddr;
4use std::str::FromStr;
5
6use crate::error::GrpcError;
7use crate::server::MassaPrivateGrpc;
8use massa_execution_exports::ExecutionQueryRequest;
9use massa_hash::Hash;
10use massa_models::config::CompactConfig;
11use massa_models::node::NodeId;
12use massa_models::slot::Slot;
13use massa_models::timeslots::get_latest_block_slot_at_timestamp;
14use massa_proto_rs::massa::api::v1 as grpc_api;
15use massa_proto_rs::massa::model::v1 as grpc_model;
16use massa_protocol_exports::{PeerConnectionType, PeerId};
17use massa_signature::KeyPair;
18use massa_time::MassaTime;
19// use massa_proto_rs::massa::model::v1 "add_to_bootstrap_blacklist"as grpc_model;
20
21/// Add IP addresses to node bootstrap blacklist
22pub(crate) fn add_to_bootstrap_blacklist(
23    grpc: &MassaPrivateGrpc,
24    request: tonic::Request<grpc_api::AddToBootstrapBlacklistRequest>,
25) -> Result<grpc_api::AddToBootstrapBlacklistResponse, GrpcError> {
26    let inner_req = request.into_inner();
27
28    let ips = inner_req
29        .ips
30        .into_iter()
31        .map(|ip| {
32            IpAddr::from_str(&ip)
33                .map_err(|_| GrpcError::InvalidArgument(format!("invalid ip address: {}", ip)))
34        })
35        .collect::<Result<Vec<_>, _>>()?;
36
37    if let Some(bs_list) = &grpc.bs_white_black_list {
38        bs_list
39            .add_ips_to_blacklist(ips)
40            .map_err(|e| GrpcError::InternalServerError(e.to_string()))?;
41    }
42
43    Ok(grpc_api::AddToBootstrapBlacklistResponse {})
44}
45/// Add IP addresses to node bootstrap whitelist
46pub(crate) fn add_to_bootstrap_whitelist(
47    grpc: &MassaPrivateGrpc,
48    request: tonic::Request<grpc_api::AddToBootstrapWhitelistRequest>,
49) -> Result<grpc_api::AddToBootstrapWhitelistResponse, GrpcError> {
50    let inner_req = request.into_inner();
51
52    let ips = inner_req
53        .ips
54        .into_iter()
55        .map(|ip| {
56            IpAddr::from_str(&ip)
57                .map_err(|_| GrpcError::InvalidArgument(format!("invalid ip address: {}", ip)))
58        })
59        .collect::<Result<Vec<_>, _>>()?;
60
61    if let Some(bs_list) = &grpc.bs_white_black_list {
62        bs_list
63            .add_ips_to_whitelist(ips)
64            .map_err(|e| GrpcError::InternalServerError(e.to_string()))?;
65    }
66
67    Ok(grpc_api::AddToBootstrapWhitelistResponse {})
68}
69/// Add IP addresses to node peers whitelist. No confirmation to expect.
70/// Note: If the ip was unknown it adds it to the known peers, otherwise it updates the peer type
71pub(crate) fn add_to_peers_whitelist(
72    _grpc: &MassaPrivateGrpc,
73    _request: tonic::Request<grpc_api::AddToPeersWhitelistRequest>,
74) -> Result<grpc_api::AddToPeersWhitelistResponse, GrpcError> {
75    Err(GrpcError::Unimplemented(
76        "add_to_peers_whitelist".to_string(),
77    ))
78}
79/// Add staking secret keys to wallet
80pub(crate) fn add_staking_secret_keys(
81    grpc: &MassaPrivateGrpc,
82    request: tonic::Request<grpc_api::AddStakingSecretKeysRequest>,
83) -> Result<grpc_api::AddStakingSecretKeysResponse, GrpcError> {
84    let secret_keys = request.into_inner().secret_keys;
85
86    if secret_keys.is_empty() {
87        return Err(GrpcError::InvalidArgument(
88            "no secret key received".to_string(),
89        ));
90    }
91
92    if secret_keys.len() as u64 > grpc.grpc_config.max_arguments {
93        return Err(GrpcError::InvalidArgument(format!(
94            "too many secret keys received. Only a maximum of {} secret keys are accepted per request",
95            grpc.grpc_config.max_arguments
96        )));
97    }
98
99    let keypairs = match secret_keys.iter().map(|x| KeyPair::from_str(x)).collect() {
100        Ok(keypairs) => keypairs,
101        Err(e) => return Err(GrpcError::InvalidArgument(e.to_string())),
102    };
103
104    grpc.node_wallet.write().add_keypairs(keypairs)?;
105
106    Ok(grpc_api::AddStakingSecretKeysResponse {})
107}
108
109/// Ban multiple nodes by their individual ids
110pub(crate) fn ban_nodes_by_ids(
111    grpc: &MassaPrivateGrpc,
112    request: tonic::Request<grpc_api::BanNodesByIdsRequest>,
113) -> Result<grpc_api::BanNodesByIdsResponse, GrpcError> {
114    let node_ids = request.into_inner().node_ids;
115
116    if node_ids.is_empty() {
117        return Err(GrpcError::InvalidArgument(
118            "no node id received".to_string(),
119        ));
120    }
121
122    if node_ids.len() as u64 > grpc.grpc_config.max_arguments {
123        return Err(GrpcError::InvalidArgument(format!(
124            "too many node ids received. Only a maximum of {} node ids are accepted per request",
125            grpc.grpc_config.max_arguments
126        )));
127    }
128
129    //TODO: Change when unify node id and peer id
130    let peer_ids = node_ids
131        .into_iter()
132        .map(|id| {
133            NodeId::from_str(&id).map(|node_id| PeerId::from_public_key(node_id.get_public_key()))
134        })
135        .collect::<Result<Vec<_>, _>>()?;
136
137    grpc.protocol_controller.ban_peers(peer_ids)?;
138
139    Ok(grpc_api::BanNodesByIdsResponse {})
140}
141
142/// Ban multiple nodes by their individual IP addresses
143pub(crate) fn ban_nodes_by_ips(
144    _grpc: &MassaPrivateGrpc,
145    _request: tonic::Request<grpc_api::BanNodesByIpsRequest>,
146) -> Result<grpc_api::BanNodesByIpsResponse, GrpcError> {
147    Err(GrpcError::Unimplemented("ban_nodes_by_ips".to_string()))
148}
149
150/// Get node bootstrap blacklist IP addresses
151pub(crate) fn get_bootstrap_blacklist(
152    grpc: &MassaPrivateGrpc,
153    _request: tonic::Request<grpc_api::GetBootstrapBlacklistRequest>,
154) -> Result<grpc_api::GetBootstrapBlacklistResponse, GrpcError> {
155    let list = {
156        match grpc.bs_white_black_list {
157            Some(ref bs_list) => bs_list
158                .get_black_list()
159                .unwrap_or_default()
160                .into_iter()
161                .map(|ip| ip.to_string())
162                .collect(),
163            None => Vec::new(),
164        }
165    };
166    Ok(grpc_api::GetBootstrapBlacklistResponse { ips: list })
167}
168/// Get node bootstrap whitelist IP addresses
169pub(crate) fn get_bootstrap_whitelist(
170    grpc: &MassaPrivateGrpc,
171    _request: tonic::Request<grpc_api::GetBootstrapWhitelistRequest>,
172) -> Result<grpc_api::GetBootstrapWhitelistResponse, GrpcError> {
173    let list = {
174        match grpc.bs_white_black_list {
175            Some(ref bs_list) => bs_list
176                .get_white_list()
177                .unwrap_or_default()
178                .into_iter()
179                .map(|ip| ip.to_string())
180                .collect(),
181            None => Vec::new(),
182        }
183    };
184
185    Ok(grpc_api::GetBootstrapWhitelistResponse { ips: list })
186}
187// Get MIP store dump
188pub(crate) fn get_mip_status(
189    grpc: &MassaPrivateGrpc,
190    _request: tonic::Request<grpc_api::GetMipStatusRequest>,
191) -> Result<grpc_api::GetMipStatusResponse, GrpcError> {
192    let mip_store_status_ = grpc.mip_store.get_mip_status();
193    let mip_store_status: Result<Vec<grpc_model::MipStatusEntry>, GrpcError> = mip_store_status_
194        .iter()
195        .map(|(mip_info, state_id_)| {
196            let state_id = grpc_model::ComponentStateId::from(state_id_);
197            Ok(grpc_model::MipStatusEntry {
198                mip_info: Some(grpc_model::MipInfo::from(mip_info)),
199                state_id: i32::from(state_id),
200            })
201        })
202        .collect();
203
204    Ok(grpc_api::GetMipStatusResponse {
205        mipstatus_entries: mip_store_status?,
206    })
207}
208
209/// Allow everyone to bootstrap from the node by removing bootstrap whitelist configuration file
210pub(crate) fn allow_everyone_to_bootstrap(
211    _grpc: &MassaPrivateGrpc,
212    _request: tonic::Request<grpc_api::AllowEveryoneToBootstrapRequest>,
213) -> Result<grpc_api::AllowEveryoneToBootstrapResponse, GrpcError> {
214    Err(GrpcError::Unimplemented(
215        "allow_everyone_to_bootstrap".to_string(),
216    ))
217}
218/// Get node status
219pub(crate) fn get_node_status(
220    grpc: &MassaPrivateGrpc,
221    _request: tonic::Request<grpc_api::GetNodeStatusRequest>,
222) -> Result<grpc_api::GetNodeStatusResponse, GrpcError> {
223    let config = CompactConfig::default();
224    let now = MassaTime::now();
225    let last_slot = get_latest_block_slot_at_timestamp(
226        grpc.grpc_config.thread_count,
227        grpc.grpc_config.t0,
228        grpc.grpc_config.genesis_timestamp,
229        now,
230    )?;
231    let execution_stats = grpc.execution_controller.get_stats();
232    let consensus_stats = grpc.consensus_controller.get_stats()?;
233    let (network_stats, peers) = grpc.protocol_controller.get_stats()?;
234    let pool_stats = grpc_model::PoolStats {
235        operations_count: grpc
236            .pool_controller
237            .get_denunciation_count(Some(MassaTime::from_millis(
238                grpc.grpc_config.timeout.as_millis() as u64,
239            )))
240            .unwrap_or(0) as u64,
241        endorsements_count: grpc
242            .pool_controller
243            .get_endorsement_count(Some(MassaTime::from_millis(
244                grpc.grpc_config.timeout.as_millis() as u64,
245            )))
246            .unwrap_or(0) as u64,
247    };
248
249    let mut connected_nodes = peers
250        .iter()
251        .map(|(id, peer)| {
252            let connection_type = match peer.1 {
253                PeerConnectionType::IN => grpc_model::ConnectionType::Incoming,
254                PeerConnectionType::OUT => grpc_model::ConnectionType::Outgoing,
255            };
256
257            grpc_model::ConnectedNode {
258                node_id: NodeId::new(id.get_public_key()).to_string(),
259                node_ip: peer.0.ip().to_string(),
260                connection_type: connection_type as i32,
261            }
262        })
263        .collect::<Vec<_>>();
264    connected_nodes.sort_by(|a, b| a.node_ip.cmp(&b.node_ip));
265
266    let current_cycle = last_slot
267        .unwrap_or_else(|| Slot::new(0, 0))
268        .get_cycle(grpc.grpc_config.periods_per_cycle);
269    let cycle_duration = grpc
270        .grpc_config
271        .t0
272        .checked_mul(grpc.grpc_config.periods_per_cycle)?;
273    let current_cycle_time = if current_cycle == 0 {
274        grpc.grpc_config.genesis_timestamp
275    } else {
276        cycle_duration
277            .checked_mul(current_cycle)
278            .and_then(|elapsed_time_before_current_cycle| {
279                grpc.grpc_config
280                    .genesis_timestamp
281                    .checked_add(elapsed_time_before_current_cycle)
282            })?
283    };
284    let next_cycle_time = current_cycle_time.checked_add(cycle_duration)?;
285    let empty_request = ExecutionQueryRequest {
286        requests: vec![],
287        max_response_size: grpc.grpc_config.max_encoding_message_size,
288        max_event_count: None,
289        query_state_deadline_ms: None,
290    };
291    let state = grpc.execution_controller.query_state(empty_request);
292    let node_ip = grpc
293        .protocol_config
294        .routable_ip
295        .map(|ip| ip.to_string())
296        .unwrap_or_default();
297
298    let status = grpc_model::NodeStatus {
299        node_id: grpc.node_id.to_string(),
300        node_ip,
301        version: grpc.version.to_string(),
302        current_time: Some(now.into()),
303        current_cycle,
304        current_cycle_time: Some(current_cycle_time.into()),
305        next_cycle_time: Some(next_cycle_time.into()),
306        connected_nodes,
307        last_executed_final_slot: Some(state.final_cursor.into()),
308        last_executed_speculative_slot: Some(state.candidate_cursor.into()),
309        final_state_fingerprint: state.final_state_fingerprint.to_string(),
310        consensus_stats: Some(consensus_stats.into()),
311        pool_stats: Some(pool_stats),
312        network_stats: Some(network_stats.into()),
313        execution_stats: Some(execution_stats.into()),
314        config: Some(config.into()),
315        chain_id: grpc.grpc_config.chain_id,
316        current_mip_version: grpc.mip_store.get_network_version_current(),
317    };
318
319    Ok(grpc_api::GetNodeStatusResponse {
320        status: Some(status),
321    })
322}
323/// Get node peers whitelist IP addresses
324pub(crate) fn get_peers_whitelist(
325    _grpc: &MassaPrivateGrpc,
326    _request: tonic::Request<grpc_api::GetPeersWhitelistRequest>,
327) -> Result<grpc_api::GetPeersWhitelistResponse, GrpcError> {
328    Err(GrpcError::Unimplemented("get_peers_whitelist".to_string()))
329}
330/// Remove from bootstrap blacklist given IP addresses
331pub(crate) fn remove_from_bootstrap_blacklist(
332    grpc: &MassaPrivateGrpc,
333    request: tonic::Request<grpc_api::RemoveFromBootstrapBlacklistRequest>,
334) -> Result<grpc_api::RemoveFromBootstrapBlacklistResponse, GrpcError> {
335    let inner_req = request.into_inner();
336    let ips = inner_req
337        .ips
338        .into_iter()
339        .map(|ip| {
340            IpAddr::from_str(&ip)
341                .map_err(|_| GrpcError::InvalidArgument(format!("invalid ip address: {}", ip)))
342        })
343        .collect::<Result<Vec<_>, _>>()?;
344
345    if let Some(bs_list) = &grpc.bs_white_black_list {
346        bs_list
347            .remove_ips_from_blacklist(ips)
348            .map_err(|e| GrpcError::InternalServerError(e.to_string()))?;
349    }
350
351    Ok(grpc_api::RemoveFromBootstrapBlacklistResponse {})
352}
353/// Remove from bootstrap whitelist given IP addresses
354pub(crate) fn remove_from_bootstrap_whitelist(
355    grpc: &MassaPrivateGrpc,
356    request: tonic::Request<grpc_api::RemoveFromBootstrapWhitelistRequest>,
357) -> Result<grpc_api::RemoveFromBootstrapWhitelistResponse, GrpcError> {
358    let inner_req = request.into_inner();
359    let ips = inner_req
360        .ips
361        .into_iter()
362        .map(|ip| {
363            IpAddr::from_str(&ip)
364                .map_err(|_| GrpcError::InvalidArgument(format!("invalid ip address: {}", ip)))
365        })
366        .collect::<Result<Vec<_>, _>>()?;
367
368    if let Some(bs_list) = &grpc.bs_white_black_list {
369        bs_list
370            .remove_ips_from_whitelist(ips)
371            .map_err(|e| GrpcError::InternalServerError(e.to_string()))?;
372    }
373
374    Ok(grpc_api::RemoveFromBootstrapWhitelistResponse {})
375}
376/// Remove from peers whitelist given IP addresses
377pub(crate) fn remove_from_peers_whitelist(
378    _grpc: &MassaPrivateGrpc,
379    _request: tonic::Request<grpc_api::RemoveFromPeersWhitelistRequest>,
380) -> Result<grpc_api::RemoveFromPeersWhitelistResponse, GrpcError> {
381    Err(GrpcError::Unimplemented(
382        "remove_from_peers_whitelist".to_string(),
383    ))
384}
385/// Remove addresses from staking
386pub(crate) fn remove_staking_addresses(
387    _grpc: &MassaPrivateGrpc,
388    _request: tonic::Request<grpc_api::RemoveStakingAddressesRequest>,
389) -> Result<grpc_api::RemoveStakingAddressesResponse, GrpcError> {
390    Err(GrpcError::Unimplemented(
391        "remove_staking_addresses".to_string(),
392    ))
393}
394/// Sign messages with node's key
395pub(crate) fn sign_messages(
396    grpc: &MassaPrivateGrpc,
397    request: tonic::Request<grpc_api::SignMessagesRequest>,
398) -> Result<grpc_api::SignMessagesResponse, GrpcError> {
399    let messages = request.into_inner().messages;
400
401    if messages.is_empty() {
402        return Err(GrpcError::InvalidArgument(
403            "no message received".to_string(),
404        ));
405    }
406
407    if messages.len() as u64 > grpc.grpc_config.max_arguments {
408        return Err(GrpcError::InvalidArgument(format!(
409            "too many messages received. Only a maximum of {} messages are accepted per request",
410            grpc.grpc_config.max_arguments
411        )));
412    }
413
414    let keypair = grpc.grpc_config.keypair.clone();
415    let signatures = messages
416        .into_iter()
417        .map(|message| {
418            keypair
419                .sign(&Hash::compute_from(&message))
420                .map(|signature| signature.to_string())
421        })
422        .collect::<Result<Vec<_>, _>>()?;
423
424    Ok(grpc_api::SignMessagesResponse {
425        public_key: keypair.get_public_key().to_string(),
426        signatures,
427    })
428}
429/// Shutdown the node gracefully
430pub(crate) fn shutdown_gracefully(
431    _grpc: &MassaPrivateGrpc,
432    _request: tonic::Request<grpc_api::ShutdownGracefullyRequest>,
433) -> Result<grpc_api::ShutdownGracefullyResponse, GrpcError> {
434    Err(GrpcError::Unimplemented("shutdown_gracefully".to_string()))
435}
436
437/// Unban multiple nodes by their individual ids
438pub(crate) fn unban_nodes_by_ids(
439    grpc: &MassaPrivateGrpc,
440    request: tonic::Request<grpc_api::UnbanNodesByIdsRequest>,
441) -> Result<grpc_api::UnbanNodesByIdsResponse, GrpcError> {
442    let node_ids = request.into_inner().node_ids;
443
444    if node_ids.is_empty() {
445        return Err(GrpcError::InvalidArgument(
446            "no node id received".to_string(),
447        ));
448    }
449
450    if node_ids.len() as u64 > grpc.grpc_config.max_arguments {
451        return Err(GrpcError::InvalidArgument(format!(
452            "too many node ids received. Only a maximum of {} node ids are accepted per request",
453            grpc.grpc_config.max_arguments
454        )));
455    }
456
457    //TODO: Change when unify node id and peer id
458    let peer_ids = node_ids
459        .into_iter()
460        .map(|id| {
461            NodeId::from_str(&id).map(|node_id| PeerId::from_public_key(node_id.get_public_key()))
462        })
463        .collect::<Result<Vec<_>, _>>()?;
464
465    grpc.protocol_controller.unban_peers(peer_ids)?;
466
467    Ok(grpc_api::UnbanNodesByIdsResponse {})
468}
469
470/// Unban multiple nodes by their individual IP addresses
471pub(crate) fn unban_nodes_by_ips(
472    _grpc: &MassaPrivateGrpc,
473    _request: tonic::Request<grpc_api::UnbanNodesByIpsRequest>,
474) -> Result<grpc_api::UnbanNodesByIpsResponse, GrpcError> {
475    Err(GrpcError::Unimplemented("unban_nodes_by_ips".to_string()))
476}