From 0c9f4cfdaa79f970b7604485442a18b9079062b5 Mon Sep 17 00:00:00 2001 From: Moncef AOUDIA Date: Fri, 3 Mar 2023 22:27:26 +0100 Subject: [PATCH] feat: add send_endorsements --- massa-grpc/src/api.rs | 226 +++++++++++++++++++++++++++--- massa-proto/proto/massa/api.proto | 5 +- massa-proto/src/api.bin | Bin 92245 -> 92254 bytes massa-proto/src/massa.api.v1.rs | 190 ++++++++++++------------- 4 files changed, 304 insertions(+), 117 deletions(-) diff --git a/massa-grpc/src/api.rs b/massa-grpc/src/api.rs index 0bc456d1b22..78f70bfb585 100644 --- a/massa-grpc/src/api.rs +++ b/massa-grpc/src/api.rs @@ -6,6 +6,7 @@ use crate::config::GrpcConfig; use massa_consensus_exports::{ConsensusChannels, ConsensusController}; use massa_models::{ block::{BlockDeserializer, BlockDeserializerArgs, SecureShareBlock}, + endorsement::{EndorsementDeserializer, SecureShareEndorsement}, error::ModelsError, operation::{OperationDeserializer, SecureShareOperation}, secure_share::SecureShareDeserializer, @@ -231,7 +232,7 @@ impl grpc::grpc_server::Grpc for MassaService { "failed to propagate block: {}", e ); - let _res = sendblocks_notify_error( + let _res = send_blocks_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::Internal, @@ -260,7 +261,7 @@ impl grpc::grpc_server::Grpc for MassaService { "wrong signature: {}", res_block.signature ); - let _res = sendblocks_notify_error( + let _res = send_blocks_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::InvalidArgument, @@ -269,7 +270,7 @@ impl grpc::grpc_server::Grpc for MassaService { .await; }; } else { - let _res = sendblocks_notify_error( + let _res = send_blocks_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::InvalidArgument, @@ -281,7 +282,7 @@ impl grpc::grpc_server::Grpc for MassaService { } Err(e) => { let error = format!("failed to deserialize block: {}", e); - let _res = sendblocks_notify_error( + let _res = send_blocks_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::InvalidArgument, @@ -292,7 +293,7 @@ impl grpc::grpc_server::Grpc for MassaService { } }; } else { - let _res = sendblocks_notify_error( + let _res = send_blocks_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::InvalidArgument, @@ -337,9 +338,166 @@ impl grpc::grpc_server::Grpc for MassaService { async fn send_endorsements( &self, - _request: tonic::Request>, + request: tonic::Request>, ) -> Result, tonic::Status> { - Err(tonic::Status::unimplemented("not implemented")) + let mut cmd_sender = self.pool_command_sender.clone(); + let mut protocol_sender = self.protocol_command_sender.clone(); + let config = self.grpc_config.clone(); + let storage = self.storage.clone_without_refs(); + + let (tx, rx) = tokio::sync::mpsc::channel(config.max_channel_size); + let mut in_stream = request.into_inner(); + + tokio::spawn(async move { + while let Some(result) = in_stream.next().await { + match result { + Ok(req_content) => { + if req_content.endorsements.is_empty() { + let _res = send_endorsements_notify_error( + req_content.id.clone(), + tx.clone(), + tonic::Code::InvalidArgument, + "the request payload is empty".to_owned(), + ) + .await; + } else { + let proto_endorsement = req_content.endorsements; + if proto_endorsement.len() as u32 > config.max_endorsements_per_message + { + let _res = send_endorsements_notify_error( + req_content.id.clone(), + tx.clone(), + tonic::Code::InvalidArgument, + "too many endorsements".to_owned(), + ) + .await; + } else { + let endorsement_deserializer = + SecureShareDeserializer::new(EndorsementDeserializer::new( + config.thread_count, + config.endorsement_count, + )); + let verified_eds_res: Result, ModelsError> = proto_endorsement + .into_iter() + .map(|proto_endorsement| { + let mut ed_serialized = Vec::new(); + ed_serialized.extend(proto_endorsement.signature.as_bytes()); + ed_serialized.extend(proto_endorsement.creator_public_key.as_bytes()); + ed_serialized.extend(proto_endorsement.serialized_content); + let verified_op = match endorsement_deserializer.deserialize::(&ed_serialized) { + Ok(tuple) => { + let (rest, res_endorsement): (&[u8], SecureShareEndorsement) = tuple; + if rest.is_empty() { + if let Ok(_verify_signature) = res_endorsement.verify_signature() { + Ok((res_endorsement.id.to_string(), res_endorsement)) + } else { + Err(ModelsError::MassaSignatureError(massa_signature::MassaSignatureError::SignatureError( + format!("wrong signature: {}", res_endorsement.signature).to_owned()) + )) + } + } else { + Err(ModelsError::DeserializeError( + "there is data left after endorsement deserialization".to_owned() + )) + } + }, + Err(e) => { + Err(ModelsError::DeserializeError(format!("failed to deserialize endorsement: {}", e).to_owned() + )) + } + }; + verified_op + }) + .collect(); + + match verified_eds_res { + Ok(verified_eds) => { + let mut endorsement_storage = storage.clone_without_refs(); + endorsement_storage.store_endorsements( + verified_eds.values().cloned().collect(), + ); + cmd_sender.add_endorsements(endorsement_storage.clone()); + + let _res = match protocol_sender + .propagate_endorsements(endorsement_storage) + { + Ok(()) => (), + Err(e) => { + let error = format!( + "failed to propagate endorsement: {}", + e + ); + let _res = send_endorsements_notify_error( + req_content.id.clone(), + tx.clone(), + tonic::Code::Internal, + error.to_owned(), + ) + .await; + } + }; + + let result = grpc::EndorsementResult { + ids: verified_eds.keys().cloned().collect(), + }; + let _res = match tx + .send(Ok(grpc::SendEndorsementsResponse { + id: req_content.id.clone(), + message: Some( + grpc::send_endorsements_response::Message::Result( + result, + ), + ), + })) + .await + { + Ok(()) => (), + Err(e) => { + error!( + "failed to send back endorsement response: {}", + e + ) + } + }; + } + Err(e) => { + let error = format!("invalid endorsement(s): {}", e); + let _res = send_endorsements_notify_error( + req_content.id.clone(), + tx.clone(), + tonic::Code::InvalidArgument, + error.to_owned(), + ) + .await; + } + } + } + } + } + Err(err) => { + if let Some(io_err) = match_for_io_error(&err) { + if io_err.kind() == ErrorKind::BrokenPipe { + warn!("client disconnected, broken pipe: {}", io_err); + break; + } + } + match tx.send(Err(err)).await { + Ok(_) => (), + Err(e) => { + error!("failed to send back send_endorsements error response: {}", e); + break; + } + } + } + } + } + }); + + let out_stream = tokio_stream::wrappers::ReceiverStream::new(rx); + + Ok(tonic::Response::new( + Box::pin(out_stream) as Self::SendEndorsementsStream + )) } type SendOperationsStream = Pin< @@ -367,7 +525,7 @@ impl grpc::grpc_server::Grpc for MassaService { match result { Ok(req_content) => { if req_content.operations.is_empty() { - let _res = sendoperations_notify_error( + let _res = send_operations_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::InvalidArgument, @@ -377,7 +535,7 @@ impl grpc::grpc_server::Grpc for MassaService { } else { let proto_operations = req_content.operations; if proto_operations.len() as u32 > config.max_operations_per_message { - let _res = sendoperations_notify_error( + let _res = send_operations_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::InvalidArgument, @@ -440,9 +598,11 @@ impl grpc::grpc_server::Grpc for MassaService { { Ok(()) => (), Err(e) => { - let error = - format!("failed to propagate operations: {}", e); - let _res = sendoperations_notify_error( + let error = format!( + "failed to propagate operations: {}", + e + ); + let _res = send_operations_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::Internal, @@ -476,8 +636,8 @@ impl grpc::grpc_server::Grpc for MassaService { }; } Err(e) => { - let error = format!("invalid operations:{}", e); - let _res = sendoperations_notify_error( + let error = format!("invalid operation(s): {}", e); + let _res = send_operations_notify_error( req_content.id.clone(), tx.clone(), tonic::Code::InvalidArgument, @@ -499,7 +659,7 @@ impl grpc::grpc_server::Grpc for MassaService { match tx.send(Err(err)).await { Ok(_) => (), Err(e) => { - error!("failed to send back sendblocks error response: {}", e); + error!("failed to send back send_operations error response: {}", e); break; } } @@ -516,7 +676,7 @@ impl grpc::grpc_server::Grpc for MassaService { } } -async fn sendblocks_notify_error( +async fn send_blocks_notify_error( id: String, sender: tokio::sync::mpsc::Sender>, code: tonic::Code, @@ -538,13 +698,41 @@ async fn sendblocks_notify_error( { Ok(()) => Ok(()), Err(e) => { - error!("failed to send back sendblocks error response: {}", e); + error!("failed to send back send_blocks error response: {}", e); + Ok(()) + } + } +} + +async fn send_endorsements_notify_error( + id: String, + sender: tokio::sync::mpsc::Sender>, + code: tonic::Code, + error: String, +) -> Result<(), Box> { + error!("{}", error); + match sender + .send(Ok(grpc::SendEndorsementsResponse { + id, + message: Some(grpc::send_endorsements_response::Message::Error( + massa_proto::google::rpc::Status { + code: code.into(), + message: error, + details: Vec::new(), + }, + )), + })) + .await + { + Ok(()) => Ok(()), + Err(e) => { + error!("failed to send back send_enorsements error response: {}", e); Ok(()) } } } -async fn sendoperations_notify_error( +async fn send_operations_notify_error( id: String, sender: tokio::sync::mpsc::Sender>, code: tonic::Code, @@ -566,7 +754,7 @@ async fn sendoperations_notify_error( { Ok(()) => Ok(()), Err(e) => { - error!("failed to send back sendoperations error response: {}", e); + error!("failed to send back send_operations error response: {}", e); Ok(()) } } diff --git a/massa-proto/proto/massa/api.proto b/massa-proto/proto/massa/api.proto index 46962abec87..e12c3c15994 100644 --- a/massa-proto/proto/massa/api.proto +++ b/massa-proto/proto/massa/api.proto @@ -3,7 +3,6 @@ syntax = "proto3"; package massa.api.v1; import "common.proto"; -import "endorsement.proto"; import "google/api/annotations.proto"; import "google/rpc/status.proto"; @@ -76,7 +75,7 @@ message SendEndorsementsRequest { // string field string id = 1; // object field - Endorsement endorsement = 2; + repeated SecureSharePayload endorsements = 2; } // SendEndorsementsResponse holds response from SendEndorsements @@ -93,7 +92,7 @@ message SendEndorsementsResponse { // Holds Endorsement response message EndorsementResult { // string field - string id = 1; + repeated string ids = 1; } // SendOperationsRequest holds parameters to SendOperations diff --git a/massa-proto/src/api.bin b/massa-proto/src/api.bin index 03bc113dcf9b335b1747ca62d15a8da59c6e9bca..ade50ebb139080c0d689271d12cd383f7486a1d1 100644 GIT binary patch delta 2272 zcmY*Z&u<$=7~OAnyj_pOCf(SHz4eccn~$sRXr6f+!|V zn^q|Y6ji-|1fmC|xgqXwsKg;cLWm1Of=h+OwSNPD0piW0577oL>2_Pd=Ze?D3~p=%OvY&kD!qmi2^WX^k@S zk0$v?j&)Wf1+$rNpRy)|E6DZjq*YQuw-g#A;G}P>V-!qTZ|Y#mx79HU4q0U# z6uzyFQSdd>PQI;;KRY55JMuNa=0k%jKm1@rAI3$=Sx{Jfj%vfXnrB5WE>dA?#ms{kXjaTT zK%iN{JgcBd`24!{76onjphKhbn?;)OuDd18l~7zg#Wmi*8P@d-BM=ySh7kyiJ!4%$ ztznH_)Yi3u@TXW~`rGF~7;i15zbb`PX|bk<1p>?3)CJLlF8p+FSntZc4)wenZm9J* zO3%91BVTpMsjnM3NPuYF$N>V;x-raAhTUTCD8uqcw~SYg61l-A^vwWJwPBVHq;g~V zI0v$gQmDOh7`)mvUICzO^Q28tZW^zUL)#|2s$!T^KEG|fPeEROw?vcPZS!fIQuA!d zqb`kiaK@gt%$|U-?XA>ltpNzsTYmH?fIz*K&516kU7z2zKA@l(%BNTGw`ecZly}!H z=!LoJg6(Kw&zJEV*)|J90*u>cVL)KqHvYM4j$Qf7G8N`_jUL1>>aOt(NRgp+425@^Rnz2!OVI<0BB- z_KlA~XxoR6XEB`cx&7FBB70pr>pgbI^-w|$b*Nk{OdXniA%@`&jekI(JT(3Rf$}h$ Q`{8H%i=pE`yYy`0U#wW^UH||9 delta 2267 zcmYjS&u&!*hoH*enezV|-Q`tLW1zuqKX zzIKl`-lON~=LlZATthT`U*yz}vy?kZ z)hX|a7X!kR+;P4n)wfCI!@Nie=8|+1PRWE!x+k2|CUm9iJ43=3uPFekPj;figpBACup2KjOV+D2YJcWljFC)?-p-(YZE;|w3D0w`OIbF2Xv)??@a z&9!0#1;zFaQ867LuEg$zQVmU0R*43QPRYyLs|>8AVkJiUZgn9i4jOkSGPoD zNC6^syFrysJb5g6q$YLeLIW|XCXcIh|4G{) zIaE!ee+&JY}T#NU`Nar%e!)xK-IBB z0fDN6p;kbZkbK3tPGPI6UX4>hot&og!HQoolnG6_YA7!t2IZ=y1Onx%r33=ys&y)% zcX5rCjZ+taFx547(o6-UlYx2N90+yS1|xA5Qt&}tb8b`EuBxdC8c;vYQBjpct!_2M_$R(Ua7AcC?;oa>pvc88~*V5+HEwSgVrSs$KTPT6KLMR_$7=l3L|sZoc3g z0J0wQtPujFbN#*d4+P4`c>nrkCbd<2)+zv$?OCgUP_}2S0z%mytU8Tpgyhfg?;eKg z&v~k4p7{kckkA7iuuv_{P_7AtRUOz>0b$_>sZ&N15XcW??==H~{9vGX^hbxj9{LX_ C3F, } -/// message struct -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Slot { - /// fixed64 field - #[prost(uint64, tag = "1")] - pub period: u64, - /// float field - #[prost(float, tag = "2")] - pub thread: f32, -} -/// region Endorsement -/// message struct -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EndorsementInfo { - /// string field - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - /// bool field - #[prost(bool, tag = "2")] - pub in_pool: bool, - /// string field - #[prost(string, repeated, tag = "3")] - pub in_blocks: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, - /// bool field - #[prost(bool, tag = "4")] - pub is_final: bool, - /// object field - #[prost(message, optional, tag = "5")] - pub endorsement: ::core::option::Option, -} -/// message struct -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Endorsement { - /// object field - #[prost(message, optional, tag = "1")] - pub slot: ::core::option::Option, - /// string field - #[prost(uint32, tag = "2")] - pub index: u32, - /// string field - #[prost(string, tag = "3")] - pub endorsed_block: ::prost::alloc::string::String, -} -/// message struct -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EndorsementContent { - /// string field - #[prost(string, tag = "1")] - pub sender_public_key: ::prost::alloc::string::String, - /// object field - #[prost(message, optional, tag = "2")] - pub slot: ::core::option::Option, - /// float field - #[prost(uint32, tag = "3")] - pub index: u32, - /// string field - #[prost(string, tag = "4")] - pub endorsed_block: ::prost::alloc::string::String, -} -/// message struct -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EndorsementId { - /// string field - #[prost(string, tag = "1")] - pub value: ::prost::alloc::string::String, -} -/// message struct -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SecureShareEndorsement { - /// object field - #[prost(message, optional, tag = "1")] - pub content: ::core::option::Option, - /// string field - #[prost(string, tag = "2")] - pub signature: ::prost::alloc::string::String, - /// string field - #[prost(string, tag = "3")] - pub content_creator_pub_key: ::prost::alloc::string::String, - /// string field - #[prost(string, tag = "4")] - pub content_creator_address: ::prost::alloc::string::String, - /// string field - #[prost(string, tag = "5")] - pub id: ::prost::alloc::string::String, -} /// GetVersionRequest holds request from GetVersion #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -184,8 +93,8 @@ pub struct SendEndorsementsRequest { #[prost(string, tag = "1")] pub id: ::prost::alloc::string::String, /// object field - #[prost(message, optional, tag = "2")] - pub endorsement: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub endorsements: ::prost::alloc::vec::Vec, } /// SendEndorsementsResponse holds response from SendEndorsements #[allow(clippy::derive_partial_eq_without_eq)] @@ -215,8 +124,8 @@ pub mod send_endorsements_response { #[derive(Clone, PartialEq, ::prost::Message)] pub struct EndorsementResult { /// string field - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "1")] + pub ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, } /// SendOperationsRequest holds parameters to SendOperations #[allow(clippy::derive_partial_eq_without_eq)] @@ -802,6 +711,97 @@ pub mod grpc_server { const NAME: &'static str = "massa.api.v1.Grpc"; } } +/// message struct +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Slot { + /// fixed64 field + #[prost(uint64, tag = "1")] + pub period: u64, + /// float field + #[prost(float, tag = "2")] + pub thread: f32, +} +/// region Endorsement +/// message struct +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EndorsementInfo { + /// string field + #[prost(string, tag = "1")] + pub id: ::prost::alloc::string::String, + /// bool field + #[prost(bool, tag = "2")] + pub in_pool: bool, + /// string field + #[prost(string, repeated, tag = "3")] + pub in_blocks: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// bool field + #[prost(bool, tag = "4")] + pub is_final: bool, + /// object field + #[prost(message, optional, tag = "5")] + pub endorsement: ::core::option::Option, +} +/// message struct +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Endorsement { + /// object field + #[prost(message, optional, tag = "1")] + pub slot: ::core::option::Option, + /// string field + #[prost(uint32, tag = "2")] + pub index: u32, + /// string field + #[prost(string, tag = "3")] + pub endorsed_block: ::prost::alloc::string::String, +} +/// message struct +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EndorsementContent { + /// string field + #[prost(string, tag = "1")] + pub sender_public_key: ::prost::alloc::string::String, + /// object field + #[prost(message, optional, tag = "2")] + pub slot: ::core::option::Option, + /// float field + #[prost(uint32, tag = "3")] + pub index: u32, + /// string field + #[prost(string, tag = "4")] + pub endorsed_block: ::prost::alloc::string::String, +} +/// message struct +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EndorsementId { + /// string field + #[prost(string, tag = "1")] + pub value: ::prost::alloc::string::String, +} +/// message struct +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SecureShareEndorsement { + /// object field + #[prost(message, optional, tag = "1")] + pub content: ::core::option::Option, + /// string field + #[prost(string, tag = "2")] + pub signature: ::prost::alloc::string::String, + /// string field + #[prost(string, tag = "3")] + pub content_creator_pub_key: ::prost::alloc::string::String, + /// string field + #[prost(string, tag = "4")] + pub content_creator_address: ::prost::alloc::string::String, + /// string field + #[prost(string, tag = "5")] + pub id: ::prost::alloc::string::String, +} /// region Operation /// message struct #[allow(clippy::derive_partial_eq_without_eq)]