Skip to content

Commit 25aaea3

Browse files
authored
Improve code for validator checks (#3881)
## Motivation Refactor the code for query_validator(s) ## Proposal - Introduce some helper functions in `linera_client` @Twey I made the new functions available to `web` , adding a dependency to `linera-version`. I figured version checks could be helpful in the web as well. ## Test Plan CI + tested manually
1 parent c01fd11 commit 25aaea3

File tree

11 files changed

+217
-149
lines changed

11 files changed

+217
-149
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

linera-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ linera-execution.workspace = true
6868
linera-rpc.workspace = true
6969
linera-sdk = { workspace = true, optional = true }
7070
linera-storage.workspace = true
71+
linera-version = { workspace = true }
7172
linera-views.workspace = true
7273
num-format = { workspace = true, optional = true }
7374
prometheus-parse = { workspace = true, optional = true }

linera-client/src/client_context.rs

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{collections::HashSet, sync::Arc};
88
use async_trait::async_trait;
99
use futures::Future;
1010
use linera_base::{
11-
crypto::{CryptoHash, Signer},
11+
crypto::{CryptoHash, Signer, ValidatorPublicKey},
1212
data_types::{BlockHeight, ChainDescription, Timestamp},
1313
identifiers::{Account, AccountOwner, BlobId, BlobType, ChainId},
1414
ownership::ChainOwnership,
@@ -17,13 +17,14 @@ use linera_base::{
1717
use linera_chain::types::ConfirmedBlockCertificate;
1818
use linera_core::{
1919
client::{BlanketMessagePolicy, ChainClient, Client, MessagePolicy, PendingProposal},
20-
data_types::ClientOutcome,
20+
data_types::{ChainInfoQuery, ClientOutcome},
2121
join_set_ext::JoinSet,
22-
node::CrossChainMessageDelivery,
23-
Environment, JoinSetExt,
22+
node::{CrossChainMessageDelivery, ValidatorNode},
23+
Environment, JoinSetExt as _,
2424
};
2525
use linera_rpc::node_provider::{NodeOptions, NodeProvider};
2626
use linera_storage::Storage;
27+
use linera_version::VersionInfo;
2728
use linera_views::views::ViewError;
2829
use thiserror_context::Context;
2930
use tracing::{debug, info};
@@ -537,6 +538,93 @@ where
537538
info!("New preferred owner set");
538539
Ok(())
539540
}
541+
542+
pub async fn check_compatible_version_info(
543+
&self,
544+
address: &str,
545+
node: &impl ValidatorNode,
546+
) -> Result<VersionInfo, Error> {
547+
match node.get_version_info().await {
548+
Ok(version_info) if version_info.is_compatible_with(&linera_version::VERSION_INFO) => {
549+
info!(
550+
"Version information for validator {address}: {}",
551+
version_info
552+
);
553+
Ok(version_info)
554+
}
555+
Ok(version_info) => Err(error::Inner::UnexpectedVersionInfo {
556+
remote: Box::new(version_info),
557+
local: Box::new(linera_version::VERSION_INFO.clone()),
558+
}
559+
.into()),
560+
Err(error) => Err(error::Inner::UnavailableVersionInfo {
561+
address: address.to_string(),
562+
error: Box::new(error),
563+
}
564+
.into()),
565+
}
566+
}
567+
568+
pub async fn check_matching_network_description(
569+
&self,
570+
address: &str,
571+
node: &impl ValidatorNode,
572+
) -> Result<CryptoHash, Error> {
573+
let network_description = self.wallet().genesis_config().network_description();
574+
match node.get_network_description().await {
575+
Ok(description) => {
576+
if description == network_description {
577+
Ok(description.genesis_config_hash)
578+
} else {
579+
Err(error::Inner::UnexpectedNetworkDescription {
580+
remote: Box::new(description),
581+
local: Box::new(network_description),
582+
}
583+
.into())
584+
}
585+
}
586+
Err(error) => Err(error::Inner::UnavailableNetworkDescription {
587+
address: address.to_string(),
588+
error: Box::new(error),
589+
}
590+
.into()),
591+
}
592+
}
593+
594+
pub async fn check_validator_chain_info_response(
595+
&self,
596+
public_key: Option<&ValidatorPublicKey>,
597+
address: &str,
598+
node: &impl ValidatorNode,
599+
chain_id: ChainId,
600+
) -> Result<(), Error> {
601+
let query = ChainInfoQuery::new(chain_id);
602+
match node.handle_chain_info_query(query).await {
603+
Ok(response) => {
604+
info!(
605+
"Validator {address} sees chain {chain_id} at block height {} and epoch {:?}",
606+
response.info.next_block_height, response.info.epoch,
607+
);
608+
if let Some(public_key) = public_key {
609+
if response.check(public_key).is_ok() {
610+
info!("Signature for public key {public_key} is OK.");
611+
} else {
612+
return Err(error::Inner::InvalidSignature {
613+
public_key: *public_key,
614+
}
615+
.into());
616+
}
617+
}
618+
Ok(())
619+
}
620+
Err(error) => Err(error::Inner::UnavailableChainInfo {
621+
address: address.to_string(),
622+
chain_id,
623+
error: Box::new(error),
624+
}
625+
.into()),
626+
}
627+
}
540628
}
541629

542630
#[cfg(feature = "fs")]

linera-client/src/config.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,14 @@ impl GenesisConfig {
336336
pub fn hash(&self) -> CryptoHash {
337337
CryptoHash::new(self)
338338
}
339+
340+
pub fn network_description(&self) -> NetworkDescription {
341+
NetworkDescription {
342+
name: self.network_name.clone(),
343+
genesis_config_hash: CryptoHash::new(self),
344+
genesis_timestamp: self.timestamp,
345+
}
346+
}
339347
}
340348

341349
/// The configuration file for the linera-exporter.

linera-client/src/error.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// Copyright (c) Zefchain Labs, Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
use linera_base::{crypto::ValidatorPublicKey, identifiers::ChainId};
5+
use linera_core::node::NodeError;
6+
use linera_storage::NetworkDescription;
7+
use linera_version::VersionInfo;
48
use thiserror_context::Context;
59

610
#[cfg(feature = "benchmark")]
@@ -35,6 +39,34 @@ pub(crate) enum Inner {
3539
#[cfg(feature = "benchmark")]
3640
#[error("Benchmark error: {0}")]
3741
Benchmark(#[from] BenchmarkError),
42+
#[error("Validator version {remote} is not compatible with local version {local}.")]
43+
UnexpectedVersionInfo {
44+
remote: Box<VersionInfo>,
45+
local: Box<VersionInfo>,
46+
},
47+
#[error("Failed to get version information for validator {address}: {error}")]
48+
UnavailableVersionInfo {
49+
address: String,
50+
error: Box<NodeError>,
51+
},
52+
#[error("Validator's network description {remote:?} does not match our own: {local:?}.")]
53+
UnexpectedNetworkDescription {
54+
remote: Box<NetworkDescription>,
55+
local: Box<NetworkDescription>,
56+
},
57+
#[error("Failed to get network description for validator {address}: {error}")]
58+
UnavailableNetworkDescription {
59+
address: String,
60+
error: Box<NodeError>,
61+
},
62+
#[error("Signature for public key {public_key} is invalid.")]
63+
InvalidSignature { public_key: ValidatorPublicKey },
64+
#[error("Failed to get chain info for validator {address} and chain {chain_id}: {error}")]
65+
UnavailableChainInfo {
66+
address: String,
67+
chain_id: ChainId,
68+
error: Box<NodeError>,
69+
},
3870
}
3971

4072
thiserror_context::impl_context!(Error(Inner));

0 commit comments

Comments
 (0)