1use std::{collections::BTreeSet, fmt::Debug};
2
3use datasize::DataSize;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6use casper_types::Timestamp;
7
8use crate::components::consensus::{
9 highway_core::{
10 endorsement::SignedEndorsement,
11 highway::{PingError, VertexError},
12 state::Panorama,
13 },
14 traits::{Context, ValidatorSecret},
15 utils::{ValidatorIndex, Validators},
16};
17
18#[allow(clippy::arithmetic_side_effects)]
19mod relaxed {
20 use casper_types::Timestamp;
24 use datasize::DataSize;
25 use serde::{Deserialize, Serialize};
26 use strum::EnumDiscriminants;
27
28 use crate::components::consensus::{
29 highway_core::evidence::Evidence, traits::Context, utils::ValidatorIndex,
30 };
31
32 use super::{Endorsements, Ping, SignedWireUnit};
33
34 #[derive(
36 DataSize,
37 Clone,
38 Debug,
39 Eq,
40 PartialEq,
41 PartialOrd,
42 Ord,
43 Hash,
44 Serialize,
45 Deserialize,
46 EnumDiscriminants,
47 )]
48 #[serde(bound(
49 serialize = "C::Hash: Serialize",
50 deserialize = "C::Hash: Deserialize<'de>",
51 ))]
52 #[strum_discriminants(derive(strum::EnumIter))]
53 pub enum Dependency<C>
54 where
55 C: Context,
56 {
57 Unit(C::Hash),
59 Evidence(ValidatorIndex),
61 Endorsement(C::Hash),
63 Ping(ValidatorIndex, Timestamp),
65 }
66
67 #[derive(
71 DataSize, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash, EnumDiscriminants,
72 )]
73 #[serde(bound(
74 serialize = "C::Hash: Serialize",
75 deserialize = "C::Hash: Deserialize<'de>",
76 ))]
77 #[strum_discriminants(derive(strum::EnumIter))]
78 pub enum Vertex<C>
79 where
80 C: Context,
81 {
82 Unit(SignedWireUnit<C>),
84 Evidence(Evidence<C>),
86 Endorsements(Endorsements<C>),
88 Ping(Ping<C>),
90 }
91}
92pub use relaxed::{Dependency, DependencyDiscriminants, Vertex, VertexDiscriminants};
93
94impl<C: Context> Dependency<C> {
95 pub fn is_unit(&self) -> bool {
97 matches!(self, Dependency::Unit(_))
98 }
99}
100
101impl<C: Context> Vertex<C> {
102 pub fn value(&self) -> Option<&C::ConsensusValue> {
109 match self {
110 Vertex::Unit(swunit) => swunit.wire_unit().value.as_ref(),
111 Vertex::Evidence(_) | Vertex::Endorsements(_) | Vertex::Ping(_) => None,
112 }
113 }
114
115 pub fn unit_hash(&self) -> Option<C::Hash> {
117 match self {
118 Vertex::Unit(swunit) => Some(swunit.hash()),
119 Vertex::Evidence(_) | Vertex::Endorsements(_) | Vertex::Ping(_) => None,
120 }
121 }
122
123 pub fn unit_seq_number(&self) -> Option<u64> {
125 match self {
126 Vertex::Unit(swunit) => Some(swunit.wire_unit().seq_number),
127 _ => None,
128 }
129 }
130
131 pub fn is_evidence(&self) -> bool {
133 matches!(self, Vertex::Evidence(_))
134 }
135
136 pub fn timestamp(&self) -> Option<Timestamp> {
138 match self {
139 Vertex::Unit(signed_wire_unit) => Some(signed_wire_unit.wire_unit().timestamp),
140 Vertex::Ping(ping) => Some(ping.timestamp()),
141 Vertex::Evidence(_) | Vertex::Endorsements(_) => None,
142 }
143 }
144
145 pub fn creator(&self) -> Option<ValidatorIndex> {
147 match self {
148 Vertex::Unit(signed_wire_unit) => Some(signed_wire_unit.wire_unit().creator),
149 Vertex::Ping(ping) => Some(ping.creator),
150 Vertex::Evidence(_) | Vertex::Endorsements(_) => None,
151 }
152 }
153
154 pub fn id(&self) -> Dependency<C> {
156 match self {
157 Vertex::Unit(signed_wire_unit) => Dependency::Unit(signed_wire_unit.hash()),
158 Vertex::Evidence(evidence) => Dependency::Evidence(evidence.perpetrator()),
159 Vertex::Endorsements(endorsement) => Dependency::Endorsement(endorsement.unit),
160 Vertex::Ping(ping) => Dependency::Ping(ping.creator(), ping.timestamp()),
161 }
162 }
163
164 pub fn unit(&self) -> Option<&SignedWireUnit<C>> {
166 match self {
167 Vertex::Unit(signed_wire_unit) => Some(signed_wire_unit),
168 _ => None,
169 }
170 }
171
172 pub fn is_proposal(&self) -> bool {
174 self.value().is_some()
175 }
176}
177
178mod specimen_support {
179 use super::{
180 Dependency, DependencyDiscriminants, Endorsements, HashedWireUnit, Ping, SignedEndorsement,
181 SignedWireUnit, Vertex, VertexDiscriminants, WireUnit,
182 };
183 use crate::{
184 components::consensus::ClContext,
185 utils::specimen::{
186 btree_set_distinct_from_prop, largest_variant, vec_prop_specimen, Cache,
187 LargestSpecimen, SizeEstimator,
188 },
189 };
190
191 impl LargestSpecimen for Vertex<ClContext> {
192 fn largest_specimen<E: SizeEstimator>(estimator: &E, cache: &mut Cache) -> Self {
193 largest_variant::<Self, VertexDiscriminants, _, _>(estimator, |variant| match variant {
194 VertexDiscriminants::Unit => {
195 Vertex::Unit(LargestSpecimen::largest_specimen(estimator, cache))
196 }
197 VertexDiscriminants::Evidence => {
198 Vertex::Evidence(LargestSpecimen::largest_specimen(estimator, cache))
199 }
200 VertexDiscriminants::Endorsements => {
201 if estimator.parameter_bool("endorsements_enabled") {
202 Vertex::Endorsements(LargestSpecimen::largest_specimen(estimator, cache))
203 } else {
204 Vertex::Ping(LargestSpecimen::largest_specimen(estimator, cache))
205 }
206 }
207 VertexDiscriminants::Ping => {
208 Vertex::Ping(LargestSpecimen::largest_specimen(estimator, cache))
209 }
210 })
211 }
212 }
213
214 impl LargestSpecimen for Dependency<ClContext> {
215 fn largest_specimen<E: SizeEstimator>(estimator: &E, cache: &mut Cache) -> Self {
216 largest_variant::<Self, DependencyDiscriminants, _, _>(estimator, |variant| {
217 match variant {
218 DependencyDiscriminants::Unit => {
219 Dependency::Unit(LargestSpecimen::largest_specimen(estimator, cache))
220 }
221 DependencyDiscriminants::Evidence => {
222 Dependency::Evidence(LargestSpecimen::largest_specimen(estimator, cache))
223 }
224 DependencyDiscriminants::Endorsement => {
225 Dependency::Endorsement(LargestSpecimen::largest_specimen(estimator, cache))
226 }
227 DependencyDiscriminants::Ping => Dependency::Ping(
228 LargestSpecimen::largest_specimen(estimator, cache),
229 LargestSpecimen::largest_specimen(estimator, cache),
230 ),
231 }
232 })
233 }
234 }
235
236 impl LargestSpecimen for SignedWireUnit<ClContext> {
237 fn largest_specimen<E: SizeEstimator>(estimator: &E, cache: &mut Cache) -> Self {
238 SignedWireUnit {
239 hashed_wire_unit: LargestSpecimen::largest_specimen(estimator, cache),
240 signature: LargestSpecimen::largest_specimen(estimator, cache),
241 }
242 }
243 }
244
245 impl LargestSpecimen for Endorsements<ClContext> {
246 fn largest_specimen<E: SizeEstimator>(estimator: &E, cache: &mut Cache) -> Self {
247 Endorsements {
248 unit: LargestSpecimen::largest_specimen(estimator, cache),
249 endorsers: if estimator.parameter_bool("endorsements_enabled") {
250 vec_prop_specimen(estimator, "validator_count", cache)
251 } else {
252 Vec::new()
253 },
254 }
255 }
256 }
257
258 impl LargestSpecimen for SignedEndorsement<ClContext> {
259 fn largest_specimen<E: SizeEstimator>(estimator: &E, cache: &mut Cache) -> Self {
260 SignedEndorsement::new(
261 LargestSpecimen::largest_specimen(estimator, cache),
262 LargestSpecimen::largest_specimen(estimator, cache),
263 )
264 }
265 }
266
267 impl LargestSpecimen for Ping<ClContext> {
268 fn largest_specimen<E: SizeEstimator>(estimator: &E, cache: &mut Cache) -> Self {
269 Ping {
270 creator: LargestSpecimen::largest_specimen(estimator, cache),
271 timestamp: LargestSpecimen::largest_specimen(estimator, cache),
272 instance_id: LargestSpecimen::largest_specimen(estimator, cache),
273 signature: LargestSpecimen::largest_specimen(estimator, cache),
274 }
275 }
276 }
277
278 impl LargestSpecimen for HashedWireUnit<ClContext> {
279 fn largest_specimen<E: SizeEstimator>(estimator: &E, cache: &mut Cache) -> Self {
280 if let Some(item) = cache.get::<Self>() {
281 return item.clone();
282 }
283
284 let hash = LargestSpecimen::largest_specimen(estimator, cache);
285 let wire_unit = LargestSpecimen::largest_specimen(estimator, cache);
286 cache.set(HashedWireUnit { hash, wire_unit }).clone()
287 }
288 }
289
290 impl LargestSpecimen for WireUnit<ClContext> {
291 fn largest_specimen<E: SizeEstimator>(estimator: &E, cache: &mut Cache) -> Self {
292 WireUnit {
293 panorama: LargestSpecimen::largest_specimen(estimator, cache),
294 creator: LargestSpecimen::largest_specimen(estimator, cache),
295 instance_id: LargestSpecimen::largest_specimen(estimator, cache),
296 value: LargestSpecimen::largest_specimen(estimator, cache),
297 seq_number: LargestSpecimen::largest_specimen(estimator, cache),
298 timestamp: LargestSpecimen::largest_specimen(estimator, cache),
299 round_exp: LargestSpecimen::largest_specimen(estimator, cache),
300 endorsed: btree_set_distinct_from_prop(estimator, "validator_count", cache),
301 }
302 }
303 }
304}
305
306#[derive(DataSize, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
308#[serde(bound(
309 serialize = "C::Hash: Serialize",
310 deserialize = "C::Hash: Deserialize<'de>",
311))]
312pub struct SignedWireUnit<C>
313where
314 C: Context,
315{
316 pub(crate) hashed_wire_unit: HashedWireUnit<C>,
317 pub(crate) signature: C::Signature,
318}
319
320impl<C: Context> SignedWireUnit<C> {
321 pub(crate) fn new(
322 hashed_wire_unit: HashedWireUnit<C>,
323 secret_key: &C::ValidatorSecret,
324 ) -> Self {
325 let signature = secret_key.sign(&hashed_wire_unit.hash);
326 SignedWireUnit {
327 hashed_wire_unit,
328 signature,
329 }
330 }
331
332 pub fn wire_unit(&self) -> &WireUnit<C> {
334 self.hashed_wire_unit.wire_unit()
335 }
336
337 pub fn hash(&self) -> C::Hash {
339 self.hashed_wire_unit.hash()
340 }
341}
342
343#[derive(Clone, DataSize, Debug, Eq, PartialEq, Hash)]
345pub struct HashedWireUnit<C>
346where
347 C: Context,
348{
349 hash: C::Hash,
350 wire_unit: WireUnit<C>,
351}
352
353impl<C> HashedWireUnit<C>
354where
355 C: Context,
356{
357 pub(crate) fn new(wire_unit: WireUnit<C>) -> Self {
359 let hash = wire_unit.compute_hash();
360 Self::new_with_hash(wire_unit, hash)
361 }
362
363 pub fn into_inner(self) -> WireUnit<C> {
365 self.wire_unit
366 }
367
368 pub fn wire_unit(&self) -> &WireUnit<C> {
370 &self.wire_unit
371 }
372
373 pub fn hash(&self) -> C::Hash {
375 self.hash
376 }
377
378 pub(crate) fn new_with_hash(wire_unit: WireUnit<C>, hash: C::Hash) -> Self {
381 HashedWireUnit { hash, wire_unit }
382 }
383}
384
385impl<C: Context> Serialize for HashedWireUnit<C> {
386 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
387 self.wire_unit.serialize(serializer)
388 }
389}
390
391impl<'de, C: Context> Deserialize<'de> for HashedWireUnit<C> {
392 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
393 Ok(HashedWireUnit::new(<_>::deserialize(deserializer)?))
394 }
395}
396
397#[derive(DataSize, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
399#[serde(bound(
400 serialize = "C::Hash: Serialize",
401 deserialize = "C::Hash: Deserialize<'de>",
402))]
403pub struct WireUnit<C>
404where
405 C: Context,
406{
407 pub panorama: Panorama<C>,
409 pub creator: ValidatorIndex,
411 pub instance_id: C::InstanceId,
413 pub value: Option<C::ConsensusValue>,
415 pub seq_number: u64,
417 pub timestamp: Timestamp,
419 pub round_exp: u8,
421 pub endorsed: BTreeSet<C::Hash>,
423}
424
425impl<C: Context> Debug for WireUnit<C> {
426 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427 struct Ellipsis;
429
430 impl Debug for Ellipsis {
431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432 write!(f, "..")
433 }
434 }
435
436 f.debug_struct("WireUnit")
437 .field("value", &self.value.as_ref().map(|_| Ellipsis))
438 .field("creator.0", &self.creator.0)
439 .field("instance_id", &self.instance_id)
440 .field("seq_number", &self.seq_number)
441 .field("timestamp", &self.timestamp.millis())
442 .field("panorama", self.panorama.as_ref())
443 .field("round_exp", &self.round_exp)
444 .field("endorsed", &self.endorsed)
445 .finish()
446 }
447}
448
449impl<C: Context> WireUnit<C> {
450 pub(crate) fn into_hashed(self) -> HashedWireUnit<C> {
451 HashedWireUnit::new(self)
452 }
453
454 pub fn previous(&self) -> Option<&C::Hash> {
456 self.panorama[self.creator].correct()
457 }
458
459 fn compute_hash(&self) -> C::Hash {
461 <C as Context>::hash(&bincode::serialize(self).expect("serialize WireUnit"))
463 }
464}
465
466#[derive(Clone, DataSize, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
468#[serde(bound(
469 serialize = "C::Hash: Serialize",
470 deserialize = "C::Hash: Deserialize<'de>",
471))]
472pub struct Endorsements<C>
473where
474 C: Context,
475{
476 pub unit: C::Hash,
478 pub endorsers: Vec<(ValidatorIndex, C::Signature)>,
480}
481
482impl<C: Context> Endorsements<C> {
483 pub fn unit(&self) -> &C::Hash {
485 &self.unit
486 }
487
488 pub fn validator_ids(&self) -> impl Iterator<Item = ValidatorIndex> + '_ {
490 self.endorsers.iter().map(|(v, _)| *v)
491 }
492}
493
494impl<C: Context> From<SignedEndorsement<C>> for Endorsements<C> {
495 fn from(signed_e: SignedEndorsement<C>) -> Self {
496 Endorsements {
497 unit: *signed_e.unit(),
498 endorsers: vec![(signed_e.validator_idx(), *signed_e.signature())],
499 }
500 }
501}
502
503#[derive(Clone, DataSize, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
506#[serde(bound(
507 serialize = "C::Hash: Serialize",
508 deserialize = "C::Hash: Deserialize<'de>",
509))]
510pub struct Ping<C>
511where
512 C: Context,
513{
514 creator: ValidatorIndex,
515 timestamp: Timestamp,
516 instance_id: C::InstanceId,
517 signature: C::Signature,
518}
519
520impl<C: Context> Ping<C> {
521 pub(crate) fn new(
523 creator: ValidatorIndex,
524 timestamp: Timestamp,
525 instance_id: C::InstanceId,
526 sk: &C::ValidatorSecret,
527 ) -> Self {
528 let signature = sk.sign(&Self::hash(creator, timestamp, instance_id));
529 Ping {
530 creator,
531 timestamp,
532 instance_id,
533 signature,
534 }
535 }
536
537 pub fn creator(&self) -> ValidatorIndex {
539 self.creator
540 }
541
542 pub fn timestamp(&self) -> Timestamp {
544 self.timestamp
545 }
546
547 pub(crate) fn validate(
549 &self,
550 validators: &Validators<C::ValidatorId>,
551 our_instance_id: &C::InstanceId,
552 ) -> Result<(), VertexError> {
553 let Ping {
554 creator,
555 timestamp,
556 instance_id,
557 signature,
558 } = self;
559 if instance_id != our_instance_id {
560 return Err(PingError::InstanceId.into());
561 }
562 let v_id = validators.id(self.creator).ok_or(PingError::Creator)?;
563 let hash = Self::hash(*creator, *timestamp, *instance_id);
564 if !C::verify_signature(&hash, v_id, signature) {
565 return Err(PingError::Signature.into());
566 }
567 Ok(())
568 }
569
570 fn hash(creator: ValidatorIndex, timestamp: Timestamp, instance_id: C::InstanceId) -> C::Hash {
572 let bytes = bincode::serialize(&(creator, timestamp, instance_id)).expect("serialize Ping");
573 <C as Context>::hash(&bytes)
574 }
575}