From 99042b811d3b2662e653bfaae94dc430fb60be63 Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 13 Nov 2024 17:23:52 +0000 Subject: [PATCH 1/7] SignatureAggregator over GenericCertificate --- linera-chain/src/data_types.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/linera-chain/src/data_types.rs b/linera-chain/src/data_types.rs index b8b1a6a2b8c..290f676e862 100644 --- a/linera-chain/src/data_types.rs +++ b/linera-chain/src/data_types.rs @@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize}; use crate::{ types::{ - Certificate, CertificateValue, HashedCertificateValue, LiteCertificate, + CertificateValue, GenericCertificate, Hashed, HashedCertificateValue, LiteCertificate, ValidatedBlockCertificate, }, ChainError, @@ -782,21 +782,21 @@ impl LiteVote { } } -pub struct SignatureAggregator<'a> { +pub struct SignatureAggregator<'a, T> { committee: &'a Committee, weight: u64, used_validators: HashSet, - partial: Certificate, + partial: GenericCertificate, } -impl<'a> SignatureAggregator<'a> { +impl<'a, T> SignatureAggregator<'a, T> { /// Starts aggregating signatures for the given value into a certificate. - pub fn new(value: HashedCertificateValue, round: Round, committee: &'a Committee) -> Self { + pub fn new(value: Hashed, round: Round, committee: &'a Committee) -> Self { Self { committee, weight: 0, used_validators: HashSet::new(), - partial: Certificate::new(value, round, Vec::new()), + partial: GenericCertificate::new(value, round, Vec::new()), } } @@ -807,7 +807,10 @@ impl<'a> SignatureAggregator<'a> { &mut self, validator: ValidatorName, signature: Signature, - ) -> Result, ChainError> { + ) -> Result>, ChainError> + where + T: Clone, + { let hash_and_round = ValueHashAndRound(self.partial.hash(), self.partial.round); signature.check(&hash_and_round, validator.0)?; // Check that each validator only appears once. From 7ecabc9c2cbf6fe71554d4aa1d8877fe7fbf1540 Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 13 Nov 2024 19:32:58 +0000 Subject: [PATCH 2/7] Vote is generic over value it contains --- linera-chain/src/block.rs | 17 ++++- linera-chain/src/certificate/hashed.rs | 41 ++++++++++- linera-chain/src/certificate/mod.rs | 2 +- linera-chain/src/certificate/value.rs | 38 +++-------- linera-chain/src/data_types.rs | 68 +++++++++++++++---- linera-chain/src/manager.rs | 8 +-- linera-chain/src/test.rs | 10 +-- .../src/unit_tests/data_types_tests.rs | 5 +- linera-client/src/client_context.rs | 12 +++- 9 files changed, 144 insertions(+), 57 deletions(-) diff --git a/linera-chain/src/block.rs b/linera-chain/src/block.rs index 39db4e2c47c..1ee9be3d4e0 100644 --- a/linera-chain/src/block.rs +++ b/linera-chain/src/block.rs @@ -8,7 +8,10 @@ use linera_base::{crypto::BcsHashable, data_types::BlockHeight, identifiers::Cha use linera_execution::committee::Epoch; use serde::{Deserialize, Serialize}; -use crate::{data_types::ExecutedBlock, types::HashedCertificateValue}; +use crate::{ + data_types::ExecutedBlock, + types::{CertificateValue, Hashed, HashedCertificateValue}, +}; /// Wrapper around an `ExecutedBlock` that has been validated. #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)] @@ -109,3 +112,15 @@ impl From for HashedCertificateValue { HashedCertificateValue::new_timeout(value.chain_id, value.height, value.epoch) } } + +impl From for Hashed { + fn from(hv: HashedCertificateValue) -> Hashed { + let hash = hv.hash(); + match hv.into_inner() { + CertificateValue::Timeout(timeout) => Hashed::unchecked_new(timeout, hash), + _ => panic!("Expected a Timeout value"), + } + } +} + +impl BcsHashable for Timeout {} diff --git a/linera-chain/src/certificate/hashed.rs b/linera-chain/src/certificate/hashed.rs index 626498bdbd2..20019a77d51 100644 --- a/linera-chain/src/certificate/hashed.rs +++ b/linera-chain/src/certificate/hashed.rs @@ -3,7 +3,13 @@ // SPDX-License-Identifier: Apache-2.0 use custom_debug_derive::Debug; -use linera_base::crypto::{BcsHashable, CryptoHash}; +use linera_base::{ + crypto::{BcsHashable, CryptoHash}, + identifiers::ChainId, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use crate::data_types::LiteValue; /// Wrapper type around hashed instance of `T` type. #[derive(Debug)] @@ -46,6 +52,34 @@ impl Hashed { pub fn into_inner(self) -> T { self.value } + + pub fn lite(&self) -> LiteValue + where + T: Has, + { + LiteValue { + value_hash: self.hash, + chain_id: *Has::::get(&self.value), + } + } +} + +impl Serialize for Hashed { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.value.serialize(serializer) + } +} + +impl<'de, T: DeserializeOwned + BcsHashable> Deserialize<'de> for Hashed { + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + Ok(Hashed::new(T::deserialize(deserializer)?)) + } } impl Clone for Hashed { @@ -66,3 +100,8 @@ impl PartialEq for Hashed { #[cfg(with_testing)] impl Eq for Hashed {} + +//TODO: Move +pub trait Has { + fn get(&self) -> &T; +} diff --git a/linera-chain/src/certificate/mod.rs b/linera-chain/src/certificate/mod.rs index dc3faf18784..f363ec14c8d 100644 --- a/linera-chain/src/certificate/mod.rs +++ b/linera-chain/src/certificate/mod.rs @@ -13,7 +13,7 @@ mod value; use std::borrow::Cow; pub use generic::GenericCertificate; -pub use hashed::Hashed; +pub use hashed::{Has, Hashed}; use linera_base::{ crypto::Signature, data_types::Round, diff --git a/linera-chain/src/certificate/value.rs b/linera-chain/src/certificate/value.rs index 0e885d98da6..157b4cf85a2 100644 --- a/linera-chain/src/certificate/value.rs +++ b/linera-chain/src/certificate/value.rs @@ -2,16 +2,21 @@ // Copyright (c) Zefchain Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use linera_base::{crypto::CryptoHash, data_types::BlockHeight, ensure, identifiers::ChainId}; +use linera_base::{ + crypto::{BcsHashable, CryptoHash}, + data_types::BlockHeight, + ensure, + identifiers::ChainId, +}; use linera_execution::committee::Epoch; -use serde::{Deserialize, Deserializer, Serialize}; +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, LiteValue}, + data_types::{Block, ExecutedBlock}, ChainError, }; @@ -127,6 +132,8 @@ impl CertificateValue { } } +impl BcsHashable for CertificateValue {} + /// A statement to be certified by the validators, with its hash. pub type HashedCertificateValue = Hashed; @@ -150,13 +157,6 @@ impl HashedCertificateValue { CertificateValue::Timeout(Timeout::new(chain_id, height, epoch)).into() } - pub fn lite(&self) -> LiteValue { - LiteValue { - value_hash: self.hash(), - chain_id: self.inner().chain_id(), - } - } - /// Returns the corresponding `ConfirmedBlock`, if this is a `ValidatedBlock`. pub fn validated_to_confirmed(&self) -> Option { match self.inner() { @@ -168,24 +168,6 @@ impl HashedCertificateValue { } } -impl Serialize for HashedCertificateValue { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.inner().serialize(serializer) - } -} - -impl<'a> Deserialize<'a> for HashedCertificateValue { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'a>, - { - Ok(CertificateValue::deserialize(deserializer)?.into()) - } -} - impl From for HashedCertificateValue { fn from(value: CertificateValue) -> HashedCertificateValue { value.with_hash() diff --git a/linera-chain/src/data_types.rs b/linera-chain/src/data_types.rs index 290f676e862..0508e17e650 100644 --- a/linera-chain/src/data_types.rs +++ b/linera-chain/src/data_types.rs @@ -20,11 +20,12 @@ use linera_execution::{ system::OpenChainConfig, Message, MessageKind, Operation, SystemMessage, SystemOperation, }; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; use crate::{ + block::Timeout, types::{ - CertificateValue, GenericCertificate, Hashed, HashedCertificateValue, LiteCertificate, + CertificateValue, GenericCertificate, Has, Hashed, LiteCertificate, ValidatedBlockCertificate, }, ChainError, @@ -421,17 +422,56 @@ pub struct LiteValue { struct ValueHashAndRound(CryptoHash, Round); /// A vote on a statement from a validator. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Vote { - pub value: HashedCertificateValue, +#[derive(Clone, Debug, Serialize)] +pub struct Vote { + pub value: Hashed, pub round: Round, pub validator: ValidatorName, pub signature: Signature, } -impl Vote { +impl<'de, T: DeserializeOwned + BcsHashable> Deserialize<'de> for Vote { + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + #[derive(Debug, Deserialize)] + struct VoteHelper { + value: T, + round: Round, + validator: ValidatorName, + signature: Signature, + } + + let VoteHelper { + value, + round, + validator, + signature, + } = VoteHelper::deserialize(deserializer)?; + + Ok(Vote { + value: Hashed::new(value), + round, + validator, + signature, + }) + } +} + +impl Has for CertificateValue { + fn get(&self) -> &ChainId { + match self { + CertificateValue::ConfirmedBlock(confirmed) => &confirmed.inner().block.chain_id, + CertificateValue::ValidatedBlock(validated) => &validated.inner().block.chain_id, + CertificateValue::Timeout(Timeout { chain_id, .. }) => chain_id, + } + } +} + +impl Vote { /// Use signing key to create a signed object. - pub fn new(value: HashedCertificateValue, round: Round, key_pair: &KeyPair) -> Self { + pub fn new(value: Hashed, round: Round, key_pair: &KeyPair) -> Self { let hash_and_round = ValueHashAndRound(value.hash(), round); let signature = Signature::new(&hash_and_round, key_pair); Self { @@ -443,7 +483,10 @@ impl Vote { } /// Returns the vote, with a `LiteValue` instead of the full value. - pub fn lite(&self) -> LiteVote { + pub fn lite(&self) -> LiteVote + where + T: Has, + { LiteVote { value: self.value.lite(), round: self.round, @@ -453,7 +496,7 @@ impl Vote { } /// Returns the value this vote is for. - pub fn value(&self) -> &CertificateValue { + pub fn value(&self) -> &T { self.value.inner() } } @@ -470,8 +513,9 @@ pub struct LiteVote { impl LiteVote { /// Returns the full vote, with the value, if it matches. - pub fn with_value(self, value: HashedCertificateValue) -> Option { - if self.value != value.lite() { + #[cfg(with_testing)] + pub fn with_value(self, value: Hashed) -> Option> { + if self.value.value_hash != value.hash() { return None; } Some(Vote { @@ -877,8 +921,6 @@ impl BcsSignable for ProposalContent {} impl BcsSignable for ValueHashAndRound {} -impl BcsHashable for CertificateValue {} - doc_scalar!( MessageAction, "Whether an incoming message is accepted or rejected." diff --git a/linera-chain/src/manager.rs b/linera-chain/src/manager.rs index 3a9ef40fece..aabf9690fe8 100644 --- a/linera-chain/src/manager.rs +++ b/linera-chain/src/manager.rs @@ -125,13 +125,13 @@ pub struct ChainManager { pub timeout: Option, /// Latest vote we have cast, to validate or confirm. #[debug(skip_if = Option::is_none)] - pub pending: Option, + pub pending: Option>, /// Latest timeout vote we cast. #[debug(skip_if = Option::is_none)] - pub timeout_vote: Option, + pub timeout_vote: Option>, /// Fallback vote we cast. #[debug(skip_if = Option::is_none)] - pub fallback_vote: Option, + pub fallback_vote: Option>, /// The time after which we are ready to sign a timeout certificate for the current round. #[debug(skip_if = Option::is_none)] pub round_timeout: Option, @@ -221,7 +221,7 @@ impl ChainManager { } /// Returns the most recent vote we cast. - pub fn pending(&self) -> Option<&Vote> { + pub fn pending(&self) -> Option<&Vote> { self.pending.as_ref() } diff --git a/linera-chain/src/test.rs b/linera-chain/src/test.rs index a97a44e70d4..37455b0f396 100644 --- a/linera-chain/src/test.rs +++ b/linera-chain/src/test.rs @@ -16,7 +16,7 @@ use linera_execution::{ use crate::{ data_types::{Block, BlockProposal, IncomingBundle, PostedMessage, SignatureAggregator, Vote}, - types::{Certificate, HashedCertificateValue}, + types::{GenericCertificate, HashedCertificateValue}, }; /// Creates a new child of the given block, with the same timestamp. @@ -124,13 +124,13 @@ impl BlockTestExt for Block { } } -pub trait VoteTestExt: Sized { +pub trait VoteTestExt: Sized { /// Returns a certificate for a committee consisting only of this validator. - fn into_certificate(self) -> Certificate; + fn into_certificate(self) -> GenericCertificate; } -impl VoteTestExt for Vote { - fn into_certificate(self) -> Certificate { +impl VoteTestExt for Vote { + fn into_certificate(self) -> GenericCertificate { let state = ValidatorState { network_address: "".to_string(), votes: 100, diff --git a/linera-chain/src/unit_tests/data_types_tests.rs b/linera-chain/src/unit_tests/data_types_tests.rs index 0bae9d94442..b5dabd0eb7a 100644 --- a/linera-chain/src/unit_tests/data_types_tests.rs +++ b/linera-chain/src/unit_tests/data_types_tests.rs @@ -5,7 +5,10 @@ use linera_base::data_types::Amount; use super::*; -use crate::test::{make_first_block, BlockTestExt}; +use crate::{ + test::{make_first_block, BlockTestExt}, + types::HashedCertificateValue, +}; #[test] fn test_signed_values() { diff --git a/linera-client/src/client_context.rs b/linera-client/src/client_context.rs index 450c0d81701..97ea6a0a83a 100644 --- a/linera-client/src/client_context.rs +++ b/linera-client/src/client_context.rs @@ -37,7 +37,7 @@ use { identifiers::{AccountOwner, ApplicationId, Owner}, }, linera_chain::data_types::{Block, BlockProposal, ExecutedBlock, SignatureAggregator, Vote}, - linera_chain::types::Certificate, + linera_chain::types::{Certificate, GenericCertificate, Has}, linera_core::data_types::ChainInfoQuery, linera_execution::{ committee::Epoch, @@ -802,14 +802,20 @@ where } /// Tries to aggregate votes into certificates. - pub fn make_benchmark_certificates_from_votes(&self, votes: Vec) -> Vec { + pub fn make_benchmark_certificates_from_votes( + &self, + votes: Vec>, + ) -> Vec> + where + T: std::fmt::Debug + Clone + Has, + { let committee = self.wallet.genesis_config().create_committee(); let mut aggregators = HashMap::new(); let mut certificates = Vec::new(); let mut done_senders = HashSet::new(); for vote in votes { // We aggregate votes indexed by sender. - let chain_id = vote.value().chain_id(); + let chain_id = *Has::::get(vote.value()); if done_senders.contains(&chain_id) { continue; } From a1e3418b312b78ff0c0332e4d94608a6c510f5f1 Mon Sep 17 00:00:00 2001 From: deuszx Date: Wed, 13 Nov 2024 19:43:57 +0000 Subject: [PATCH 3/7] Use actual Timeout type in ChainManager instead --- linera-chain/src/block.rs | 8 +++++++- linera-chain/src/manager.rs | 9 +++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/linera-chain/src/block.rs b/linera-chain/src/block.rs index 1ee9be3d4e0..37864b8660e 100644 --- a/linera-chain/src/block.rs +++ b/linera-chain/src/block.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::{ data_types::ExecutedBlock, - types::{CertificateValue, Hashed, HashedCertificateValue}, + types::{CertificateValue, Has, Hashed, HashedCertificateValue}, }; /// Wrapper around an `ExecutedBlock` that has been validated. @@ -124,3 +124,9 @@ impl From for Hashed { } impl BcsHashable for Timeout {} + +impl Has for Timeout { + fn get(&self) -> &ChainId { + &self.chain_id + } +} diff --git a/linera-chain/src/manager.rs b/linera-chain/src/manager.rs index aabf9690fe8..e94c14bdaf9 100644 --- a/linera-chain/src/manager.rs +++ b/linera-chain/src/manager.rs @@ -84,6 +84,7 @@ use rand_distr::{Distribution, WeightedAliasIndex}; use serde::{Deserialize, Serialize}; use crate::{ + block::Timeout, data_types::{Block, BlockExecutionOutcome, BlockProposal, LiteVote, ProposalContent, Vote}, types::{ CertificateValue, ConfirmedBlockCertificate, HashedCertificateValue, TimeoutCertificate, @@ -128,10 +129,10 @@ pub struct ChainManager { pub pending: Option>, /// Latest timeout vote we cast. #[debug(skip_if = Option::is_none)] - pub timeout_vote: Option>, + pub timeout_vote: Option>, /// Fallback vote we cast. #[debug(skip_if = Option::is_none)] - pub fallback_vote: Option>, + pub fallback_vote: Option>, /// The time after which we are ready to sign a timeout certificate for the current round. #[debug(skip_if = Option::is_none)] pub round_timeout: Option, @@ -318,7 +319,7 @@ impl ChainManager { } } let value = HashedCertificateValue::new_timeout(chain_id, height, epoch); - self.timeout_vote = Some(Vote::new(value, current_round, key_pair)); + self.timeout_vote = Some(Vote::new(value.into(), current_round, key_pair)); true } @@ -341,7 +342,7 @@ impl ChainManager { } let value = HashedCertificateValue::new_timeout(chain_id, height, epoch); let last_regular_round = Round::SingleLeader(u32::MAX); - self.fallback_vote = Some(Vote::new(value, last_regular_round, key_pair)); + self.fallback_vote = Some(Vote::new(value.into(), last_regular_round, key_pair)); true } From bdbad2f527b1de1ac3a773dee42ccd6d40577289 Mon Sep 17 00:00:00 2001 From: deuszx Date: Thu, 14 Nov 2024 17:15:00 +0000 Subject: [PATCH 4/7] Replace From with TryFrom --- linera-chain/src/block.rs | 13 +++++++------ linera-chain/src/manager.rs | 12 ++++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/linera-chain/src/block.rs b/linera-chain/src/block.rs index 37864b8660e..37da584e6c1 100644 --- a/linera-chain/src/block.rs +++ b/linera-chain/src/block.rs @@ -113,12 +113,13 @@ impl From for HashedCertificateValue { } } -impl From for Hashed { - fn from(hv: HashedCertificateValue) -> Hashed { - let hash = hv.hash(); - match hv.into_inner() { - CertificateValue::Timeout(timeout) => Hashed::unchecked_new(timeout, hash), - _ => panic!("Expected a Timeout value"), +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"), } } } diff --git a/linera-chain/src/manager.rs b/linera-chain/src/manager.rs index e94c14bdaf9..54f5598d7ff 100644 --- a/linera-chain/src/manager.rs +++ b/linera-chain/src/manager.rs @@ -319,7 +319,11 @@ impl ChainManager { } } let value = HashedCertificateValue::new_timeout(chain_id, height, epoch); - self.timeout_vote = Some(Vote::new(value.into(), current_round, key_pair)); + self.timeout_vote = Some(Vote::new( + value.try_into().expect("Timeout certificate"), + current_round, + key_pair, + )); true } @@ -342,7 +346,11 @@ impl ChainManager { } let value = HashedCertificateValue::new_timeout(chain_id, height, epoch); let last_regular_round = Round::SingleLeader(u32::MAX); - self.fallback_vote = Some(Vote::new(value.into(), last_regular_round, key_pair)); + self.fallback_vote = Some(Vote::new( + value.try_into().expect("Timeout certificate"), + last_regular_round, + key_pair, + )); true } From dcf0a66b2491c12b1756262da6e08cdaa8584fbb Mon Sep 17 00:00:00 2001 From: deuszx Date: Thu, 14 Nov 2024 20:31:30 +0000 Subject: [PATCH 5/7] Replace TODO with a comment. --- linera-chain/src/certificate/hashed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linera-chain/src/certificate/hashed.rs b/linera-chain/src/certificate/hashed.rs index 20019a77d51..a53ba2bc6c0 100644 --- a/linera-chain/src/certificate/hashed.rs +++ b/linera-chain/src/certificate/hashed.rs @@ -101,7 +101,7 @@ impl PartialEq for Hashed { #[cfg(with_testing)] impl Eq for Hashed {} -//TODO: Move +/// A constraint summing a value of type `T`. pub trait Has { fn get(&self) -> &T; } From b4e8e64d208ddef2025be12f8031485aa2dcf4d3 Mon Sep 17 00:00:00 2001 From: deuszx Date: Thu, 14 Nov 2024 21:17:22 +0000 Subject: [PATCH 6/7] Replace hand-written Deserialize for Vote with derive --- linera-chain/src/data_types.rs | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/linera-chain/src/data_types.rs b/linera-chain/src/data_types.rs index 0508e17e650..ed8704e5327 100644 --- a/linera-chain/src/data_types.rs +++ b/linera-chain/src/data_types.rs @@ -20,7 +20,7 @@ use linera_execution::{ system::OpenChainConfig, Message, MessageKind, Operation, SystemMessage, SystemOperation, }; -use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ block::Timeout, @@ -422,7 +422,8 @@ pub struct LiteValue { struct ValueHashAndRound(CryptoHash, Round); /// A vote on a statement from a validator. -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(bound(deserialize = "T: DeserializeOwned + BcsHashable"))] pub struct Vote { pub value: Hashed, pub round: Round, @@ -430,35 +431,6 @@ pub struct Vote { pub signature: Signature, } -impl<'de, T: DeserializeOwned + BcsHashable> Deserialize<'de> for Vote { - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - #[derive(Debug, Deserialize)] - struct VoteHelper { - value: T, - round: Round, - validator: ValidatorName, - signature: Signature, - } - - let VoteHelper { - value, - round, - validator, - signature, - } = VoteHelper::deserialize(deserializer)?; - - Ok(Vote { - value: Hashed::new(value), - round, - validator, - signature, - }) - } -} - impl Has for CertificateValue { fn get(&self) -> &ChainId { match self { From 07bb76a04bcb7bcedf50b3170701e5a31b33fbea Mon Sep 17 00:00:00 2001 From: deuszx Date: Thu, 14 Nov 2024 21:24:36 +0000 Subject: [PATCH 7/7] Simplify call to extract ChainId --- linera-chain/src/certificate/hashed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linera-chain/src/certificate/hashed.rs b/linera-chain/src/certificate/hashed.rs index a53ba2bc6c0..d03d225a27e 100644 --- a/linera-chain/src/certificate/hashed.rs +++ b/linera-chain/src/certificate/hashed.rs @@ -59,7 +59,7 @@ impl Hashed { { LiteValue { value_hash: self.hash, - chain_id: *Has::::get(&self.value), + chain_id: *self.value.get(), } } }