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