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(context: &Context, author: AuthorityIndex) -> Self {
117 let timestamp_ms = if context
118 .protocol_config
119 .consensus_median_timestamp_with_checkpoint_enforcement()
120 {
121 context.epoch_start_timestamp_ms
122 } else {
123 0
124 };
125 Self {
126 epoch: context.committee.epoch(),
127 round: GENESIS_ROUND,
128 author,
129 timestamp_ms,
130 ancestors: vec![],
131 transactions: vec![],
132 commit_votes: vec![],
133 misbehavior_reports: vec![],
134 }
135 }
136}
137
138impl BlockAPI for BlockV1 {
139 fn epoch(&self) -> Epoch {
140 self.epoch
141 }
142
143 fn round(&self) -> Round {
144 self.round
145 }
146
147 fn author(&self) -> AuthorityIndex {
148 self.author
149 }
150
151 fn slot(&self) -> Slot {
152 Slot::new(self.round, self.author)
153 }
154
155 fn timestamp_ms(&self) -> BlockTimestampMs {
156 self.timestamp_ms
157 }
158
159 fn ancestors(&self) -> &[BlockRef] {
160 &self.ancestors
161 }
162
163 fn transactions(&self) -> &[Transaction] {
164 &self.transactions
165 }
166
167 fn commit_votes(&self) -> &[CommitVote] {
168 &self.commit_votes
169 }
170
171 fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
172 &self.misbehavior_reports
173 }
174}
175
176#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
180pub struct BlockRef {
181 pub round: Round,
182 pub author: AuthorityIndex,
183 pub digest: BlockDigest,
184}
185
186impl BlockRef {
187 pub const MIN: Self = Self {
188 round: 0,
189 author: AuthorityIndex::MIN,
190 digest: BlockDigest::MIN,
191 };
192
193 pub const MAX: Self = Self {
194 round: u32::MAX,
195 author: AuthorityIndex::MAX,
196 digest: BlockDigest::MAX,
197 };
198
199 pub fn new(round: Round, author: AuthorityIndex, digest: BlockDigest) -> Self {
200 Self {
201 round,
202 author,
203 digest,
204 }
205 }
206}
207
208impl fmt::Display for BlockRef {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
210 write!(f, "B{}({},{})", self.round, self.author, self.digest)
211 }
212}
213
214impl fmt::Debug for BlockRef {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
216 fmt::Display::fmt(self, f)
217 }
218}
219
220impl Hash for BlockRef {
221 fn hash<H: Hasher>(&self, state: &mut H) {
222 state.write(&self.digest.0[..8]);
223 }
224}
225
226#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
233pub struct BlockDigest([u8; consensus_config::DIGEST_LENGTH]);
234
235impl BlockDigest {
236 pub const MIN: Self = Self([u8::MIN; consensus_config::DIGEST_LENGTH]);
238 pub const MAX: Self = Self([u8::MAX; consensus_config::DIGEST_LENGTH]);
239}
240
241impl Hash for BlockDigest {
242 fn hash<H: Hasher>(&self, state: &mut H) {
243 state.write(&self.0[..8]);
244 }
245}
246
247impl From<BlockDigest> for Digest<{ DIGEST_LENGTH }> {
248 fn from(hd: BlockDigest) -> Self {
249 Digest::new(hd.0)
250 }
251}
252
253impl fmt::Display for BlockDigest {
254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
255 write!(
256 f,
257 "{}",
258 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, self.0)
259 .get(0..4)
260 .ok_or(fmt::Error)?
261 )
262 }
263}
264
265impl fmt::Debug for BlockDigest {
266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
267 write!(
268 f,
269 "{}",
270 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, self.0)
271 )
272 }
273}
274
275impl AsRef<[u8]> for BlockDigest {
276 fn as_ref(&self) -> &[u8] {
277 &self.0
278 }
279}
280
281#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Hash)]
284pub struct Slot {
285 pub round: Round,
286 pub authority: AuthorityIndex,
287}
288
289impl Slot {
290 pub fn new(round: Round, authority: AuthorityIndex) -> Self {
291 Self { round, authority }
292 }
293
294 #[cfg(test)]
295 pub fn new_for_test(round: Round, authority: u32) -> Self {
296 Self {
297 round,
298 authority: AuthorityIndex::new_for_test(authority),
299 }
300 }
301}
302
303impl From<BlockRef> for Slot {
304 fn from(value: BlockRef) -> Self {
305 Slot::new(value.round, value.author)
306 }
307}
308
309impl fmt::Display for Slot {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 write!(f, "S{}{}", self.round, self.authority)
312 }
313}
314
315impl fmt::Debug for Slot {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 fmt::Display::fmt(&self, f)
318 }
319}
320
321#[derive(Deserialize, Serialize)]
327pub(crate) struct SignedBlock {
328 inner: Block,
329 signature: Bytes,
330}
331
332impl SignedBlock {
333 pub(crate) fn new_genesis(block: Block) -> Self {
335 Self {
336 inner: block,
337 signature: Bytes::default(),
338 }
339 }
340
341 pub(crate) fn new(block: Block, protocol_keypair: &ProtocolKeyPair) -> ConsensusResult<Self> {
342 let signature = compute_block_signature(&block, protocol_keypair)?;
343 Ok(Self {
344 inner: block,
345 signature: Bytes::copy_from_slice(signature.to_bytes()),
346 })
347 }
348
349 pub(crate) fn signature(&self) -> &Bytes {
350 &self.signature
351 }
352
353 pub(crate) fn verify_signature(&self, context: &Context) -> ConsensusResult<()> {
356 let block = &self.inner;
357 let committee = &context.committee;
358 ensure!(
359 committee.is_valid_index(block.author()),
360 ConsensusError::InvalidAuthorityIndex {
361 index: block.author(),
362 max: committee.size() - 1
363 }
364 );
365 let authority = committee.authority(block.author());
366 verify_block_signature(block, self.signature(), &authority.protocol_key)
367 }
368
369 pub(crate) fn serialize(&self) -> Result<Bytes, bcs::Error> {
371 let bytes = bcs::to_bytes(self)?;
372 Ok(bytes.into())
373 }
374
375 #[cfg(test)]
377 pub(crate) fn clear_signature(&mut self) {
378 self.signature = Bytes::default();
379 }
380}
381
382#[derive(Serialize, Deserialize)]
387struct InnerBlockDigest([u8; consensus_config::DIGEST_LENGTH]);
388
389fn compute_inner_block_digest(block: &Block) -> ConsensusResult<InnerBlockDigest> {
391 let mut hasher = DefaultHashFunction::new();
392 hasher.update(bcs::to_bytes(block).map_err(ConsensusError::SerializationFailure)?);
393 Ok(InnerBlockDigest(hasher.finalize().into()))
394}
395
396fn to_consensus_block_intent(digest: InnerBlockDigest) -> IntentMessage<InnerBlockDigest> {
398 IntentMessage::new(Intent::consensus_app(IntentScope::ConsensusBlock), digest)
399}
400
401fn compute_block_signature(
406 block: &Block,
407 protocol_keypair: &ProtocolKeyPair,
408) -> ConsensusResult<ProtocolKeySignature> {
409 let digest = compute_inner_block_digest(block)?;
410 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
411 .map_err(ConsensusError::SerializationFailure)?;
412 Ok(protocol_keypair.sign(&message))
413}
414fn verify_block_signature(
415 block: &Block,
416 signature: &[u8],
417 protocol_pubkey: &ProtocolPublicKey,
418) -> ConsensusResult<()> {
419 let digest = compute_inner_block_digest(block)?;
420 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
421 .map_err(ConsensusError::SerializationFailure)?;
422 let sig =
423 ProtocolKeySignature::from_bytes(signature).map_err(ConsensusError::MalformedSignature)?;
424 protocol_pubkey
425 .verify(&message, &sig)
426 .map_err(ConsensusError::SignatureVerificationFailure)
427}
428
429impl Deref for SignedBlock {
432 type Target = Block;
433
434 fn deref(&self) -> &Self::Target {
435 &self.inner
436 }
437}
438
439#[derive(Clone)]
442pub struct VerifiedBlock {
443 block: Arc<SignedBlock>,
444
445 digest: BlockDigest,
447 serialized: Bytes,
448}
449
450impl VerifiedBlock {
451 pub(crate) fn new_verified(signed_block: SignedBlock, serialized: Bytes) -> Self {
454 let digest = Self::compute_digest(&serialized);
455 VerifiedBlock {
456 block: Arc::new(signed_block),
457 digest,
458 serialized,
459 }
460 }
461
462 pub fn new_for_test(block: Block) -> Self {
464 let signed_block = SignedBlock {
465 inner: block,
466 signature: Default::default(),
467 };
468 let serialized: Bytes = bcs::to_bytes(&signed_block)
469 .expect("Serialization should not fail")
470 .into();
471 let digest = Self::compute_digest(&serialized);
472 VerifiedBlock {
473 block: Arc::new(signed_block),
474 digest,
475 serialized,
476 }
477 }
478
479 pub fn reference(&self) -> BlockRef {
481 BlockRef {
482 round: self.round(),
483 author: self.author(),
484 digest: self.digest(),
485 }
486 }
487
488 pub(crate) fn digest(&self) -> BlockDigest {
489 self.digest
490 }
491
492 pub(crate) fn serialized(&self) -> &Bytes {
494 &self.serialized
495 }
496
497 pub(crate) fn compute_digest(serialized: &[u8]) -> BlockDigest {
499 let mut hasher = DefaultHashFunction::new();
500 hasher.update(serialized);
501 BlockDigest(hasher.finalize().into())
502 }
503}
504
505impl Deref for VerifiedBlock {
508 type Target = Block;
509
510 fn deref(&self) -> &Self::Target {
511 &self.block.inner
512 }
513}
514
515impl PartialEq for VerifiedBlock {
516 fn eq(&self, other: &Self) -> bool {
517 self.digest() == other.digest()
518 }
519}
520
521impl fmt::Display for VerifiedBlock {
522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
523 write!(f, "{}", self.reference())
524 }
525}
526
527impl fmt::Debug for VerifiedBlock {
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
529 write!(
530 f,
531 "{:?}({}ms;{:?};{}t;{}c)",
532 self.reference(),
533 self.timestamp_ms(),
534 self.ancestors(),
535 self.transactions().len(),
536 self.commit_votes().len(),
537 )
538 }
539}
540
541#[derive(Clone, Debug)]
546pub(crate) struct ExtendedBlock {
547 pub block: VerifiedBlock,
548 pub excluded_ancestors: Vec<BlockRef>,
549}
550
551pub(crate) fn genesis_blocks(context: &Context) -> Vec<VerifiedBlock> {
554 context
555 .committee
556 .authorities()
557 .map(|(authority_index, _)| {
558 let signed_block = SignedBlock::new_genesis(Block::V1(BlockV1::genesis_block(
559 context,
560 authority_index,
561 )));
562 let serialized = signed_block
563 .serialize()
564 .expect("Genesis block serialization failed.");
565 VerifiedBlock::new_verified(signed_block, serialized)
567 })
568 .collect::<Vec<VerifiedBlock>>()
569}
570
571#[derive(Clone)]
573pub struct TestBlock {
574 block: BlockV1,
575}
576
577impl TestBlock {
578 pub fn new(round: Round, author: u32) -> Self {
579 Self {
580 block: BlockV1 {
581 round,
582 author: AuthorityIndex::new_for_test(author),
583 ..Default::default()
584 },
585 }
586 }
587
588 pub fn set_epoch(mut self, epoch: Epoch) -> Self {
589 self.block.epoch = epoch;
590 self
591 }
592
593 pub fn set_round(mut self, round: Round) -> Self {
594 self.block.round = round;
595 self
596 }
597
598 pub fn set_author(mut self, author: AuthorityIndex) -> Self {
599 self.block.author = author;
600 self
601 }
602
603 pub fn set_timestamp_ms(mut self, timestamp_ms: BlockTimestampMs) -> Self {
604 self.block.timestamp_ms = timestamp_ms;
605 self
606 }
607
608 pub fn set_ancestors(mut self, ancestors: Vec<BlockRef>) -> Self {
609 self.block.ancestors = ancestors;
610 self
611 }
612
613 pub fn set_transactions(mut self, transactions: Vec<Transaction>) -> Self {
614 self.block.transactions = transactions;
615 self
616 }
617
618 pub fn set_commit_votes(mut self, commit_votes: Vec<CommitVote>) -> Self {
619 self.block.commit_votes = commit_votes;
620 self
621 }
622
623 pub fn build(self) -> Block {
624 Block::V1(self.block)
625 }
626}
627
628#[derive(Clone, Serialize, Deserialize, Debug)]
630pub struct MisbehaviorReport {
631 pub target: AuthorityIndex,
632 pub proof: MisbehaviorProof,
633}
634
635#[derive(Clone, Serialize, Deserialize, Debug)]
638pub enum MisbehaviorProof {
639 InvalidBlock(BlockRef),
640}
641
642#[cfg(test)]
646mod tests {
647 use std::sync::Arc;
648
649 use fastcrypto::error::FastCryptoError;
650
651 use crate::{
652 BlockAPI,
653 block::{SignedBlock, TestBlock, genesis_blocks},
654 context::Context,
655 error::ConsensusError,
656 };
657
658 #[tokio::test]
659 async fn test_sign_and_verify() {
660 let (context, key_pairs) = Context::new_for_test(4);
661 let context = Arc::new(context);
662
663 let block = TestBlock::new(10, 2).build();
665
666 let author_two_key = &key_pairs[2].1;
668 let signed_block = SignedBlock::new(block, author_two_key).expect("Shouldn't fail signing");
669
670 let result = signed_block.verify_signature(&context);
672 assert!(result.is_ok());
673
674 let block = TestBlock::new(10, 2).build();
676 let author_one_key = &key_pairs[1].1;
677 let signed_block = SignedBlock::new(block, author_one_key).expect("Shouldn't fail signing");
678
679 let result = signed_block.verify_signature(&context);
681 match result.err().unwrap() {
682 ConsensusError::SignatureVerificationFailure(err) => {
683 assert_eq!(err, FastCryptoError::InvalidSignature);
684 }
685 err => panic!("Unexpected error: {err:?}"),
686 }
687 }
688
689 #[tokio::test]
690 async fn test_genesis_blocks() {
691 let (context, _) = Context::new_for_test(4);
692 const TIMESTAMP_MS: u64 = 1000;
693 let context = Arc::new(context.with_epoch_start_timestamp_ms(TIMESTAMP_MS));
694 let blocks = genesis_blocks(&context);
695 for (i, block) in blocks.into_iter().enumerate() {
696 assert_eq!(block.author().value(), i);
697 assert_eq!(block.round(), 0);
698 assert_eq!(block.timestamp_ms(), TIMESTAMP_MS);
699 }
700 }
701}