linera_core/client/chain_client/
state.rs

1// Copyright (c) Facebook, Inc. and its affiliates.
2// Copyright (c) Zefchain Labs, Inc.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{collections::BTreeSet, sync::Arc};
6
7use linera_base::data_types::Blob;
8use linera_chain::data_types::ProposedBlock;
9use tokio::sync::Mutex;
10
11use super::super::PendingProposal;
12use crate::data_types::ChainInfo;
13
14/// The state of our interaction with a particular chain: how far we have synchronized it and
15/// whether we are currently attempting to propose a new block.
16pub struct State {
17    /// The block we are currently trying to propose for the next height, if any.
18    ///
19    /// This is always at the same height as `next_block_height`.
20    pending_proposal: Option<PendingProposal>,
21
22    /// A mutex that is held whilst we are performing operations that should not be
23    /// attempted by multiple clients at the same time.
24    client_mutex: Arc<Mutex<()>>,
25
26    /// If true, only download blocks for this chain without fetching manager values.
27    /// Use this for chains we're interested in observing but don't intend to propose blocks for.
28    follow_only: bool,
29}
30
31impl State {
32    pub fn new(pending_proposal: Option<PendingProposal>, follow_only: bool) -> State {
33        State {
34            pending_proposal,
35            client_mutex: Arc::default(),
36            follow_only,
37        }
38    }
39
40    /// Clones the state. This must only be used to update the state, and one of the two clones
41    /// must be dropped.
42    pub(crate) fn clone_for_update_unchecked(&self) -> State {
43        State {
44            pending_proposal: self.pending_proposal.clone(),
45            client_mutex: Arc::clone(&self.client_mutex),
46            follow_only: self.follow_only,
47        }
48    }
49
50    /// Returns whether this chain is in follow-only mode.
51    pub fn is_follow_only(&self) -> bool {
52        self.follow_only
53    }
54
55    /// Sets whether this chain is in follow-only mode.
56    pub fn set_follow_only(&mut self, follow_only: bool) {
57        self.follow_only = follow_only;
58    }
59
60    pub fn pending_proposal(&self) -> &Option<PendingProposal> {
61        &self.pending_proposal
62    }
63
64    pub(super) fn set_pending_proposal(&mut self, block: ProposedBlock, blobs: Vec<Blob>) {
65        if self
66            .pending_proposal
67            .as_ref()
68            .is_some_and(|pending| pending.block.height >= block.height)
69        {
70            tracing::error!(
71                "Not setting pending block at {}, because we already have a pending proposal.",
72                block.height
73            );
74            return;
75        }
76        assert_eq!(
77            block.published_blob_ids(),
78            BTreeSet::from_iter(blobs.iter().map(Blob::id))
79        );
80        self.pending_proposal = Some(PendingProposal { block, blobs });
81    }
82
83    pub(crate) fn update_from_info(&mut self, info: &ChainInfo) {
84        if let Some(pending) = &self.pending_proposal {
85            if pending.block.height < info.next_block_height {
86                tracing::debug!(
87                    "Clearing pending proposal: a block was committed at height {}",
88                    pending.block.height
89                );
90                self.clear_pending_proposal();
91            }
92        }
93    }
94
95    pub(super) fn clear_pending_proposal(&mut self) {
96        self.pending_proposal = None;
97    }
98
99    pub(super) fn client_mutex(&self) -> Arc<Mutex<()>> {
100        self.client_mutex.clone()
101    }
102}