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 V3(BlockV3),
66}
67
68#[allow(private_interfaces)]
69#[enum_dispatch]
70pub trait BlockAPI {
71 fn epoch(&self) -> Epoch;
72 fn round(&self) -> Round;
73 fn author(&self) -> AuthorityIndex;
74 fn slot(&self) -> Slot;
75 fn timestamp_ms(&self) -> BlockTimestampMs;
76 fn ancestors(&self) -> &[BlockRef];
77
78 fn transactions(&self) -> &[Transaction];
80 fn transactions_data(&self) -> Vec<&[u8]>;
81
82 fn transaction_votes(&self) -> &[BlockTransactionVotes];
84
85 fn transaction_votes_cutoff_round(&self) -> Round;
89
90 fn commit_votes(&self) -> &[CommitVote];
92
93 fn misbehavior_reports(&self) -> &[MisbehaviorReport];
94}
95
96#[derive(Clone, Default, Deserialize, Serialize)]
97pub(crate) struct BlockV1 {
98 epoch: Epoch,
99 round: Round,
100 author: AuthorityIndex,
101 timestamp_ms: BlockTimestampMs,
102 ancestors: Vec<BlockRef>,
103 transactions: Vec<Transaction>,
104 commit_votes: Vec<CommitVote>,
105 misbehavior_reports: Vec<MisbehaviorReport>,
106}
107
108impl BlockV1 {
109 pub(crate) fn new(
110 epoch: Epoch,
111 round: Round,
112 author: AuthorityIndex,
113 timestamp_ms: BlockTimestampMs,
114 ancestors: Vec<BlockRef>,
115 transactions: Vec<Transaction>,
116 commit_votes: Vec<CommitVote>,
117 misbehavior_reports: Vec<MisbehaviorReport>,
118 ) -> BlockV1 {
119 Self {
120 epoch,
121 round,
122 author,
123 timestamp_ms,
124 ancestors,
125 transactions,
126 commit_votes,
127 misbehavior_reports,
128 }
129 }
130
131 fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
132 Self {
133 epoch: context.committee.epoch(),
134 round: GENESIS_ROUND,
135 author,
136 timestamp_ms: context.epoch_start_timestamp_ms,
137 ancestors: vec![],
138 transactions: vec![],
139 commit_votes: vec![],
140 misbehavior_reports: vec![],
141 }
142 }
143}
144
145impl BlockAPI for BlockV1 {
146 fn epoch(&self) -> Epoch {
147 self.epoch
148 }
149
150 fn round(&self) -> Round {
151 self.round
152 }
153
154 fn author(&self) -> AuthorityIndex {
155 self.author
156 }
157
158 fn slot(&self) -> Slot {
159 Slot::new(self.round, self.author)
160 }
161
162 fn timestamp_ms(&self) -> BlockTimestampMs {
163 self.timestamp_ms
164 }
165
166 fn ancestors(&self) -> &[BlockRef] {
167 &self.ancestors
168 }
169
170 fn transactions(&self) -> &[Transaction] {
171 &self.transactions
172 }
173
174 fn transactions_data(&self) -> Vec<&[u8]> {
175 self.transactions.iter().map(|t| t.data()).collect()
176 }
177
178 fn transaction_votes(&self) -> &[BlockTransactionVotes] {
179 &[]
180 }
181
182 fn transaction_votes_cutoff_round(&self) -> Round {
183 panic!("transaction_votes_cutoff_round() is not supported on BlockV1");
184 }
185
186 fn commit_votes(&self) -> &[CommitVote] {
187 &self.commit_votes
188 }
189
190 fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
191 &self.misbehavior_reports
192 }
193}
194
195#[derive(Clone, Default, Deserialize, Serialize)]
196pub(crate) struct BlockV2 {
197 epoch: Epoch,
198 round: Round,
199 author: AuthorityIndex,
200 timestamp_ms: BlockTimestampMs,
201 ancestors: Vec<BlockRef>,
202 transactions: Vec<Transaction>,
203 transaction_votes: Vec<BlockTransactionVotes>,
204 commit_votes: Vec<CommitVote>,
205 misbehavior_reports: Vec<MisbehaviorReport>,
206}
207
208impl BlockV2 {
209 pub(crate) fn new(
210 epoch: Epoch,
211 round: Round,
212 author: AuthorityIndex,
213 timestamp_ms: BlockTimestampMs,
214 ancestors: Vec<BlockRef>,
215 transactions: Vec<Transaction>,
216 transaction_votes: Vec<BlockTransactionVotes>,
217 commit_votes: Vec<CommitVote>,
218 misbehavior_reports: Vec<MisbehaviorReport>,
219 ) -> BlockV2 {
220 Self {
221 epoch,
222 round,
223 author,
224 timestamp_ms,
225 ancestors,
226 transactions,
227 transaction_votes,
228 commit_votes,
229 misbehavior_reports,
230 }
231 }
232
233 fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
234 Self {
235 epoch: context.committee.epoch(),
236 round: GENESIS_ROUND,
237 author,
238 timestamp_ms: context.epoch_start_timestamp_ms,
239 ancestors: vec![],
240 transactions: vec![],
241 transaction_votes: vec![],
242 commit_votes: vec![],
243 misbehavior_reports: vec![],
244 }
245 }
246}
247
248impl BlockAPI for BlockV2 {
249 fn epoch(&self) -> Epoch {
250 self.epoch
251 }
252
253 fn round(&self) -> Round {
254 self.round
255 }
256
257 fn author(&self) -> AuthorityIndex {
258 self.author
259 }
260
261 fn slot(&self) -> Slot {
262 Slot::new(self.round, self.author)
263 }
264
265 fn timestamp_ms(&self) -> BlockTimestampMs {
266 self.timestamp_ms
267 }
268
269 fn ancestors(&self) -> &[BlockRef] {
270 &self.ancestors
271 }
272
273 fn transactions(&self) -> &[Transaction] {
274 &self.transactions
275 }
276
277 fn transactions_data(&self) -> Vec<&[u8]> {
278 self.transactions.iter().map(|t| t.data()).collect()
279 }
280
281 fn transaction_votes(&self) -> &[BlockTransactionVotes] {
282 &self.transaction_votes
283 }
284
285 fn transaction_votes_cutoff_round(&self) -> Round {
286 panic!("transaction_votes_cutoff_round() is not supported on BlockV2");
287 }
288
289 fn commit_votes(&self) -> &[CommitVote] {
290 &self.commit_votes
291 }
292
293 fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
294 &self.misbehavior_reports
295 }
296}
297
298#[derive(Clone, Default, Deserialize, Serialize)]
299pub(crate) struct BlockV3 {
300 epoch: Epoch,
301 round: Round,
302 author: AuthorityIndex,
303 timestamp_ms: BlockTimestampMs,
304 ancestors: Vec<BlockRef>,
305 transactions: Vec<Transaction>,
306 transaction_votes: Vec<BlockTransactionVotes>,
307 transaction_votes_cutoff_round: Round,
308 commit_votes: Vec<CommitVote>,
309 misbehavior_reports: Vec<MisbehaviorReport>,
310}
311
312#[allow(dead_code)]
313impl BlockV3 {
314 pub(crate) fn new(
315 epoch: Epoch,
316 round: Round,
317 author: AuthorityIndex,
318 timestamp_ms: BlockTimestampMs,
319 ancestors: Vec<BlockRef>,
320 transactions: Vec<Transaction>,
321 transaction_votes: Vec<BlockTransactionVotes>,
322 transaction_votes_cutoff_round: Round,
323 commit_votes: Vec<CommitVote>,
324 misbehavior_reports: Vec<MisbehaviorReport>,
325 ) -> BlockV3 {
326 Self {
327 epoch,
328 round,
329 author,
330 timestamp_ms,
331 ancestors,
332 transactions,
333 transaction_votes,
334 transaction_votes_cutoff_round,
335 commit_votes,
336 misbehavior_reports,
337 }
338 }
339
340 fn genesis_block(context: &Context, author: AuthorityIndex) -> Self {
341 Self {
342 epoch: context.committee.epoch(),
343 round: GENESIS_ROUND,
344 author,
345 timestamp_ms: context.epoch_start_timestamp_ms,
346 ancestors: vec![],
347 transactions: vec![],
348 transaction_votes: vec![],
349 transaction_votes_cutoff_round: GENESIS_ROUND,
350 commit_votes: vec![],
351 misbehavior_reports: vec![],
352 }
353 }
354}
355
356impl BlockAPI for BlockV3 {
357 fn epoch(&self) -> Epoch {
358 self.epoch
359 }
360
361 fn round(&self) -> Round {
362 self.round
363 }
364
365 fn author(&self) -> AuthorityIndex {
366 self.author
367 }
368
369 fn slot(&self) -> Slot {
370 Slot::new(self.round, self.author)
371 }
372
373 fn timestamp_ms(&self) -> BlockTimestampMs {
374 self.timestamp_ms
375 }
376
377 fn ancestors(&self) -> &[BlockRef] {
378 &self.ancestors
379 }
380
381 fn transactions(&self) -> &[Transaction] {
382 &self.transactions
383 }
384
385 fn transactions_data(&self) -> Vec<&[u8]> {
386 self.transactions.iter().map(|t| t.data()).collect()
387 }
388
389 fn transaction_votes(&self) -> &[BlockTransactionVotes] {
390 &self.transaction_votes
391 }
392
393 fn transaction_votes_cutoff_round(&self) -> Round {
394 self.transaction_votes_cutoff_round
395 }
396
397 fn commit_votes(&self) -> &[CommitVote] {
398 &self.commit_votes
399 }
400
401 fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
402 &self.misbehavior_reports
403 }
404}
405
406#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
409pub struct Slot {
410 pub round: Round,
411 pub authority: AuthorityIndex,
412}
413
414impl Slot {
415 pub fn new(round: Round, authority: AuthorityIndex) -> Self {
416 Self { round, authority }
417 }
418
419 pub fn new_for_test(round: Round, authority: u32) -> Self {
420 Self {
421 round,
422 authority: AuthorityIndex::new_for_test(authority),
423 }
424 }
425}
426
427impl From<BlockRef> for Slot {
428 fn from(value: BlockRef) -> Self {
429 Slot::new(value.round, value.author)
430 }
431}
432
433impl fmt::Display for Slot {
435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 write!(f, "{}{}", self.authority, self.round)
437 }
438}
439
440impl fmt::Debug for Slot {
441 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
442 write!(f, "{}", self)
443 }
444}
445
446#[derive(Deserialize, Serialize)]
451pub(crate) struct SignedBlock {
452 inner: Block,
453 signature: Bytes,
454}
455
456impl SignedBlock {
457 pub(crate) fn new_genesis(block: Block) -> Self {
459 Self {
460 inner: block,
461 signature: Bytes::default(),
462 }
463 }
464
465 pub(crate) fn new(block: Block, protocol_keypair: &ProtocolKeyPair) -> ConsensusResult<Self> {
466 let signature = compute_block_signature(&block, protocol_keypair)?;
467 Ok(Self {
468 inner: block,
469 signature: Bytes::copy_from_slice(signature.to_bytes()),
470 })
471 }
472
473 pub(crate) fn signature(&self) -> &Bytes {
474 &self.signature
475 }
476
477 pub(crate) fn verify_signature(&self, context: &Context) -> ConsensusResult<()> {
480 let block = &self.inner;
481 let committee = &context.committee;
482 ensure!(
483 committee.is_valid_index(block.author()),
484 ConsensusError::InvalidAuthorityIndex {
485 index: block.author(),
486 max: committee.size() - 1
487 }
488 );
489 let authority = committee.authority(block.author());
490 verify_block_signature(block, self.signature(), &authority.protocol_key)
491 }
492
493 pub(crate) fn serialize(&self) -> Result<Bytes, bcs::Error> {
495 let bytes = bcs::to_bytes(self)?;
496 Ok(bytes.into())
497 }
498
499 #[cfg(test)]
501 pub(crate) fn clear_signature(&mut self) {
502 self.signature = Bytes::default();
503 }
504}
505
506#[derive(Serialize, Deserialize)]
510struct InnerBlockDigest([u8; consensus_config::DIGEST_LENGTH]);
511
512fn compute_inner_block_digest(block: &Block) -> ConsensusResult<InnerBlockDigest> {
514 let mut hasher = DefaultHashFunction::new();
515 bcs::serialize_into(&mut hasher, block).map_err(ConsensusError::SerializationFailure)?;
516 Ok(InnerBlockDigest(hasher.finalize().into()))
517}
518
519fn to_consensus_block_intent(digest: InnerBlockDigest) -> IntentMessage<InnerBlockDigest> {
521 IntentMessage::new(Intent::consensus_app(IntentScope::ConsensusBlock), digest)
522}
523
524fn compute_block_signature(
529 block: &Block,
530 protocol_keypair: &ProtocolKeyPair,
531) -> ConsensusResult<ProtocolKeySignature> {
532 let digest = compute_inner_block_digest(block)?;
533 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
534 .map_err(ConsensusError::SerializationFailure)?;
535 Ok(protocol_keypair.sign(&message))
536}
537
538fn verify_block_signature(
539 block: &Block,
540 signature: &[u8],
541 protocol_pubkey: &ProtocolPublicKey,
542) -> ConsensusResult<()> {
543 let digest = compute_inner_block_digest(block)?;
544 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
545 .map_err(ConsensusError::SerializationFailure)?;
546 let sig =
547 ProtocolKeySignature::from_bytes(signature).map_err(ConsensusError::MalformedSignature)?;
548 protocol_pubkey
549 .verify(&message, &sig)
550 .map_err(ConsensusError::SignatureVerificationFailure)
551}
552
553impl Deref for SignedBlock {
555 type Target = Block;
556
557 fn deref(&self) -> &Self::Target {
558 &self.inner
559 }
560}
561
562#[derive(Clone)]
565pub struct VerifiedBlock {
566 block: Arc<SignedBlock>,
567
568 digest: BlockDigest,
570 serialized: Bytes,
571}
572
573impl VerifiedBlock {
574 pub(crate) fn new_verified(signed_block: SignedBlock, serialized: Bytes) -> Self {
576 let digest = Self::compute_digest(&serialized);
577 VerifiedBlock {
578 block: Arc::new(signed_block),
579 digest,
580 serialized,
581 }
582 }
583
584 pub fn new_for_test(block: Block) -> Self {
586 let signed_block = SignedBlock {
588 inner: block,
589 signature: Default::default(),
590 };
591 let serialized: Bytes = bcs::to_bytes(&signed_block)
592 .expect("Serialization should not fail")
593 .into();
594 let digest = Self::compute_digest(&serialized);
595 VerifiedBlock {
596 block: Arc::new(signed_block),
597 digest,
598 serialized,
599 }
600 }
601
602 pub fn reference(&self) -> BlockRef {
604 BlockRef {
605 round: self.round(),
606 author: self.author(),
607 digest: self.digest(),
608 }
609 }
610
611 pub(crate) fn digest(&self) -> BlockDigest {
612 self.digest
613 }
614
615 pub(crate) fn serialized(&self) -> &Bytes {
617 &self.serialized
618 }
619
620 pub(crate) fn compute_digest(serialized: &[u8]) -> BlockDigest {
622 let mut hasher = DefaultHashFunction::new();
623 hasher.update(serialized);
624 BlockDigest(hasher.finalize().into())
625 }
626}
627
628impl Deref for VerifiedBlock {
630 type Target = Block;
631
632 fn deref(&self) -> &Self::Target {
633 &self.block.inner
634 }
635}
636
637impl PartialEq for VerifiedBlock {
638 fn eq(&self, other: &Self) -> bool {
639 self.digest() == other.digest()
640 }
641}
642
643impl fmt::Display for VerifiedBlock {
644 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
645 write!(f, "{}", self.reference())
646 }
647}
648
649impl fmt::Debug for VerifiedBlock {
651 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
652 write!(
653 f,
654 "{:?}([{}];{}ms;{}t;{}c)",
655 self.reference(),
656 self.ancestors().iter().map(|a| a.to_string()).join(", "),
657 self.timestamp_ms(),
658 self.transactions().len(),
659 self.commit_votes().len(),
660 )
661 }
662}
663
664#[derive(Clone, Debug)]
668pub(crate) struct ExtendedBlock {
669 pub block: VerifiedBlock,
670 pub excluded_ancestors: Vec<BlockRef>,
671}
672
673pub(crate) fn genesis_blocks(context: &Context) -> Vec<VerifiedBlock> {
676 context
677 .committee
678 .authorities()
679 .map(|(authority_index, _)| {
680 let block = if context.protocol_config.transaction_voting_enabled() {
681 Block::V2(BlockV2::genesis_block(context, authority_index))
682 } else {
683 Block::V1(BlockV1::genesis_block(context, authority_index))
684 };
685 let signed_block = SignedBlock::new_genesis(block);
686 let serialized = signed_block
687 .serialize()
688 .expect("Genesis block serialization failed.");
689 VerifiedBlock::new_verified(signed_block, serialized)
691 })
692 .collect::<Vec<VerifiedBlock>>()
693}
694
695#[derive(Clone)]
698pub struct TestBlock {
699 block: BlockV2,
700}
701
702impl TestBlock {
703 pub fn new(round: Round, author: u32) -> Self {
704 Self {
705 block: BlockV2 {
706 round,
707 author: AuthorityIndex::new_for_test(author),
708 ..Default::default()
709 },
710 }
711 }
712
713 pub fn set_epoch(mut self, epoch: Epoch) -> Self {
714 self.block.epoch = epoch;
715 self
716 }
717
718 pub fn set_round(mut self, round: Round) -> Self {
719 self.block.round = round;
720 self
721 }
722
723 pub fn set_author(mut self, author: AuthorityIndex) -> Self {
724 self.block.author = author;
725 self
726 }
727
728 pub fn set_timestamp_ms(mut self, timestamp_ms: BlockTimestampMs) -> Self {
729 self.block.timestamp_ms = timestamp_ms;
730 self
731 }
732
733 pub fn set_ancestors(mut self, mut ancestors: Vec<BlockRef>) -> Self {
737 ancestors.sort_by(|a, b| {
738 if a.author == self.block.author {
739 return std::cmp::Ordering::Less;
740 }
741 if b.author == self.block.author {
742 return std::cmp::Ordering::Greater;
743 }
744 a.author.cmp(&b.author)
745 });
746 self.block.ancestors = ancestors;
747 self
748 }
749
750 pub fn set_ancestors_raw(mut self, ancestors: Vec<BlockRef>) -> Self {
752 self.block.ancestors = ancestors;
753 self
754 }
755
756 pub fn set_transactions(mut self, transactions: Vec<Transaction>) -> Self {
757 self.block.transactions = transactions;
758 self
759 }
760
761 pub(crate) fn set_transaction_votes(mut self, votes: Vec<BlockTransactionVotes>) -> Self {
762 self.block.transaction_votes = votes;
763 self
764 }
765
766 #[cfg(test)]
767 pub(crate) fn set_commit_votes(mut self, commit_votes: Vec<CommitVote>) -> Self {
768 self.block.commit_votes = commit_votes;
769 self
770 }
771
772 pub fn build(self) -> Block {
773 Block::V2(self.block)
774 }
775}
776
777#[derive(Clone, Serialize, Deserialize, Debug)]
779pub struct MisbehaviorReport {
780 pub target: AuthorityIndex,
781 pub proof: MisbehaviorProof,
782}
783
784#[derive(Clone, Serialize, Deserialize, Debug)]
786pub enum MisbehaviorProof {
787 InvalidBlock(BlockRef),
788}
789
790#[cfg(test)]
794mod tests {
795 use std::sync::Arc;
796
797 use fastcrypto::error::FastCryptoError;
798
799 use crate::{
800 block::{BlockAPI, SignedBlock, TestBlock, genesis_blocks},
801 context::Context,
802 error::ConsensusError,
803 };
804
805 #[tokio::test]
806 async fn test_sign_and_verify() {
807 let (context, key_pairs) = Context::new_for_test(4);
808 let context = Arc::new(context);
809
810 let block = TestBlock::new(10, 2).build();
812
813 let author_two_key = &key_pairs[2].1;
815 let signed_block = SignedBlock::new(block, author_two_key).expect("Shouldn't fail signing");
816
817 let result = signed_block.verify_signature(&context);
819 assert!(result.is_ok());
820
821 let block = TestBlock::new(10, 2).build();
823 let author_one_key = &key_pairs[1].1;
824 let signed_block = SignedBlock::new(block, author_one_key).expect("Shouldn't fail signing");
825
826 let result = signed_block.verify_signature(&context);
828 match result.err().unwrap() {
829 ConsensusError::SignatureVerificationFailure(err) => {
830 assert_eq!(err, FastCryptoError::InvalidSignature);
831 }
832 err => panic!("Unexpected error: {err:?}"),
833 }
834 }
835
836 #[tokio::test]
837 async fn test_genesis_blocks() {
838 let (context, _) = Context::new_for_test(4);
839 const TIMESTAMP_MS: u64 = 1000;
840 let context = Arc::new(context.with_epoch_start_timestamp_ms(TIMESTAMP_MS));
841 let blocks = genesis_blocks(&context);
842 for (i, block) in blocks.into_iter().enumerate() {
843 assert_eq!(block.author().value(), i);
844 assert_eq!(block.round(), 0);
845 assert_eq!(block.timestamp_ms(), TIMESTAMP_MS);
846 }
847 }
848}