Skip to content

Commit 860056d

Browse files
authored
Move ChainState to a submodule and make it guard its own invariant. (#2523)
* Use synchronize_from_validators instead of duplicating it. * Move client.rs → client/mod.rs * Make ChainState guard its own invariant * Add an error message for pending block at wrong height. * Cleanup: Use convenience methods. * Fix deadlock.
1 parent 1928a0a commit 860056d

File tree

3 files changed

+230
-133
lines changed

3 files changed

+230
-133
lines changed

linera-client/src/wallet.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,11 @@ impl Wallet {
174174
UserChain {
175175
chain_id: chain_client.chain_id(),
176176
key_pair,
177-
block_hash: state.block_hash,
178-
next_block_height: state.next_block_height,
179-
timestamp: state.timestamp,
180-
pending_block: state.pending_block.clone(),
181-
pending_blobs: state.pending_blobs.clone(),
177+
block_hash: state.block_hash(),
178+
next_block_height: state.next_block_height(),
179+
timestamp: state.timestamp(),
180+
pending_block: state.pending_block().clone(),
181+
pending_blobs: state.pending_blobs().clone(),
182182
},
183183
);
184184
}

linera-core/src/client/chain_state.rs

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
// Copyright (c) Zefchain Labs, Inc.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
use std::{
6+
collections::{BTreeMap, HashMap},
7+
sync::Arc,
8+
};
9+
10+
use linera_base::{
11+
crypto::{CryptoHash, KeyPair, PublicKey},
12+
data_types::{Blob, BlockHeight, Timestamp},
13+
identifiers::{BlobId, ChainId, Owner},
14+
};
15+
use linera_chain::data_types::Block;
16+
use linera_execution::committee::ValidatorName;
17+
use tokio::sync::Mutex;
18+
19+
use crate::data_types::ChainInfo;
20+
21+
/// The state of our interaction with a particular chain: how far we have synchronized it and
22+
/// whether we are currently attempting to propose a new block.
23+
pub struct ChainState {
24+
/// Latest block hash, if any.
25+
block_hash: Option<CryptoHash>,
26+
/// The earliest possible timestamp for the next block.
27+
timestamp: Timestamp,
28+
/// Sequence number that we plan to use for the next block.
29+
/// We track this value outside local storage mainly for security reasons.
30+
next_block_height: BlockHeight,
31+
/// The block we are currently trying to propose for the next height, if any.
32+
///
33+
/// This is always at the same height as `next_block_height`.
34+
pending_block: Option<Block>,
35+
/// Known key pairs from present and past identities.
36+
known_key_pairs: BTreeMap<Owner, KeyPair>,
37+
/// The ID of the admin chain.
38+
admin_id: ChainId,
39+
40+
/// For each validator, up to which index we have synchronized their
41+
/// [`ChainStateView::received_log`].
42+
received_certificate_trackers: HashMap<ValidatorName, u64>,
43+
/// This contains blobs belonging to our `pending_block` that may not even have
44+
/// been processed by (i.e. been proposed to) our own local chain manager yet.
45+
pending_blobs: BTreeMap<BlobId, Blob>,
46+
47+
/// A mutex that is held whilst we are preparing the next block, to ensure that no
48+
/// other client can begin preparing a block.
49+
preparing_block: Arc<Mutex<()>>,
50+
}
51+
52+
impl ChainState {
53+
pub fn new(
54+
known_key_pairs: Vec<KeyPair>,
55+
admin_id: ChainId,
56+
block_hash: Option<CryptoHash>,
57+
timestamp: Timestamp,
58+
next_block_height: BlockHeight,
59+
pending_block: Option<Block>,
60+
pending_blobs: BTreeMap<BlobId, Blob>,
61+
) -> ChainState {
62+
let known_key_pairs = known_key_pairs
63+
.into_iter()
64+
.map(|kp| (Owner::from(kp.public()), kp))
65+
.collect();
66+
let mut state = ChainState {
67+
known_key_pairs,
68+
admin_id,
69+
block_hash,
70+
timestamp,
71+
next_block_height,
72+
pending_block: None,
73+
pending_blobs,
74+
received_certificate_trackers: HashMap::new(),
75+
preparing_block: Arc::default(),
76+
};
77+
if let Some(block) = pending_block {
78+
state.set_pending_block(block);
79+
}
80+
state
81+
}
82+
83+
pub fn block_hash(&self) -> Option<CryptoHash> {
84+
self.block_hash
85+
}
86+
87+
pub fn timestamp(&self) -> Timestamp {
88+
self.timestamp
89+
}
90+
91+
pub fn next_block_height(&self) -> BlockHeight {
92+
self.next_block_height
93+
}
94+
95+
pub fn admin_id(&self) -> ChainId {
96+
self.admin_id
97+
}
98+
99+
pub fn pending_block(&self) -> &Option<Block> {
100+
&self.pending_block
101+
}
102+
103+
pub fn set_pending_block(&mut self, block: Block) {
104+
if block.height == self.next_block_height {
105+
self.pending_block = Some(block);
106+
} else {
107+
tracing::error!(
108+
"Not setting pending block at height {}, because next_block_height is {}.",
109+
block.height,
110+
self.next_block_height
111+
);
112+
}
113+
}
114+
115+
pub fn pending_blobs(&self) -> &BTreeMap<BlobId, Blob> {
116+
&self.pending_blobs
117+
}
118+
119+
pub fn insert_pending_blob(&mut self, blob: Blob) {
120+
self.pending_blobs.insert(blob.id(), blob);
121+
}
122+
123+
pub fn known_key_pairs(&self) -> &BTreeMap<Owner, KeyPair> {
124+
&self.known_key_pairs
125+
}
126+
127+
pub fn insert_known_key_pair(&mut self, key_pair: KeyPair) -> PublicKey {
128+
let new_public_key = key_pair.public();
129+
self.known_key_pairs.insert(new_public_key.into(), key_pair);
130+
new_public_key
131+
}
132+
133+
pub fn received_certificate_trackers(&self) -> &HashMap<ValidatorName, u64> {
134+
&self.received_certificate_trackers
135+
}
136+
137+
pub fn update_received_certificate_tracker(&mut self, name: ValidatorName, tracker: u64) {
138+
self.received_certificate_trackers
139+
.entry(name)
140+
.and_modify(|t| {
141+
// Because several synchronizations could happen in parallel, we need to make
142+
// sure to never go backward.
143+
if tracker > *t {
144+
*t = tracker;
145+
}
146+
})
147+
.or_insert(tracker);
148+
}
149+
150+
pub fn update_from_info(&mut self, info: &ChainInfo) {
151+
if info.next_block_height > self.next_block_height {
152+
self.next_block_height = info.next_block_height;
153+
self.clear_pending_block();
154+
self.block_hash = info.block_hash;
155+
self.timestamp = info.timestamp;
156+
}
157+
}
158+
159+
pub fn clear_pending_block(&mut self) {
160+
self.pending_block = None;
161+
self.pending_blobs.clear();
162+
}
163+
164+
pub fn preparing_block(&self) -> Arc<Mutex<()>> {
165+
self.preparing_block.clone()
166+
}
167+
}

0 commit comments

Comments
 (0)