From ee2332dd7ebc278fa8fa606359ab5363cf69fa9c Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 27 Nov 2024 11:14:59 +0000 Subject: [PATCH 01/10] RPC messages work on ConfirmedBlockCertificates --- linera-chain/src/certificate/confirmed.rs | 56 ++++++++++++++++++- linera-rpc/src/message.rs | 28 +++------- linera-rpc/src/simple/client.rs | 18 ++---- .../tests/snapshots/format__format.yaml.snap | 15 ++++- 4 files changed, 79 insertions(+), 38 deletions(-) diff --git a/linera-chain/src/certificate/confirmed.rs b/linera-chain/src/certificate/confirmed.rs index a559d08ecb0..39ef9d28d68 100644 --- a/linera-chain/src/certificate/confirmed.rs +++ b/linera-chain/src/certificate/confirmed.rs @@ -2,8 +2,13 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use linera_base::identifiers::{BlobId, ChainId, MessageId}; -use linera_execution::committee::Epoch; +use linera_base::{ + crypto::Signature, + data_types::Round, + identifiers::{BlobId, ChainId, MessageId}, +}; +use linera_execution::committee::{Epoch, ValidatorName}; +use serde::{Deserialize, Deserializer, Serialize}; use super::{ generic::GenericCertificate, hashed::Hashed, Certificate, CertificateValue, @@ -11,7 +16,7 @@ use super::{ }; use crate::{ block::{ConfirmedBlock, ConversionError, ValidatedBlock}, - data_types::{ExecutedBlock, Medium, MessageBundle}, + data_types::{is_strictly_ordered, ExecutedBlock, Medium, MessageBundle}, }; impl GenericCertificate { @@ -82,3 +87,48 @@ impl From> for Certificate { Certificate::new(value, round, signatures) } } + +impl Serialize for GenericCertificate { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + #[derive(Debug, Serialize)] + #[serde(rename = "ConfirmedBlockCertificate")] + struct CertificateHelper<'a> { + value: &'a ConfirmedBlock, + round: Round, + signatures: &'a Vec<(ValidatorName, Signature)>, + } + + let helper = CertificateHelper { + value: self.inner(), + round: self.round, + signatures: self.signatures(), + }; + + helper.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for GenericCertificate { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Debug, Deserialize)] + #[serde(rename = "ConfirmedBlockCertificate")] + struct CertificateHelper { + value: Hashed, + round: Round, + signatures: Vec<(ValidatorName, Signature)>, + } + + let helper: CertificateHelper = Deserialize::deserialize(deserializer)?; + if !is_strictly_ordered(&helper.signatures) { + Err(serde::de::Error::custom("Vector is not strictly sorted")) + } else { + Ok(Self::new(helper.value, helper.round, helper.signatures)) + } + } +} diff --git a/linera-rpc/src/message.rs b/linera-rpc/src/message.rs index cc12bc6a535..3cb87325779 100644 --- a/linera-rpc/src/message.rs +++ b/linera-rpc/src/message.rs @@ -9,7 +9,7 @@ use linera_base::{ }; use linera_chain::{ data_types::{BlockProposal, LiteVote}, - types::{Certificate, ConfirmedBlock, ConfirmedBlockCertificate}, + types::{ConfirmedBlock, ConfirmedBlockCertificate}, }; use linera_core::{ data_types::{ChainInfoQuery, ChainInfoResponse, CrossChainRequest}, @@ -45,8 +45,8 @@ pub enum RpcMessage { GenesisConfigHashResponse(Box), DownloadBlobContentResponse(Box), DownloadConfirmedBlockResponse(Box), - DownloadCertificateResponse(Box), - DownloadCertificatesResponse(Box>), + DownloadCertificateResponse(Box), + DownloadCertificatesResponse(Box>), BlobLastUsedByResponse(Box), MissingBlobIdsResponse(Box>), @@ -171,7 +171,7 @@ impl TryFrom for ConfirmedBlock { } } -impl TryFrom for Certificate { +impl TryFrom for ConfirmedBlockCertificate { type Error = NodeError; fn try_from(message: RpcMessage) -> Result { match message { @@ -182,7 +182,7 @@ impl TryFrom for Certificate { } } -impl TryFrom for Vec { +impl TryFrom for Vec { type Error = NodeError; fn try_from(message: RpcMessage) -> Result { match message { @@ -288,28 +288,14 @@ impl From for RpcMessage { } } -impl From for RpcMessage { - fn from(certificate: Certificate) -> Self { - RpcMessage::DownloadCertificateResponse(Box::new(certificate)) - } -} - impl From for RpcMessage { fn from(certificate: ConfirmedBlockCertificate) -> Self { - RpcMessage::DownloadCertificateResponse(Box::new(certificate.into())) - } -} - -impl From> for RpcMessage { - fn from(certificates: Vec) -> Self { - RpcMessage::DownloadCertificatesResponse(Box::new(certificates)) + RpcMessage::DownloadCertificateResponse(Box::new(certificate)) } } impl From> for RpcMessage { fn from(certificates: Vec) -> Self { - RpcMessage::DownloadCertificatesResponse(Box::new( - certificates.into_iter().map(|c| c.into()).collect(), - )) + RpcMessage::DownloadCertificatesResponse(Box::new(certificates)) } } diff --git a/linera-rpc/src/simple/client.rs b/linera-rpc/src/simple/client.rs index 1e2559d4019..8dbac0047b7 100644 --- a/linera-rpc/src/simple/client.rs +++ b/linera-rpc/src/simple/client.rs @@ -149,24 +149,18 @@ impl ValidatorNode for SimpleClient { &self, hash: CryptoHash, ) -> Result { - self.query::(RpcMessage::DownloadCertificate(Box::new(hash))) - .await? - .try_into() - .map_err(|_| NodeError::UnexpectedCertificateValue) + self.query::(RpcMessage::DownloadCertificate(Box::new(hash))) + .await } async fn download_certificates( &self, hashes: Vec, ) -> Result, NodeError> { - self.query::>(RpcMessage::DownloadCertificates(Box::new(hashes))) - .await? - .into_iter() - .map(|cert| { - cert.try_into() - .map_err(|_| NodeError::UnexpectedCertificateValue) - }) - .collect() + self.query::>(RpcMessage::DownloadCertificates(Box::new( + hashes, + ))) + .await } async fn blob_last_used_by(&self, blob_id: BlobId) -> Result { diff --git a/linera-rpc/tests/snapshots/format__format.yaml.snap b/linera-rpc/tests/snapshots/format__format.yaml.snap index ac365d25eb9..a0b2a705b66 100644 --- a/linera-rpc/tests/snapshots/format__format.yaml.snap +++ b/linera-rpc/tests/snapshots/format__format.yaml.snap @@ -342,6 +342,17 @@ ConfirmedBlock: STRUCT: - executed_block: TYPENAME: ExecutedBlock +ConfirmedBlockCertificate: + STRUCT: + - value: + TYPENAME: ConfirmedBlock + - round: + TYPENAME: Round + - signatures: + SEQ: + TUPLE: + - TYPENAME: ValidatorName + - TYPENAME: Signature CrateVersion: STRUCT: - major: U32 @@ -835,12 +846,12 @@ RpcMessage: 19: DownloadCertificateResponse: NEWTYPE: - TYPENAME: Certificate + TYPENAME: ConfirmedBlockCertificate 20: DownloadCertificatesResponse: NEWTYPE: SEQ: - TYPENAME: Certificate + TYPENAME: ConfirmedBlockCertificate 21: BlobLastUsedByResponse: NEWTYPE: From 2d41fa9789feef9f50eb5188a2aa4e7078dfb038 Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 27 Nov 2024 16:57:08 +0000 Subject: [PATCH 02/10] RM singular DownloadCertificate RPC query. --- linera-core/src/node.rs | 4 ++ linera-rpc/src/message.rs | 23 ------ linera-rpc/src/simple/client.rs | 11 ++- linera-rpc/src/simple/server.rs | 2 - .../tests/snapshots/format__format.yaml.snap | 70 +++++++++---------- linera-service/src/proxy/main.rs | 4 -- 6 files changed, 46 insertions(+), 68 deletions(-) diff --git a/linera-core/src/node.rs b/linera-core/src/node.rs index e2523d9a503..fd442bc44df 100644 --- a/linera-core/src/node.rs +++ b/linera-core/src/node.rs @@ -188,6 +188,10 @@ pub enum NodeError { #[error("We don't have the value for the certificate.")] MissingCertificateValue, + #[error("Reponse doesn't contain requested ceritifcates: {missing}")] + // NOTE: Can't use Vec b/c we cannot write Display impl for it. + MissingCertificates { missing: String }, + #[error("Validator's response to block proposal failed to include a vote")] MissingVoteInValidatorResponse, diff --git a/linera-rpc/src/message.rs b/linera-rpc/src/message.rs index 3cb87325779..be96fa0ae07 100644 --- a/linera-rpc/src/message.rs +++ b/linera-rpc/src/message.rs @@ -30,7 +30,6 @@ pub enum RpcMessage { ChainInfoQuery(Box), DownloadBlobContent(Box), DownloadConfirmedBlock(Box), - DownloadCertificate(Box), DownloadCertificates(Box>), BlobLastUsedBy(Box), MissingBlobIds(Box>), @@ -45,7 +44,6 @@ pub enum RpcMessage { GenesisConfigHashResponse(Box), DownloadBlobContentResponse(Box), DownloadConfirmedBlockResponse(Box), - DownloadCertificateResponse(Box), DownloadCertificatesResponse(Box>), BlobLastUsedByResponse(Box), MissingBlobIdsResponse(Box>), @@ -78,13 +76,11 @@ impl RpcMessage { | DownloadBlobContentResponse(_) | DownloadConfirmedBlock(_) | DownloadConfirmedBlockResponse(_) - | DownloadCertificate(_) | DownloadCertificates(_) | BlobLastUsedBy(_) | BlobLastUsedByResponse(_) | MissingBlobIds(_) | MissingBlobIdsResponse(_) - | DownloadCertificateResponse(_) | DownloadCertificatesResponse(_) => { return None; } @@ -105,7 +101,6 @@ impl RpcMessage { | DownloadConfirmedBlock(_) | BlobLastUsedBy(_) | MissingBlobIds(_) - | DownloadCertificate(_) | DownloadCertificates(_) => true, BlockProposal(_) | LiteCertificate(_) @@ -121,7 +116,6 @@ impl RpcMessage { | DownloadConfirmedBlockResponse(_) | BlobLastUsedByResponse(_) | MissingBlobIdsResponse(_) - | DownloadCertificateResponse(_) | DownloadCertificatesResponse(_) => false, } } @@ -171,17 +165,6 @@ impl TryFrom for ConfirmedBlock { } } -impl TryFrom for ConfirmedBlockCertificate { - type Error = NodeError; - fn try_from(message: RpcMessage) -> Result { - match message { - RpcMessage::DownloadCertificateResponse(certificate) => Ok(*certificate), - RpcMessage::Error(error) => Err(*error), - _ => Err(NodeError::UnexpectedMessage), - } - } -} - impl TryFrom for Vec { type Error = NodeError; fn try_from(message: RpcMessage) -> Result { @@ -288,12 +271,6 @@ impl From for RpcMessage { } } -impl From for RpcMessage { - fn from(certificate: ConfirmedBlockCertificate) -> Self { - RpcMessage::DownloadCertificateResponse(Box::new(certificate)) - } -} - impl From> for RpcMessage { fn from(certificates: Vec) -> Self { RpcMessage::DownloadCertificatesResponse(Box::new(certificates)) diff --git a/linera-rpc/src/simple/client.rs b/linera-rpc/src/simple/client.rs index 8dbac0047b7..58e472d7657 100644 --- a/linera-rpc/src/simple/client.rs +++ b/linera-rpc/src/simple/client.rs @@ -149,8 +149,15 @@ impl ValidatorNode for SimpleClient { &self, hash: CryptoHash, ) -> Result { - self.query::(RpcMessage::DownloadCertificate(Box::new(hash))) - .await + let response = self.download_certificates(vec![hash]).await?; + if response.len() != 1 { + Err(NodeError::MissingCertificates { + missing: hash.to_string(), + }) + } else { + // UNWRAP: Safe b/c we just checked if len != 1. + Ok(response.into_iter().next().unwrap()) + } } async fn download_certificates( diff --git a/linera-rpc/src/simple/server.rs b/linera-rpc/src/simple/server.rs index 3be052c5f60..3d7d41c8105 100644 --- a/linera-rpc/src/simple/server.rs +++ b/linera-rpc/src/simple/server.rs @@ -310,9 +310,7 @@ where | RpcMessage::BlobLastUsedByResponse(_) | RpcMessage::MissingBlobIds(_) | RpcMessage::MissingBlobIdsResponse(_) - | RpcMessage::DownloadCertificate(_) | RpcMessage::DownloadCertificates(_) - | RpcMessage::DownloadCertificateResponse(_) | RpcMessage::DownloadCertificatesResponse(_) => Err(NodeError::UnexpectedMessage), }; diff --git a/linera-rpc/tests/snapshots/format__format.yaml.snap b/linera-rpc/tests/snapshots/format__format.yaml.snap index a0b2a705b66..97ef9ee41a7 100644 --- a/linera-rpc/tests/snapshots/format__format.yaml.snap +++ b/linera-rpc/tests/snapshots/format__format.yaml.snap @@ -570,48 +570,52 @@ NodeError: 8: MissingCertificateValue: UNIT 9: - MissingVoteInValidatorResponse: UNIT + MissingCertificates: + STRUCT: + - missing: STR 10: + MissingVoteInValidatorResponse: UNIT + 11: InactiveLocalChain: NEWTYPE: TYPENAME: ChainId - 11: - InvalidChainInfoResponse: UNIT 12: - UnexpectedCertificateValue: UNIT + InvalidChainInfoResponse: UNIT 13: - InvalidDecoding: UNIT + UnexpectedCertificateValue: UNIT 14: - UnexpectedMessage: UNIT + InvalidDecoding: UNIT 15: + UnexpectedMessage: UNIT + 16: GrpcError: STRUCT: - error: STR - 16: + 17: ClientIoError: STRUCT: - error: STR - 17: + 18: CannotResolveValidatorAddress: STRUCT: - address: STR - 18: + 19: SubscriptionError: STRUCT: - transport: STR - 19: + 20: SubscriptionFailed: STRUCT: - status: STR - 20: + 21: InvalidCertificateForBlob: NEWTYPE: TYPENAME: BlobId - 21: - DuplicatesInBlobsNotFound: UNIT 22: - UnexpectedEntriesInBlobsNotFound: UNIT + DuplicatesInBlobsNotFound: UNIT 23: + UnexpectedEntriesInBlobsNotFound: UNIT + 24: ResponseHandlingError: STRUCT: - error: STR @@ -794,74 +798,66 @@ RpcMessage: NEWTYPE: TYPENAME: CryptoHash 6: - DownloadCertificate: - NEWTYPE: - TYPENAME: CryptoHash - 7: DownloadCertificates: NEWTYPE: SEQ: TYPENAME: CryptoHash - 8: + 7: BlobLastUsedBy: NEWTYPE: TYPENAME: BlobId - 9: + 8: MissingBlobIds: NEWTYPE: SEQ: TYPENAME: BlobId - 10: + 9: VersionInfoQuery: UNIT - 11: + 10: GenesisConfigHashQuery: UNIT - 12: + 11: Vote: NEWTYPE: TYPENAME: LiteVote - 13: + 12: ChainInfoResponse: NEWTYPE: TYPENAME: ChainInfoResponse - 14: + 13: Error: NEWTYPE: TYPENAME: NodeError - 15: + 14: VersionInfoResponse: NEWTYPE: TYPENAME: VersionInfo - 16: + 15: GenesisConfigHashResponse: NEWTYPE: TYPENAME: CryptoHash - 17: + 16: DownloadBlobContentResponse: NEWTYPE: TYPENAME: BlobContent - 18: + 17: DownloadConfirmedBlockResponse: NEWTYPE: TYPENAME: ConfirmedBlock - 19: - DownloadCertificateResponse: - NEWTYPE: - TYPENAME: ConfirmedBlockCertificate - 20: + 18: DownloadCertificatesResponse: NEWTYPE: SEQ: TYPENAME: ConfirmedBlockCertificate - 21: + 19: BlobLastUsedByResponse: NEWTYPE: TYPENAME: CryptoHash - 22: + 20: MissingBlobIdsResponse: NEWTYPE: SEQ: TYPENAME: BlobId - 23: + 21: CrossChainRequest: NEWTYPE: TYPENAME: CrossChainRequest diff --git a/linera-service/src/proxy/main.rs b/linera-service/src/proxy/main.rs index b12df2ab8e6..f0973c9140d 100644 --- a/linera-service/src/proxy/main.rs +++ b/linera-service/src/proxy/main.rs @@ -312,9 +312,6 @@ where .into_inner(), )))) } - DownloadCertificate(hash) => { - Ok(Some(self.storage.read_certificate(*hash).await?.into())) - } DownloadCertificates(hashes) => { Ok(Some(self.storage.read_certificates(*hashes).await?.into())) } @@ -338,7 +335,6 @@ where | BlobLastUsedByResponse(_) | MissingBlobIdsResponse(_) | DownloadConfirmedBlockResponse(_) - | DownloadCertificateResponse(_) | DownloadCertificatesResponse(_) => { Err(anyhow::Error::from(NodeError::UnexpectedMessage)) } From fc807502258e81b1eabc03d33e64664d5aaeed43 Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 27 Nov 2024 17:34:03 +0000 Subject: [PATCH 03/10] Ensure that we get correct number of certs in resposen. --- linera-rpc/src/grpc/client.rs | 9 ++++++++ linera-rpc/src/simple/client.rs | 40 ++++++++++++++++++++++----------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/linera-rpc/src/grpc/client.rs b/linera-rpc/src/grpc/client.rs index a7340f4d9cc..b473452cfe2 100644 --- a/linera-rpc/src/grpc/client.rs +++ b/linera-rpc/src/grpc/client.rs @@ -364,6 +364,15 @@ impl ValidatorNode for GrpcClient { missing_hashes = missing_hashes[received.len()..].to_vec(); certs_collected.append(&mut received); } + if !missing_hashes.is_empty() { + return Err(NodeError::MissingCertificates { + missing: missing_hashes + .into_iter() + .map(|hash| hash.to_string()) + .collect::>() + .join(", "), + }); + } Ok(certs_collected) } diff --git a/linera-rpc/src/simple/client.rs b/linera-rpc/src/simple/client.rs index 58e472d7657..95bd2fc5f2b 100644 --- a/linera-rpc/src/simple/client.rs +++ b/linera-rpc/src/simple/client.rs @@ -149,25 +149,39 @@ impl ValidatorNode for SimpleClient { &self, hash: CryptoHash, ) -> Result { - let response = self.download_certificates(vec![hash]).await?; - if response.len() != 1 { - Err(NodeError::MissingCertificates { - missing: hash.to_string(), - }) - } else { - // UNWRAP: Safe b/c we just checked if len != 1. - Ok(response.into_iter().next().unwrap()) - } + Ok(self + .download_certificates(vec![hash]) + .await? + .into_iter() + .next() + .unwrap()) // UNWRAP: We know there is exactly one certificate, otherwise we would have an error. } async fn download_certificates( &self, hashes: Vec, ) -> Result, NodeError> { - self.query::>(RpcMessage::DownloadCertificates(Box::new( - hashes, - ))) - .await + let certificates = self + .query::>(RpcMessage::DownloadCertificates(Box::new( + hashes.clone(), + ))) + .await?; + + if certificates.len() != hashes.len() { + let missing_hashes: Vec = hashes + .into_iter() + .filter(|hash| !certificates.iter().any(|cert| cert.hash() == *hash)) + .collect(); + Err(NodeError::MissingCertificates { + missing: missing_hashes + .iter() + .map(|hash| hash.to_string()) + .collect::>() + .join(", "), + }) + } else { + Ok(certificates) + } } async fn blob_last_used_by(&self, blob_id: BlobId) -> Result { From 78edcaeb33fee655e9ba3a9f8e1a6d049028975d Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 27 Nov 2024 17:39:56 +0000 Subject: [PATCH 04/10] Don't Box Vectors, --- linera-rpc/src/message.rs | 10 +++++----- linera-rpc/src/simple/client.rs | 4 ++-- linera-service/src/proxy/main.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/linera-rpc/src/message.rs b/linera-rpc/src/message.rs index be96fa0ae07..4f3c631c367 100644 --- a/linera-rpc/src/message.rs +++ b/linera-rpc/src/message.rs @@ -30,7 +30,7 @@ pub enum RpcMessage { ChainInfoQuery(Box), DownloadBlobContent(Box), DownloadConfirmedBlock(Box), - DownloadCertificates(Box>), + DownloadCertificates(Vec), BlobLastUsedBy(Box), MissingBlobIds(Box>), VersionInfoQuery, @@ -44,7 +44,7 @@ pub enum RpcMessage { GenesisConfigHashResponse(Box), DownloadBlobContentResponse(Box), DownloadConfirmedBlockResponse(Box), - DownloadCertificatesResponse(Box>), + DownloadCertificatesResponse(Vec), BlobLastUsedByResponse(Box), MissingBlobIdsResponse(Box>), @@ -169,7 +169,7 @@ impl TryFrom for Vec { type Error = NodeError; fn try_from(message: RpcMessage) -> Result { match message { - RpcMessage::DownloadCertificatesResponse(certificates) => Ok(*certificates), + RpcMessage::DownloadCertificatesResponse(certificates) => Ok(certificates), RpcMessage::Error(error) => Err(*error), _ => Err(NodeError::UnexpectedMessage), } @@ -219,7 +219,7 @@ impl From for RpcMessage { impl From> for RpcMessage { fn from(hashes: Vec) -> Self { - RpcMessage::DownloadCertificates(Box::new(hashes)) + RpcMessage::DownloadCertificates(hashes) } } @@ -273,6 +273,6 @@ impl From for RpcMessage { impl From> for RpcMessage { fn from(certificates: Vec) -> Self { - RpcMessage::DownloadCertificatesResponse(Box::new(certificates)) + RpcMessage::DownloadCertificatesResponse(certificates) } } diff --git a/linera-rpc/src/simple/client.rs b/linera-rpc/src/simple/client.rs index 95bd2fc5f2b..987bc106f26 100644 --- a/linera-rpc/src/simple/client.rs +++ b/linera-rpc/src/simple/client.rs @@ -162,9 +162,9 @@ impl ValidatorNode for SimpleClient { hashes: Vec, ) -> Result, NodeError> { let certificates = self - .query::>(RpcMessage::DownloadCertificates(Box::new( + .query::>(RpcMessage::DownloadCertificates( hashes.clone(), - ))) + )) .await?; if certificates.len() != hashes.len() { diff --git a/linera-service/src/proxy/main.rs b/linera-service/src/proxy/main.rs index f0973c9140d..9280b04d516 100644 --- a/linera-service/src/proxy/main.rs +++ b/linera-service/src/proxy/main.rs @@ -313,7 +313,7 @@ where )))) } DownloadCertificates(hashes) => { - Ok(Some(self.storage.read_certificates(*hashes).await?.into())) + Ok(Some(self.storage.read_certificates(hashes).await?.into())) } BlobLastUsedBy(blob_id) => Ok(Some(RpcMessage::BlobLastUsedByResponse(Box::new( self.storage.read_blob_state(*blob_id).await?.last_used_by, From ee2cdee9adadadfa6a77aa6747d4dafb4a249c1b Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 27 Nov 2024 18:04:53 +0000 Subject: [PATCH 05/10] RM unnecessary conversion --- linera-core/src/client/mod.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/linera-core/src/client/mod.rs b/linera-core/src/client/mod.rs index 737168acdea..ba05feef2e1 100644 --- a/linera-core/src/client/mod.rs +++ b/linera-core/src/client/mod.rs @@ -1693,13 +1693,7 @@ where let certificates: Vec = remote_node .download_certificates(info.requested_sent_certificate_hashes) - .await? - .into_iter() - .map(|c| { - ConfirmedBlockCertificate::try_from(c) - .map_err(|_| NodeError::InvalidChainInfoResponse) - }) - .collect::>()?; + .await?; if !certificates.is_empty() && self From 903a96ff2ca3b7eda91aa9bc9611faa5f2848648 Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 27 Nov 2024 18:32:44 +0000 Subject: [PATCH 06/10] Error contains CryptoHash type --- linera-core/src/node.rs | 5 ++--- linera-rpc/src/grpc/client.rs | 8 +------- linera-rpc/src/simple/client.rs | 8 +------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/linera-core/src/node.rs b/linera-core/src/node.rs index fd442bc44df..e2c55b67dcc 100644 --- a/linera-core/src/node.rs +++ b/linera-core/src/node.rs @@ -188,9 +188,8 @@ pub enum NodeError { #[error("We don't have the value for the certificate.")] MissingCertificateValue, - #[error("Reponse doesn't contain requested ceritifcates: {missing}")] - // NOTE: Can't use Vec b/c we cannot write Display impl for it. - MissingCertificates { missing: String }, + #[error("Reponse doesn't contain requested ceritifcates: {0:?}")] + MissingCertificates(Vec), #[error("Validator's response to block proposal failed to include a vote")] MissingVoteInValidatorResponse, diff --git a/linera-rpc/src/grpc/client.rs b/linera-rpc/src/grpc/client.rs index b473452cfe2..a6edcbbded4 100644 --- a/linera-rpc/src/grpc/client.rs +++ b/linera-rpc/src/grpc/client.rs @@ -365,13 +365,7 @@ impl ValidatorNode for GrpcClient { certs_collected.append(&mut received); } if !missing_hashes.is_empty() { - return Err(NodeError::MissingCertificates { - missing: missing_hashes - .into_iter() - .map(|hash| hash.to_string()) - .collect::>() - .join(", "), - }); + return Err(NodeError::MissingCertificates(missing_hashes)); } Ok(certs_collected) } diff --git a/linera-rpc/src/simple/client.rs b/linera-rpc/src/simple/client.rs index 987bc106f26..a6dd9cd24a9 100644 --- a/linera-rpc/src/simple/client.rs +++ b/linera-rpc/src/simple/client.rs @@ -172,13 +172,7 @@ impl ValidatorNode for SimpleClient { .into_iter() .filter(|hash| !certificates.iter().any(|cert| cert.hash() == *hash)) .collect(); - Err(NodeError::MissingCertificates { - missing: missing_hashes - .iter() - .map(|hash| hash.to_string()) - .collect::>() - .join(", "), - }) + Err(NodeError::MissingCertificates(missing_hashes)) } else { Ok(certificates) } From 13f22b8c7f8e0e7f407ef2077457e000e243aa49 Mon Sep 17 00:00:00 2001 From: deuszx Date: Thu, 28 Nov 2024 13:42:00 +0000 Subject: [PATCH 07/10] Map ConfirmedBlockCertificate to Certificate before serializing --- linera-chain/src/certificate/confirmed.rs | 44 +++++------------------ 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/linera-chain/src/certificate/confirmed.rs b/linera-chain/src/certificate/confirmed.rs index 39ef9d28d68..271ac9ec9d8 100644 --- a/linera-chain/src/certificate/confirmed.rs +++ b/linera-chain/src/certificate/confirmed.rs @@ -2,12 +2,8 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use linera_base::{ - crypto::Signature, - data_types::Round, - identifiers::{BlobId, ChainId, MessageId}, -}; -use linera_execution::committee::{Epoch, ValidatorName}; +use linera_base::identifiers::{BlobId, ChainId, MessageId}; +use linera_execution::committee::Epoch; use serde::{Deserialize, Deserializer, Serialize}; use super::{ @@ -16,7 +12,7 @@ use super::{ }; use crate::{ block::{ConfirmedBlock, ConversionError, ValidatedBlock}, - data_types::{is_strictly_ordered, ExecutedBlock, Medium, MessageBundle}, + data_types::{ExecutedBlock, Medium, MessageBundle}, }; impl GenericCertificate { @@ -93,21 +89,8 @@ impl Serialize for GenericCertificate { where S: serde::Serializer, { - #[derive(Debug, Serialize)] - #[serde(rename = "ConfirmedBlockCertificate")] - struct CertificateHelper<'a> { - value: &'a ConfirmedBlock, - round: Round, - signatures: &'a Vec<(ValidatorName, Signature)>, - } - - let helper = CertificateHelper { - value: self.inner(), - round: self.round, - signatures: self.signatures(), - }; - - helper.serialize(serializer) + let certificate: Certificate = self.clone().into(); + certificate.serialize(serializer) } } @@ -116,19 +99,10 @@ impl<'de> Deserialize<'de> for GenericCertificate { where D: Deserializer<'de>, { - #[derive(Debug, Deserialize)] - #[serde(rename = "ConfirmedBlockCertificate")] - struct CertificateHelper { - value: Hashed, - round: Round, - signatures: Vec<(ValidatorName, Signature)>, - } - - let helper: CertificateHelper = Deserialize::deserialize(deserializer)?; - if !is_strictly_ordered(&helper.signatures) { - Err(serde::de::Error::custom("Vector is not strictly sorted")) - } else { - Ok(Self::new(helper.value, helper.round, helper.signatures)) + let cert = Certificate::deserialize(deserializer); + match cert { + Ok(cert) => Ok(cert.try_into().unwrap()), + Err(e) => Err(e), } } } From c0b0f414c7db092b02c3de220988ed92b71e7487 Mon Sep 17 00:00:00 2001 From: deuszx <95355183+deuszx@users.noreply.github.com> Date: Sat, 30 Nov 2024 18:18:04 +0000 Subject: [PATCH 08/10] Obliterate CertificateValue for real this time (#2988) * Remove CertificateValue and change Certificate encoding. * Review part 1 * Update linera-chain/src/certificate/mod.rs Co-authored-by: Andreas Fackler Signed-off-by: deuszx <95355183+deuszx@users.noreply.github.com> --------- Signed-off-by: deuszx <95355183+deuszx@users.noreply.github.com> Co-authored-by: Andreas Fackler --- examples/hex-game/tests/hex_game.rs | 4 +- linera-chain/src/block.rs | 75 ++----- linera-chain/src/certificate/confirmed.rs | 74 +++---- linera-chain/src/certificate/mod.rs | 167 ++++----------- linera-chain/src/certificate/timeout.rs | 42 ++-- linera-chain/src/certificate/validated.rs | 48 ++--- linera-chain/src/certificate/value.rs | 200 ------------------ linera-chain/src/manager.rs | 19 +- linera-chain/src/unit_tests/chain_tests.rs | 5 +- .../src/unit_tests/data_types_tests.rs | 25 ++- .../chain_worker/state/temporary_changes.rs | 4 +- linera-core/src/client/mod.rs | 26 +-- .../src/unit_tests/wasm_worker_tests.rs | 14 +- linera-core/src/unit_tests/worker_tests.rs | 102 +++++---- linera-core/src/updater.rs | 12 +- linera-core/src/worker.rs | 37 +--- linera-indexer/lib/src/indexer.rs | 13 +- linera-indexer/lib/src/plugin.rs | 4 +- linera-indexer/lib/src/service.rs | 4 +- linera-indexer/plugins/src/operations.rs | 18 +- linera-rpc/proto/rpc.proto | 15 ++ linera-rpc/src/grpc/conversions.rs | 95 ++++++--- linera-rpc/src/message.rs | 2 +- linera-rpc/tests/format.rs | 7 +- .../tests/snapshots/format__format.yaml.snap | 26 +-- linera-sdk/src/test/block.rs | 17 +- linera-sdk/src/test/chain.rs | 29 ++- linera-sdk/src/test/validator.rs | 5 +- linera-service-graphql-client/src/service.rs | 9 +- linera-service/src/linera/main.rs | 4 +- linera-service/src/proxy/grpc.rs | 8 +- 31 files changed, 411 insertions(+), 699 deletions(-) delete mode 100644 linera-chain/src/certificate/value.rs diff --git a/examples/hex-game/tests/hex_game.rs b/examples/hex-game/tests/hex_game.rs index 75aa5164772..efe3209e4c6 100644 --- a/examples/hex-game/tests/hex_game.rs +++ b/examples/hex-game/tests/hex_game.rs @@ -31,7 +31,7 @@ async fn hex_game() { }) .await; - let executed_block = certificate.inner().executed_block().unwrap(); + let executed_block = certificate.inner().executed_block(); let message_id = executed_block.message_id_for_operation(0, 0).unwrap(); let description = ChainDescription::Child(message_id); let mut chain = ActiveChain::new(key_pair1.copy(), description, validator); @@ -105,7 +105,7 @@ async fn hex_game_clock() { }) .await; - let executed_block = certificate.inner().executed_block().unwrap(); + let executed_block = certificate.inner().executed_block(); let message_id = executed_block.message_id_for_operation(0, 0).unwrap(); let description = ChainDescription::Child(message_id); let mut chain = ActiveChain::new(key_pair1.copy(), description, validator.clone()); diff --git a/linera-chain/src/block.rs b/linera-chain/src/block.rs index 8ff820681f1..a6651f2afeb 100644 --- a/linera-chain/src/block.rs +++ b/linera-chain/src/block.rs @@ -13,11 +13,7 @@ use linera_execution::committee::Epoch; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::{ - data_types::ExecutedBlock, - types::{CertificateValue, Hashed, HashedCertificateValue}, - ChainError, -}; +use crate::{data_types::ExecutedBlock, types::Hashed, ChainError}; /// Wrapper around an `ExecutedBlock` that has been validated. #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)] @@ -32,7 +28,7 @@ impl ValidatedBlock { } /// Returns a reference to the `ExecutedBlock` contained in this `ValidatedBlock`. - pub fn inner(&self) -> &ExecutedBlock { + pub fn executed_block(&self) -> &ExecutedBlock { &self.executed_block } @@ -48,20 +44,6 @@ impl ValidatedBlock { impl BcsHashable for ValidatedBlock {} -impl TryFrom for Hashed { - type Error = ConversionError; - - fn try_from(value: HashedCertificateValue) -> Result { - let hash = value.hash(); - match value.into_inner() { - CertificateValue::ValidatedBlock(validated) => { - Ok(Hashed::unchecked_new(validated, hash)) - } - _ => Err(ConversionError::ValidatedBlock), - } - } -} - /// Wrapper around an `ExecutedBlock` that has been confirmed. #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)] pub struct ConfirmedBlock { @@ -94,29 +76,9 @@ impl Hashed { } } -impl TryFrom for Hashed { - type Error = ConversionError; - - fn try_from(value: HashedCertificateValue) -> Result { - let hash = value.hash(); - match value.into_inner() { - CertificateValue::ConfirmedBlock(confirmed) => { - Ok(Hashed::unchecked_new(confirmed, hash)) - } - _ => Err(ConversionError::ConfirmedBlock), - } - } -} - impl BcsHashable for ConfirmedBlock {} impl ConfirmedBlock { - #[cfg(not(feature = "benchmark"))] - pub(super) fn new(executed_block: ExecutedBlock) -> Self { - Self { executed_block } - } - - #[cfg(feature = "benchmark")] pub fn new(executed_block: ExecutedBlock) -> Self { Self { executed_block } } @@ -140,6 +102,14 @@ impl ConfirmedBlock { self.executed_block } + pub fn chain_id(&self) -> ChainId { + self.executed_block.block.chain_id + } + + pub fn height(&self) -> BlockHeight { + self.executed_block.block.height + } + pub fn to_log_str(&self) -> &'static str { "confirmed_block" } @@ -189,27 +159,20 @@ impl Timeout { } } -impl TryFrom for Hashed { - type Error = &'static str; - fn try_from(value: HashedCertificateValue) -> Result, Self::Error> { - let hash = value.hash(); - match value.into_inner() { - CertificateValue::Timeout(timeout) => Ok(Hashed::unchecked_new(timeout, hash)), - _ => Err("Expected a Timeout value"), - } - } -} - impl BcsHashable for Timeout {} -/// Failure to convert a [`HashedCertificateValue`] into one of the block types. +/// Failure to convert a `Certificate` into one of the expected certificate types. #[derive(Clone, Copy, Debug, Error)] pub enum ConversionError { - /// Failure to convert to [`ConfirmedBlock`]. - #[error("Expected a `ConfirmedBlock` value")] + /// Failure to convert to [`ConfirmedBlock`] certificate. + #[error("Expected a `ConfirmedBlockCertificate` value")] ConfirmedBlock, - /// Failure to convert to [`ValidatedBlock`]. - #[error("Expected a `ValidatedBlock` value")] + /// Failure to convert to [`ValidatedBlock`] certificate. + #[error("Expected a `ValidatedBlockCertificate` value")] ValidatedBlock, + + /// Failure to convert to [`Timeout`] certificate. + #[error("Expected a `TimeoutCertificate` value")] + Timeout, } diff --git a/linera-chain/src/certificate/confirmed.rs b/linera-chain/src/certificate/confirmed.rs index 271ac9ec9d8..e4aefc33068 100644 --- a/linera-chain/src/certificate/confirmed.rs +++ b/linera-chain/src/certificate/confirmed.rs @@ -2,32 +2,21 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use linera_base::identifiers::{BlobId, ChainId, MessageId}; -use linera_execution::committee::Epoch; -use serde::{Deserialize, Deserializer, Serialize}; - -use super::{ - generic::GenericCertificate, hashed::Hashed, Certificate, CertificateValue, - HashedCertificateValue, +use linera_base::{ + crypto::Signature, + data_types::Round, + identifiers::{BlobId, ChainId, MessageId}, }; +use linera_execution::committee::{Epoch, ValidatorName}; +use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serialize}; + +use super::{generic::GenericCertificate, hashed::Hashed, Certificate}; use crate::{ - block::{ConfirmedBlock, ConversionError, ValidatedBlock}, + block::{ConfirmedBlock, ConversionError}, data_types::{ExecutedBlock, Medium, MessageBundle}, }; impl GenericCertificate { - /// Creates a new `ConfirmedBlockCertificate` from a `ValidatedBlockCertificate`. - pub fn from_validated(validated: GenericCertificate) -> Self { - let round = validated.round; - let validated_block = validated.into_inner(); - // To keep the signature checks passing, we need to obtain a hash over the old type. - let old_confirmed = HashedCertificateValue::new_confirmed(validated_block.inner().clone()); - let confirmed = ConfirmedBlock::from_validated(validated_block); - let hashed = Hashed::unchecked_new(confirmed, old_confirmed.hash()); - - Self::new(hashed, round, vec![]) - } - /// Returns reference to the `ExecutedBlock` contained in this certificate. pub fn executed_block(&self) -> &ExecutedBlock { self.inner().executed_block() @@ -55,20 +44,19 @@ impl GenericCertificate { pub fn requires_blob(&self, blob_id: &BlobId) -> bool { self.executed_block().requires_blob(blob_id) } + + #[cfg(with_testing)] + pub fn outgoing_message_count(&self) -> usize { + self.executed_block().messages().iter().map(Vec::len).sum() + } } impl TryFrom for GenericCertificate { type Error = ConversionError; fn try_from(cert: Certificate) -> Result { - let hash = cert.hash(); - let (value, round, signatures) = cert.destructure(); - match value.into_inner() { - CertificateValue::ConfirmedBlock(confirmed) => Ok(Self::new( - Hashed::unchecked_new(confirmed, hash), - round, - signatures, - )), + match cert { + Certificate::Confirmed(confirmed) => Ok(confirmed), _ => Err(ConversionError::ConfirmedBlock), } } @@ -76,11 +64,7 @@ impl TryFrom for GenericCertificate { impl From> for Certificate { fn from(cert: GenericCertificate) -> Certificate { - let (value, round, signatures) = cert.destructure(); - let hash = value.hash(); - let value = - Hashed::unchecked_new(CertificateValue::ConfirmedBlock(value.into_inner()), hash); - Certificate::new(value, round, signatures) + Certificate::Confirmed(cert) } } @@ -89,8 +73,11 @@ impl Serialize for GenericCertificate { where S: serde::Serializer, { - let certificate: Certificate = self.clone().into(); - certificate.serialize(serializer) + let mut state = serializer.serialize_struct("ConfirmedBlockCertificate", 3)?; + state.serialize_field("value", self.inner())?; + state.serialize_field("round", &self.round)?; + state.serialize_field("signatures", self.signatures())?; + state.end() } } @@ -99,10 +86,19 @@ impl<'de> Deserialize<'de> for GenericCertificate { where D: Deserializer<'de>, { - let cert = Certificate::deserialize(deserializer); - match cert { - Ok(cert) => Ok(cert.try_into().unwrap()), - Err(e) => Err(e), + #[derive(Debug, Deserialize)] + #[serde(rename = "ConfirmedBlockCertificate")] + struct Helper { + value: Hashed, + round: Round, + signatures: Vec<(ValidatorName, Signature)>, + } + + let helper = Helper::deserialize(deserializer)?; + if !crate::data_types::is_strictly_ordered(&helper.signatures) { + Err(serde::de::Error::custom("Vector is not strictly sorted")) + } else { + Ok(Self::new(helper.value, helper.round, helper.signatures)) } } } diff --git a/linera-chain/src/certificate/mod.rs b/linera-chain/src/certificate/mod.rs index 517e542d292..8793cbecfad 100644 --- a/linera-chain/src/certificate/mod.rs +++ b/linera-chain/src/certificate/mod.rs @@ -8,7 +8,6 @@ mod hashed; mod lite; mod timeout; mod validated; -mod value; use std::collections::HashSet; @@ -21,110 +20,70 @@ use linera_base::{ }; use linera_execution::committee::{Epoch, ValidatorName}; pub use lite::LiteCertificate; -use serde::{Deserialize, Deserializer, Serialize}; -pub use value::{CertificateValue, HashedCertificateValue}; +use serde::{Deserialize, Serialize}; -use crate::{ - data_types::{is_strictly_ordered, LiteValue, Medium, MessageBundle}, - types::{ConfirmedBlock, Timeout, ValidatedBlock}, -}; +use crate::types::{ConfirmedBlock, Timeout, ValidatedBlock}; /// Certificate for a [`ValidatedBlock`]` instance. +/// A validated block certificate means the block is valid (but not necessarily finalized yet). +/// Since only one block per round is validated, +/// there can be at most one such certificate in every round. pub type ValidatedBlockCertificate = GenericCertificate; /// Certificate for a [`ConfirmedBlock`] instance. +/// A confirmed block certificate means that the block is finalized: +/// It is the agreed block at that height on that chain. pub type ConfirmedBlockCertificate = GenericCertificate; /// Certificate for a [`Timeout`] instance. +/// A timeout certificate means that the next consensus round has begun. pub type TimeoutCertificate = GenericCertificate; +/// Enum wrapping all types of certificates that can be created. /// A certified statement from the committee. -pub type Certificate = GenericCertificate; - -impl Serialize for Certificate { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - #[derive(Debug, Serialize)] - #[serde(rename = "Certificate")] - struct CertificateHelper<'a> { - value: &'a CertificateValue, - round: Round, - signatures: &'a Vec<(ValidatorName, Signature)>, - } - - let helper = CertificateHelper { - value: self.inner(), - round: self.round, - signatures: self.signatures(), - }; - - helper.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for Certificate { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Debug, Deserialize)] - #[serde(rename = "Certificate")] - struct CertificateHelper { - value: HashedCertificateValue, - round: Round, - signatures: Vec<(ValidatorName, Signature)>, - } - - let helper: CertificateHelper = Deserialize::deserialize(deserializer)?; - if !is_strictly_ordered(&helper.signatures) { - Err(serde::de::Error::custom("Vector is not strictly sorted")) - } else { - Ok(Self::new(helper.value, helper.round, helper.signatures)) - } - } +/// Every certificate is a statement signed by the quorum of the committee. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(with_testing, derive(Eq, PartialEq))] +pub enum Certificate { + /// Certificate for [`ValidatedBlock`]. + Validated(ValidatedBlockCertificate), + /// Certificate for [`ConfirmedBlock`]. + Confirmed(ConfirmedBlockCertificate), + /// Certificate for [`Timeout`]. + Timeout(TimeoutCertificate), } impl Certificate { - /// Returns the `LiteValue` corresponding to the certified value. - pub fn lite_value(&self) -> LiteValue { - LiteValue { - value_hash: self.hash(), - chain_id: self.inner().chain_id(), + pub fn round(&self) -> Round { + match self { + Certificate::Validated(cert) => cert.round, + Certificate::Confirmed(cert) => cert.round, + Certificate::Timeout(cert) => cert.round, } } - /// Returns the bundles of messages sent via the given medium to the specified - /// recipient. Messages originating from different transactions of the original block - /// are kept in separate bundles. If the medium is a channel, does not verify that the - /// recipient is actually subscribed to that channel. - pub fn message_bundles_for<'a>( - &'a self, - medium: &'a Medium, - recipient: ChainId, - ) -> impl Iterator + 'a { - let certificate_hash = self.hash(); - self.inner() - .executed_block() - .into_iter() - .flat_map(move |executed_block| { - executed_block.message_bundles_for(medium, recipient, certificate_hash) - }) + pub fn height(&self) -> BlockHeight { + match self { + Certificate::Validated(cert) => cert.value().inner().executed_block().block.height, + Certificate::Confirmed(cert) => cert.value().inner().executed_block().block.height, + Certificate::Timeout(cert) => cert.inner().height, + } } - pub fn requires_blob(&self, blob_id: &BlobId) -> bool { - self.inner() - .executed_block() - .is_some_and(|executed_block| executed_block.requires_blob(blob_id)) + pub fn chain_id(&self) -> ChainId { + match self { + Certificate::Validated(cert) => cert.value().inner().executed_block().block.chain_id, + Certificate::Confirmed(cert) => cert.value().inner().executed_block().block.chain_id, + Certificate::Timeout(cert) => cert.inner().chain_id, + } } - #[cfg(with_testing)] - pub fn outgoing_message_count(&self) -> usize { - let Some(executed_block) = self.inner().executed_block() else { - return 0; - }; - executed_block.messages().iter().map(Vec::len).sum() + pub fn signatures(&self) -> &Vec<(ValidatorName, Signature)> { + match self { + Certificate::Validated(cert) => cert.signatures(), + Certificate::Confirmed(cert) => cert.signatures(), + Certificate::Timeout(cert) => cert.signatures(), + } } } @@ -163,19 +122,19 @@ impl CertificateValueT for Timeout { impl CertificateValueT for ValidatedBlock { fn chain_id(&self) -> ChainId { - self.inner().block.chain_id + self.executed_block().block.chain_id } fn epoch(&self) -> Epoch { - self.inner().block.epoch + self.executed_block().block.epoch } fn height(&self) -> BlockHeight { - self.inner().block.height + self.executed_block().block.height } fn required_blob_ids(&self) -> HashSet { - self.inner().outcome.required_blob_ids().clone() + self.executed_block().outcome.required_blob_ids().clone() } #[cfg(with_testing)] @@ -201,37 +160,3 @@ impl CertificateValueT for ConfirmedBlock { self.executed_block().outcome.required_blob_ids().clone() } } - -impl CertificateValueT for CertificateValue { - fn chain_id(&self) -> ChainId { - match self { - CertificateValue::ValidatedBlock(validated) => validated.chain_id(), - CertificateValue::ConfirmedBlock(confirmed) => confirmed.chain_id(), - CertificateValue::Timeout(timeout) => timeout.chain_id(), - } - } - - fn epoch(&self) -> Epoch { - match self { - CertificateValue::ValidatedBlock(validated) => validated.epoch(), - CertificateValue::ConfirmedBlock(confirmed) => confirmed.epoch(), - CertificateValue::Timeout(timeout) => timeout.epoch(), - } - } - - fn height(&self) -> BlockHeight { - match self { - CertificateValue::ValidatedBlock(validated) => validated.height(), - CertificateValue::ConfirmedBlock(confirmed) => confirmed.height(), - CertificateValue::Timeout(timeout) => timeout.height(), - } - } - - fn required_blob_ids(&self) -> HashSet { - match self { - CertificateValue::ValidatedBlock(validated) => validated.required_blob_ids(), - CertificateValue::ConfirmedBlock(confirmed) => confirmed.required_blob_ids(), - CertificateValue::Timeout(timeout) => timeout.required_blob_ids(), - } - } -} diff --git a/linera-chain/src/certificate/timeout.rs b/linera-chain/src/certificate/timeout.rs index a8404866fc3..bf7fabe07bb 100644 --- a/linera-chain/src/certificate/timeout.rs +++ b/linera-chain/src/certificate/timeout.rs @@ -9,31 +9,26 @@ use serde::{ Deserialize, Deserializer, }; -use super::{ - generic::GenericCertificate, hashed::Hashed, Certificate, CertificateValue, - HashedCertificateValue, +use super::{generic::GenericCertificate, Certificate}; +use crate::{ + block::{ConversionError, Timeout}, + types::Hashed, }; -use crate::block::Timeout; -impl From for GenericCertificate { - fn from(cert: Certificate) -> Self { - let hash = cert.hash(); - let (value, round, signatures) = cert.destructure(); - match value.into_inner() { - CertificateValue::Timeout(timeout) => { - Self::new(Hashed::unchecked_new(timeout, hash), round, signatures) - } - _ => panic!("Expected a timeout certificate"), +impl TryFrom for GenericCertificate { + type Error = ConversionError; + + fn try_from(cert: Certificate) -> Result { + match cert { + Certificate::Timeout(timeout) => Ok(timeout), + _ => Err(ConversionError::Timeout), } } } impl From> for Certificate { fn from(cert: GenericCertificate) -> Certificate { - let (value, round, signatures) = cert.destructure(); - let hash = value.hash(); - let value = Hashed::unchecked_new(CertificateValue::Timeout(value.into_inner()), hash); - Certificate::new(value, round, signatures) + Certificate::Timeout(cert) } } @@ -55,16 +50,15 @@ impl<'de> Deserialize<'de> for GenericCertificate { #[derive(Deserialize)] #[serde(rename = "TimeoutCertificate")] struct Inner { - value: Timeout, + value: Hashed, round: Round, signatures: Vec<(ValidatorName, Signature)>, } let inner = Inner::deserialize(deserializer)?; - let timeout_hashed = HashedCertificateValue::new_timeout( - inner.value.chain_id, - inner.value.height, - inner.value.epoch, - ); - Ok(Self::new(timeout_hashed, inner.round, inner.signatures)) + if !crate::data_types::is_strictly_ordered(&inner.signatures) { + Err(serde::de::Error::custom("Vector is not strictly sorted")) + } else { + Ok(Self::new(inner.value, inner.round, inner.signatures)) + } } } diff --git a/linera-chain/src/certificate/validated.rs b/linera-chain/src/certificate/validated.rs index f10e9d743fc..58fdcbd8045 100644 --- a/linera-chain/src/certificate/validated.rs +++ b/linera-chain/src/certificate/validated.rs @@ -9,13 +9,11 @@ use serde::{ Deserialize, Deserializer, }; -use super::{ - generic::GenericCertificate, hashed::Hashed, Certificate, CertificateValue, - HashedCertificateValue, -}; +use super::{generic::GenericCertificate, Certificate}; use crate::{ - block::ValidatedBlock, + block::{ConversionError, ValidatedBlock}, data_types::{ExecutedBlock, LiteValue}, + types::Hashed, }; impl GenericCertificate { @@ -38,39 +36,30 @@ impl GenericCertificate { /// Returns reference to the `ExecutedBlock` contained in this certificate. pub fn executed_block(&self) -> &ExecutedBlock { - self.inner().inner() + self.inner().executed_block() } } -impl From for GenericCertificate { - fn from(cert: Certificate) -> Self { - let (value, round, signatures) = cert.destructure(); - let value_hash = value.hash(); - match value.into_inner() { - CertificateValue::ValidatedBlock(validated) => Self::new( - Hashed::unchecked_new(validated, value_hash), - round, - signatures, - ), - _ => panic!("Expected a validated block certificate"), +impl TryFrom for GenericCertificate { + type Error = ConversionError; + + fn try_from(cert: Certificate) -> Result { + match cert { + Certificate::Validated(validated) => Ok(validated), + _ => Err(ConversionError::ValidatedBlock), } } } impl From> for Certificate { fn from(cert: GenericCertificate) -> Certificate { - let (value, round, signatures) = cert.destructure(); - Certificate::new( - HashedCertificateValue::new_validated(value.into_inner().into_inner()).into(), - round, - signatures, - ) + Certificate::Validated(cert) } } impl Serialize for GenericCertificate { fn serialize(&self, serializer: S) -> Result { - let mut state = serializer.serialize_struct("ValidatedBlockCertificate", 4)?; + let mut state = serializer.serialize_struct("ValidatedBlockCertificate", 3)?; state.serialize_field("value", self.inner())?; state.serialize_field("round", &self.round)?; state.serialize_field("signatures", self.signatures())?; @@ -86,12 +75,17 @@ impl<'de> Deserialize<'de> for GenericCertificate { #[derive(Deserialize)] #[serde(rename = "ValidatedBlockCertificate")] struct Inner { - value: ValidatedBlock, + value: Hashed, round: Round, signatures: Vec<(ValidatorName, Signature)>, } let inner = Inner::deserialize(deserializer)?; - let validated_hashed = HashedCertificateValue::new_validated(inner.value.into_inner()); - Ok(Self::new(validated_hashed, inner.round, inner.signatures)) + if !crate::data_types::is_strictly_ordered(&inner.signatures) { + Err(serde::de::Error::custom( + "Signatures are not strictly ordered", + )) + } else { + Ok(Self::new(inner.value, inner.round, inner.signatures)) + } } } diff --git a/linera-chain/src/certificate/value.rs b/linera-chain/src/certificate/value.rs deleted file mode 100644 index 798e70d49f8..00000000000 --- a/linera-chain/src/certificate/value.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) Facebook, Inc. and its affiliates. -// Copyright (c) Zefchain Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use linera_base::{ - crypto::{BcsHashable, CryptoHash}, - data_types::BlockHeight, - identifiers::ChainId, -}; -use linera_execution::committee::Epoch; -use serde::{Deserialize, Serialize}; - -use super::Hashed; -#[cfg(with_testing)] -use crate::data_types::OutgoingMessage; -use crate::{ - block::{ConfirmedBlock, Timeout, ValidatedBlock}, - data_types::{Block, ExecutedBlock}, -}; - -/// A statement to be certified by the validators. -#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)] -pub enum CertificateValue { - ValidatedBlock(ValidatedBlock), - ConfirmedBlock(ConfirmedBlock), - Timeout(Timeout), -} - -impl CertificateValue { - pub fn chain_id(&self) -> ChainId { - match self { - CertificateValue::ConfirmedBlock(confirmed) => { - confirmed.executed_block().block.chain_id - } - CertificateValue::ValidatedBlock(validated) => validated.inner().block.chain_id, - CertificateValue::Timeout(Timeout { chain_id, .. }) => *chain_id, - } - } - - pub fn height(&self) -> BlockHeight { - match self { - CertificateValue::ConfirmedBlock(confirmed) => confirmed.executed_block().block.height, - CertificateValue::ValidatedBlock(validated) => validated.inner().block.height, - CertificateValue::Timeout(Timeout { height, .. }) => *height, - } - } - - pub fn epoch(&self) -> Epoch { - match self { - CertificateValue::ConfirmedBlock(confirmed) => confirmed.executed_block().block.epoch, - CertificateValue::ValidatedBlock(validated) => validated.inner().block.epoch, - CertificateValue::Timeout(Timeout { epoch, .. }) => *epoch, - } - } - - /// Creates a `HashedCertificateValue` by hashing `self`. No hash checks are made! - pub fn with_hash(self) -> HashedCertificateValue { - let hash = CryptoHash::new(&self); - HashedCertificateValue::unchecked_new(self, hash) - } - - /// Creates a `HashedCertificateValue` without checking that this is the correct hash! - pub fn with_hash_unchecked(self, hash: CryptoHash) -> HashedCertificateValue { - HashedCertificateValue::unchecked_new(self, hash) - } - - pub fn is_confirmed(&self) -> bool { - matches!(self, CertificateValue::ConfirmedBlock { .. }) - } - - pub fn is_validated(&self) -> bool { - matches!(self, CertificateValue::ValidatedBlock { .. }) - } - - pub fn is_timeout(&self) -> bool { - matches!(self, CertificateValue::Timeout { .. }) - } - - #[cfg(with_testing)] - pub fn messages(&self) -> Option<&Vec>> { - Some(self.executed_block()?.messages()) - } - - pub fn executed_block(&self) -> Option<&ExecutedBlock> { - match self { - CertificateValue::ConfirmedBlock(confirmed) => Some(confirmed.executed_block()), - CertificateValue::ValidatedBlock(validated) => Some(validated.inner()), - CertificateValue::Timeout(_) => None, - } - } - - pub fn block(&self) -> Option<&Block> { - self.executed_block() - .map(|executed_block| &executed_block.block) - } - - pub fn to_log_str(&self) -> &'static str { - match self { - CertificateValue::ConfirmedBlock { .. } => "confirmed_block", - CertificateValue::ValidatedBlock { .. } => "validated_block", - CertificateValue::Timeout { .. } => "timeout", - } - } -} - -#[async_graphql::Object(cache_control(no_cache))] -impl CertificateValue { - #[graphql(derived(name = "executed_block"))] - async fn _executed_block(&self) -> Option { - self.executed_block().cloned() - } - - async fn status(&self) -> String { - match self { - CertificateValue::ValidatedBlock { .. } => "validated".to_string(), - CertificateValue::ConfirmedBlock { .. } => "confirmed".to_string(), - CertificateValue::Timeout { .. } => "timeout".to_string(), - } - } -} - -impl BcsHashable for CertificateValue {} - -/// A statement to be certified by the validators, with its hash. -pub type HashedCertificateValue = Hashed; - -impl HashedCertificateValue { - /// Creates a [`ConfirmedBlock`](CertificateValue::ConfirmedBlock) value. - pub fn new_confirmed(executed_block: ExecutedBlock) -> Hashed { - HashedCertificateValue::from(CertificateValue::ConfirmedBlock(ConfirmedBlock::new( - executed_block, - ))) - .try_into() - .unwrap() - } - - /// Creates a [`ValidatedBlock`](CertificateValue::ValidatedBlock) value. - pub fn new_validated(executed_block: ExecutedBlock) -> Hashed { - HashedCertificateValue::from(CertificateValue::ValidatedBlock(ValidatedBlock::new( - executed_block, - ))) - .try_into() - .unwrap() - } - - /// Creates a [`Timeout`](CertificateValue::Timeout) value. - pub fn new_timeout(chain_id: ChainId, height: BlockHeight, epoch: Epoch) -> Hashed { - HashedCertificateValue::from(CertificateValue::Timeout(Timeout::new( - chain_id, height, epoch, - ))) - .try_into() - .unwrap() - } - - /// Returns the corresponding `ConfirmedBlock`, if this is a `ValidatedBlock`. - pub fn validated_to_confirmed(self) -> Option { - match self.into_inner() { - CertificateValue::ValidatedBlock(validated) => { - Some(HashedCertificateValue::new_confirmed(validated.into_inner()).into()) - } - CertificateValue::ConfirmedBlock(_) | CertificateValue::Timeout(_) => None, - } - } -} - -impl From for HashedCertificateValue { - fn from(value: CertificateValue) -> HashedCertificateValue { - value.with_hash() - } -} - -impl From for CertificateValue { - fn from(hv: HashedCertificateValue) -> CertificateValue { - hv.into_inner() - } -} - -impl From> for HashedCertificateValue { - fn from(confirmed: Hashed) -> HashedCertificateValue { - let hash = confirmed.hash(); - let value = confirmed.into_inner(); - Hashed::unchecked_new(CertificateValue::ConfirmedBlock(value), hash) - } -} - -impl From> for HashedCertificateValue { - fn from(validated: Hashed) -> HashedCertificateValue { - let hash = validated.hash(); - let value = validated.into_inner(); - Hashed::unchecked_new(CertificateValue::ValidatedBlock(value), hash) - } -} - -impl From> for HashedCertificateValue { - fn from(timeout: Hashed) -> HashedCertificateValue { - let hash = timeout.hash(); - let value = timeout.into_inner(); - Hashed::unchecked_new(CertificateValue::Timeout(value), hash) - } -} diff --git a/linera-chain/src/manager.rs b/linera-chain/src/manager.rs index 1c5eadb0557..3d3bfdc90f9 100644 --- a/linera-chain/src/manager.rs +++ b/linera-chain/src/manager.rs @@ -86,10 +86,7 @@ use serde::{Deserialize, Serialize}; use crate::{ block::{ConfirmedBlock, Timeout, ValidatedBlock}, data_types::{Block, BlockExecutionOutcome, BlockProposal, LiteVote, ProposalContent, Vote}, - types::{ - ConfirmedBlockCertificate, Hashed, HashedCertificateValue, TimeoutCertificate, - ValidatedBlockCertificate, - }, + types::{Hashed, TimeoutCertificate, ValidatedBlockCertificate}, ChainError, }; @@ -337,7 +334,7 @@ impl ChainManager { return false; // We already signed this timeout. } } - let value = HashedCertificateValue::new_timeout(chain_id, height, epoch); + let value = Hashed::new(Timeout::new(chain_id, height, epoch)); self.timeout_vote = Some(Vote::new(value, current_round, key_pair)); true } @@ -359,7 +356,7 @@ impl ChainManager { if self.fallback_vote.is_some() || self.current_round >= Round::Validator(0) { return false; // We already signed this or are already in fallback mode. } - let value = HashedCertificateValue::new_timeout(chain_id, height, epoch); + let value = Hashed::new(Timeout::new(chain_id, height, epoch)); let last_regular_round = Round::SingleLeader(u32::MAX); self.fallback_vote = Some(Vote::new(value, last_regular_round, key_pair)); true @@ -418,7 +415,7 @@ impl ChainManager { .as_ref() .map_or(true, |locked| locked.round < lite_cert.round) { - let value = HashedCertificateValue::new_validated(executed_block.clone()); + let value = Hashed::new(ValidatedBlock::new(executed_block.clone())); if let Some(certificate) = lite_cert.with_value(value) { self.locked = Some(certificate); } @@ -433,14 +430,14 @@ impl ChainManager { // If this is a fast block, vote to confirm. Otherwise vote to validate. if round.is_fast() { self.confirmed_vote = Some(Vote::new( - HashedCertificateValue::new_confirmed(executed_block), + Hashed::new(ConfirmedBlock::new(executed_block)), round, key_pair, )); self.validated_vote = None } else { self.validated_vote = Some(Vote::new( - HashedCertificateValue::new_validated(executed_block), + Hashed::new(ValidatedBlock::new(executed_block)), round, key_pair, )); @@ -464,12 +461,12 @@ impl ChainManager { if key_pair.is_some() && round < self.current_round { return; } - let confirmed = ConfirmedBlockCertificate::from_validated(validated.clone()); + let confirmed_block = ConfirmedBlock::new(validated.inner().executed_block().clone()); self.locked = Some(validated); self.update_current_round(local_time); if let Some(key_pair) = key_pair { // Vote to confirm. - let vote = Vote::new(confirmed.value().clone(), round, key_pair); + let vote = Vote::new(Hashed::new(confirmed_block), round, key_pair); // Ok to overwrite validation votes with confirmation votes at equal or higher round. self.confirmed_vote = Some(vote); self.validated_vote = None; diff --git a/linera-chain/src/unit_tests/chain_tests.rs b/linera-chain/src/unit_tests/chain_tests.rs index 9b21572a3ce..73d57fcfa3e 100644 --- a/linera-chain/src/unit_tests/chain_tests.rs +++ b/linera-chain/src/unit_tests/chain_tests.rs @@ -30,9 +30,10 @@ use linera_views::{ }; use crate::{ + block::ConfirmedBlock, data_types::{IncomingBundle, MessageAction, MessageBundle, Origin}, test::{make_child_block, make_first_block, BlockTestExt, MessageTestExt}, - types::HashedCertificateValue, + types::Hashed, ChainError, ChainExecutionContext, ChainStateView, }; @@ -246,7 +247,7 @@ async fn test_application_permissions() -> anyhow::Result<()> { .with_incoming_bundle(bundle) .with_operation(app_operation.clone()); let outcome = chain.execute_block(&valid_block, time, None).await?; - let value = HashedCertificateValue::new_confirmed(outcome.with(valid_block)); + let value = Hashed::new(ConfirmedBlock::new(outcome.with(valid_block))); // In the second block, other operations are still not allowed. let invalid_block = make_child_block(&value.clone()) diff --git a/linera-chain/src/unit_tests/data_types_tests.rs b/linera-chain/src/unit_tests/data_types_tests.rs index b5dabd0eb7a..bf4206f088a 100644 --- a/linera-chain/src/unit_tests/data_types_tests.rs +++ b/linera-chain/src/unit_tests/data_types_tests.rs @@ -6,8 +6,8 @@ use linera_base::data_types::Amount; use super::*; use crate::{ + block::{ConfirmedBlock, ValidatedBlock}, test::{make_first_block, BlockTestExt}, - types::HashedCertificateValue, }; #[test] @@ -25,7 +25,7 @@ fn test_signed_values() { events: vec![Vec::new()], } .with(block); - let value = HashedCertificateValue::new_confirmed(executed_block); + let value = Hashed::new(ConfirmedBlock::new(executed_block)); let v = LiteVote::new(value.lite(), Round::Fast, &key1); assert!(v.check().is_ok()); @@ -35,6 +35,25 @@ fn test_signed_values() { assert!(v.check().is_err()); } +#[test] +fn test_hashes() { + // Test that hash of confirmed and validated blocks are different, + // even if the blocks are the same. + let block = + make_first_block(ChainId::root(1)).with_simple_transfer(ChainId::root(2), Amount::ONE); + let executed_block = BlockExecutionOutcome { + messages: vec![Vec::new()], + state_hash: CryptoHash::test_hash("state"), + oracle_responses: vec![Vec::new()], + events: vec![Vec::new()], + } + .with(block.clone()); + let confirmed_hashed = Hashed::new(ConfirmedBlock::new(executed_block.clone())); + let validated_hashed = Hashed::new(ValidatedBlock::new(executed_block)); + + assert_ne!(confirmed_hashed.hash(), validated_hashed.hash()); +} + #[test] fn test_certificates() { let key1 = KeyPair::generate(); @@ -54,7 +73,7 @@ fn test_certificates() { events: vec![Vec::new()], } .with(block); - let value = HashedCertificateValue::new_confirmed(executed_block); + let value = Hashed::new(ConfirmedBlock::new(executed_block)); let v1 = LiteVote::new(value.lite(), Round::Fast, &key1); let v2 = LiteVote::new(value.lite(), Round::Fast, &key2); diff --git a/linera-core/src/chain_worker/state/temporary_changes.rs b/linera-core/src/chain_worker/state/temporary_changes.rs index b47106c9390..bd02a083c93 100644 --- a/linera-core/src/chain_worker/state/temporary_changes.rs +++ b/linera-core/src/chain_worker/state/temporary_changes.rs @@ -14,7 +14,7 @@ use linera_chain::{ IncomingBundle, Medium, MessageAction, ProposalContent, }, manager, - types::HashedCertificateValue, + types::{Hashed, ValidatedBlock}, }; use linera_execution::{ChannelSubscription, Query, ResourceControlPolicy, Response}; use linera_storage::{Clock as _, Storage}; @@ -234,7 +234,7 @@ where )) .await?; if let Some(lite_certificate) = &validated_block_certificate { - let value = HashedCertificateValue::new_validated(outcome.clone().with(block.clone())); + let value = Hashed::new(ValidatedBlock::new(outcome.clone().with(block.clone()))); lite_certificate .clone() .with_value(value) diff --git a/linera-core/src/client/mod.rs b/linera-core/src/client/mod.rs index ba05feef2e1..929fbb1587c 100644 --- a/linera-core/src/client/mod.rs +++ b/linera-core/src/client/mod.rs @@ -44,9 +44,8 @@ use linera_chain::{ MessageAction, }, types::{ - Certificate, CertificateValueT, ConfirmedBlock, ConfirmedBlockCertificate, - GenericCertificate, Hashed, HashedCertificateValue, LiteCertificate, Timeout, - TimeoutCertificate, ValidatedBlockCertificate, + CertificateValueT, ConfirmedBlock, ConfirmedBlockCertificate, GenericCertificate, Hashed, + LiteCertificate, Timeout, TimeoutCertificate, ValidatedBlock, ValidatedBlockCertificate, }, ChainError, ChainExecutionContext, ChainStateView, }; @@ -1004,13 +1003,14 @@ where committee: &Committee, certificate: ValidatedBlockCertificate, ) -> Result { - let hashed_value = - HashedCertificateValue::new_confirmed(certificate.executed_block().clone()); + let hashed_value = Hashed::new(ConfirmedBlock::new( + certificate.inner().executed_block().clone(), + )); let finalize_action = CommunicateAction::FinalizeBlock { - certificate: certificate.into(), + certificate, delivery: self.options.cross_chain_message_delivery, }; - let certificate: ConfirmedBlockCertificate = self + let certificate = self .communicate_chain_action(committee, finalize_action, hashed_value) .await?; self.receive_certificate_and_update_validators_internal( @@ -1623,7 +1623,7 @@ where round, chain_id, }; - let value: Hashed = HashedCertificateValue::new_timeout(chain_id, height, epoch); + let value: Hashed = Hashed::new(Timeout::new(chain_id, height, epoch)); let certificate = self .communicate_chain_action(&committee, action, value) .await?; @@ -2054,7 +2054,7 @@ where block.check_proposal_size(max_size, &blobs)?; self.state_mut().set_pending_block(block.clone()); - Ok(HashedCertificateValue::new_confirmed(executed_block)) + Ok(Hashed::new(ConfirmedBlock::new(executed_block))) } /// Returns a suitable timestamp for the next block. @@ -2383,11 +2383,11 @@ where let committee = self.local_committee().await?; // Send the query to validators. let certificate = if round.is_fast() { - let hashed_value = HashedCertificateValue::new_confirmed(executed_block); + let hashed_value = Hashed::new(ConfirmedBlock::new(executed_block)); self.submit_block_proposal(&committee, proposal, hashed_value) .await? } else { - let hashed_value = HashedCertificateValue::new_validated(executed_block); + let hashed_value = Hashed::new(ValidatedBlock::new(executed_block)); let certificate = self .submit_block_proposal(&committee, proposal, hashed_value.clone()) .await?; @@ -2815,7 +2815,7 @@ where pub async fn stage_new_committee( &self, committee: Committee, - ) -> Result, ChainClientError> { + ) -> Result, ChainClientError> { loop { self.prepare_chain().await?; let epoch = self.epoch().await?; @@ -2829,7 +2829,7 @@ where .await? { ExecuteBlockOutcome::Executed(certificate) => { - return Ok(ClientOutcome::Committed(certificate.into())) + return Ok(ClientOutcome::Committed(certificate)) } ExecuteBlockOutcome::Conflict(_) => continue, ExecuteBlockOutcome::WaitForTimeout(timeout) => { diff --git a/linera-core/src/unit_tests/wasm_worker_tests.rs b/linera-core/src/unit_tests/wasm_worker_tests.rs index 7e17662c473..d0cdd1189d1 100644 --- a/linera-core/src/unit_tests/wasm_worker_tests.rs +++ b/linera-core/src/unit_tests/wasm_worker_tests.rs @@ -23,7 +23,7 @@ use linera_base::{ use linera_chain::{ data_types::{BlockExecutionOutcome, OutgoingMessage}, test::{make_child_block, make_first_block, BlockTestExt}, - types::HashedCertificateValue, + types::{ConfirmedBlock, Hashed}, }; use linera_execution::{ committee::Epoch, @@ -138,7 +138,7 @@ where ..SystemExecutionState::new(Epoch::ZERO, publisher_chain, admin_id) }; let publisher_state_hash = publisher_system_state.clone().into_hash().await; - let publish_block_proposal = HashedCertificateValue::new_confirmed( + let publish_block_proposal = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![Vec::new()], events: vec![Vec::new()], @@ -149,7 +149,7 @@ where ]], } .with(publish_block), - ); + )); let publish_certificate = make_certificate(&committee, &worker, publish_block_proposal); let info = worker @@ -217,7 +217,7 @@ where service_blob, ) .await?; - let create_block_proposal = HashedCertificateValue::new_confirmed( + let create_block_proposal = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![vec![OutgoingMessage { destination: Destination::Recipient(creator_chain.into()), @@ -235,7 +235,7 @@ where ]], } .with(create_block), - ); + )); let create_certificate = make_certificate(&committee, &worker, create_block_proposal); let info = worker @@ -280,7 +280,7 @@ where ) .await?; creator_state.system.timestamp.set(Timestamp::from(3)); - let run_block_proposal = HashedCertificateValue::new_confirmed( + let run_block_proposal = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![Vec::new()], events: vec![Vec::new()], @@ -288,7 +288,7 @@ where oracle_responses: vec![Vec::new()], } .with(run_block), - ); + )); let run_certificate = make_certificate(&committee, &worker, run_block_proposal); let info = worker diff --git a/linera-core/src/unit_tests/worker_tests.rs b/linera-core/src/unit_tests/worker_tests.rs index 3d6d1c000b7..fff2fdc5339 100644 --- a/linera-core/src/unit_tests/worker_tests.rs +++ b/linera-core/src/unit_tests/worker_tests.rs @@ -33,8 +33,8 @@ use linera_chain::{ }, test::{make_child_block, make_first_block, BlockTestExt, MessageTestExt, VoteTestExt}, types::{ - CertificateValueT, ConfirmedBlockCertificate, GenericCertificate, Hashed, - HashedCertificateValue, + CertificateValueT, ConfirmedBlock, ConfirmedBlockCertificate, GenericCertificate, Hashed, + Timeout, ValidatedBlock, }, ChainError, ChainExecutionContext, }; @@ -326,7 +326,7 @@ where let oracle_responses = iter::repeat_with(Vec::new).take(tx_count).collect(); let events = iter::repeat_with(Vec::new).take(tx_count).collect(); let state_hash = system_state.into_hash().await; - let value = HashedCertificateValue::new_confirmed( + let value = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages, events, @@ -334,7 +334,7 @@ where oracle_responses, } .with(block), - ); + )); make_certificate(committee, worker, value) } @@ -552,13 +552,13 @@ where ..SystemExecutionState::new(epoch, ChainDescription::Root(1), ChainId::root(0)) }; let state_hash = system_state.into_hash().await; - let value = HashedCertificateValue::new_confirmed( + let value = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { state_hash, ..BlockExecutionOutcome::default() } .with(block), - ); + )); make_certificate(&committee, &worker, value) }; worker @@ -688,7 +688,7 @@ where .unwrap() .value() .inner() - .inner() + .executed_block() .block, &block_proposal0.content.block ); // Multi-leader round - it's not confirmed yet. @@ -702,13 +702,12 @@ where .validated_vote() .unwrap() .value() - .clone() - .into(), + .clone(), ); drop(chain); worker - .handle_certificate(block_certificate0, vec![], None) + .handle_validated_certificate(block_certificate0, vec![]) .await?; let chain = worker.chain_state_view(ChainId::root(1)).await?; assert!(chain.is_active()); @@ -747,7 +746,7 @@ where .unwrap() .value() .inner() - .inner() + .executed_block() .block, &block_proposal1.content.block ); @@ -800,7 +799,7 @@ where let certificate0 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![ vec![direct_credit_message(ChainId::root(2), Amount::ONE)], @@ -826,13 +825,13 @@ where .with_simple_transfer(ChainId::root(2), Amount::from_tokens(2)) .with_authenticated_signer(Some(sender_key_pair.public().into())), ), - ), + )), ); let certificate1 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![vec![direct_credit_message( ChainId::root(2), @@ -853,7 +852,7 @@ where .with_simple_transfer(ChainId::root(2), Amount::from_tokens(3)) .with_authenticated_signer(Some(sender_key_pair.public().into())), ), - ), + )), ); // Missing earlier blocks assert_matches!( @@ -1068,7 +1067,7 @@ where let certificate: ConfirmedBlockCertificate = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![ Vec::new(), @@ -1085,7 +1084,7 @@ where oracle_responses: vec![Vec::new(); 2], } .with(block_proposal.content.block), - ), + )), ); worker .handle_confirmed_certificate(certificate.clone(), vec![], None) @@ -1371,7 +1370,7 @@ where }, action: MessageAction::Accept, }; - let value = HashedCertificateValue::new_confirmed( + let value = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![Vec::new()], events: vec![Vec::new()], @@ -1379,7 +1378,7 @@ where oracle_responses: vec![Vec::new()], } .with(make_first_block(chain_id).with_incoming_bundle(open_chain_message)), - ); + )); let certificate = make_certificate(&committee, &worker, value); let info = worker .fully_handle_certificate(certificate, vec![]) @@ -2374,7 +2373,7 @@ where let certificate0 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![vec![ direct_outgoing_message( @@ -2421,7 +2420,7 @@ where })) .with_authenticated_signer(Some(key_pair.public().into())), ), - ), + )), ); worker .fully_handle_certificate(certificate0.clone(), vec![]) @@ -2455,7 +2454,7 @@ where let certificate1 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![ vec![channel_admin_message(SystemMessage::CreateCommittee { @@ -2483,7 +2482,7 @@ where })) .with_simple_transfer(user_id, Amount::from_tokens(2)), ), - ), + )), ); worker .fully_handle_certificate(certificate1.clone(), vec![]) @@ -2563,7 +2562,7 @@ where let certificate3 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![Vec::new(); 3], events: vec![Vec::new(); 3], @@ -2635,7 +2634,7 @@ where action: MessageAction::Accept, }), ), - ), + )), ); worker .fully_handle_certificate(certificate3, vec![]) @@ -2719,7 +2718,7 @@ where let certificate0 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![vec![direct_credit_message(admin_id, Amount::ONE)]], events: vec![Vec::new()], @@ -2738,7 +2737,7 @@ where .with_simple_transfer(admin_id, Amount::ONE) .with_authenticated_signer(Some(key_pair1.public().into())), ), - ), + )), ); // Have the admin chain create a new epoch without retiring the old one. let committees2 = BTreeMap::from_iter([ @@ -2748,7 +2747,7 @@ where let certificate1 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![vec![channel_admin_message( SystemMessage::CreateCommittee { @@ -2774,7 +2773,7 @@ where }, )), ), - ), + )), ); worker .fully_handle_certificate(certificate1.clone(), vec![]) @@ -2854,7 +2853,7 @@ where let certificate0 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![vec![direct_credit_message(admin_id, Amount::ONE)]], events: vec![Vec::new()], @@ -2873,14 +2872,14 @@ where .with_simple_transfer(admin_id, Amount::ONE) .with_authenticated_signer(Some(key_pair1.public().into())), ), - ), + )), ); // Have the admin chain create a new epoch and retire the old one immediately. let committees3 = BTreeMap::from_iter([(Epoch::from(1), committee.clone())]); let certificate1 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![ vec![channel_admin_message(SystemMessage::CreateCommittee { @@ -2911,7 +2910,7 @@ where epoch: Epoch::ZERO, })), ), - ), + )), ); worker .fully_handle_certificate(certificate1.clone(), vec![]) @@ -2949,7 +2948,7 @@ where let certificate2 = make_certificate( &committee, &worker, - HashedCertificateValue::new_confirmed( + Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![Vec::new()], events: vec![Vec::new()], @@ -2979,7 +2978,7 @@ where action: MessageAction::Accept, }), ), - ), + )), ); worker .fully_handle_certificate(certificate2.clone(), vec![]) @@ -3259,7 +3258,7 @@ where }) .with_authenticated_signer(Some(pub_key0.into())); let (executed_block0, _) = worker.stage_block_execution(block0).await?; - let value0 = HashedCertificateValue::new_confirmed(executed_block0); + let value0 = Hashed::new(ConfirmedBlock::new(executed_block0)); let certificate0 = make_certificate(&committee, &worker, value0.clone()); let response = worker .fully_handle_certificate(certificate0, vec![]) @@ -3292,8 +3291,7 @@ where let query = ChainInfoQuery::new(chain_id).with_timeout(); let (response, _) = worker.handle_chain_info_query(query).await?; let vote = response.info.manager.timeout_vote.clone().unwrap(); - let value_timeout = - HashedCertificateValue::new_timeout(chain_id, BlockHeight::from(1), Epoch::from(0)); + let value_timeout = Hashed::new(Timeout::new(chain_id, BlockHeight::from(1), Epoch::from(0))); // Once we provide the validator with a timeout certificate, the next round starts, where owner // 0 happens to be the leader. @@ -3319,7 +3317,7 @@ where .clone() .into_proposal_with_round(&key_pairs[0], Round::SingleLeader(1)); let (response, _) = worker.handle_block_proposal(proposal1).await?; - let value1 = HashedCertificateValue::new_validated(executed_block1.clone()); + let value1 = Hashed::new(ValidatedBlock::new(executed_block1.clone())); // If we send the validated block certificate to the worker, it votes to confirm. let vote = response.info.manager.pending.clone().unwrap(); @@ -3328,7 +3326,7 @@ where .handle_validated_certificate(certificate1.clone(), vec![]) .await?; let vote = response.info.manager.pending.as_ref().unwrap(); - let value = HashedCertificateValue::new_confirmed(executed_block1.clone()); + let value = Hashed::new(ConfirmedBlock::new(executed_block1.clone())); assert_eq!(vote.value, value.lite()); // Instead of submitting the confirmed block certificate, let rounds 2 to 4 time out, too. @@ -3351,7 +3349,7 @@ where // Since round 3 is already over, a validated block from round 3 won't update the validator's // locked block; certificate1 (with block1) remains locked. - let value2 = HashedCertificateValue::new_validated(executed_block2.clone()); + let value2 = Hashed::new(ValidatedBlock::new(executed_block2.clone())); let certificate = make_certificate_with_round(&committee, &worker, value2.clone(), Round::SingleLeader(2)); worker @@ -3466,7 +3464,7 @@ where }, }); let (executed_block0, _) = worker.stage_block_execution(block0).await?; - let value0 = HashedCertificateValue::new_confirmed(executed_block0); + let value0 = Hashed::new(ConfirmedBlock::new(executed_block0)); let certificate0 = make_certificate(&committee, &worker, value0.clone()); let response = worker .fully_handle_certificate(certificate0, vec![]) @@ -3500,8 +3498,7 @@ where let query = ChainInfoQuery::new(chain_id).with_timeout(); let (response, _) = worker.handle_chain_info_query(query).await?; let vote = response.info.manager.timeout_vote.clone().unwrap(); - let value_timeout = - HashedCertificateValue::new_timeout(chain_id, BlockHeight::from(1), Epoch::from(0)); + let value_timeout = Hashed::new(Timeout::new(chain_id, BlockHeight::from(1), Epoch::from(0))); // Once we provide the validator with a timeout certificate, the next round starts. let certificate_timeout = vote @@ -3555,7 +3552,7 @@ where }, }); let (executed_block0, _) = worker.stage_block_execution(block0).await?; - let value0 = HashedCertificateValue::new_confirmed(executed_block0); + let value0 = Hashed::new(ConfirmedBlock::new(executed_block0)); let certificate0 = make_certificate(&committee, &worker, value0.clone()); let response = worker .fully_handle_certificate(certificate0, vec![]) @@ -3571,7 +3568,7 @@ where .clone() .into_proposal_with_round(&key_pairs[0], Round::Fast); let (executed_block1, _) = worker.stage_block_execution(block1.clone()).await?; - let value1 = HashedCertificateValue::new_confirmed(executed_block1); + let value1 = Hashed::new(ConfirmedBlock::new(executed_block1)); let (response, _) = worker.handle_block_proposal(proposal1).await?; let vote = response.info.manager.pending.as_ref().unwrap(); assert_eq!(vote.value.value_hash, value1.hash()); @@ -3580,8 +3577,7 @@ where clock.set(response.info.manager.round_timeout.unwrap()); // Once we provide the validator with a timeout certificate, the next round starts. - let value_timeout = - HashedCertificateValue::new_timeout(chain_id, BlockHeight::from(1), Epoch::from(0)); + let value_timeout = Hashed::new(Timeout::new(chain_id, BlockHeight::from(1), Epoch::from(0))); let certificate_timeout = make_certificate_with_round(&committee, &worker, value_timeout.clone(), Round::Fast); let (response, _) = worker @@ -3608,7 +3604,7 @@ where // A validated block certificate from a later round can override the locked fast block. let (executed_block2, _) = worker.stage_block_execution(block2.clone()).await?; - let value2 = HashedCertificateValue::new_validated(executed_block2.clone()); + let value2 = Hashed::new(ValidatedBlock::new(executed_block2.clone())); let certificate2 = make_certificate_with_round(&committee, &worker, value2.clone(), Round::MultiLeader(0)); let proposal = BlockProposal::new_retry( @@ -3667,7 +3663,7 @@ where .with_simple_transfer(chain_id, Amount::ONE) .with_authenticated_signer(Some(key_pair.public().into())); let (executed_block, _) = worker.stage_block_execution(block).await?; - let value = HashedCertificateValue::new_confirmed(executed_block); + let value = Hashed::new(ConfirmedBlock::new(executed_block)); let certificate = make_certificate(&committee, &worker, value); worker.fully_handle_certificate(certificate, vec![]).await?; @@ -3679,7 +3675,7 @@ where clock.add(fallback_duration); let (response, _) = worker.handle_chain_info_query(query.clone()).await?; let vote = response.info.manager.fallback_vote.unwrap(); - let value = HashedCertificateValue::new_timeout(chain_id, BlockHeight(1), Epoch::ZERO); + let value = Hashed::new(Timeout::new(chain_id, BlockHeight(1), Epoch::ZERO)); let round = Round::SingleLeader(u32::MAX); assert_eq!(vote.value.value_hash, value.hash()); assert_eq!(vote.round, round); @@ -3908,7 +3904,7 @@ where .await; let _ = state.register_mock_application().await?; - let value = HashedCertificateValue::new_confirmed( + let value = Hashed::new(ConfirmedBlock::new( BlockExecutionOutcome { messages: vec![], events: vec![], @@ -3916,7 +3912,7 @@ where oracle_responses: vec![], } .with(block), - ); + )); let certificate = make_certificate(&committee, &worker, value); worker .handle_confirmed_certificate(certificate, vec![], None) diff --git a/linera-core/src/updater.rs b/linera-core/src/updater.rs index 1cbd6f03b13..ee18ef9f6f2 100644 --- a/linera-core/src/updater.rs +++ b/linera-core/src/updater.rs @@ -17,7 +17,7 @@ use linera_base::{ }; use linera_chain::{ data_types::{BlockProposal, LiteVote}, - types::{Certificate, GenericCertificate}, + types::{Certificate, GenericCertificate, ValidatedBlockCertificate}, }; use linera_execution::committee::Committee; use linera_storage::Storage; @@ -47,7 +47,7 @@ pub enum CommunicateAction { blob_ids: HashSet, }, FinalizeBlock { - certificate: Certificate, + certificate: ValidatedBlockCertificate, delivery: CrossChainMessageDelivery, }, RequestTimeout { @@ -385,10 +385,10 @@ where let block = &proposal.content.block; (block.height, block.chain_id) } - CommunicateAction::FinalizeBlock { certificate, .. } => { - let value = certificate.inner(); - (value.height(), value.chain_id()) - } + CommunicateAction::FinalizeBlock { certificate, .. } => ( + certificate.inner().executed_block().block.height, + certificate.inner().executed_block().block.chain_id, + ), CommunicateAction::RequestTimeout { height, chain_id, .. } => (*height, *chain_id), diff --git a/linera-core/src/worker.rs b/linera-core/src/worker.rs index 1869cc40593..7552d7e5841 100644 --- a/linera-core/src/worker.rs +++ b/linera-core/src/worker.rs @@ -26,9 +26,9 @@ use linera_chain::{ Block, BlockExecutionOutcome, BlockProposal, ExecutedBlock, MessageBundle, Origin, Target, }, types::{ - Certificate, CertificateValue, CertificateValueT, ConfirmedBlock, - ConfirmedBlockCertificate, GenericCertificate, Hashed, LiteCertificate, Timeout, - TimeoutCertificate, ValidatedBlock, ValidatedBlockCertificate, + Certificate, CertificateValueT, ConfirmedBlock, ConfirmedBlockCertificate, + GenericCertificate, Hashed, LiteCertificate, Timeout, TimeoutCertificate, ValidatedBlock, + ValidatedBlockCertificate, }, ChainError, ChainStateView, }; @@ -427,16 +427,6 @@ pub trait ProcessableCertificate: CertificateValueT + Sized + 'static { ) -> Result<(ChainInfoResponse, NetworkActions), WorkerError>; } -impl ProcessableCertificate for CertificateValue { - async fn process_certificate( - worker: &WorkerState, - certificate: GenericCertificate, - blobs: Vec, - ) -> Result<(ChainInfoResponse, NetworkActions), WorkerError> { - worker.handle_certificate(certificate, blobs, None).await - } -} - impl ProcessableCertificate for ConfirmedBlock { async fn process_certificate( worker: &WorkerState, @@ -842,8 +832,8 @@ where /// Processes a certificate. #[instrument(skip_all, fields( nick = self.nickname, - chain_id = format!("{:.8}", certificate.inner().chain_id()), - height = %certificate.inner().height(), + chain_id = format!("{:.8}", certificate.chain_id()), + height = %certificate.height(), ))] pub async fn handle_certificate( &self, @@ -851,24 +841,19 @@ where blobs: Vec, notify_when_messages_are_delivered: Option>, ) -> Result<(ChainInfoResponse, NetworkActions), WorkerError> { - match certificate.inner() { - CertificateValue::ConfirmedBlock(_) => { - let certificate = ConfirmedBlockCertificate::try_from(certificate).unwrap(); + match certificate { + Certificate::Confirmed(confirmed) => { self.handle_confirmed_certificate( - certificate, + confirmed, blobs, notify_when_messages_are_delivered, ) .await } - CertificateValue::ValidatedBlock(_) => { - let certificate = ValidatedBlockCertificate::from(certificate); - self.handle_validated_certificate(certificate, blobs).await - } - CertificateValue::Timeout(_) => { - let certificate = TimeoutCertificate::from(certificate); - self.handle_timeout_certificate(certificate).await + Certificate::Validated(validated) => { + self.handle_validated_certificate(validated, blobs).await } + Certificate::Timeout(timeout) => self.handle_timeout_certificate(timeout).await, } } diff --git a/linera-indexer/lib/src/indexer.rs b/linera-indexer/lib/src/indexer.rs index 74c2caa9960..f287273a37f 100644 --- a/linera-indexer/lib/src/indexer.rs +++ b/linera-indexer/lib/src/indexer.rs @@ -9,7 +9,7 @@ use async_graphql::{EmptyMutation, EmptySubscription, Schema, SimpleObject}; use async_graphql_axum::{GraphQLRequest, GraphQLResponse}; use axum::{extract::Extension, routing::get, Router}; use linera_base::{crypto::CryptoHash, data_types::BlockHeight, identifiers::ChainId}; -use linera_chain::types::HashedCertificateValue; +use linera_chain::types::{ConfirmedBlock, Hashed}; use linera_views::{ context::{Context, ViewContext}, map_view::MapView, @@ -82,7 +82,7 @@ where pub async fn process_value( &self, state: &mut StateView>, - value: &HashedCertificateValue, + value: &Hashed, ) -> Result<(), IndexerError> { for plugin in self.plugins.values() { plugin.register(value).await? @@ -102,7 +102,7 @@ where pub async fn process( &self, listener: &Listener, - value: &HashedCertificateValue, + value: &Hashed, ) -> Result<(), IndexerError> { let chain_id = value.inner().chain_id(); let hash = value.hash(); @@ -125,12 +125,7 @@ where let mut values = Vec::new(); let mut value = value.clone(); loop { - let Some(block) = value.inner().block() else { - return Err(IndexerError::InvalidCertificateValue(value.hash())); - }; - if !value.inner().is_confirmed() { - return Err(IndexerError::InvalidCertificateValue(value.hash())); - }; + let block = &value.inner().executed_block().block; values.push(value.clone()); if let Some(hash) = block.previous_block_hash { match latest_block { diff --git a/linera-indexer/lib/src/plugin.rs b/linera-indexer/lib/src/plugin.rs index c418fd6a378..8a3560f8b72 100644 --- a/linera-indexer/lib/src/plugin.rs +++ b/linera-indexer/lib/src/plugin.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use async_graphql::{EmptyMutation, EmptySubscription, ObjectType, Schema}; use axum::Router; -use linera_chain::types::HashedCertificateValue; +use linera_chain::types::{ConfirmedBlock, Hashed}; use linera_views::{context::ViewContext, store::KeyValueStore, views::View}; use tokio::sync::Mutex; @@ -30,7 +30,7 @@ where Self: Sized; /// Main function of the plugin: registers the information required for a hashed value - async fn register(&self, value: &HashedCertificateValue) -> Result<(), IndexerError>; + async fn register(&self, value: &Hashed) -> Result<(), IndexerError>; /// Produces the GraphQL schema for the plugin fn sdl(&self) -> String; diff --git a/linera-indexer/lib/src/service.rs b/linera-indexer/lib/src/service.rs index 49853b1604c..bb9fe50262f 100644 --- a/linera-indexer/lib/src/service.rs +++ b/linera-indexer/lib/src/service.rs @@ -16,7 +16,7 @@ use graphql_ws_client::{graphql::StreamingOperation, GraphQLClientClientBuilder} use linera_base::{ crypto::CryptoHash, data_types::BlockHeight, identifiers::ChainId, time::Duration, }; -use linera_chain::types::HashedCertificateValue; +use linera_chain::types::{ConfirmedBlock, Hashed}; use linera_core::worker::Reason; use linera_service_graphql_client::{block, chains, notifications, Block, Chains, Notifications}; use linera_views::store::KeyValueStore; @@ -86,7 +86,7 @@ impl Service { &self, chain_id: ChainId, hash: Option, - ) -> Result { + ) -> Result, IndexerError> { let client = reqwest_client(); let variables = block::Variables { hash, chain_id }; let response = post_graphql::(&client, &self.http(), variables).await?; diff --git a/linera-indexer/plugins/src/operations.rs b/linera-indexer/plugins/src/operations.rs index c44b5ae183a..cac188156b9 100644 --- a/linera-indexer/plugins/src/operations.rs +++ b/linera-indexer/plugins/src/operations.rs @@ -9,7 +9,7 @@ use std::{ use async_graphql::{OneofObject, SimpleObject}; use axum::Router; use linera_base::{crypto::CryptoHash, data_types::BlockHeight, doc_scalar, identifiers::ChainId}; -use linera_chain::types::HashedCertificateValue; +use linera_chain::types::{ConfirmedBlock, Hashed}; use linera_execution::Operation; use linera_indexer::{ common::IndexerError, @@ -126,16 +126,20 @@ where Ok(Self(load(store, NAME).await?)) } - async fn register(&self, value: &HashedCertificateValue) -> Result<(), IndexerError> { + async fn register(&self, value: &Hashed) -> Result<(), IndexerError> { let mut plugin = self.0.lock().await; - let Some(executed_block) = value.inner().executed_block() else { - return Ok(()); - }; let chain_id = value.inner().chain_id(); - for (index, content) in executed_block.block.operations.iter().enumerate() { + for (index, content) in value + .inner() + .executed_block() + .block + .operations + .iter() + .enumerate() + { let key = OperationKey { chain_id, - height: executed_block.block.height, + height: value.inner().height(), index, }; match plugin diff --git a/linera-rpc/proto/rpc.proto b/linera-rpc/proto/rpc.proto index 4ad0acc79c1..36d9e6391f7 100644 --- a/linera-rpc/proto/rpc.proto +++ b/linera-rpc/proto/rpc.proto @@ -241,6 +241,21 @@ message Certificate { // Signatures on the value hash and round bytes signatures = 3; + + // The type of the `value` field. + CertificateType kind = 4; +} + +// Type of the value within the certificate. +enum CertificateType { + // A value for validated block. + ValidatedBlock = 0; + + // A value for confirmed block. + ConfirmedBlock = 1; + + // A value for a timeout message. + Timeout = 2; } message ChainId { diff --git a/linera-rpc/src/grpc/conversions.rs b/linera-rpc/src/grpc/conversions.rs index 5bb3b3ad85d..3c5aad8ae4d 100644 --- a/linera-rpc/src/grpc/conversions.rs +++ b/linera-rpc/src/grpc/conversions.rs @@ -9,7 +9,10 @@ use linera_base::{ }; use linera_chain::{ data_types::{BlockProposal, LiteValue, ProposalContent}, - types::{Certificate, HashedCertificateValue, LiteCertificate}, + types::{ + Certificate, ConfirmedBlock, ConfirmedBlockCertificate, Hashed, LiteCertificate, Timeout, + TimeoutCertificate, ValidatedBlock, ValidatedBlockCertificate, + }, }; use linera_core::{ data_types::{ChainInfoQuery, ChainInfoResponse, CrossChainRequest}, @@ -35,6 +38,8 @@ pub enum GrpcProtoConversionError { CryptoError(#[from] CryptoError), #[error("Inconsistent outer/inner chain ids")] InconsistentChainId, + #[error("Unrecognized certificate type")] + InvalidCertificateType, } impl From for GrpcProtoConversionError { @@ -318,17 +323,23 @@ impl TryFrom for HandleCertificateRequest { type Error = GrpcProtoConversionError; fn try_from(cert_request: api::HandleCertificateRequest) -> Result { - let certificate = cert_request + let certificate: Certificate = cert_request .certificate - .ok_or(GrpcProtoConversionError::MissingField)?; - let value: HashedCertificateValue = bincode::deserialize(&certificate.value)?; + .ok_or(GrpcProtoConversionError::MissingField)? + .try_into()?; + + let req_chain_id: ChainId = cert_request + .chain_id + .ok_or(GrpcProtoConversionError::MissingField)? + .try_into()?; + ensure!( - Some(value.inner().chain_id().into()) == cert_request.chain_id, + certificate.chain_id() == req_chain_id, GrpcProtoConversionError::InconsistentChainId ); let blobs = bincode::deserialize(&cert_request.blobs)?; Ok(HandleCertificateRequest { - certificate: certificate.try_into()?, + certificate, wait_for_outgoing_messages: cert_request.wait_for_outgoing_messages, blobs, }) @@ -340,7 +351,7 @@ impl TryFrom for api::HandleCertificateRequest { fn try_from(request: HandleCertificateRequest) -> Result { Ok(Self { - chain_id: Some(request.certificate.inner().chain_id().into()), + chain_id: Some(request.certificate.chain_id().into()), certificate: Some(request.certificate.try_into()?), blobs: bincode::serialize(&request.blobs)?, wait_for_outgoing_messages: request.wait_for_outgoing_messages, @@ -352,11 +363,29 @@ impl TryFrom for Certificate { type Error = GrpcProtoConversionError; fn try_from(certificate: api::Certificate) -> Result { - Ok(Certificate::new( - bincode::deserialize(&certificate.value)?, - bincode::deserialize(&certificate.round)?, - bincode::deserialize(&certificate.signatures)?, - )) + let cert_type = certificate.kind; + + let round = bincode::deserialize(&certificate.round)?; + let signatures = bincode::deserialize(&certificate.signatures)?; + + if cert_type == api::CertificateType::ValidatedBlock as i32 { + let value: Hashed = bincode::deserialize(&certificate.value)?; + Ok(Certificate::Validated(ValidatedBlockCertificate::new( + value, round, signatures, + ))) + } else if cert_type == api::CertificateType::ConfirmedBlock as i32 { + let value: Hashed = bincode::deserialize(&certificate.value)?; + Ok(Certificate::Confirmed(ConfirmedBlockCertificate::new( + value, round, signatures, + ))) + } else if cert_type == api::CertificateType::Timeout as i32 { + let value: Hashed = bincode::deserialize(&certificate.value)?; + Ok(Certificate::Timeout(TimeoutCertificate::new( + value, round, signatures, + ))) + } else { + Err(GrpcProtoConversionError::InvalidCertificateType) + } } } @@ -364,10 +393,29 @@ impl TryFrom for api::Certificate { type Error = GrpcProtoConversionError; fn try_from(certificate: Certificate) -> Result { + let round = bincode::serialize(&certificate.round())?; + let signatures = bincode::serialize(certificate.signatures())?; + + let (kind, value) = match certificate { + Certificate::Confirmed(confirmed) => ( + api::CertificateType::ConfirmedBlock, + bincode::serialize(confirmed.value())?, + ), + Certificate::Validated(validated) => ( + api::CertificateType::ValidatedBlock, + bincode::serialize(validated.value())?, + ), + Certificate::Timeout(timeout) => ( + api::CertificateType::Timeout, + bincode::serialize(timeout.value())?, + ), + }; + Ok(Self { - value: bincode::serialize(certificate.inner())?, - round: bincode::serialize(&certificate.round)?, - signatures: bincode::serialize(certificate.signatures())?, + kind: kind as i32, + value, + round, + signatures, }) } } @@ -663,7 +711,6 @@ pub mod tests { use linera_chain::{ data_types::{Block, BlockExecutionOutcome}, test::make_first_block, - types::HashedCertificateValue, }; use linera_core::data_types::ChainInfo; use serde::{Deserialize, Serialize}; @@ -817,15 +864,14 @@ pub mod tests { #[test] pub fn test_certificate() { let key_pair = KeyPair::generate(); - let certificate = Certificate::new( - HashedCertificateValue::new_validated( + let certificate = ValidatedBlockCertificate::new( + Hashed::new(ValidatedBlock::new( BlockExecutionOutcome { state_hash: CryptoHash::new(&Foo("test".into())), ..BlockExecutionOutcome::default() } .with(get_block()), - ) - .into(), + )), Round::MultiLeader(3), vec![( ValidatorName::from(key_pair.public()), @@ -833,7 +879,7 @@ pub mod tests { )], ); let request = HandleCertificateRequest { - certificate, + certificate: Certificate::Validated(certificate), blobs: vec![], wait_for_outgoing_messages: false, }; @@ -867,15 +913,14 @@ pub mod tests { #[test] pub fn test_block_proposal() { let key_pair = KeyPair::generate(); - let cert = Certificate::new( - HashedCertificateValue::new_validated( + let cert = ValidatedBlockCertificate::new( + Hashed::new(ValidatedBlock::new( BlockExecutionOutcome { state_hash: CryptoHash::new(&Foo("validated".into())), ..BlockExecutionOutcome::default() } .with(get_block()), - ) - .into(), + )), Round::SingleLeader(2), vec![( ValidatorName::from(key_pair.public()), diff --git a/linera-rpc/src/message.rs b/linera-rpc/src/message.rs index 4f3c631c367..cad320ce823 100644 --- a/linera-rpc/src/message.rs +++ b/linera-rpc/src/message.rs @@ -62,7 +62,7 @@ impl RpcMessage { let chain_id = match self { BlockProposal(proposal) => proposal.content.block.chain_id, LiteCertificate(request) => request.certificate.value.chain_id, - Certificate(request) => request.certificate.inner().chain_id(), + Certificate(request) => request.certificate.chain_id(), ChainInfoQuery(query) => query.chain_id, CrossChainRequest(request) => request.target_chain_id(), Vote(_) diff --git a/linera-rpc/tests/format.rs b/linera-rpc/tests/format.rs index bdf019c05a1..2cbcfb10a9b 100644 --- a/linera-rpc/tests/format.rs +++ b/linera-rpc/tests/format.rs @@ -10,7 +10,7 @@ use linera_base::{ use linera_chain::{ data_types::{Medium, MessageAction}, manager::ChainManagerInfo, - types::{CertificateValue, ConfirmedBlock, Hashed, HashedCertificateValue}, + types::{Certificate, ConfirmedBlock, Hashed, Timeout, ValidatedBlock}, }; use linera_core::{data_types::CrossChainRequest, node::NodeError}; use linera_execution::{ @@ -40,10 +40,11 @@ fn get_registry() -> Result { tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; - tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; tracer.trace_type::>(&samples)?; - tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; + tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; tracer.trace_type::(&samples)?; diff --git a/linera-rpc/tests/snapshots/format__format.yaml.snap b/linera-rpc/tests/snapshots/format__format.yaml.snap index 97ef9ee41a7..30fd120a3c1 100644 --- a/linera-rpc/tests/snapshots/format__format.yaml.snap +++ b/linera-rpc/tests/snapshots/format__format.yaml.snap @@ -137,30 +137,19 @@ BytecodeId: - service_blob_hash: TYPENAME: CryptoHash Certificate: - STRUCT: - - value: - TYPENAME: CertificateValue - - round: - TYPENAME: Round - - signatures: - SEQ: - TUPLE: - - TYPENAME: ValidatorName - - TYPENAME: Signature -CertificateValue: ENUM: 0: - ValidatedBlock: + Validated: NEWTYPE: - TYPENAME: ValidatedBlock + TYPENAME: ValidatedBlockCertificate 1: - ConfirmedBlock: + Confirmed: NEWTYPE: - TYPENAME: ConfirmedBlock + TYPENAME: ConfirmedBlockCertificate 2: Timeout: NEWTYPE: - TYPENAME: Timeout + TYPENAME: TimeoutCertificate ChainAndHeight: STRUCT: - chain_id: @@ -571,8 +560,9 @@ NodeError: MissingCertificateValue: UNIT 9: MissingCertificates: - STRUCT: - - missing: STR + NEWTYPE: + SEQ: + TYPENAME: CryptoHash 10: MissingVoteInValidatorResponse: UNIT 11: diff --git a/linera-sdk/src/test/block.rs b/linera-sdk/src/test/block.rs index cff89c6df8a..ccc423b8eaa 100644 --- a/linera-sdk/src/test/block.rs +++ b/linera-sdk/src/test/block.rs @@ -16,7 +16,7 @@ use linera_chain::{ Block, ChannelFullName, IncomingBundle, LiteVote, Medium, MessageAction, Origin, SignatureAggregator, }, - types::{Certificate, HashedCertificateValue}, + types::{ConfirmedBlock, ConfirmedBlockCertificate, Hashed}, }; use linera_execution::{ system::{Recipient, SystemChannel, SystemOperation}, @@ -27,7 +27,7 @@ use super::TestValidator; use crate::ToBcsBytes; /// A helper type to build [`Block`]s using the builder pattern, and then signing them into -/// [`Certificate`]s using a [`TestValidator`]. +/// [`ConfirmedBlockCertificate`]s using a [`TestValidator`]. pub struct BlockBuilder { block: Block, validator: TestValidator, @@ -48,7 +48,7 @@ impl BlockBuilder { pub(crate) fn new( chain_id: ChainId, owner: Owner, - previous_block: Option<&Certificate>, + previous_block: Option<&ConfirmedBlockCertificate>, validator: TestValidator, ) -> Self { let previous_block_hash = previous_block.map(|certificate| certificate.hash()); @@ -171,7 +171,7 @@ impl BlockBuilder { /// Receives all admin messages that were sent to this chain by the given certificate. pub fn with_system_messages_from( &mut self, - certificate: &Certificate, + certificate: &ConfirmedBlockCertificate, channel: SystemChannel, ) -> &mut Self { let medium = Medium::Channel(ChannelFullName { @@ -182,14 +182,14 @@ impl BlockBuilder { } /// Receives all direct messages that were sent to this chain by the given certificate. - pub fn with_messages_from(&mut self, certificate: &Certificate) -> &mut Self { + pub fn with_messages_from(&mut self, certificate: &ConfirmedBlockCertificate) -> &mut Self { self.with_messages_from_by_medium(certificate, &Medium::Direct, MessageAction::Accept) } /// Receives all messages that were sent to this chain by the given certificate. pub fn with_messages_from_by_medium( &mut self, - certificate: &Certificate, + certificate: &ConfirmedBlockCertificate, medium: &Medium, action: MessageAction, ) -> &mut Self { @@ -209,15 +209,14 @@ impl BlockBuilder { /// Tries to sign the prepared [`Block`] with the [`TestValidator`]'s keys and return the /// resulting [`Certificate`]. Returns an error if block execution fails. - pub(crate) async fn try_sign(self) -> anyhow::Result { + pub(crate) async fn try_sign(self) -> anyhow::Result { let (executed_block, _) = self .validator .worker() .stage_block_execution(self.block) .await?; - let value: HashedCertificateValue = - HashedCertificateValue::new_confirmed(executed_block).into(); + let value = Hashed::new(ConfirmedBlock::new(executed_block)); let vote = LiteVote::new(value.lite(), Round::Fast, self.validator.key_pair()); let mut builder = SignatureAggregator::new(value, Round::Fast, self.validator.committee()); let certificate = builder diff --git a/linera-sdk/src/test/chain.rs b/linera-sdk/src/test/chain.rs index fced9502711..15bcb711fe3 100644 --- a/linera-sdk/src/test/chain.rs +++ b/linera-sdk/src/test/chain.rs @@ -17,7 +17,7 @@ use linera_base::{ data_types::{Blob, BlockHeight, Bytecode, CompressedBytecode}, identifiers::{ApplicationId, BytecodeId, ChainDescription, ChainId, MessageId}, }; -use linera_chain::{types::Certificate, ChainError, ChainExecutionContext}; +use linera_chain::{types::ConfirmedBlockCertificate, ChainError, ChainExecutionContext}; use linera_core::{data_types::ChainInfoQuery, worker::WorkerError}; use linera_execution::{ system::{SystemExecutionError, SystemOperation, CREATE_APPLICATION_MESSAGE_INDEX}, @@ -33,7 +33,7 @@ use crate::{ContractAbi, ServiceAbi}; pub struct ActiveChain { key_pair: KeyPair, description: ChainDescription, - tip: Arc>>, + tip: Arc>>, validator: TestValidator, } @@ -87,7 +87,10 @@ impl ActiveChain { /// /// The `block_builder` parameter is a closure that should use the [`BlockBuilder`] parameter /// to provide the block's contents. - pub async fn add_block(&self, block_builder: impl FnOnce(&mut BlockBuilder)) -> Certificate { + pub async fn add_block( + &self, + block_builder: impl FnOnce(&mut BlockBuilder), + ) -> ConfirmedBlockCertificate { self.try_add_block(block_builder) .await .expect("Failed to execute block.") @@ -101,7 +104,7 @@ impl ActiveChain { &self, block_builder: impl FnOnce(&mut BlockBuilder), blobs: Vec, - ) -> Certificate { + ) -> ConfirmedBlockCertificate { self.try_add_block_with_blobs(block_builder, blobs) .await .expect("Failed to execute block.") @@ -114,7 +117,7 @@ impl ActiveChain { pub async fn try_add_block( &self, block_builder: impl FnOnce(&mut BlockBuilder), - ) -> anyhow::Result { + ) -> anyhow::Result { self.try_add_block_with_blobs(block_builder, vec![]).await } @@ -122,7 +125,7 @@ impl ActiveChain { &self, block_builder: impl FnOnce(&mut BlockBuilder), blobs: Vec, - ) -> anyhow::Result { + ) -> anyhow::Result { let mut tip = self.tip.lock().await; let mut block = BlockBuilder::new( self.description.into(), @@ -208,10 +211,7 @@ impl ActiveChain { ) .await; - let executed_block = certificate - .inner() - .executed_block() - .expect("Failed to obtain executed block from certificate"); + let executed_block = certificate.inner().executed_block(); assert_eq!(executed_block.messages().len(), 1); assert_eq!(executed_block.messages()[0].len(), 0); @@ -320,7 +320,9 @@ impl ActiveChain { .as_ref() .expect("Block was not successfully added") .inner() - .height() + .executed_block() + .block + .height } /// Creates an application on this microchain, using the bytecode referenced by `bytecode_id`. @@ -363,10 +365,7 @@ impl ActiveChain { }) .await; - let executed_block = creation_certificate - .inner() - .executed_block() - .expect("Failed to obtain executed block from certificate"); + let executed_block = creation_certificate.inner().executed_block(); assert_eq!(executed_block.messages().len(), 1); let creation = MessageId { chain_id: executed_block.block.chain_id, diff --git a/linera-sdk/src/test/validator.rs b/linera-sdk/src/test/validator.rs index 7035b061cea..c17a3328d3c 100644 --- a/linera-sdk/src/test/validator.rs +++ b/linera-sdk/src/test/validator.rs @@ -200,10 +200,7 @@ impl TestValidator { block.with_system_operation(SystemOperation::OpenChain(new_chain_config)); }) .await; - let executed_block = certificate - .inner() - .executed_block() - .expect("Failed to obtain executed block from certificate"); + let executed_block = certificate.inner().executed_block(); ChainDescription::Child(MessageId { chain_id: executed_block.block.chain_id, diff --git a/linera-service-graphql-client/src/service.rs b/linera-service-graphql-client/src/service.rs index 7c6f530e66b..7ff88c3505c 100644 --- a/linera-service-graphql-client/src/service.rs +++ b/linera-service-graphql-client/src/service.rs @@ -136,7 +136,7 @@ mod from { BlockExecutionOutcome, EventRecord, ExecutedBlock, IncomingBundle, MessageBundle, OutgoingMessage, PostedMessage, }, - types::HashedCertificateValue, + types::{ConfirmedBlock, Hashed}, }; use super::*; @@ -299,15 +299,12 @@ mod from { } } - impl TryFrom for HashedCertificateValue { + impl TryFrom for Hashed { type Error = String; fn try_from(val: block::BlockBlock) -> Result { match (val.value.status.as_str(), val.value.executed_block) { - ("validated", executed_block) => { - Ok(HashedCertificateValue::new_validated(executed_block.into()).into()) - } ("confirmed", executed_block) => { - Ok(HashedCertificateValue::new_confirmed(executed_block.into()).into()) + Ok(Hashed::new(ConfirmedBlock::new(executed_block.into()))) } _ => Err(val.value.status), } diff --git a/linera-service/src/linera/main.rs b/linera-service/src/linera/main.rs index 9dc3a124fb0..41230340854 100644 --- a/linera-service/src/linera/main.rs +++ b/linera-service/src/linera/main.rs @@ -62,7 +62,7 @@ mod net_up_utils; #[cfg(feature = "benchmark")] use { - linera_chain::types::HashedCertificateValue, + linera_chain::types::{ConfirmedBlock, Hashed}, linera_core::data_types::ChainInfoResponse, linera_rpc::{HandleCertificateRequest, RpcMessage}, std::collections::HashSet, @@ -751,7 +751,7 @@ impl Runnable for Job { let executed_block = context .stage_block_execution(proposal.content.block.clone()) .await?; - let value = HashedCertificateValue::new_confirmed(executed_block); + let value = Hashed::new(ConfirmedBlock::new(executed_block)); values.insert(value.hash(), value); } } diff --git a/linera-service/src/proxy/grpc.rs b/linera-service/src/proxy/grpc.rs index 69b05c0c726..1bbba67f5d6 100644 --- a/linera-service/src/proxy/grpc.rs +++ b/linera-service/src/proxy/grpc.rs @@ -584,7 +584,7 @@ mod proto_message_cap { use linera_base::crypto::{KeyPair, Signature}; use linera_chain::{ data_types::{BlockExecutionOutcome, ExecutedBlock}, - types::{Certificate, HashedCertificateValue}, + types::{Certificate, ConfirmedBlock, ConfirmedBlockCertificate, Hashed}, }; use linera_execution::committee::ValidatorName; use linera_sdk::base::{ChainId, TestString}; @@ -600,11 +600,11 @@ mod proto_message_cap { outcome: BlockExecutionOutcome::default(), }; let signatures = vec![(validator, signature)]; - Certificate::new( - HashedCertificateValue::new_confirmed(executed_block).into(), + Certificate::Confirmed(ConfirmedBlockCertificate::new( + Hashed::new(ConfirmedBlock::new(executed_block)), Default::default(), signatures, - ) + )) } #[test] From 4c621f13187e2484d8f3c1b96ef2b2ece9330c2a Mon Sep 17 00:00:00 2001 From: deuszx Date: Mon, 2 Dec 2024 10:41:33 +0000 Subject: [PATCH 09/10] Use ensure macro. --- linera-rpc/src/grpc/client.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/linera-rpc/src/grpc/client.rs b/linera-rpc/src/grpc/client.rs index a6edcbbded4..8ae50ffdbe8 100644 --- a/linera-rpc/src/grpc/client.rs +++ b/linera-rpc/src/grpc/client.rs @@ -364,9 +364,10 @@ impl ValidatorNode for GrpcClient { missing_hashes = missing_hashes[received.len()..].to_vec(); certs_collected.append(&mut received); } - if !missing_hashes.is_empty() { - return Err(NodeError::MissingCertificates(missing_hashes)); - } + ensure!( + missing_hashes.is_empty(), + Err(NodeError::MissingCertificates(missing_hashes)) + ); Ok(certs_collected) } From f5370aa20f828045637b64ddc56e4fb52f3db993 Mon Sep 17 00:00:00 2001 From: deuszx <95355183+deuszx@users.noreply.github.com> Date: Mon, 2 Dec 2024 10:49:58 +0000 Subject: [PATCH 10/10] Update linera-rpc/src/grpc/client.rs Co-authored-by: Andreas Fackler Signed-off-by: deuszx <95355183+deuszx@users.noreply.github.com> --- linera-rpc/src/grpc/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linera-rpc/src/grpc/client.rs b/linera-rpc/src/grpc/client.rs index 8ae50ffdbe8..b9b4d4fca8c 100644 --- a/linera-rpc/src/grpc/client.rs +++ b/linera-rpc/src/grpc/client.rs @@ -366,7 +366,7 @@ impl ValidatorNode for GrpcClient { } ensure!( missing_hashes.is_empty(), - Err(NodeError::MissingCertificates(missing_hashes)) + NodeError::MissingCertificates(missing_hashes) ); Ok(certs_collected) }