From fc8fb81bf25cf45f6acaac9e8ecc445bf6f365e9 Mon Sep 17 00:00:00 2001 From: Andre da Silva Date: Tue, 18 Jun 2024 18:11:41 +0200 Subject: [PATCH] Endpoint to request which certificate published a blob last in a given validator --- linera-core/src/node.rs | 3 +++ linera-core/src/unit_tests/test_utils.rs | 24 +++++++++++++++++++ linera-rpc/proto/rpc.proto | 3 +++ linera-rpc/src/client.rs | 9 +++++++ linera-rpc/src/grpc/client.rs | 10 ++++++++ linera-rpc/src/message.rs | 24 +++++++++++++++++++ linera-rpc/src/simple/client.rs | 5 ++++ linera-rpc/src/simple/server.rs | 2 ++ .../tests/snapshots/format__format.yaml.snap | 24 ++++++++++++------- linera-service/src/grpc_proxy.rs | 15 ++++++++++++ linera-service/src/proxy.rs | 8 +++++++ linera-service/src/schema_export.rs | 4 ++++ 12 files changed, 123 insertions(+), 8 deletions(-) diff --git a/linera-core/src/node.rs b/linera-core/src/node.rs index 5a5855f47dd0..32118dba3871 100644 --- a/linera-core/src/node.rs +++ b/linera-core/src/node.rs @@ -89,6 +89,9 @@ pub trait LocalValidatorNode { ) -> Result; async fn download_certificate(&mut self, hash: CryptoHash) -> Result; + + /// Returns the hash of the `Certificate` that last used a blob. + async fn blob_last_used_by(&mut self, blob_id: BlobId) -> 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 b6b0da3f2875..a8a1f574c6f9 100644 --- a/linera-core/src/unit_tests/test_utils.rs +++ b/linera-core/src/unit_tests/test_utils.rs @@ -175,6 +175,13 @@ where }) .await } + + async fn blob_last_used_by(&mut self, blob_id: BlobId) -> Result { + self.spawn_and_receive(move |validator, sender| { + validator.do_blob_last_used_by(blob_id, sender) + }) + .await + } } impl LocalValidatorClient @@ -388,6 +395,23 @@ where sender.send(certificate) } + + async fn do_blob_last_used_by( + self, + blob_id: BlobId, + sender: oneshot::Sender>, + ) -> Result<(), Result> { + let validator = self.client.lock().await; + let certificate_hash = validator + .state + .storage_client() + .read_blob_state(blob_id) + .await + .map(|blob_state| blob_state.last_used_by) + .map_err(Into::into); + + sender.send(certificate_hash) + } } #[derive(Clone)] diff --git a/linera-rpc/proto/rpc.proto b/linera-rpc/proto/rpc.proto index ac3cd31c93dc..c68bef32dcf7 100644 --- a/linera-rpc/proto/rpc.proto +++ b/linera-rpc/proto/rpc.proto @@ -60,6 +60,9 @@ service ValidatorNode { // Downloads a certificate. rpc DownloadCertificate(CryptoHash) returns (Certificate); + + // Returns the hash of the `Certificate` that last used a blob. + rpc BlobLastUsedBy(BlobId) returns (CryptoHash); } // Information about the Linera crate version the validator is running diff --git a/linera-rpc/src/client.rs b/linera-rpc/src/client.rs index e745546708fc..c768db48c1fa 100644 --- a/linera-rpc/src/client.rs +++ b/linera-rpc/src/client.rs @@ -175,4 +175,13 @@ impl ValidatorNode for Client { Client::Simple(simple_client) => simple_client.download_certificate(hash).await?, }) } + + async fn blob_last_used_by(&mut self, blob_id: BlobId) -> Result { + Ok(match self { + Client::Grpc(grpc_client) => grpc_client.blob_last_used_by(blob_id).await?, + + #[cfg(with_simple_network)] + Client::Simple(simple_client) => simple_client.blob_last_used_by(blob_id).await?, + }) + } } diff --git a/linera-rpc/src/grpc/client.rs b/linera-rpc/src/grpc/client.rs index 02d2c404286c..f98ef460bc32 100644 --- a/linera-rpc/src/grpc/client.rs +++ b/linera-rpc/src/grpc/client.rs @@ -299,6 +299,16 @@ impl ValidatorNode for GrpcClient { .into_inner() .try_into()?) } + + #[instrument(target = "grpc_client", skip_all, err, fields(address = self.address))] + async fn blob_last_used_by(&mut self, blob_id: BlobId) -> Result { + Ok(self + .client + .blob_last_used_by(>::into(blob_id)) + .await? + .into_inner() + .try_into()?) + } } #[cfg(not(web))] diff --git a/linera-rpc/src/message.rs b/linera-rpc/src/message.rs index 9a1dbad24dbe..c08f6ef6c38d 100644 --- a/linera-rpc/src/message.rs +++ b/linera-rpc/src/message.rs @@ -28,6 +28,7 @@ pub enum RpcMessage { DownloadBlob(Box), DownloadCertificateValue(Box), DownloadCertificate(Box), + BlobLastUsedBy(Box), VersionInfoQuery, // Outbound @@ -38,6 +39,7 @@ pub enum RpcMessage { DownloadBlobResponse(Box), DownloadCertificateValueResponse(Box), DownloadCertificateResponse(Box), + BlobLastUsedByResponse(Box), // Internal to a validator CrossChainRequest(Box), @@ -66,6 +68,8 @@ impl RpcMessage { | DownloadCertificateValue(_) | DownloadCertificateValueResponse(_) | DownloadCertificate(_) + | BlobLastUsedBy(_) + | BlobLastUsedByResponse(_) | DownloadCertificateResponse(_) => { return None; } @@ -83,6 +87,7 @@ impl RpcMessage { VersionInfoQuery | DownloadBlob(_) | DownloadCertificateValue(_) + | BlobLastUsedBy(_) | DownloadCertificate(_) => true, BlockProposal(_) | LiteCertificate(_) @@ -95,6 +100,7 @@ impl RpcMessage { | VersionInfoResponse(_) | DownloadBlobResponse(_) | DownloadCertificateValueResponse(_) + | BlobLastUsedByResponse(_) | DownloadCertificateResponse(_) => false, } } @@ -160,6 +166,18 @@ impl TryFrom for Certificate { } } +impl TryFrom for CryptoHash { + type Error = NodeError; + fn try_from(message: RpcMessage) -> Result { + use RpcMessage::*; + match message { + BlobLastUsedByResponse(hash) => Ok(*hash), + Error(error) => Err(*error), + _ => Err(NodeError::UnexpectedMessage), + } + } +} + impl From for RpcMessage { fn from(block_proposal: BlockProposal) -> Self { RpcMessage::BlockProposal(Box::new(block_proposal)) @@ -231,3 +249,9 @@ impl From for RpcMessage { RpcMessage::DownloadCertificateResponse(Box::new(certificate)) } } + +impl From for RpcMessage { + fn from(hash: CryptoHash) -> Self { + RpcMessage::BlobLastUsedByResponse(Box::new(hash)) + } +} diff --git a/linera-rpc/src/simple/client.rs b/linera-rpc/src/simple/client.rs index e918f4c7e051..515db4a7f36e 100644 --- a/linera-rpc/src/simple/client.rs +++ b/linera-rpc/src/simple/client.rs @@ -156,6 +156,11 @@ impl ValidatorNode for SimpleClient { self.query(RpcMessage::DownloadCertificate(Box::new(hash))) .await } + + async fn blob_last_used_by(&mut self, blob_id: BlobId) -> Result { + self.query(RpcMessage::BlobLastUsedBy(Box::new(blob_id))) + .await + } } #[derive(Clone)] diff --git a/linera-rpc/src/simple/server.rs b/linera-rpc/src/simple/server.rs index 7b516d8bf195..0b3b9df8e3e3 100644 --- a/linera-rpc/src/simple/server.rs +++ b/linera-rpc/src/simple/server.rs @@ -302,6 +302,8 @@ where | RpcMessage::DownloadBlobResponse(_) | RpcMessage::DownloadCertificateValue(_) | RpcMessage::DownloadCertificateValueResponse(_) + | RpcMessage::BlobLastUsedBy(_) + | RpcMessage::BlobLastUsedByResponse(_) | RpcMessage::DownloadCertificate(_) | RpcMessage::DownloadCertificateResponse(_) => Err(NodeError::UnexpectedMessage), }; diff --git a/linera-rpc/tests/snapshots/format__format.yaml.snap b/linera-rpc/tests/snapshots/format__format.yaml.snap index d80cda440af1..19cf06ac724a 100644 --- a/linera-rpc/tests/snapshots/format__format.yaml.snap +++ b/linera-rpc/tests/snapshots/format__format.yaml.snap @@ -770,36 +770,44 @@ RpcMessage: NEWTYPE: TYPENAME: CryptoHash 7: - VersionInfoQuery: UNIT + BlobLastUsedBy: + NEWTYPE: + TYPENAME: BlobId 8: + VersionInfoQuery: UNIT + 9: Vote: NEWTYPE: TYPENAME: LiteVote - 9: + 10: ChainInfoResponse: NEWTYPE: TYPENAME: ChainInfoResponse - 10: + 11: Error: NEWTYPE: TYPENAME: NodeError - 11: + 12: VersionInfoResponse: NEWTYPE: TYPENAME: VersionInfo - 12: + 13: DownloadBlobResponse: NEWTYPE: TYPENAME: Blob - 13: + 14: DownloadCertificateValueResponse: NEWTYPE: TYPENAME: CertificateValue - 14: + 15: DownloadCertificateResponse: NEWTYPE: TYPENAME: Certificate - 15: + 16: + BlobLastUsedByResponse: + NEWTYPE: + TYPENAME: CryptoHash + 17: CrossChainRequest: NEWTYPE: TYPENAME: CrossChainRequest diff --git a/linera-service/src/grpc_proxy.rs b/linera-service/src/grpc_proxy.rs index 6f7b97f9d785..377c7517d8f8 100644 --- a/linera-service/src/grpc_proxy.rs +++ b/linera-service/src/grpc_proxy.rs @@ -441,6 +441,21 @@ where .map_err(|err| Status::from_error(Box::new(err)))?; Ok(Response::new(certificate.try_into()?)) } + + #[instrument(skip_all, err(Display))] + async fn blob_last_used_by( + &self, + request: Request, + ) -> Result, Status> { + let blob_id = request.into_inner().try_into()?; + let blob_state = self + .0 + .storage + .read_blob_state(blob_id) + .await + .map_err(|err| Status::from_error(Box::new(err)))?; + Ok(Response::new(blob_state.last_used_by.into())) + } } #[async_trait] diff --git a/linera-service/src/proxy.rs b/linera-service/src/proxy.rs index e74a27688c88..9ee2905af8b3 100644 --- a/linera-service/src/proxy.rs +++ b/linera-service/src/proxy.rs @@ -304,6 +304,13 @@ where DownloadCertificate(hash) => { Ok(Some(self.storage.read_certificate(*hash).await?.into())) } + BlobLastUsedBy(blob_id) => Ok(Some( + self.storage + .read_blob_state(*blob_id) + .await? + .last_used_by + .into(), + )), BlockProposal(_) | LiteCertificate(_) | Certificate(_) @@ -314,6 +321,7 @@ where | ChainInfoResponse(_) | VersionInfoResponse(_) | DownloadBlobResponse(_) + | BlobLastUsedByResponse(_) | DownloadCertificateValueResponse(_) | DownloadCertificateResponse(_) => { Err(anyhow::Error::from(NodeError::UnexpectedMessage)) diff --git a/linera-service/src/schema_export.rs b/linera-service/src/schema_export.rs index 848789912b43..9ef28e3dad5f 100644 --- a/linera-service/src/schema_export.rs +++ b/linera-service/src/schema_export.rs @@ -88,6 +88,10 @@ impl ValidatorNode for DummyValidatorNode { async fn download_certificate(&mut self, _: CryptoHash) -> Result { Err(NodeError::UnexpectedMessage) } + + async fn blob_last_used_by(&mut self, _: BlobId) -> Result { + Err(NodeError::UnexpectedMessage) + } } struct DummyValidatorNodeProvider;