1use 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;
19pub(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}
45pub(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}
69pub(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}
79pub(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
109pub(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 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
142pub(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
150pub(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}
168pub(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}
187pub(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
209pub(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}
218pub(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}
323pub(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}
330pub(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}
353pub(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}
376pub(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}
385pub(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}
394pub(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}
429pub(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
437pub(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 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
470pub(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}