diem_types/
ledger_info.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4// Copyright 2021 Conflux Foundation. All rights reserved.
5// Conflux is free software and distributed under GNU General Public License.
6// See http://www.gnu.org/licenses/
7
8use crate::{
9    account_address::AccountAddress,
10    block_info::{BlockInfo, Round},
11    epoch_state::EpochState,
12    on_chain_config::ValidatorSet,
13    transaction::Version,
14    validator_verifier::{ValidatorVerifier, VerifyError},
15};
16use diem_crypto::hash::HashValue;
17use diem_crypto_derive::{BCSCryptoHash, CryptoHasher};
18#[cfg(any(test, feature = "fuzzing"))]
19use proptest_derive::Arbitrary;
20use serde::{Deserialize, Deserializer, Serialize};
21use std::{
22    collections::BTreeMap,
23    fmt::{Display, Formatter},
24    ops::{Deref, DerefMut},
25};
26
27/// This structure serves a dual purpose.
28///
29/// First, if this structure is signed by 2f+1 validators it signifies the state
30/// of the ledger at version `version` -- it contains the transaction
31/// accumulator at that version which commits to all historical transactions.
32/// This structure may be expanded to include other information that is derived
33/// from that accumulator (e.g. the current time according to the time contract)
34/// to reduce the number of proofs a client must get.
35///
36/// Second, the structure contains a `consensus_data_hash` value. This is the
37/// hash of an internal data structure that represents a block that is voted on
38/// in HotStuff. If 2f+1 signatures are gathered on the same ledger info that
39/// represents a Quorum Certificate (QC) on the consensus data.
40///
41/// Combining these two concepts, when a validator votes on a block, B it votes
42/// for a LedgerInfo with the `version` being the latest version that will be
43/// committed if B gets 2f+1 votes. It sets `consensus_data_hash` to represent B
44/// so that if those 2f+1 votes are gathered a QC is formed on B.
45#[derive(
46    Clone,
47    Debug,
48    Eq,
49    PartialEq,
50    Serialize,
51    Deserialize,
52    CryptoHasher,
53    BCSCryptoHash,
54)]
55#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
56pub struct LedgerInfo {
57    commit_info: BlockInfo,
58
59    /// Hash of consensus specific data that is opaque to all parts of the
60    /// system other than consensus.
61    consensus_data_hash: HashValue,
62}
63
64impl Display for LedgerInfo {
65    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
66        write!(f, "LedgerInfo: [commit_info: {}]", self.commit_info())
67    }
68}
69
70impl LedgerInfo {
71    /// Constructs a `LedgerInfo` object based on the given commit info and vote
72    /// data hash.
73    pub fn new(commit_info: BlockInfo, consensus_data_hash: HashValue) -> Self {
74        Self {
75            commit_info,
76            consensus_data_hash,
77        }
78    }
79
80    /// Create a new LedgerInfo at genesis with the given genesis state and
81    /// initial validator set.
82    pub fn genesis(
83        genesis_state_root_hash: HashValue, validator_set: ValidatorSet,
84    ) -> Self {
85        Self::new(
86            BlockInfo::genesis(genesis_state_root_hash, validator_set),
87            HashValue::zero(),
88        )
89    }
90
91    #[cfg(any(test, feature = "fuzzing"))]
92    pub fn mock_genesis(validator_set: Option<ValidatorSet>) -> Self {
93        Self::new(BlockInfo::mock_genesis(validator_set), HashValue::zero())
94    }
95
96    /// The `BlockInfo` of a committed block.
97    pub fn commit_info(&self) -> &BlockInfo { &self.commit_info }
98
99    /// A series of wrapper functions for the data stored in the commit info.
100    /// For the detailed information, please refer to `BlockInfo`
101    pub fn epoch(&self) -> u64 { self.commit_info.epoch() }
102
103    pub fn next_block_epoch(&self) -> u64 {
104        self.commit_info.next_block_epoch()
105    }
106
107    pub fn round(&self) -> Round { self.commit_info.round() }
108
109    pub fn consensus_block_id(&self) -> HashValue { self.commit_info.id() }
110
111    pub fn transaction_accumulator_hash(&self) -> HashValue {
112        self.commit_info.executed_state_id()
113    }
114
115    pub fn version(&self) -> Version { self.commit_info.version() }
116
117    pub fn timestamp_usecs(&self) -> u64 { self.commit_info.timestamp_usecs() }
118
119    pub fn next_epoch_state(&self) -> Option<&EpochState> {
120        self.commit_info.next_epoch_state()
121    }
122
123    pub fn ends_epoch(&self) -> bool { self.next_epoch_state().is_some() }
124
125    /// Returns hash of consensus voting data in this `LedgerInfo`.
126    pub fn consensus_data_hash(&self) -> HashValue { self.consensus_data_hash }
127
128    pub fn pivot_decision(&self) -> Option<&PivotBlockDecision> {
129        self.commit_info.pivot_decision()
130    }
131
132    pub fn set_consensus_data_hash(&mut self, consensus_data_hash: HashValue) {
133        self.consensus_data_hash = consensus_data_hash;
134    }
135}
136
137/// Wrapper around LedgerInfoWithScheme to support future upgrades, this is the
138/// data being persisted.
139#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
140pub enum LedgerInfoWithSignatures {
141    V0(LedgerInfoWithV0),
142}
143
144impl Display for LedgerInfoWithSignatures {
145    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
146        match self {
147            LedgerInfoWithSignatures::V0(ledger) => write!(f, "{}", ledger),
148        }
149    }
150}
151
152// proxy to create LedgerInfoWithConsensus
153impl LedgerInfoWithSignatures {
154    pub fn new(
155        ledger_info: LedgerInfo,
156        signatures: BTreeMap<AccountAddress, ConsensusSignature>,
157    ) -> Self {
158        LedgerInfoWithSignatures::V0(LedgerInfoWithV0::new(
159            ledger_info,
160            signatures,
161        ))
162    }
163
164    pub fn genesis(
165        genesis_state_root_hash: HashValue, validator_set: ValidatorSet,
166    ) -> Self {
167        LedgerInfoWithSignatures::V0(LedgerInfoWithV0::genesis(
168            genesis_state_root_hash,
169            validator_set,
170        ))
171    }
172}
173
174// Temporary hack to avoid massive changes, it won't work when new variant comes
175// and needs proper dispatch at that time.
176impl Deref for LedgerInfoWithSignatures {
177    type Target = LedgerInfoWithV0;
178
179    fn deref(&self) -> &LedgerInfoWithV0 {
180        match &self {
181            LedgerInfoWithSignatures::V0(ledger) => ledger,
182        }
183    }
184}
185
186impl DerefMut for LedgerInfoWithSignatures {
187    fn deref_mut(&mut self) -> &mut LedgerInfoWithV0 {
188        match self {
189            LedgerInfoWithSignatures::V0(ref mut ledger) => ledger,
190        }
191    }
192}
193
194/// The validator node returns this structure which includes signatures
195/// from validators that confirm the state.  The client needs to only pass back
196/// the LedgerInfo element since the validator node doesn't need to know the
197/// signatures again when the client performs a query, those are only there for
198/// the client to be able to verify the state
199#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
200pub struct LedgerInfoWithV0 {
201    ledger_info: LedgerInfo,
202    /// The validator is identified by its account address: in order to verify
203    /// a signature one needs to retrieve the public key of the validator
204    /// for the given epoch.
205    signatures: BTreeMap<AccountAddress, ConsensusSignature>,
206}
207
208impl Display for LedgerInfoWithV0 {
209    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
210        write!(f, "{}", self.ledger_info)
211    }
212}
213
214impl LedgerInfoWithV0 {
215    pub fn new(
216        ledger_info: LedgerInfo,
217        signatures: BTreeMap<AccountAddress, ConsensusSignature>,
218    ) -> Self {
219        LedgerInfoWithV0 {
220            ledger_info,
221            signatures,
222        }
223    }
224
225    /// Create a new `LedgerInfoWithConsensus` at genesis with the given genesis
226    /// state and initial validator set.
227    ///
228    /// Note that the genesis `LedgerInfoWithConsensus` is unsigned. Validators
229    /// and FullNodes are configured with the same genesis transaction and
230    /// generate an identical genesis `LedgerInfoWithConsensus` independently.
231    /// In contrast, Clients will likely use a waypoint generated from the
232    /// genesis `LedgerInfo`.
233    pub fn genesis(
234        genesis_state_root_hash: HashValue, validator_set: ValidatorSet,
235    ) -> Self {
236        Self::new(
237            LedgerInfo::genesis(genesis_state_root_hash, validator_set),
238            BTreeMap::new(),
239        )
240    }
241
242    pub fn ledger_info(&self) -> &LedgerInfo { &self.ledger_info }
243
244    pub fn add_signature(
245        &mut self, validator: AccountAddress, signature: ConsensusSignature,
246    ) {
247        self.signatures.entry(validator).or_insert(signature);
248    }
249
250    pub fn remove_signature(&mut self, validator: AccountAddress) {
251        self.signatures.remove(&validator);
252    }
253
254    pub fn signatures(&self) -> &BTreeMap<AccountAddress, ConsensusSignature> {
255        &self.signatures
256    }
257
258    pub fn verify_signatures(
259        &self, validator: &ValidatorVerifier,
260    ) -> ::std::result::Result<(), VerifyError> {
261        validator.batch_verify_aggregated_signatures(
262            self.ledger_info(),
263            self.signatures(),
264        )
265    }
266}
267
268#[derive(Deserialize)]
269pub struct LedgerInfoWithV0Unchecked {
270    pub ledger_info: LedgerInfo,
271    /// The validator is identified by its account address: in order to verify
272    /// a signature one needs to retrieve the public key of the validator
273    /// for the given epoch.
274    pub signatures: BTreeMap<AccountAddress, BLSSignatureUnchecked>,
275}
276
277impl From<LedgerInfoWithV0Unchecked> for LedgerInfoWithV0 {
278    fn from(unchecked: LedgerInfoWithV0Unchecked) -> Self {
279        Self::new(
280            unchecked.ledger_info,
281            unchecked
282                .signatures
283                .into_iter()
284                .map(|(k, v)| (k, v.into()))
285                .collect(),
286        )
287    }
288}
289
290#[derive(Deserialize)]
291pub enum LedgerInfoWithSignaturesUnchecked {
292    V0(LedgerInfoWithV0Unchecked),
293}
294
295impl From<LedgerInfoWithSignaturesUnchecked> for LedgerInfoWithSignatures {
296    fn from(unchecked: LedgerInfoWithSignaturesUnchecked) -> Self {
297        match unchecked {
298            LedgerInfoWithSignaturesUnchecked::V0(l) => Self::V0(l.into()),
299        }
300    }
301}
302
303pub fn deserialize_ledger_info_unchecked<'de, D>(
304    deserializer: D,
305) -> Result<LedgerInfoWithSignatures, D::Error>
306where D: Deserializer<'de> {
307    LedgerInfoWithSignaturesUnchecked::deserialize(deserializer).map(Into::into)
308}
309
310//
311// Arbitrary implementation of LedgerInfoWithV0 (for fuzzing)
312//
313
314use crate::{
315    block_info::PivotBlockDecision, validator_config::ConsensusSignature,
316};
317#[cfg(any(test, feature = "fuzzing"))]
318use ::proptest::prelude::*;
319use diem_crypto::bls::BLSSignatureUnchecked;
320
321#[cfg(any(test, feature = "fuzzing"))]
322impl Arbitrary for LedgerInfoWithV0 {
323    type Parameters = ();
324    type Strategy = BoxedStrategy<Self>;
325
326    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
327        let dummy_signature = ConsensusSignature::dummy_signature();
328        (
329            proptest::arbitrary::any::<LedgerInfo>(),
330            proptest::collection::vec(
331                proptest::arbitrary::any::<AccountAddress>(),
332                0..100,
333            ),
334        )
335            .prop_map(move |(ledger_info, addresses)| {
336                let mut signatures = BTreeMap::new();
337                for address in addresses {
338                    let signature = dummy_signature.clone();
339                    signatures.insert(address, signature);
340                }
341                Self {
342                    ledger_info,
343                    signatures,
344                }
345            })
346            .boxed()
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353    use crate::validator_signer::ValidatorSigner;
354
355    #[test]
356    fn test_signatures_hash() {
357        let ledger_info =
358            LedgerInfo::new(BlockInfo::empty(), HashValue::random());
359
360        const NUM_SIGNERS: u8 = 7;
361        // Generate NUM_SIGNERS random signers.
362        let validator_signers: Vec<ValidatorSigner> = (0..NUM_SIGNERS)
363            .map(|i| ValidatorSigner::random([i; 32]))
364            .collect();
365        let mut author_to_signature_map = BTreeMap::new();
366        for validator in validator_signers.iter() {
367            author_to_signature_map
368                .insert(validator.author(), validator.sign(&ledger_info));
369        }
370
371        let ledger_info_with_signatures =
372            LedgerInfoWithV0::new(ledger_info.clone(), author_to_signature_map);
373
374        // Add the signatures in reverse order and ensure the serialization
375        // matches
376        let mut author_to_signature_map = BTreeMap::new();
377        for validator in validator_signers.iter().rev() {
378            author_to_signature_map
379                .insert(validator.author(), validator.sign(&ledger_info));
380        }
381
382        let ledger_info_with_signatures_reversed =
383            LedgerInfoWithV0::new(ledger_info, author_to_signature_map);
384
385        let ledger_info_with_signatures_bytes =
386            bcs::to_bytes(&ledger_info_with_signatures)
387                .expect("block serialization failed");
388        let ledger_info_with_signatures_reversed_bytes =
389            bcs::to_bytes(&ledger_info_with_signatures_reversed)
390                .expect("block serialization failed");
391
392        assert_eq!(
393            ledger_info_with_signatures_bytes,
394            ledger_info_with_signatures_reversed_bytes
395        );
396    }
397}