Skip to content

Commit

Permalink
Introduce and use TimeoutCertificate
Browse files Browse the repository at this point in the history
  • Loading branch information
deuszx committed Nov 7, 2024
1 parent 9ce84b5 commit f1a1bf1
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 50 deletions.
20 changes: 20 additions & 0 deletions linera-chain/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,23 @@ impl ConfirmedBlock {
self.executed_block
}
}

use linera_base::{data_types::BlockHeight, identifiers::ChainId};
use linera_execution::committee::Epoch;

#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)]
pub struct Timeout {
pub chain_id: ChainId,
pub height: BlockHeight,
pub epoch: Epoch,
}

impl Timeout {
pub fn new(chain_id: ChainId, height: BlockHeight, epoch: Epoch) -> Self {
Self {
chain_id,
height,
epoch,
}
}
}
58 changes: 56 additions & 2 deletions linera-chain/src/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use linera_execution::committee::{Committee, ValidatorName};
use serde::{ser::Serializer, Deserialize, Deserializer, Serialize};

use crate::{
block::{ConfirmedBlock, ValidatedBlock},
block::{ConfirmedBlock, Timeout, ValidatedBlock},
data_types::{
Certificate, CertificateValue, ExecutedBlock, HashedCertificateValue, LiteCertificate,
LiteValue,
Expand All @@ -30,6 +30,9 @@ pub type ValidatedBlockCertificate = CertificateT<ValidatedBlock>;
/// Certificate for a [`ConfirmedBlock`] instance.
pub type ConfirmedBlockCertificate = CertificateT<ConfirmedBlock>;

/// Certificate for a Timeout instance.
pub type TimeoutCerificate = CertificateT<Timeout>;

/// Generic type representing a certificate for `value` of type `T`.
pub struct CertificateT<T> {
value: Hashed<T>,
Expand Down Expand Up @@ -88,6 +91,22 @@ impl<'de> Deserialize<'de> for ValidatedBlockCertificate {
}
}

impl Serialize for TimeoutCerificate {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let cert = Certificate::from(self.clone());
cert.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for TimeoutCerificate {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Certificate::deserialize(deserializer).map(TimeoutCerificate::from)
}
}

#[cfg(with_testing)]
impl<T: Eq + PartialEq> Eq for CertificateT<T> {}
#[cfg(with_testing)]
Expand Down Expand Up @@ -129,7 +148,23 @@ impl From<ValidatedBlockCertificate> for Certificate {
}
}

// TODO(#2842): In practice, it should be HashedCertificateValue = Hashed<CertificateValue>
impl From<TimeoutCerificate> for Certificate {
fn from(cert: TimeoutCerificate) -> Certificate {
let TimeoutCerificate {
value,
round,
signatures,
} = cert;
let timeout = value.into_inner();
Certificate::new(
HashedCertificateValue::new_timeout(timeout.chain_id, timeout.height, timeout.epoch),
round,
signatures,
)
}
}

// In practice, it should be HashedCertificateValue = Hashed<CertificateValue>
// but [`HashedCertificateValue`] is used in too many places to change it now.
/// Wrapper type around hashed instance of `T` type.
pub struct Hashed<T> {
Expand Down Expand Up @@ -319,3 +354,22 @@ impl TryFrom<Certificate> for ConfirmedBlockCertificate {
}
}
}

impl From<Certificate> for TimeoutCerificate {
fn from(cert: Certificate) -> Self {
let signatures = cert.signatures().clone();
let hash = cert.value.hash();
match cert.value.into_inner() {
CertificateValue::Timeout {
chain_id,
epoch,
height,
} => Self {
value: Hashed::unchecked_new(Timeout::new(chain_id, height, epoch), hash),
round: cert.round,
signatures,
},
_ => panic!("Expected a timeout certificate"),
}
}
}
22 changes: 10 additions & 12 deletions linera-chain/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,13 @@ use linera_execution::committee::Epoch;
use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng};
use rand_distr::{Distribution, WeightedAliasIndex};
use serde::{Deserialize, Serialize};
use tracing::error;

use crate::{
data_types::{
Block, BlockExecutionOutcome, BlockProposal, Certificate, CertificateValue,
HashedCertificateValue, LiteVote, ProposalContent, Vote,
Block, BlockExecutionOutcome, BlockProposal, CertificateValue, HashedCertificateValue,
LiteVote, ProposalContent, Vote,
},
types::{ConfirmedBlockCertificate, ValidatedBlockCertificate},
types::{ConfirmedBlockCertificate, TimeoutCerificate, ValidatedBlockCertificate},
ChainError,
};

Expand Down Expand Up @@ -117,7 +116,7 @@ pub struct ChainManager {
/// validator).
pub locked: Option<ValidatedBlockCertificate>,
/// Latest leader timeout certificate we have received.
pub timeout: Option<Certificate>,
pub timeout: Option<TimeoutCerificate>,
/// Latest vote we have cast, to validate or confirm.
pub pending: Option<Vote>,
/// Latest timeout vote we cast.
Expand Down Expand Up @@ -470,12 +469,11 @@ impl ChainManager {

/// Updates the round number and timer if the timeout certificate is from a higher round than
/// any known certificate.
pub fn handle_timeout_certificate(&mut self, certificate: Certificate, local_time: Timestamp) {
if !certificate.value().is_timeout() {
// Unreachable: This is only called with timeout certificates.
error!("Unexpected certificate; expected leader timeout");
return;
}
pub fn handle_timeout_certificate(
&mut self,
certificate: TimeoutCerificate,
local_time: Timestamp,
) {
let round = certificate.round;
if let Some(known_certificate) = &self.timeout {
if known_certificate.round >= round {
Expand Down Expand Up @@ -565,7 +563,7 @@ pub struct ChainManagerInfo {
/// validator).
pub requested_locked: Option<Box<ValidatedBlockCertificate>>,
/// Latest timeout certificate we have seen.
pub timeout: Option<Box<Certificate>>,
pub timeout: Option<Box<TimeoutCerificate>>,
/// Latest vote we cast (either to validate or to confirm a block).
pub pending: Option<LiteVote>,
/// Latest timeout vote we cast.
Expand Down
4 changes: 2 additions & 2 deletions linera-core/src/chain_worker/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use linera_chain::{
Block, BlockProposal, Certificate, ExecutedBlock, HashedCertificateValue, MessageBundle,

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / execution-wasmtime-test

unused import: `Certificate`

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / check-outdated-cli-md

unused import: `Certificate`

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / lint-check-linera-service-graphql-schema

unused import: `Certificate`

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / web

unused import: `Certificate`

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / benchmark-test

unused import: `Certificate`

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / remote-net-test

unused import: `Certificate`

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / lint-check-all-features

unused import: `Certificate`

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / lint-cargo-clippy

unused import: `Certificate`

Check failure on line 19 in linera-core/src/chain_worker/actor.rs

View workflow job for this annotation

GitHub Actions / storage-service-tests

unused import: `Certificate`
Origin, Target,
},
types::{ConfirmedBlockCertificate, ValidatedBlockCertificate},
types::{ConfirmedBlockCertificate, TimeoutCerificate, ValidatedBlockCertificate},
ChainStateView,
};
use linera_execution::{
Expand Down Expand Up @@ -84,7 +84,7 @@ where

/// Process a leader timeout issued for this multi-owner chain.
ProcessTimeout {
certificate: Certificate,
certificate: TimeoutCerificate,
callback: oneshot::Sender<Result<(ChainInfoResponse, NetworkActions), WorkerError>>,
},

Expand Down
38 changes: 15 additions & 23 deletions linera-core/src/chain_worker/state/attempted_changes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ use linera_base::{
};
use linera_chain::{
data_types::{
BlockExecutionOutcome, BlockProposal, Certificate, CertificateValue, MessageBundle, Origin,
Target,
BlockExecutionOutcome, BlockProposal, Certificate, MessageBundle, Origin, Target,
},
manager,
types::{ConfirmedBlockCertificate, ValidatedBlockCertificate},
types::{ConfirmedBlockCertificate, TimeoutCerificate, ValidatedBlockCertificate},
ChainStateView,
};
use linera_execution::{
Expand Down Expand Up @@ -68,17 +67,8 @@ where
/// Processes a leader timeout issued for this multi-owner chain.
pub(super) async fn process_timeout(
&mut self,
certificate: Certificate,
certificate: TimeoutCerificate,
) -> Result<(ChainInfoResponse, NetworkActions), WorkerError> {
let (chain_id, height, epoch) = match certificate.value() {
CertificateValue::Timeout {
chain_id,
height,
epoch,
..
} => (*chain_id, *height, *epoch),
_ => panic!("Expecting a leader timeout certificate"),
};
// Check that the chain is active and ready for this timeout.
// Verify the certificate. Returns a catch-all error to make client code more robust.
self.state.ensure_is_active()?;
Expand All @@ -90,11 +80,11 @@ where
.current_committee()
.expect("chain is active");
ensure!(
epoch == chain_epoch,
certificate.inner().epoch == chain_epoch,
WorkerError::InvalidEpoch {
chain_id,
chain_id: certificate.inner().chain_id,
chain_epoch,
epoch
epoch: certificate.inner().epoch
}
);
certificate.check(committee)?;
Expand All @@ -104,27 +94,29 @@ where
.chain
.tip_state
.get()
.already_validated_block(height)?
.already_validated_block(certificate.inner().height)?
{
return Ok((
ChainInfoResponse::new(&self.state.chain, self.state.config.key_pair()),
actions,
));
}
let old_round = self.state.chain.manager.get().current_round;
let timeout_chainid = certificate.inner().chain_id;
let timeout_height = certificate.inner().height;
self.state
.chain
.manager
.get_mut()
.handle_timeout_certificate(
certificate.clone(),
self.state.storage.clock().current_time(),
);
.handle_timeout_certificate(certificate, self.state.storage.clock().current_time());
let round = self.state.chain.manager.get().current_round;
if round > old_round {
actions.notifications.push(Notification {
chain_id,
reason: Reason::NewRound { height, round },
chain_id: timeout_chainid,
reason: Reason::NewRound {
height: timeout_height,
round,
},
})
}
let info = ChainInfoResponse::new(&self.state.chain, self.state.config.key_pair());
Expand Down
4 changes: 2 additions & 2 deletions linera-core/src/chain_worker/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use linera_chain::{
Block, BlockProposal, Certificate, ExecutedBlock, HashedCertificateValue, Medium,

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / execution-wasmtime-test

unused import: `Certificate`

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / check-outdated-cli-md

unused import: `Certificate`

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / lint-check-linera-service-graphql-schema

unused import: `Certificate`

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / web

unused import: `Certificate`

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / benchmark-test

unused import: `Certificate`

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / remote-net-test

unused import: `Certificate`

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / lint-check-all-features

unused import: `Certificate`

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / lint-cargo-clippy

unused import: `Certificate`

Check failure on line 22 in linera-core/src/chain_worker/state/mod.rs

View workflow job for this annotation

GitHub Actions / storage-service-tests

unused import: `Certificate`
MessageBundle, Origin, Target,
},
types::{ConfirmedBlockCertificate, ValidatedBlockCertificate},
types::{ConfirmedBlockCertificate, TimeoutCerificate, ValidatedBlockCertificate},
ChainError, ChainStateView,
};
use linera_execution::{
Expand Down Expand Up @@ -188,7 +188,7 @@ where
/// Processes a leader timeout issued for this multi-owner chain.
pub(super) async fn process_timeout(
&mut self,
certificate: Certificate,
certificate: TimeoutCerificate,
) -> Result<(ChainInfoResponse, NetworkActions), WorkerError> {
ChainWorkerStateWithAttemptedChanges::new(self)
.await
Expand Down
4 changes: 2 additions & 2 deletions linera-core/src/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,8 @@ where
}
}
if let Some(cert) = manager.timeout {
if cert.value().is_timeout() && cert.value().chain_id() == chain_id {
self.send_certificate(cert, CrossChainMessageDelivery::NonBlocking)
if cert.inner().chain_id == chain_id {
self.send_certificate(cert.into(), CrossChainMessageDelivery::NonBlocking)
.await?;
}
}
Expand Down
15 changes: 8 additions & 7 deletions linera-core/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use linera_chain::{
Block, BlockExecutionOutcome, BlockProposal, Certificate, CertificateValue, ExecutedBlock,
HashedCertificateValue, LiteCertificate, MessageBundle, Origin, Target,
},
types::{ConfirmedBlockCertificate, ValidatedBlockCertificate},
types::{ConfirmedBlockCertificate, TimeoutCerificate, ValidatedBlockCertificate},
ChainStateView,
};
use linera_execution::{committee::Epoch, Query, Response};
Expand Down Expand Up @@ -514,12 +514,10 @@ where
#[instrument(level = "trace", skip(self, certificate))]
async fn process_timeout(
&self,
certificate: Certificate,
certificate: TimeoutCerificate,
) -> Result<(ChainInfoResponse, NetworkActions), WorkerError> {
let CertificateValue::Timeout { chain_id, .. } = certificate.value() else {
panic!("Expecting a leader timeout certificate");
};
self.query_chain_worker(*chain_id, move |callback| {
let chain_id = certificate.inner().chain_id;
self.query_chain_worker(chain_id, move |callback| {
ChainWorkerRequest::ProcessTimeout {
certificate,
callback,
Expand Down Expand Up @@ -792,7 +790,10 @@ where
}
CertificateValue::Timeout { .. } => {
// Handle the leader timeout.
self.process_timeout(certificate).await?
// Note: This conversion panics if `certificate` is not a timeout certificate
// but we just checked that it is.
let timeout_certificate = certificate.into();
self.process_timeout(timeout_certificate).await?
}
};

Expand Down

0 comments on commit f1a1bf1

Please sign in to comment.