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
27impl State {
28    pub fn new(pending_proposal: Option<PendingProposal>) -> State {
29        State {
30            pending_proposal,
31            client_mutex: Arc::default(),
32        }
33    }
34
35    /// Clones the state. This must only be used to update the state, and one of the two clones
36    /// must be dropped.
37    pub(crate) fn clone_for_update_unchecked(&self) -> State {
38        State {
39            pending_proposal: self.pending_proposal.clone(),
40            client_mutex: Arc::clone(&self.client_mutex),
41        }
42    }
43
44    pub fn pending_proposal(&self) -> &Option<PendingProposal> {
45        &self.pending_proposal
46    }
47
48    pub(super) fn set_pending_proposal(&mut self, block: ProposedBlock, blobs: Vec<Blob>) {
49        if self
50            .pending_proposal
51            .as_ref()
52            .is_some_and(|pending| pending.block.height >= block.height)
53        {
54            tracing::error!(
55                "Not setting pending block at {}, because we already have a pending proposal.",
56                block.height
57            );
58            return;
59        }
60        assert_eq!(
61            block.published_blob_ids(),
62            BTreeSet::from_iter(blobs.iter().map(Blob::id))
63        );
64        self.pending_proposal = Some(PendingProposal { block, blobs });
65    }
66
67    pub(crate) fn update_from_info(&mut self, info: &ChainInfo) {
68        if let Some(pending) = &self.pending_proposal {
69            if pending.block.height < info.next_block_height {
70                tracing::debug!(
71                    "Clearing pending proposal: a block was committed at height {}",
72                    pending.block.height
73                );
74                self.clear_pending_proposal();
75            }
76        }
77    }
78
79    pub(super) fn clear_pending_proposal(&mut self) {
80        self.pending_proposal = None;
81    }
82
83    pub(super) fn client_mutex(&self) -> Arc<Mutex<()>> {
84        self.client_mutex.clone()
85    }
86}