1use std::{borrow::Cow, collections::BTreeMap, str::FromStr};
6
7use allocative::Allocative;
8use linera_base::crypto::{AccountPublicKey, CryptoError, ValidatorPublicKey};
9use serde::{Deserialize, Serialize};
10
11use crate::policy::ResourceControlPolicy;
12
13#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Debug)]
15pub struct ValidatorName(pub ValidatorPublicKey);
16
17impl Serialize for ValidatorName {
18 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
19 where
20 S: serde::ser::Serializer,
21 {
22 if serializer.is_human_readable() {
23 serializer.serialize_str(&self.to_string())
24 } else {
25 serializer.serialize_newtype_struct("ValidatorName", &self.0)
26 }
27 }
28}
29
30impl<'de> Deserialize<'de> for ValidatorName {
31 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
32 where
33 D: serde::de::Deserializer<'de>,
34 {
35 if deserializer.is_human_readable() {
36 let s = String::deserialize(deserializer)?;
37 let value = Self::from_str(&s).map_err(serde::de::Error::custom)?;
38 Ok(value)
39 } else {
40 #[derive(Deserialize)]
41 #[serde(rename = "ValidatorName")]
42 struct ValidatorNameDerived(ValidatorPublicKey);
43
44 let value = ValidatorNameDerived::deserialize(deserializer)?;
45 Ok(Self(value.0))
46 }
47 }
48}
49
50#[derive(Eq, PartialEq, Hash, Clone, Debug, Serialize, Deserialize, Allocative)]
52pub struct ValidatorState {
53 pub network_address: String,
55 pub votes: u64,
57 pub account_public_key: AccountPublicKey,
59}
60
61#[derive(Eq, PartialEq, Hash, Clone, Debug, Default, Allocative)]
63#[cfg_attr(with_graphql, derive(async_graphql::InputObject))]
64pub struct Committee {
65 pub validators: BTreeMap<ValidatorPublicKey, ValidatorState>,
67 total_votes: u64,
69 quorum_threshold: u64,
71 validity_threshold: u64,
73 policy: ResourceControlPolicy,
75}
76
77impl Serialize for Committee {
78 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
79 where
80 S: serde::ser::Serializer,
81 {
82 if serializer.is_human_readable() {
83 CommitteeFull::from(self).serialize(serializer)
84 } else {
85 CommitteeMinimal::from(self).serialize(serializer)
86 }
87 }
88}
89
90impl<'de> Deserialize<'de> for Committee {
91 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
92 where
93 D: serde::de::Deserializer<'de>,
94 {
95 if deserializer.is_human_readable() {
96 let committee_full = CommitteeFull::deserialize(deserializer)?;
97 Committee::try_from(committee_full).map_err(serde::de::Error::custom)
98 } else {
99 let committee_minimal = CommitteeMinimal::deserialize(deserializer)?;
100 Ok(Committee::from(committee_minimal))
101 }
102 }
103}
104
105#[derive(Serialize, Deserialize)]
106#[serde(rename = "Committee")]
107struct CommitteeFull<'a> {
108 validators: Cow<'a, BTreeMap<ValidatorPublicKey, ValidatorState>>,
109 total_votes: u64,
110 quorum_threshold: u64,
111 validity_threshold: u64,
112 policy: Cow<'a, ResourceControlPolicy>,
113}
114
115#[derive(Serialize, Deserialize)]
116#[serde(rename = "Committee")]
117struct CommitteeMinimal<'a> {
118 validators: Cow<'a, BTreeMap<ValidatorPublicKey, ValidatorState>>,
119 policy: Cow<'a, ResourceControlPolicy>,
120}
121
122impl TryFrom<CommitteeFull<'static>> for Committee {
123 type Error = String;
124
125 fn try_from(committee_full: CommitteeFull) -> Result<Committee, Self::Error> {
126 let CommitteeFull {
127 validators,
128 total_votes,
129 quorum_threshold,
130 validity_threshold,
131 policy,
132 } = committee_full;
133 let committee = Committee::new(validators.into_owned(), policy.into_owned());
134 if total_votes != committee.total_votes {
135 Err(format!(
136 "invalid committee: total_votes is {}; should be {}",
137 total_votes, committee.total_votes,
138 ))
139 } else if quorum_threshold != committee.quorum_threshold {
140 Err(format!(
141 "invalid committee: quorum_threshold is {}; should be {}",
142 quorum_threshold, committee.quorum_threshold,
143 ))
144 } else if validity_threshold != committee.validity_threshold {
145 Err(format!(
146 "invalid committee: validity_threshold is {}; should be {}",
147 validity_threshold, committee.validity_threshold,
148 ))
149 } else {
150 Ok(committee)
151 }
152 }
153}
154
155impl<'a> From<&'a Committee> for CommitteeFull<'a> {
156 fn from(committee: &'a Committee) -> CommitteeFull<'a> {
157 let Committee {
158 validators,
159 total_votes,
160 quorum_threshold,
161 validity_threshold,
162 policy,
163 } = committee;
164 CommitteeFull {
165 validators: Cow::Borrowed(validators),
166 total_votes: *total_votes,
167 quorum_threshold: *quorum_threshold,
168 validity_threshold: *validity_threshold,
169 policy: Cow::Borrowed(policy),
170 }
171 }
172}
173
174impl From<CommitteeMinimal<'static>> for Committee {
175 fn from(committee_min: CommitteeMinimal) -> Committee {
176 let CommitteeMinimal { validators, policy } = committee_min;
177 Committee::new(validators.into_owned(), policy.into_owned())
178 }
179}
180
181impl<'a> From<&'a Committee> for CommitteeMinimal<'a> {
182 fn from(committee: &'a Committee) -> CommitteeMinimal<'a> {
183 let Committee {
184 validators,
185 total_votes: _,
186 quorum_threshold: _,
187 validity_threshold: _,
188 policy,
189 } = committee;
190 CommitteeMinimal {
191 validators: Cow::Borrowed(validators),
192 policy: Cow::Borrowed(policy),
193 }
194 }
195}
196
197impl std::fmt::Display for ValidatorName {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
199 self.0.fmt(f)
200 }
201}
202
203impl std::str::FromStr for ValidatorName {
204 type Err = CryptoError;
205
206 fn from_str(s: &str) -> Result<Self, Self::Err> {
207 Ok(ValidatorName(ValidatorPublicKey::from_str(s)?))
208 }
209}
210
211impl From<ValidatorPublicKey> for ValidatorName {
212 fn from(value: ValidatorPublicKey) -> Self {
213 Self(value)
214 }
215}
216
217impl Committee {
218 pub fn new(
219 validators: BTreeMap<ValidatorPublicKey, ValidatorState>,
220 policy: ResourceControlPolicy,
221 ) -> Self {
222 let total_votes = validators.values().fold(0, |sum, state| sum + state.votes);
223 let quorum_threshold = 2 * total_votes / 3 + 1;
228 let validity_threshold = total_votes.div_ceil(3);
229
230 Committee {
231 validators,
232 total_votes,
233 quorum_threshold,
234 validity_threshold,
235 policy,
236 }
237 }
238
239 #[cfg(with_testing)]
240 pub fn make_simple(keys: Vec<(ValidatorPublicKey, AccountPublicKey)>) -> Self {
241 let map = keys
242 .into_iter()
243 .map(|(validator_key, account_key)| {
244 (
245 validator_key,
246 ValidatorState {
247 network_address: "Tcp:localhost:8080".to_string(),
248 votes: 100,
249 account_public_key: account_key,
250 },
251 )
252 })
253 .collect();
254 Committee::new(map, ResourceControlPolicy::default())
255 }
256
257 pub fn weight(&self, author: &ValidatorPublicKey) -> u64 {
258 match self.validators.get(author) {
259 Some(state) => state.votes,
260 None => 0,
261 }
262 }
263
264 pub fn keys_and_weights(&self) -> impl Iterator<Item = (ValidatorPublicKey, u64)> + '_ {
265 self.validators
266 .iter()
267 .map(|(name, validator)| (*name, validator.votes))
268 }
269
270 pub fn account_keys_and_weights(&self) -> impl Iterator<Item = (AccountPublicKey, u64)> + '_ {
271 self.validators
272 .values()
273 .map(|validator| (validator.account_public_key, validator.votes))
274 }
275
276 pub fn network_address(&self, author: &ValidatorPublicKey) -> Option<&str> {
277 self.validators
278 .get(author)
279 .map(|state| state.network_address.as_ref())
280 }
281
282 pub fn quorum_threshold(&self) -> u64 {
283 self.quorum_threshold
284 }
285
286 pub fn validity_threshold(&self) -> u64 {
287 self.validity_threshold
288 }
289
290 pub fn validators(&self) -> &BTreeMap<ValidatorPublicKey, ValidatorState> {
291 &self.validators
292 }
293
294 pub fn validator_addresses(&self) -> impl Iterator<Item = (ValidatorPublicKey, &str)> {
295 self.validators
296 .iter()
297 .map(|(name, validator)| (*name, &*validator.network_address))
298 }
299
300 pub fn total_votes(&self) -> u64 {
301 self.total_votes
302 }
303
304 pub fn policy(&self) -> &ResourceControlPolicy {
305 &self.policy
306 }
307
308 pub fn policy_mut(&mut self) -> &mut ResourceControlPolicy {
310 &mut self.policy
311 }
312}