1use std::{
6 fmt,
7 hash::{Hash, Hasher},
8 ops::Deref,
9 sync::Arc,
10};
11
12use bytes::Bytes;
13use consensus_config::{
14 AuthorityIndex, DIGEST_LENGTH, DefaultHashFunction, Epoch, ProtocolKeyPair,
15 ProtocolKeySignature, ProtocolPublicKey,
16};
17use enum_dispatch::enum_dispatch;
18use fastcrypto::hash::{Digest, HashFunction};
19use serde::{Deserialize, Serialize};
20use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
21
22use crate::{
23 commit::CommitVote,
24 context::Context,
25 ensure,
26 error::{ConsensusError, ConsensusResult},
27};
28
29pub type Round = u32;
31
32pub(crate) const GENESIS_ROUND: Round = 0;
33
34pub type BlockTimestampMs = u64;
36
37#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Default, Debug)]
39pub struct Transaction {
40 data: Bytes,
41}
42
43impl Transaction {
44 pub fn new(data: Vec<u8>) -> Self {
45 Self { data: data.into() }
46 }
47
48 pub fn data(&self) -> &[u8] {
49 &self.data
50 }
51
52 pub fn into_data(self) -> Bytes {
53 self.data
54 }
55}
56
57#[derive(Clone, Deserialize, Serialize)]
62#[enum_dispatch(BlockAPI)]
63pub enum Block {
64 V1(BlockV1),
65}
66
67#[enum_dispatch]
68pub trait BlockAPI {
69 fn epoch(&self) -> Epoch;
70 fn round(&self) -> Round;
71 fn author(&self) -> AuthorityIndex;
72 fn slot(&self) -> Slot;
73 fn timestamp_ms(&self) -> BlockTimestampMs;
74 fn ancestors(&self) -> &[BlockRef];
75 fn transactions(&self) -> &[Transaction];
76 fn commit_votes(&self) -> &[CommitVote];
77 fn misbehavior_reports(&self) -> &[MisbehaviorReport];
78}
79
80#[derive(Clone, Default, Deserialize, Serialize)]
81pub struct BlockV1 {
82 epoch: Epoch,
83 round: Round,
84 author: AuthorityIndex,
85 timestamp_ms: BlockTimestampMs,
87 ancestors: Vec<BlockRef>,
88 transactions: Vec<Transaction>,
89 commit_votes: Vec<CommitVote>,
90 misbehavior_reports: Vec<MisbehaviorReport>,
91}
92
93impl BlockV1 {
94 pub(crate) fn new(
95 epoch: Epoch,
96 round: Round,
97 author: AuthorityIndex,
98 timestamp_ms: BlockTimestampMs,
99 ancestors: Vec<BlockRef>,
100 transactions: Vec<Transaction>,
101 commit_votes: Vec<CommitVote>,
102 misbehavior_reports: Vec<MisbehaviorReport>,
103 ) -> BlockV1 {
104 Self {
105 epoch,
106 round,
107 author,
108 timestamp_ms,
109 ancestors,
110 transactions,
111 commit_votes,
112 misbehavior_reports,
113 }
114 }
115
116 fn genesis_block(epoch: Epoch, author: AuthorityIndex) -> Self {
117 Self {
118 epoch,
119 round: GENESIS_ROUND,
120 author,
121 timestamp_ms: 0,
122 ancestors: vec![],
123 transactions: vec![],
124 commit_votes: vec![],
125 misbehavior_reports: vec![],
126 }
127 }
128}
129
130impl BlockAPI for BlockV1 {
131 fn epoch(&self) -> Epoch {
132 self.epoch
133 }
134
135 fn round(&self) -> Round {
136 self.round
137 }
138
139 fn author(&self) -> AuthorityIndex {
140 self.author
141 }
142
143 fn slot(&self) -> Slot {
144 Slot::new(self.round, self.author)
145 }
146
147 fn timestamp_ms(&self) -> BlockTimestampMs {
148 self.timestamp_ms
149 }
150
151 fn ancestors(&self) -> &[BlockRef] {
152 &self.ancestors
153 }
154
155 fn transactions(&self) -> &[Transaction] {
156 &self.transactions
157 }
158
159 fn commit_votes(&self) -> &[CommitVote] {
160 &self.commit_votes
161 }
162
163 fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
164 &self.misbehavior_reports
165 }
166}
167
168#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
172pub struct BlockRef {
173 pub round: Round,
174 pub author: AuthorityIndex,
175 pub digest: BlockDigest,
176}
177
178impl BlockRef {
179 pub const MIN: Self = Self {
180 round: 0,
181 author: AuthorityIndex::MIN,
182 digest: BlockDigest::MIN,
183 };
184
185 pub const MAX: Self = Self {
186 round: u32::MAX,
187 author: AuthorityIndex::MAX,
188 digest: BlockDigest::MAX,
189 };
190
191 pub fn new(round: Round, author: AuthorityIndex, digest: BlockDigest) -> Self {
192 Self {
193 round,
194 author,
195 digest,
196 }
197 }
198}
199
200impl fmt::Display for BlockRef {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
203 write!(f, "B{}({},{})", self.round, self.author, self.digest)
204 }
205}
206
207impl fmt::Debug for BlockRef {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
209 write!(f, "B{}({},{:?})", self.round, self.author, self.digest)
210 }
211}
212
213impl Hash for BlockRef {
214 fn hash<H: Hasher>(&self, state: &mut H) {
215 state.write(&self.digest.0[..8]);
216 }
217}
218
219#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
226pub struct BlockDigest([u8; consensus_config::DIGEST_LENGTH]);
227
228impl BlockDigest {
229 pub const MIN: Self = Self([u8::MIN; consensus_config::DIGEST_LENGTH]);
231 pub const MAX: Self = Self([u8::MAX; consensus_config::DIGEST_LENGTH]);
232}
233
234impl Hash for BlockDigest {
235 fn hash<H: Hasher>(&self, state: &mut H) {
236 state.write(&self.0[..8]);
237 }
238}
239
240impl From<BlockDigest> for Digest<{ DIGEST_LENGTH }> {
241 fn from(hd: BlockDigest) -> Self {
242 Digest::new(hd.0)
243 }
244}
245
246impl fmt::Display for BlockDigest {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
248 write!(
249 f,
250 "{}",
251 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, self.0)
252 .get(0..4)
253 .ok_or(fmt::Error)?
254 )
255 }
256}
257
258impl fmt::Debug for BlockDigest {
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
260 write!(
261 f,
262 "{}",
263 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, self.0)
264 )
265 }
266}
267
268impl AsRef<[u8]> for BlockDigest {
269 fn as_ref(&self) -> &[u8] {
270 &self.0
271 }
272}
273
274#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Hash)]
277pub struct Slot {
278 pub round: Round,
279 pub authority: AuthorityIndex,
280}
281
282impl Slot {
283 pub fn new(round: Round, authority: AuthorityIndex) -> Self {
284 Self { round, authority }
285 }
286
287 #[cfg(test)]
288 pub fn new_for_test(round: Round, authority: u32) -> Self {
289 Self {
290 round,
291 authority: AuthorityIndex::new_for_test(authority),
292 }
293 }
294}
295
296impl From<BlockRef> for Slot {
297 fn from(value: BlockRef) -> Self {
298 Slot::new(value.round, value.author)
299 }
300}
301
302impl fmt::Display for Slot {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 write!(f, "{}{}", self.authority, self.round,)
306 }
307}
308
309impl fmt::Debug for Slot {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 write!(f, "{}", self)
312 }
313}
314
315#[derive(Deserialize, Serialize)]
321pub(crate) struct SignedBlock {
322 inner: Block,
323 signature: Bytes,
324}
325
326impl SignedBlock {
327 pub(crate) fn new_genesis(block: Block) -> Self {
329 Self {
330 inner: block,
331 signature: Bytes::default(),
332 }
333 }
334
335 pub(crate) fn new(block: Block, protocol_keypair: &ProtocolKeyPair) -> ConsensusResult<Self> {
336 let signature = compute_block_signature(&block, protocol_keypair)?;
337 Ok(Self {
338 inner: block,
339 signature: Bytes::copy_from_slice(signature.to_bytes()),
340 })
341 }
342
343 pub(crate) fn signature(&self) -> &Bytes {
344 &self.signature
345 }
346
347 pub(crate) fn verify_signature(&self, context: &Context) -> ConsensusResult<()> {
350 let block = &self.inner;
351 let committee = &context.committee;
352 ensure!(
353 committee.is_valid_index(block.author()),
354 ConsensusError::InvalidAuthorityIndex {
355 index: block.author(),
356 max: committee.size() - 1
357 }
358 );
359 let authority = committee.authority(block.author());
360 verify_block_signature(block, self.signature(), &authority.protocol_key)
361 }
362
363 pub(crate) fn serialize(&self) -> Result<Bytes, bcs::Error> {
365 let bytes = bcs::to_bytes(self)?;
366 Ok(bytes.into())
367 }
368
369 #[cfg(test)]
371 pub(crate) fn clear_signature(&mut self) {
372 self.signature = Bytes::default();
373 }
374}
375
376#[derive(Serialize, Deserialize)]
381struct InnerBlockDigest([u8; consensus_config::DIGEST_LENGTH]);
382
383fn compute_inner_block_digest(block: &Block) -> ConsensusResult<InnerBlockDigest> {
385 let mut hasher = DefaultHashFunction::new();
386 hasher.update(bcs::to_bytes(block).map_err(ConsensusError::SerializationFailure)?);
387 Ok(InnerBlockDigest(hasher.finalize().into()))
388}
389
390fn to_consensus_block_intent(digest: InnerBlockDigest) -> IntentMessage<InnerBlockDigest> {
392 IntentMessage::new(Intent::consensus_app(IntentScope::ConsensusBlock), digest)
393}
394
395fn compute_block_signature(
400 block: &Block,
401 protocol_keypair: &ProtocolKeyPair,
402) -> ConsensusResult<ProtocolKeySignature> {
403 let digest = compute_inner_block_digest(block)?;
404 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
405 .map_err(ConsensusError::SerializationFailure)?;
406 Ok(protocol_keypair.sign(&message))
407}
408fn verify_block_signature(
409 block: &Block,
410 signature: &[u8],
411 protocol_pubkey: &ProtocolPublicKey,
412) -> ConsensusResult<()> {
413 let digest = compute_inner_block_digest(block)?;
414 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
415 .map_err(ConsensusError::SerializationFailure)?;
416 let sig =
417 ProtocolKeySignature::from_bytes(signature).map_err(ConsensusError::MalformedSignature)?;
418 protocol_pubkey
419 .verify(&message, &sig)
420 .map_err(ConsensusError::SignatureVerificationFailure)
421}
422
423impl Deref for SignedBlock {
426 type Target = Block;
427
428 fn deref(&self) -> &Self::Target {
429 &self.inner
430 }
431}
432
433#[derive(Clone)]
436pub struct VerifiedBlock {
437 block: Arc<SignedBlock>,
438
439 digest: BlockDigest,
441 serialized: Bytes,
442}
443
444impl VerifiedBlock {
445 pub(crate) fn new_verified(signed_block: SignedBlock, serialized: Bytes) -> Self {
448 let digest = Self::compute_digest(&serialized);
449 VerifiedBlock {
450 block: Arc::new(signed_block),
451 digest,
452 serialized,
453 }
454 }
455
456 pub fn new_for_test(block: Block) -> Self {
458 let signed_block = SignedBlock {
459 inner: block,
460 signature: Default::default(),
461 };
462 let serialized: Bytes = bcs::to_bytes(&signed_block)
463 .expect("Serialization should not fail")
464 .into();
465 let digest = Self::compute_digest(&serialized);
466 VerifiedBlock {
467 block: Arc::new(signed_block),
468 digest,
469 serialized,
470 }
471 }
472
473 pub fn reference(&self) -> BlockRef {
475 BlockRef {
476 round: self.round(),
477 author: self.author(),
478 digest: self.digest(),
479 }
480 }
481
482 pub(crate) fn digest(&self) -> BlockDigest {
483 self.digest
484 }
485
486 pub(crate) fn serialized(&self) -> &Bytes {
488 &self.serialized
489 }
490
491 pub(crate) fn compute_digest(serialized: &[u8]) -> BlockDigest {
493 let mut hasher = DefaultHashFunction::new();
494 hasher.update(serialized);
495 BlockDigest(hasher.finalize().into())
496 }
497}
498
499impl Deref for VerifiedBlock {
502 type Target = Block;
503
504 fn deref(&self) -> &Self::Target {
505 &self.block.inner
506 }
507}
508
509impl PartialEq for VerifiedBlock {
510 fn eq(&self, other: &Self) -> bool {
511 self.digest() == other.digest()
512 }
513}
514
515impl fmt::Display for VerifiedBlock {
516 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
517 write!(f, "{}", self.reference())
518 }
519}
520
521impl fmt::Debug for VerifiedBlock {
523 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
524 write!(
525 f,
526 "{:?}({}ms;{:?};{}t;{}c)",
527 self.reference(),
528 self.timestamp_ms(),
529 self.ancestors(),
530 self.transactions().len(),
531 self.commit_votes().len(),
532 )
533 }
534}
535
536#[derive(Clone, Debug)]
541pub(crate) struct ExtendedBlock {
542 pub block: VerifiedBlock,
543 pub excluded_ancestors: Vec<BlockRef>,
544}
545
546pub(crate) fn genesis_blocks(context: Arc<Context>) -> Vec<VerifiedBlock> {
549 context
550 .committee
551 .authorities()
552 .map(|(authority_index, _)| {
553 let signed_block = SignedBlock::new_genesis(Block::V1(BlockV1::genesis_block(
554 context.committee.epoch(),
555 authority_index,
556 )));
557 let serialized = signed_block
558 .serialize()
559 .expect("Genesis block serialization failed.");
560 VerifiedBlock::new_verified(signed_block, serialized)
562 })
563 .collect::<Vec<VerifiedBlock>>()
564}
565
566#[derive(Clone)]
568pub struct TestBlock {
569 block: BlockV1,
570}
571
572impl TestBlock {
573 pub fn new(round: Round, author: u32) -> Self {
574 Self {
575 block: BlockV1 {
576 round,
577 author: AuthorityIndex::new_for_test(author),
578 ..Default::default()
579 },
580 }
581 }
582
583 pub fn set_epoch(mut self, epoch: Epoch) -> Self {
584 self.block.epoch = epoch;
585 self
586 }
587
588 pub fn set_round(mut self, round: Round) -> Self {
589 self.block.round = round;
590 self
591 }
592
593 pub fn set_author(mut self, author: AuthorityIndex) -> Self {
594 self.block.author = author;
595 self
596 }
597
598 pub fn set_timestamp_ms(mut self, timestamp_ms: BlockTimestampMs) -> Self {
599 self.block.timestamp_ms = timestamp_ms;
600 self
601 }
602
603 pub fn set_ancestors(mut self, ancestors: Vec<BlockRef>) -> Self {
604 self.block.ancestors = ancestors;
605 self
606 }
607
608 pub fn set_transactions(mut self, transactions: Vec<Transaction>) -> Self {
609 self.block.transactions = transactions;
610 self
611 }
612
613 pub fn set_commit_votes(mut self, commit_votes: Vec<CommitVote>) -> Self {
614 self.block.commit_votes = commit_votes;
615 self
616 }
617
618 pub fn build(self) -> Block {
619 Block::V1(self.block)
620 }
621}
622
623#[derive(Clone, Serialize, Deserialize, Debug)]
625pub struct MisbehaviorReport {
626 pub target: AuthorityIndex,
627 pub proof: MisbehaviorProof,
628}
629
630#[derive(Clone, Serialize, Deserialize, Debug)]
633pub enum MisbehaviorProof {
634 InvalidBlock(BlockRef),
635}
636
637#[cfg(test)]
641mod tests {
642 use std::sync::Arc;
643
644 use fastcrypto::error::FastCryptoError;
645
646 use crate::{
647 block::{SignedBlock, TestBlock},
648 context::Context,
649 error::ConsensusError,
650 };
651
652 #[tokio::test]
653 async fn test_sign_and_verify() {
654 let (context, key_pairs) = Context::new_for_test(4);
655 let context = Arc::new(context);
656
657 let block = TestBlock::new(10, 2).build();
659
660 let author_two_key = &key_pairs[2].1;
662 let signed_block = SignedBlock::new(block, author_two_key).expect("Shouldn't fail signing");
663
664 let result = signed_block.verify_signature(&context);
666 assert!(result.is_ok());
667
668 let block = TestBlock::new(10, 2).build();
670 let author_one_key = &key_pairs[1].1;
671 let signed_block = SignedBlock::new(block, author_one_key).expect("Shouldn't fail signing");
672
673 let result = signed_block.verify_signature(&context);
675 match result.err().unwrap() {
676 ConsensusError::SignatureVerificationFailure(err) => {
677 assert_eq!(err, FastCryptoError::InvalidSignature);
678 }
679 err => panic!("Unexpected error: {err:?}"),
680 }
681 }
682}