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,
74 policy: ResourceControlPolicy,
76}
77
78impl Serialize for Committee {
79 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80 where
81 S: serde::ser::Serializer,
82 {
83 if serializer.is_human_readable() {
84 CommitteeFull::from(self).serialize(serializer)
85 } else {
86 CommitteeMinimal::from(self).serialize(serializer)
87 }
88 }
89}
90
91impl<'de> Deserialize<'de> for Committee {
92 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93 where
94 D: serde::de::Deserializer<'de>,
95 {
96 if deserializer.is_human_readable() {
97 let committee_full = CommitteeFull::deserialize(deserializer)?;
98 Committee::try_from(committee_full).map_err(serde::de::Error::custom)
99 } else {
100 let committee_minimal = CommitteeMinimal::deserialize(deserializer)?;
101 Ok(Committee::from(committee_minimal))
102 }
103 }
104}
105
106#[derive(Serialize, Deserialize)]
107#[serde(rename = "Committee")]
108struct CommitteeFull<'a> {
109 validators: Cow<'a, BTreeMap<ValidatorPublicKey, ValidatorState>>,
110 total_votes: u64,
111 quorum_threshold: u64,
112 validity_threshold: u64,
113 policy: Cow<'a, ResourceControlPolicy>,
114}
115
116#[derive(Serialize, Deserialize)]
117#[serde(rename = "Committee")]
118struct CommitteeMinimal<'a> {
119 validators: Cow<'a, BTreeMap<ValidatorPublicKey, ValidatorState>>,
120 policy: Cow<'a, ResourceControlPolicy>,
121}
122
123impl TryFrom<CommitteeFull<'static>> for Committee {
124 type Error = String;
125
126 fn try_from(committee_full: CommitteeFull) -> Result<Committee, Self::Error> {
127 let CommitteeFull {
128 validators,
129 total_votes,
130 quorum_threshold,
131 validity_threshold,
132 policy,
133 } = committee_full;
134 let committee = Committee::new(validators.into_owned(), policy.into_owned());
135 if total_votes != committee.total_votes {
136 Err(format!(
137 "invalid committee: total_votes is {}; should be {}",
138 total_votes, committee.total_votes,
139 ))
140 } else if quorum_threshold != committee.quorum_threshold {
141 Err(format!(
142 "invalid committee: quorum_threshold is {}; should be {}",
143 quorum_threshold, committee.quorum_threshold,
144 ))
145 } else if validity_threshold != committee.validity_threshold {
146 Err(format!(
147 "invalid committee: validity_threshold is {}; should be {}",
148 validity_threshold, committee.validity_threshold,
149 ))
150 } else {
151 Ok(committee)
152 }
153 }
154}
155
156impl<'a> From<&'a Committee> for CommitteeFull<'a> {
157 fn from(committee: &'a Committee) -> CommitteeFull<'a> {
158 let Committee {
159 validators,
160 total_votes,
161 quorum_threshold,
162 validity_threshold,
163 policy,
164 } = committee;
165 CommitteeFull {
166 validators: Cow::Borrowed(validators),
167 total_votes: *total_votes,
168 quorum_threshold: *quorum_threshold,
169 validity_threshold: *validity_threshold,
170 policy: Cow::Borrowed(policy),
171 }
172 }
173}
174
175impl From<CommitteeMinimal<'static>> for Committee {
176 fn from(committee_min: CommitteeMinimal) -> Committee {
177 let CommitteeMinimal { validators, policy } = committee_min;
178 Committee::new(validators.into_owned(), policy.into_owned())
179 }
180}
181
182impl<'a> From<&'a Committee> for CommitteeMinimal<'a> {
183 fn from(committee: &'a Committee) -> CommitteeMinimal<'a> {
184 let Committee {
185 validators,
186 total_votes: _,
187 quorum_threshold: _,
188 validity_threshold: _,
189 policy,
190 } = committee;
191 CommitteeMinimal {
192 validators: Cow::Borrowed(validators),
193 policy: Cow::Borrowed(policy),
194 }
195 }
196}
197
198impl std::fmt::Display for ValidatorName {
199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
200 self.0.fmt(f)
201 }
202}
203
204impl std::str::FromStr for ValidatorName {
205 type Err = CryptoError;
206
207 fn from_str(s: &str) -> Result<Self, Self::Err> {
208 Ok(ValidatorName(ValidatorPublicKey::from_str(s)?))
209 }
210}
211
212impl From<ValidatorPublicKey> for ValidatorName {
213 fn from(value: ValidatorPublicKey) -> Self {
214 Self(value)
215 }
216}
217
218impl Committee {
219 pub fn new(
220 validators: BTreeMap<ValidatorPublicKey, ValidatorState>,
221 policy: ResourceControlPolicy,
222 ) -> Self {
223 let total_votes = validators.values().fold(0, |sum, state| sum + state.votes);
224 let validity_threshold = total_votes.div_ceil(3);
227 let quorum_threshold = (total_votes + validity_threshold).div_ceil(2);
230
231 Committee {
232 validators,
233 total_votes,
234 quorum_threshold,
235 validity_threshold,
236 policy,
237 }
238 }
239
240 #[cfg(with_testing)]
241 pub fn make_simple(keys: Vec<(ValidatorPublicKey, AccountPublicKey)>) -> Self {
242 let map = keys
243 .into_iter()
244 .map(|(validator_key, account_key)| {
245 (
246 validator_key,
247 ValidatorState {
248 network_address: "Tcp:localhost:8080".to_string(),
249 votes: 100,
250 account_public_key: account_key,
251 },
252 )
253 })
254 .collect();
255 Committee::new(map, ResourceControlPolicy::default())
256 }
257
258 pub fn weight(&self, author: &ValidatorPublicKey) -> u64 {
259 match self.validators.get(author) {
260 Some(state) => state.votes,
261 None => 0,
262 }
263 }
264
265 pub fn keys_and_weights(&self) -> impl Iterator<Item = (ValidatorPublicKey, u64)> + '_ {
266 self.validators
267 .iter()
268 .map(|(name, validator)| (*name, validator.votes))
269 }
270
271 pub fn account_keys_and_weights(&self) -> impl Iterator<Item = (AccountPublicKey, u64)> + '_ {
272 self.validators
273 .values()
274 .map(|validator| (validator.account_public_key, validator.votes))
275 }
276
277 pub fn network_address(&self, author: &ValidatorPublicKey) -> Option<&str> {
278 self.validators
279 .get(author)
280 .map(|state| state.network_address.as_ref())
281 }
282
283 pub fn quorum_threshold(&self) -> u64 {
284 self.quorum_threshold
285 }
286
287 pub fn validity_threshold(&self) -> u64 {
288 self.validity_threshold
289 }
290
291 pub fn validators(&self) -> &BTreeMap<ValidatorPublicKey, ValidatorState> {
292 &self.validators
293 }
294
295 pub fn validator_addresses(&self) -> impl Iterator<Item = (ValidatorPublicKey, &str)> {
296 self.validators
297 .iter()
298 .map(|(name, validator)| (*name, &*validator.network_address))
299 }
300
301 pub fn total_votes(&self) -> u64 {
302 self.total_votes
303 }
304
305 pub fn policy(&self) -> &ResourceControlPolicy {
306 &self.policy
307 }
308
309 pub fn policy_mut(&mut self) -> &mut ResourceControlPolicy {
311 &mut self.policy
312 }
313}