From 7a20c90b94cb621b07fbc466fd488b4687645217 Mon Sep 17 00:00:00 2001 From: Andre da Silva Date: Tue, 28 May 2024 12:33:57 -0300 Subject: [PATCH] GRPC entrypoint to download a certificate --- linera-core/src/node.rs | 12 +- linera-core/src/unit_tests/test_utils.rs | 28 ++++- linera-rpc/proto/rpc.proto | 33 +++++ linera-rpc/src/client.rs | 15 ++- linera-rpc/src/grpc/client.rs | 16 ++- linera-rpc/src/grpc/conversions.rs | 116 +++++++++++++++++- linera-rpc/src/message.rs | 32 ++++- linera-rpc/src/simple/client.rs | 11 +- linera-rpc/src/simple/server.rs | 4 +- .../tests/snapshots/format__format.yaml.snap | 20 ++- linera-service/src/grpc_proxy.rs | 20 ++- linera-service/src/schema_export.rs | 11 +- 12 files changed, 295 insertions(+), 23 deletions(-) diff --git a/linera-core/src/node.rs b/linera-core/src/node.rs index d12a8c27e4ab..d7544c8116b7 100644 --- a/linera-core/src/node.rs +++ b/linera-core/src/node.rs @@ -4,12 +4,15 @@ use futures::stream::{BoxStream, LocalBoxStream, Stream}; use linera_base::{ - crypto::CryptoError, + crypto::{CryptoError, CryptoHash}, data_types::{ArithmeticError, Blob, BlockHeight, HashedBlob}, identifiers::{BlobId, ChainId}, }; use linera_chain::{ - data_types::{BlockProposal, Certificate, HashedCertificateValue, LiteCertificate, Origin}, + data_types::{ + BlockProposal, Certificate, CertificateValue, HashedCertificateValue, LiteCertificate, + Origin, + }, ChainError, }; use linera_execution::{ @@ -82,6 +85,11 @@ pub trait LocalValidatorNode { ) -> Result; async fn download_blob(&mut self, blob_id: BlobId) -> Result; + + async fn download_certificate_value( + &mut self, + hash: CryptoHash, + ) -> Result; } /// Turn an address into a validator node. diff --git a/linera-core/src/unit_tests/test_utils.rs b/linera-core/src/unit_tests/test_utils.rs index f580e38ae73d..751fa2efd386 100644 --- a/linera-core/src/unit_tests/test_utils.rs +++ b/linera-core/src/unit_tests/test_utils.rs @@ -15,7 +15,7 @@ use linera_base::{ identifiers::{BlobId, ChainDescription, ChainId}, }; use linera_chain::data_types::{ - BlockProposal, Certificate, HashedCertificateValue, LiteCertificate, + BlockProposal, Certificate, CertificateValue, HashedCertificateValue, LiteCertificate, }; use linera_execution::{ committee::{Committee, ValidatorName}, @@ -163,6 +163,16 @@ where self.spawn_and_receive(move |validator, sender| validator.do_download_blob(blob_id, sender)) .await } + + async fn download_certificate_value( + &mut self, + hash: CryptoHash, + ) -> Result { + self.spawn_and_receive(move |validator, sender| { + validator.do_download_certificate_value(hash, sender) + }) + .await + } } impl LocalValidatorClient @@ -344,6 +354,22 @@ where .map_err(Into::into); sender.send(hashed_blob.map(|hashed_blob| hashed_blob.blob().clone())) } + + async fn do_download_certificate_value( + self, + hash: CryptoHash, + sender: oneshot::Sender>, + ) -> Result<(), Result> { + let validator = self.client.lock().await; + let certificate = validator + .state + .storage_client() + .read_hashed_certificate_value(hash) + .await + .map(Into::into) + .map_err(Into::into); + sender.send(certificate) + } } #[derive(Clone)] diff --git a/linera-rpc/proto/rpc.proto b/linera-rpc/proto/rpc.proto index 58984f55cc1d..59f1f83339a6 100644 --- a/linera-rpc/proto/rpc.proto +++ b/linera-rpc/proto/rpc.proto @@ -54,6 +54,9 @@ service ValidatorNode { // Downloads a blob. rpc DownloadBlob(BlobId) returns (Blob); + + // Downloads a certificate value. + rpc DownloadCertificateValue(CryptoHash) returns (CertificateValue); } // Information about the Linera crate version the validator is running @@ -224,6 +227,28 @@ message Certificate { bytes blobs = 7; } +message CertificateValue { + oneof inner { + ValidatedBlock validated_block = 1; + ConfirmedBlock confirmed_block = 2; + Timeout timeout = 3; + } +} + +message ValidatedBlock { + bytes executed_block = 1; +} + +message ConfirmedBlock { + bytes executed_block = 1; +} + +message Timeout { + ChainId chain_id = 1; + BlockHeight height = 2; + Epoch epoch = 3; +} + message ChainId { bytes bytes = 1; } @@ -232,6 +257,10 @@ message PublicKey { bytes bytes = 1; } +message CryptoHash { + bytes bytes = 1; +} + message Owner { bytes bytes = 1; } @@ -262,3 +291,7 @@ message ChainInfoResponse { message BlockHeight { uint64 height = 1; } + +message Epoch { + uint32 epoch = 1; +} diff --git a/linera-rpc/src/client.rs b/linera-rpc/src/client.rs index 6de7c1521c61..6370269dd9d1 100644 --- a/linera-rpc/src/client.rs +++ b/linera-rpc/src/client.rs @@ -2,11 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 use linera_base::{ + crypto::CryptoHash, data_types::{Blob, HashedBlob}, identifiers::{BlobId, ChainId}, }; use linera_chain::data_types::{ - BlockProposal, Certificate, HashedCertificateValue, LiteCertificate, + BlockProposal, Certificate, CertificateValue, HashedCertificateValue, LiteCertificate, }; #[cfg(web)] use linera_core::node::{ @@ -153,4 +154,16 @@ impl ValidatorNode for Client { Client::Simple(simple_client) => simple_client.download_blob(blob_id).await?, }) } + + async fn download_certificate_value( + &mut self, + hash: CryptoHash, + ) -> Result { + Ok(match self { + Client::Grpc(grpc_client) => grpc_client.download_certificate_value(hash).await?, + + #[cfg(with_simple_network)] + Client::Simple(simple_client) => simple_client.download_certificate_value(hash).await?, + }) + } } diff --git a/linera-rpc/src/grpc/client.rs b/linera-rpc/src/grpc/client.rs index 68c033957a68..6171f4cefb71 100644 --- a/linera-rpc/src/grpc/client.rs +++ b/linera-rpc/src/grpc/client.rs @@ -5,10 +5,11 @@ use std::{iter, time::Duration}; use futures::{future, stream, StreamExt}; use linera_base::{ + crypto::CryptoHash, data_types::{Blob, HashedBlob}, identifiers::{BlobId, ChainId}, }; -use linera_chain::data_types; +use linera_chain::data_types::{self, CertificateValue}; #[cfg(web)] use linera_core::node::{ LocalNotificationStream as NotificationStream, LocalValidatorNode as ValidatorNode, @@ -274,6 +275,19 @@ impl ValidatorNode for GrpcClient { .into_inner() .into()) } + + #[instrument(target = "grpc_client", skip_all, err, fields(address = self.address))] + async fn download_certificate_value( + &mut self, + hash: CryptoHash, + ) -> Result { + Ok(self + .client + .download_certificate_value(>::into(hash)) + .await? + .into_inner() + .try_into()?) + } } #[cfg(not(web))] diff --git a/linera-rpc/src/grpc/conversions.rs b/linera-rpc/src/grpc/conversions.rs index 504d8d498768..dd8a956a1375 100644 --- a/linera-rpc/src/grpc/conversions.rs +++ b/linera-rpc/src/grpc/conversions.rs @@ -8,14 +8,15 @@ use linera_base::{ identifiers::{BlobId, ChainId, Owner}, }; use linera_chain::data_types::{ - BlockAndRound, BlockProposal, Certificate, HashedCertificateValue, LiteCertificate, LiteValue, + BlockAndRound, BlockProposal, Certificate, CertificateValue, HashedCertificateValue, + LiteCertificate, LiteValue, }; use linera_core::{ data_types::{ChainInfoQuery, ChainInfoResponse, CrossChainRequest}, node::NodeError, worker::Notification, }; -use linera_execution::committee::ValidatorName; +use linera_execution::committee::{Epoch, ValidatorName}; use thiserror::Error; use tonic::{Code, Status}; @@ -50,6 +51,14 @@ impl From for Status { } } +impl From for NodeError { + fn from(error: GrpcProtoConversionError) -> Self { + NodeError::GrpcError { + error: error.to_string(), + } + } +} + impl From for api::CrateVersion { fn from( linera_version::CrateVersion { @@ -514,6 +523,18 @@ impl From for BlockHeight { } } +impl From for Epoch { + fn from(epoch: api::Epoch) -> Self { + Self(epoch.epoch) + } +} + +impl From for api::Epoch { + fn from(epoch: Epoch) -> Self { + Self { epoch: epoch.0 } + } +} + impl From for api::Owner { fn from(owner: Owner) -> Self { Self { @@ -546,6 +567,14 @@ impl From for api::BlobId { } } +impl TryFrom for CryptoHash { + type Error = GrpcProtoConversionError; + + fn try_from(hash: api::CryptoHash) -> Result { + Ok(CryptoHash::try_from(hash.bytes.as_slice())?) + } +} + impl From for api::Blob { fn from(blob: Blob) -> Self { Self { bytes: blob.bytes } @@ -558,6 +587,89 @@ impl From for Blob { } } +impl TryFrom for CertificateValue { + type Error = GrpcProtoConversionError; + + fn try_from(certificate: api::CertificateValue) -> Result { + use api::certificate_value::Inner; + + Ok( + match certificate + .inner + .ok_or(GrpcProtoConversionError::MissingField)? + { + Inner::ConfirmedBlock(api::ConfirmedBlock { executed_block }) => { + CertificateValue::ConfirmedBlock { + executed_block: bincode::deserialize(&executed_block)?, + } + } + Inner::ValidatedBlock(api::ValidatedBlock { executed_block }) => { + CertificateValue::ValidatedBlock { + executed_block: bincode::deserialize(&executed_block)?, + } + } + Inner::Timeout(api::Timeout { + chain_id, + height, + epoch, + }) => CertificateValue::Timeout { + chain_id: try_proto_convert(chain_id)?, + height: height.ok_or(GrpcProtoConversionError::MissingField)?.into(), + epoch: epoch.ok_or(GrpcProtoConversionError::MissingField)?.into(), + }, + }, + ) + } +} + +impl TryFrom for api::CertificateValue { + type Error = GrpcProtoConversionError; + + fn try_from(certificate: CertificateValue) -> Result { + use api::certificate_value::Inner; + + let inner = match certificate { + CertificateValue::ConfirmedBlock { executed_block } => { + Inner::ConfirmedBlock(api::ConfirmedBlock { + executed_block: bincode::serialize(&executed_block)?, + }) + } + CertificateValue::ValidatedBlock { executed_block } => { + Inner::ValidatedBlock(api::ValidatedBlock { + executed_block: bincode::serialize(&executed_block)?, + }) + } + CertificateValue::Timeout { + chain_id, + height, + epoch, + } => Inner::Timeout(api::Timeout { + chain_id: Some(chain_id.into()), + height: Some(height.into()), + epoch: Some(epoch.into()), + }), + }; + + Ok(Self { inner: Some(inner) }) + } +} + +impl TryFrom for api::CertificateValue { + type Error = GrpcProtoConversionError; + + fn try_from(hv: HashedCertificateValue) -> Result { + >::into(hv).try_into() + } +} + +impl From for api::CryptoHash { + fn from(hash: CryptoHash) -> Self { + Self { + bytes: hash.as_bytes().to_vec(), + } + } +} + #[cfg(test)] pub mod tests { use std::{borrow::Cow, fmt::Debug}; diff --git a/linera-rpc/src/message.rs b/linera-rpc/src/message.rs index b8af6cf11f7c..37f497cccb16 100644 --- a/linera-rpc/src/message.rs +++ b/linera-rpc/src/message.rs @@ -3,10 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 use linera_base::{ + crypto::CryptoHash, data_types::Blob, identifiers::{BlobId, ChainId}, }; -use linera_chain::data_types::{BlockProposal, LiteVote}; +use linera_chain::data_types::{BlockProposal, CertificateValue, LiteVote}; use linera_core::{ data_types::{ChainInfoQuery, ChainInfoResponse, CrossChainRequest}, node::NodeError, @@ -25,6 +26,7 @@ pub enum RpcMessage { LiteCertificate(Box>), ChainInfoQuery(Box), DownloadBlob(Box), + DownloadCertificateValue(Box), VersionInfoQuery, // Outbound @@ -33,6 +35,7 @@ pub enum RpcMessage { Error(Box), VersionInfoResponse(Box), DownloadBlobResponse(Box), + DownloadCertificateValueResponse(Box), // Internal to a validator CrossChainRequest(Box), @@ -57,7 +60,9 @@ impl RpcMessage { | VersionInfoQuery | VersionInfoResponse(_) | DownloadBlob(_) - | DownloadBlobResponse(_) => { + | DownloadBlobResponse(_) + | DownloadCertificateValue(_) + | DownloadCertificateValueResponse(_) => { return None; } }; @@ -71,7 +76,7 @@ impl RpcMessage { use RpcMessage::*; match self { - VersionInfoQuery | DownloadBlob(_) => true, + VersionInfoQuery | DownloadBlob(_) | DownloadCertificateValue(_) => true, BlockProposal(_) | LiteCertificate(_) | Certificate(_) @@ -81,7 +86,8 @@ impl RpcMessage { | Error(_) | ChainInfoResponse(_) | VersionInfoResponse(_) - | DownloadBlobResponse(_) => false, + | DownloadBlobResponse(_) + | DownloadCertificateValueResponse(_) => false, } } } @@ -122,6 +128,18 @@ impl TryFrom for Blob { } } +impl TryFrom for CertificateValue { + type Error = NodeError; + fn try_from(message: RpcMessage) -> Result { + use RpcMessage::*; + match message { + DownloadCertificateValueResponse(certificate) => Ok(*certificate), + Error(error) => Err(*error), + _ => Err(NodeError::UnexpectedMessage), + } + } +} + impl From for RpcMessage { fn from(block_proposal: BlockProposal) -> Self { RpcMessage::BlockProposal(Box::new(block_proposal)) @@ -181,3 +199,9 @@ impl From for RpcMessage { RpcMessage::DownloadBlobResponse(Box::new(blob)) } } + +impl From for RpcMessage { + fn from(certificate: CertificateValue) -> Self { + RpcMessage::DownloadCertificateValueResponse(Box::new(certificate)) + } +} diff --git a/linera-rpc/src/simple/client.rs b/linera-rpc/src/simple/client.rs index 171906beb3c3..ad0e22d86a68 100644 --- a/linera-rpc/src/simple/client.rs +++ b/linera-rpc/src/simple/client.rs @@ -7,11 +7,12 @@ use std::{future::Future, time::Duration}; use async_trait::async_trait; use futures::{sink::SinkExt, stream::StreamExt}; use linera_base::{ + crypto::CryptoHash, data_types::{Blob, HashedBlob}, identifiers::{BlobId, ChainId}, }; use linera_chain::data_types::{ - BlockProposal, Certificate, HashedCertificateValue, LiteCertificate, + BlockProposal, Certificate, CertificateValue, HashedCertificateValue, LiteCertificate, }; use linera_core::{ data_types::{ChainInfoQuery, ChainInfoResponse}, @@ -140,6 +141,14 @@ impl ValidatorNode for SimpleClient { self.query(RpcMessage::DownloadBlob(Box::new(blob_id))) .await } + + async fn download_certificate_value( + &mut self, + hash: CryptoHash, + ) -> Result { + self.query(RpcMessage::DownloadCertificateValue(Box::new(hash))) + .await + } } #[derive(Clone)] diff --git a/linera-rpc/src/simple/server.rs b/linera-rpc/src/simple/server.rs index 1f720a69b31a..479a844e4015 100644 --- a/linera-rpc/src/simple/server.rs +++ b/linera-rpc/src/simple/server.rs @@ -293,7 +293,9 @@ where | RpcMessage::ChainInfoResponse(_) | RpcMessage::VersionInfoResponse(_) | RpcMessage::DownloadBlob(_) - | RpcMessage::DownloadBlobResponse(_) => Err(NodeError::UnexpectedMessage), + | RpcMessage::DownloadBlobResponse(_) + | RpcMessage::DownloadCertificateValue(_) + | RpcMessage::DownloadCertificateValueResponse(_) => Err(NodeError::UnexpectedMessage), }; self.server.packets_processed += 1; diff --git a/linera-rpc/tests/snapshots/format__format.yaml.snap b/linera-rpc/tests/snapshots/format__format.yaml.snap index 0dc1bf348261..f619a80a94cd 100644 --- a/linera-rpc/tests/snapshots/format__format.yaml.snap +++ b/linera-rpc/tests/snapshots/format__format.yaml.snap @@ -769,28 +769,36 @@ RpcMessage: NEWTYPE: TYPENAME: BlobId 5: - VersionInfoQuery: UNIT + DownloadCertificateValue: + NEWTYPE: + TYPENAME: CryptoHash 6: + VersionInfoQuery: UNIT + 7: Vote: NEWTYPE: TYPENAME: LiteVote - 7: + 8: ChainInfoResponse: NEWTYPE: TYPENAME: ChainInfoResponse - 8: + 9: Error: NEWTYPE: TYPENAME: NodeError - 9: + 10: VersionInfoResponse: NEWTYPE: TYPENAME: VersionInfo - 10: + 11: DownloadBlobResponse: NEWTYPE: TYPENAME: Blob - 11: + 12: + DownloadCertificateValueResponse: + NEWTYPE: + TYPENAME: CertificateValue + 13: CrossChainRequest: NEWTYPE: TYPENAME: CrossChainRequest diff --git a/linera-service/src/grpc_proxy.rs b/linera-service/src/grpc_proxy.rs index 9c334e317406..c84b4f864842 100644 --- a/linera-service/src/grpc_proxy.rs +++ b/linera-service/src/grpc_proxy.rs @@ -27,8 +27,9 @@ use linera_rpc::{ notifier_service_server::{NotifierService, NotifierServiceServer}, validator_node_server::{ValidatorNode, ValidatorNodeServer}, validator_worker_client::ValidatorWorkerClient, - Blob, BlobId, BlockProposal, Certificate, ChainInfoQuery, ChainInfoResult, - LiteCertificate, Notification, SubscriptionRequest, VersionInfo, + Blob, BlobId, BlockProposal, Certificate, CertificateValue, ChainInfoQuery, + ChainInfoResult, CryptoHash, LiteCertificate, Notification, SubscriptionRequest, + VersionInfo, }, pool::GrpcConnectionPool, GrpcProxyable, GRPC_MAX_MESSAGE_SIZE, @@ -405,6 +406,21 @@ where .map_err(|err| Status::from_error(Box::new(err)))?; Ok(Response::new(hashed_blob.into_inner().into())) } + + #[instrument(skip_all, err(Display))] + async fn download_certificate_value( + &self, + request: Request, + ) -> Result, Status> { + let hash = request.into_inner().try_into()?; + let certificate = self + .0 + .storage + .read_hashed_certificate_value(hash) + .await + .map_err(|err| Status::from_error(Box::new(err)))?; + Ok(Response::new(certificate.try_into()?)) + } } #[async_trait] diff --git a/linera-service/src/schema_export.rs b/linera-service/src/schema_export.rs index 3ac10bf85493..69ae9d36e55c 100644 --- a/linera-service/src/schema_export.rs +++ b/linera-service/src/schema_export.rs @@ -3,12 +3,12 @@ use async_trait::async_trait; use linera_base::{ - crypto::KeyPair, + crypto::{CryptoHash, KeyPair}, data_types::{Blob, HashedBlob, Timestamp}, identifiers::{BlobId, ChainId}, }; use linera_chain::data_types::{ - BlockProposal, Certificate, HashedCertificateValue, LiteCertificate, + BlockProposal, Certificate, CertificateValue, HashedCertificateValue, LiteCertificate, }; use linera_core::{ client::ChainClient, @@ -80,6 +80,13 @@ impl ValidatorNode for DummyValidatorNode { async fn download_blob(&mut self, _: BlobId) -> Result { Err(NodeError::UnexpectedMessage) } + + async fn download_certificate_value( + &mut self, + _: CryptoHash, + ) -> Result { + Err(NodeError::UnexpectedMessage) + } } struct DummyValidatorNodeProvider;