1use std::{fmt, hash::Hash, ops::Deref, sync::Arc};
5
6use bytes::Bytes;
7use consensus_config::{
8 AuthorityIndex, DefaultHashFunction, Epoch, ProtocolKeyPair, ProtocolKeySignature,
9 ProtocolPublicKey,
10};
11use consensus_types::block::{BlockDigest, BlockRef, BlockTimestampMs, Round, TransactionIndex};
12use enum_dispatch::enum_dispatch;
13use fastcrypto::hash::HashFunction;
14use itertools::Itertools as _;
15use serde::{Deserialize, Serialize};
16use shared_crypto::intent::{Intent, IntentMessage, IntentScope};
17
18use crate::{
19 commit::CommitVote,
20 context::Context,
21 ensure,
22 error::{ConsensusError, ConsensusResult},
23};
24
25pub(crate) const GENESIS_ROUND: Round = 0;
26
27#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Default, Debug)]
29pub struct Transaction {
30 data: Bytes,
31}
32
33impl Transaction {
34 pub fn new(data: Vec<u8>) -> Self {
35 Self { data: data.into() }
36 }
37
38 pub fn data(&self) -> &[u8] {
39 &self.data
40 }
41
42 pub fn into_data(self) -> Bytes {
43 self.data
44 }
45}
46#[derive(Clone, Deserialize, Serialize)]
50pub(crate) struct BlockTransactionVotes {
51 pub(crate) block_ref: BlockRef,
52 pub(crate) rejects: Vec<TransactionIndex>,
53}
54
55#[allow(private_interfaces)]
60#[derive(Clone, Deserialize, Serialize)]
61#[enum_dispatch(BlockAPI)]
62pub enum Block {
63 V1(BlockV1),
64 V2(BlockV2),
65}
66
67#[allow(private_interfaces)]
68#[enum_dispatch]
69pub trait BlockAPI {
70 fn epoch(&self) -> Epoch;
71 fn round(&self) -> Round;
72 fn author(&self) -> AuthorityIndex;
73 fn slot(&self) -> Slot;
74 fn timestamp_ms(&self) -> BlockTimestampMs;
75 fn ancestors(&self) -> &[BlockRef];
76 fn transactions(&self) -> &[Transaction];
77 fn transactions_data(&self) -> Vec<&[u8]>;
78 fn commit_votes(&self) -> &[CommitVote];
79 fn transaction_votes(&self) -> &[BlockTransactionVotes];
80 fn misbehavior_reports(&self) -> &[MisbehaviorReport];
81}
82
83#[derive(Clone, Default, Deserialize, Serialize)]
84pub(crate) struct BlockV1 {
85 epoch: Epoch,
86 round: Round,
87 author: AuthorityIndex,
88 timestamp_ms: BlockTimestampMs,
89 ancestors: Vec<BlockRef>,
90 transactions: Vec<Transaction>,
91 commit_votes: Vec<CommitVote>,
92 misbehavior_reports: Vec<MisbehaviorReport>,
93}
94
95impl BlockV1 {
96 pub(crate) fn new(
97 epoch: Epoch,
98 round: Round,
99 author: AuthorityIndex,
100 timestamp_ms: BlockTimestampMs,
101 ancestors: Vec<BlockRef>,
102 transactions: Vec<Transaction>,
103 commit_votes: Vec<CommitVote>,
104 misbehavior_reports: Vec<MisbehaviorReport>,
105 ) -> BlockV1 {
106 Self {
107 epoch,
108 round,
109 author,
110 timestamp_ms,
111 ancestors,
112 transactions,
113 commit_votes,
114 misbehavior_reports,
115 }
116 }
117
118 fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
119 Self {
120 epoch: context.committee.epoch(),
121 round: GENESIS_ROUND,
122 author,
123 timestamp_ms: context.epoch_start_timestamp_ms,
124 ancestors: vec![],
125 transactions: vec![],
126 commit_votes: vec![],
127 misbehavior_reports: vec![],
128 }
129 }
130}
131
132impl BlockAPI for BlockV1 {
133 fn epoch(&self) -> Epoch {
134 self.epoch
135 }
136
137 fn round(&self) -> Round {
138 self.round
139 }
140
141 fn author(&self) -> AuthorityIndex {
142 self.author
143 }
144
145 fn slot(&self) -> Slot {
146 Slot::new(self.round, self.author)
147 }
148
149 fn timestamp_ms(&self) -> BlockTimestampMs {
150 self.timestamp_ms
151 }
152
153 fn ancestors(&self) -> &[BlockRef] {
154 &self.ancestors
155 }
156
157 fn transactions(&self) -> &[Transaction] {
158 &self.transactions
159 }
160
161 fn transactions_data(&self) -> Vec<&[u8]> {
162 self.transactions.iter().map(|t| t.data()).collect()
163 }
164
165 fn commit_votes(&self) -> &[CommitVote] {
166 &self.commit_votes
167 }
168
169 fn transaction_votes(&self) -> &[BlockTransactionVotes] {
170 &[]
171 }
172
173 fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
174 &self.misbehavior_reports
175 }
176}
177
178#[derive(Clone, Default, Deserialize, Serialize)]
179pub(crate) struct BlockV2 {
180 epoch: Epoch,
181 round: Round,
182 author: AuthorityIndex,
183 timestamp_ms: BlockTimestampMs,
184 ancestors: Vec<BlockRef>,
185 transactions: Vec<Transaction>,
186 transaction_votes: Vec<BlockTransactionVotes>,
187 commit_votes: Vec<CommitVote>,
188 misbehavior_reports: Vec<MisbehaviorReport>,
189}
190
191#[allow(unused)]
192impl BlockV2 {
193 pub(crate) fn new(
194 epoch: Epoch,
195 round: Round,
196 author: AuthorityIndex,
197 timestamp_ms: BlockTimestampMs,
198 ancestors: Vec<BlockRef>,
199 transactions: Vec<Transaction>,
200 commit_votes: Vec<CommitVote>,
201 transaction_votes: Vec<BlockTransactionVotes>,
202 misbehavior_reports: Vec<MisbehaviorReport>,
203 ) -> BlockV2 {
204 Self {
205 epoch,
206 round,
207 author,
208 timestamp_ms,
209 ancestors,
210 transactions,
211 commit_votes,
212 transaction_votes,
213 misbehavior_reports,
214 }
215 }
216
217 fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
218 Self {
219 epoch: context.committee.epoch(),
220 round: GENESIS_ROUND,
221 author,
222 timestamp_ms: context.epoch_start_timestamp_ms,
223 ancestors: vec![],
224 transactions: vec![],
225 commit_votes: vec![],
226 transaction_votes: vec![],
227 misbehavior_reports: vec![],
228 }
229 }
230}
231
232impl BlockAPI for BlockV2 {
233 fn epoch(&self) -> Epoch {
234 self.epoch
235 }
236
237 fn round(&self) -> Round {
238 self.round
239 }
240
241 fn author(&self) -> AuthorityIndex {
242 self.author
243 }
244
245 fn slot(&self) -> Slot {
246 Slot::new(self.round, self.author)
247 }
248
249 fn timestamp_ms(&self) -> BlockTimestampMs {
250 self.timestamp_ms
251 }
252
253 fn ancestors(&self) -> &[BlockRef] {
254 &self.ancestors
255 }
256
257 fn transactions(&self) -> &[Transaction] {
258 &self.transactions
259 }
260
261 fn transactions_data(&self) -> Vec<&[u8]> {
262 self.transactions.iter().map(|t| t.data()).collect()
263 }
264
265 fn transaction_votes(&self) -> &[BlockTransactionVotes] {
266 &self.transaction_votes
267 }
268
269 fn commit_votes(&self) -> &[CommitVote] {
270 &self.commit_votes
271 }
272
273 fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
274 &self.misbehavior_reports
275 }
276}
277
278#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Hash)]
281pub struct Slot {
282 pub round: Round,
283 pub authority: AuthorityIndex,
284}
285
286impl Slot {
287 pub fn new(round: Round, authority: AuthorityIndex) -> Self {
288 Self { round, authority }
289 }
290
291 #[cfg(test)]
292 pub fn new_for_test(round: Round, authority: u32) -> Self {
293 Self {
294 round,
295 authority: AuthorityIndex::new_for_test(authority),
296 }
297 }
298}
299
300impl From<BlockRef> for Slot {
301 fn from(value: BlockRef) -> Self {
302 Slot::new(value.round, value.author)
303 }
304}
305
306impl fmt::Display for Slot {
308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
309 write!(f, "{}{}", self.authority, self.round)
310 }
311}
312
313impl fmt::Debug for Slot {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 write!(f, "{}", self)
316 }
317}
318
319#[derive(Deserialize, Serialize)]
324pub(crate) struct SignedBlock {
325 inner: Block,
326 signature: Bytes,
327}
328
329impl SignedBlock {
330 pub(crate) fn new_genesis(block: Block) -> Self {
332 Self {
333 inner: block,
334 signature: Bytes::default(),
335 }
336 }
337
338 pub(crate) fn new(block: Block, protocol_keypair: &ProtocolKeyPair) -> ConsensusResult<Self> {
339 let signature = compute_block_signature(&block, protocol_keypair)?;
340 Ok(Self {
341 inner: block,
342 signature: Bytes::copy_from_slice(signature.to_bytes()),
343 })
344 }
345
346 pub(crate) fn signature(&self) -> &Bytes {
347 &self.signature
348 }
349
350 pub(crate) fn verify_signature(&self, context: &Context) -> ConsensusResult<()> {
353 let block = &self.inner;
354 let committee = &context.committee;
355 ensure!(
356 committee.is_valid_index(block.author()),
357 ConsensusError::InvalidAuthorityIndex {
358 index: block.author(),
359 max: committee.size() - 1
360 }
361 );
362 let authority = committee.authority(block.author());
363 verify_block_signature(block, self.signature(), &authority.protocol_key)
364 }
365
366 pub(crate) fn serialize(&self) -> Result<Bytes, bcs::Error> {
368 let bytes = bcs::to_bytes(self)?;
369 Ok(bytes.into())
370 }
371
372 #[cfg(test)]
374 pub(crate) fn clear_signature(&mut self) {
375 self.signature = Bytes::default();
376 }
377}
378
379#[derive(Serialize, Deserialize)]
383struct InnerBlockDigest([u8; consensus_config::DIGEST_LENGTH]);
384
385fn compute_inner_block_digest(block: &Block) -> ConsensusResult<InnerBlockDigest> {
387 let mut hasher = DefaultHashFunction::new();
388 hasher.update(bcs::to_bytes(block).map_err(ConsensusError::SerializationFailure)?);
389 Ok(InnerBlockDigest(hasher.finalize().into()))
390}
391
392fn to_consensus_block_intent(digest: InnerBlockDigest) -> IntentMessage<InnerBlockDigest> {
394 IntentMessage::new(Intent::consensus_app(IntentScope::ConsensusBlock), digest)
395}
396
397fn compute_block_signature(
402 block: &Block,
403 protocol_keypair: &ProtocolKeyPair,
404) -> ConsensusResult<ProtocolKeySignature> {
405 let digest = compute_inner_block_digest(block)?;
406 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
407 .map_err(ConsensusError::SerializationFailure)?;
408 Ok(protocol_keypair.sign(&message))
409}
410
411fn verify_block_signature(
412 block: &Block,
413 signature: &[u8],
414 protocol_pubkey: &ProtocolPublicKey,
415) -> ConsensusResult<()> {
416 let digest = compute_inner_block_digest(block)?;
417 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
418 .map_err(ConsensusError::SerializationFailure)?;
419 let sig =
420 ProtocolKeySignature::from_bytes(signature).map_err(ConsensusError::MalformedSignature)?;
421 protocol_pubkey
422 .verify(&message, &sig)
423 .map_err(ConsensusError::SignatureVerificationFailure)
424}
425
426impl Deref for SignedBlock {
428 type Target = Block;
429
430 fn deref(&self) -> &Self::Target {
431 &self.inner
432 }
433}
434
435#[derive(Clone)]
438pub struct VerifiedBlock {
439 block: Arc<SignedBlock>,
440
441 digest: BlockDigest,
443 serialized: Bytes,
444}
445
446impl VerifiedBlock {
447 pub(crate) fn new_verified(signed_block: SignedBlock, serialized: Bytes) -> Self {
449 let digest = Self::compute_digest(&serialized);
450 VerifiedBlock {
451 block: Arc::new(signed_block),
452 digest,
453 serialized,
454 }
455 }
456
457 pub fn new_for_test(block: Block) -> Self {
459 let signed_block = SignedBlock {
461 inner: block,
462 signature: Default::default(),
463 };
464 let serialized: Bytes = bcs::to_bytes(&signed_block)
465 .expect("Serialization should not fail")
466 .into();
467 let digest = Self::compute_digest(&serialized);
468 VerifiedBlock {
469 block: Arc::new(signed_block),
470 digest,
471 serialized,
472 }
473 }
474
475 pub fn reference(&self) -> BlockRef {
477 BlockRef {
478 round: self.round(),
479 author: self.author(),
480 digest: self.digest(),
481 }
482 }
483
484 pub(crate) fn digest(&self) -> BlockDigest {
485 self.digest
486 }
487
488 pub(crate) fn serialized(&self) -> &Bytes {
490 &self.serialized
491 }
492
493 pub(crate) fn compute_digest(serialized: &[u8]) -> BlockDigest {
495 let mut hasher = DefaultHashFunction::new();
496 hasher.update(serialized);
497 BlockDigest(hasher.finalize().into())
498 }
499}
500
501impl Deref for VerifiedBlock {
503 type Target = Block;
504
505 fn deref(&self) -> &Self::Target {
506 &self.block.inner
507 }
508}
509
510impl PartialEq for VerifiedBlock {
511 fn eq(&self, other: &Self) -> bool {
512 self.digest() == other.digest()
513 }
514}
515
516impl fmt::Display for VerifiedBlock {
517 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
518 write!(f, "{}", self.reference())
519 }
520}
521
522impl fmt::Debug for VerifiedBlock {
524 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
525 write!(
526 f,
527 "{:?}([{}];{}ms;{}t;{}c)",
528 self.reference(),
529 self.ancestors().iter().map(|a| a.to_string()).join(", "),
530 self.timestamp_ms(),
531 self.transactions().len(),
532 self.commit_votes().len(),
533 )
534 }
535}
536
537#[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: &Context) -> Vec<VerifiedBlock> {
549 context
550 .committee
551 .authorities()
552 .map(|(authority_index, _)| {
553 let block = if context.protocol_config.mysticeti_fastpath() {
554 Block::V2(BlockV2::genesis_block(context, authority_index))
555 } else {
556 Block::V1(BlockV1::genesis_block(context, authority_index))
557 };
558 let signed_block = SignedBlock::new_genesis(block);
559 let serialized = signed_block
560 .serialize()
561 .expect("Genesis block serialization failed.");
562 VerifiedBlock::new_verified(signed_block, serialized)
564 })
565 .collect::<Vec<VerifiedBlock>>()
566}
567
568#[derive(Clone)]
570pub struct CertifiedBlock {
571 pub block: VerifiedBlock,
573 pub rejected: Vec<TransactionIndex>,
575}
576
577impl CertifiedBlock {
578 pub fn new(block: VerifiedBlock, rejected: Vec<TransactionIndex>) -> Self {
579 Self { block, rejected }
580 }
581}
582
583pub struct CertifiedBlocksOutput {
585 pub blocks: Vec<CertifiedBlock>,
586}
587
588#[derive(Clone)]
591pub struct TestBlock {
592 block: BlockV2,
593}
594
595impl TestBlock {
596 pub fn new(round: Round, author: u32) -> Self {
597 Self {
598 block: BlockV2 {
599 round,
600 author: AuthorityIndex::new_for_test(author),
601 ..Default::default()
602 },
603 }
604 }
605
606 pub fn set_epoch(mut self, epoch: Epoch) -> Self {
607 self.block.epoch = epoch;
608 self
609 }
610
611 pub fn set_round(mut self, round: Round) -> Self {
612 self.block.round = round;
613 self
614 }
615
616 pub fn set_author(mut self, author: AuthorityIndex) -> Self {
617 self.block.author = author;
618 self
619 }
620
621 pub fn set_timestamp_ms(mut self, timestamp_ms: BlockTimestampMs) -> Self {
622 self.block.timestamp_ms = timestamp_ms;
623 self
624 }
625
626 pub fn set_ancestors(mut self, ancestors: Vec<BlockRef>) -> Self {
627 self.block.ancestors = ancestors;
628 self
629 }
630
631 pub fn set_transactions(mut self, transactions: Vec<Transaction>) -> Self {
632 self.block.transactions = transactions;
633 self
634 }
635
636 pub(crate) fn set_transaction_votes(mut self, votes: Vec<BlockTransactionVotes>) -> Self {
637 self.block.transaction_votes = votes;
638 self
639 }
640
641 #[cfg(test)]
642 pub(crate) fn set_commit_votes(mut self, commit_votes: Vec<CommitVote>) -> Self {
643 self.block.commit_votes = commit_votes;
644 self
645 }
646
647 pub fn build(self) -> Block {
648 Block::V2(self.block)
649 }
650}
651
652#[derive(Clone, Serialize, Deserialize, Debug)]
654pub struct MisbehaviorReport {
655 pub target: AuthorityIndex,
656 pub proof: MisbehaviorProof,
657}
658
659#[derive(Clone, Serialize, Deserialize, Debug)]
661pub enum MisbehaviorProof {
662 InvalidBlock(BlockRef),
663}
664
665#[cfg(test)]
669mod tests {
670 use std::sync::Arc;
671
672 use fastcrypto::error::FastCryptoError;
673
674 use crate::{
675 block::{BlockAPI, SignedBlock, TestBlock, genesis_blocks},
676 context::Context,
677 error::ConsensusError,
678 };
679
680 #[tokio::test]
681 async fn test_sign_and_verify() {
682 let (context, key_pairs) = Context::new_for_test(4);
683 let context = Arc::new(context);
684
685 let block = TestBlock::new(10, 2).build();
687
688 let author_two_key = &key_pairs[2].1;
690 let signed_block = SignedBlock::new(block, author_two_key).expect("Shouldn't fail signing");
691
692 let result = signed_block.verify_signature(&context);
694 assert!(result.is_ok());
695
696 let block = TestBlock::new(10, 2).build();
698 let author_one_key = &key_pairs[1].1;
699 let signed_block = SignedBlock::new(block, author_one_key).expect("Shouldn't fail signing");
700
701 let result = signed_block.verify_signature(&context);
703 match result.err().unwrap() {
704 ConsensusError::SignatureVerificationFailure(err) => {
705 assert_eq!(err, FastCryptoError::InvalidSignature);
706 }
707 err => panic!("Unexpected error: {err:?}"),
708 }
709 }
710
711 #[tokio::test]
712 async fn test_genesis_blocks() {
713 let (context, _) = Context::new_for_test(4);
714 const TIMESTAMP_MS: u64 = 1000;
715 let context = Arc::new(context.with_epoch_start_timestamp_ms(TIMESTAMP_MS));
716 let blocks = genesis_blocks(&context);
717 for (i, block) in blocks.into_iter().enumerate() {
718 assert_eq!(block.author().value(), i);
719 assert_eq!(block.round(), 0);
720 assert_eq!(block.timestamp_ms(), TIMESTAMP_MS);
721 }
722 }
723}