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};
21use tracing::instrument;
22
23use crate::{
24 commit::CommitVote,
25 context::Context,
26 ensure,
27 error::{ConsensusError, ConsensusResult},
28};
29
30pub type Round = u32;
32
33pub(crate) const GENESIS_ROUND: Round = 0;
34
35pub type BlockTimestampMs = u64;
37
38#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Default, Debug)]
40pub struct Transaction {
41 data: Bytes,
42}
43
44impl Transaction {
45 pub fn new(data: Vec<u8>) -> Self {
46 Self { data: data.into() }
47 }
48
49 pub fn data(&self) -> &[u8] {
50 &self.data
51 }
52
53 pub fn into_data(self) -> Bytes {
54 self.data
55 }
56}
57
58#[derive(Clone, Deserialize, Serialize)]
63#[enum_dispatch(BlockAPI)]
64pub enum Block {
65 V1(BlockV1),
66}
67
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 commit_votes(&self) -> &[CommitVote];
78 fn misbehavior_reports(&self) -> &[MisbehaviorReport];
79}
80
81#[derive(Clone, Default, Deserialize, Serialize)]
82pub struct BlockV1 {
83 epoch: Epoch,
84 round: Round,
85 author: AuthorityIndex,
86 timestamp_ms: BlockTimestampMs,
88 ancestors: Vec<BlockRef>,
89 transactions: Vec<Transaction>,
90 commit_votes: Vec<CommitVote>,
91 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 let timestamp_ms = if context
120 .protocol_config
121 .consensus_median_timestamp_with_checkpoint_enforcement()
122 {
123 context.epoch_start_timestamp_ms
124 } else {
125 0
126 };
127 Self {
128 epoch: context.committee.epoch(),
129 round: GENESIS_ROUND,
130 author,
131 timestamp_ms,
132 ancestors: vec![],
133 transactions: vec![],
134 commit_votes: vec![],
135 misbehavior_reports: vec![],
136 }
137 }
138}
139
140impl BlockAPI for BlockV1 {
141 fn epoch(&self) -> Epoch {
142 self.epoch
143 }
144
145 fn round(&self) -> Round {
146 self.round
147 }
148
149 fn author(&self) -> AuthorityIndex {
150 self.author
151 }
152
153 fn slot(&self) -> Slot {
154 Slot::new(self.round, self.author)
155 }
156
157 fn timestamp_ms(&self) -> BlockTimestampMs {
158 self.timestamp_ms
159 }
160
161 fn ancestors(&self) -> &[BlockRef] {
162 &self.ancestors
163 }
164
165 fn transactions(&self) -> &[Transaction] {
166 &self.transactions
167 }
168
169 fn commit_votes(&self) -> &[CommitVote] {
170 &self.commit_votes
171 }
172
173 fn misbehavior_reports(&self) -> &[MisbehaviorReport] {
174 &self.misbehavior_reports
175 }
176}
177
178#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
182pub struct BlockRef {
183 pub round: Round,
184 pub author: AuthorityIndex,
185 pub digest: BlockDigest,
186}
187
188impl BlockRef {
189 pub const MIN: Self = Self {
190 round: 0,
191 author: AuthorityIndex::MIN,
192 digest: BlockDigest::MIN,
193 };
194
195 pub const MAX: Self = Self {
196 round: u32::MAX,
197 author: AuthorityIndex::MAX,
198 digest: BlockDigest::MAX,
199 };
200
201 pub fn new(round: Round, author: AuthorityIndex, digest: BlockDigest) -> Self {
202 Self {
203 round,
204 author,
205 digest,
206 }
207 }
208}
209
210impl fmt::Display for BlockRef {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
212 write!(f, "B{}({},{})", self.round, self.author, self.digest)
213 }
214}
215
216impl fmt::Debug for BlockRef {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
218 fmt::Display::fmt(self, f)
219 }
220}
221
222impl Hash for BlockRef {
223 fn hash<H: Hasher>(&self, state: &mut H) {
224 state.write(&self.digest.0[..8]);
225 }
226}
227
228#[derive(Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq, PartialOrd, Ord)]
235pub struct BlockDigest([u8; consensus_config::DIGEST_LENGTH]);
236
237impl BlockDigest {
238 pub const MIN: Self = Self([u8::MIN; consensus_config::DIGEST_LENGTH]);
240 pub const MAX: Self = Self([u8::MAX; consensus_config::DIGEST_LENGTH]);
241}
242
243impl Hash for BlockDigest {
244 fn hash<H: Hasher>(&self, state: &mut H) {
245 state.write(&self.0[..8]);
246 }
247}
248
249impl From<BlockDigest> for Digest<{ DIGEST_LENGTH }> {
250 fn from(hd: BlockDigest) -> Self {
251 Digest::new(hd.0)
252 }
253}
254
255impl fmt::Display for BlockDigest {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
257 write!(
258 f,
259 "{}",
260 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, self.0)
261 .get(0..4)
262 .ok_or(fmt::Error)?
263 )
264 }
265}
266
267impl fmt::Debug for BlockDigest {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
269 write!(
270 f,
271 "{}",
272 base64::Engine::encode(&base64::engine::general_purpose::STANDARD, self.0)
273 )
274 }
275}
276
277impl AsRef<[u8]> for BlockDigest {
278 fn as_ref(&self) -> &[u8] {
279 &self.0
280 }
281}
282
283#[derive(Clone, Copy, PartialEq, PartialOrd, Default, Hash)]
286pub struct Slot {
287 pub round: Round,
288 pub authority: AuthorityIndex,
289}
290
291impl Slot {
292 pub fn new(round: Round, authority: AuthorityIndex) -> Self {
293 Self { round, authority }
294 }
295
296 #[cfg(test)]
297 pub fn new_for_test(round: Round, authority: u32) -> Self {
298 Self {
299 round,
300 authority: AuthorityIndex::new_for_test(authority),
301 }
302 }
303}
304
305impl From<BlockRef> for Slot {
306 fn from(value: BlockRef) -> Self {
307 Slot::new(value.round, value.author)
308 }
309}
310
311impl fmt::Display for Slot {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 write!(f, "S{}{}", self.round, self.authority)
314 }
315}
316
317impl fmt::Debug for Slot {
318 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319 fmt::Display::fmt(&self, f)
320 }
321}
322
323#[derive(Deserialize, Serialize)]
329pub(crate) struct SignedBlock {
330 inner: Block,
331 signature: Bytes,
332}
333
334impl SignedBlock {
335 pub(crate) fn new_genesis(block: Block) -> Self {
337 Self {
338 inner: block,
339 signature: Bytes::default(),
340 }
341 }
342
343 pub(crate) fn new(block: Block, protocol_keypair: &ProtocolKeyPair) -> ConsensusResult<Self> {
344 let signature = compute_block_signature(&block, protocol_keypair)?;
345 Ok(Self {
346 inner: block,
347 signature: Bytes::copy_from_slice(signature.to_bytes()),
348 })
349 }
350
351 pub(crate) fn signature(&self) -> &Bytes {
352 &self.signature
353 }
354
355 #[instrument(level = "trace", skip_all)]
358 pub(crate) fn verify_signature(&self, context: &Context) -> ConsensusResult<()> {
359 let block = &self.inner;
360 let committee = &context.committee;
361 ensure!(
362 committee.is_valid_index(block.author()),
363 ConsensusError::InvalidAuthorityIndex {
364 index: block.author(),
365 max: committee.size() - 1
366 }
367 );
368 let authority = committee.authority(block.author());
369 verify_block_signature(block, self.signature(), &authority.protocol_key)
370 }
371
372 pub(crate) fn serialize(&self) -> Result<Bytes, bcs::Error> {
374 let bytes = bcs::to_bytes(self)?;
375 Ok(bytes.into())
376 }
377
378 #[cfg(test)]
380 pub(crate) fn clear_signature(&mut self) {
381 self.signature = Bytes::default();
382 }
383}
384
385#[derive(Serialize, Deserialize)]
390struct InnerBlockDigest([u8; consensus_config::DIGEST_LENGTH]);
391
392fn compute_inner_block_digest(block: &Block) -> ConsensusResult<InnerBlockDigest> {
394 let mut hasher = DefaultHashFunction::new();
395 hasher.update(bcs::to_bytes(block).map_err(ConsensusError::SerializationFailure)?);
396 Ok(InnerBlockDigest(hasher.finalize().into()))
397}
398
399fn to_consensus_block_intent(digest: InnerBlockDigest) -> IntentMessage<InnerBlockDigest> {
401 IntentMessage::new(Intent::consensus_app(IntentScope::ConsensusBlock), digest)
402}
403
404#[instrument(level = "trace", skip_all)]
409fn compute_block_signature(
410 block: &Block,
411 protocol_keypair: &ProtocolKeyPair,
412) -> ConsensusResult<ProtocolKeySignature> {
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 Ok(protocol_keypair.sign(&message))
417}
418#[instrument(level = "trace", skip_all)]
419fn verify_block_signature(
420 block: &Block,
421 signature: &[u8],
422 protocol_pubkey: &ProtocolPublicKey,
423) -> ConsensusResult<()> {
424 let digest = compute_inner_block_digest(block)?;
425 let message = bcs::to_bytes(&to_consensus_block_intent(digest))
426 .map_err(ConsensusError::SerializationFailure)?;
427 let sig =
428 ProtocolKeySignature::from_bytes(signature).map_err(ConsensusError::MalformedSignature)?;
429 protocol_pubkey
430 .verify(&message, &sig)
431 .map_err(ConsensusError::SignatureVerificationFailure)
432}
433
434impl Deref for SignedBlock {
437 type Target = Block;
438
439 fn deref(&self) -> &Self::Target {
440 &self.inner
441 }
442}
443
444#[derive(Clone)]
447pub struct VerifiedBlock {
448 block: Arc<SignedBlock>,
449
450 digest: BlockDigest,
452 serialized: Bytes,
453}
454
455impl VerifiedBlock {
456 pub(crate) fn new_verified(signed_block: SignedBlock, serialized: Bytes) -> Self {
459 let digest = Self::compute_digest(&serialized);
460 VerifiedBlock {
461 block: Arc::new(signed_block),
462 digest,
463 serialized,
464 }
465 }
466
467 pub(crate) fn new_verified_with_digest(
468 signed_block: SignedBlock,
469 serialized: Bytes,
470 digest: BlockDigest,
471 ) -> Self {
472 VerifiedBlock {
473 block: Arc::new(signed_block),
474 digest,
475 serialized,
476 }
477 }
478
479 pub fn new_for_test(block: Block) -> Self {
481 let signed_block = SignedBlock {
482 inner: block,
483 signature: Default::default(),
484 };
485 let serialized: Bytes = bcs::to_bytes(&signed_block)
486 .expect("Serialization should not fail")
487 .into();
488 let digest = Self::compute_digest(&serialized);
489 VerifiedBlock {
490 block: Arc::new(signed_block),
491 digest,
492 serialized,
493 }
494 }
495
496 pub fn reference(&self) -> BlockRef {
498 BlockRef {
499 round: self.round(),
500 author: self.author(),
501 digest: self.digest(),
502 }
503 }
504
505 pub(crate) fn digest(&self) -> BlockDigest {
506 self.digest
507 }
508
509 pub(crate) fn serialized(&self) -> &Bytes {
511 &self.serialized
512 }
513
514 pub(crate) fn compute_digest(serialized: &[u8]) -> BlockDigest {
516 let mut hasher = DefaultHashFunction::new();
517 hasher.update(serialized);
518 BlockDigest(hasher.finalize().into())
519 }
520}
521
522impl Deref for VerifiedBlock {
525 type Target = Block;
526
527 fn deref(&self) -> &Self::Target {
528 &self.block.inner
529 }
530}
531
532impl PartialEq for VerifiedBlock {
533 fn eq(&self, other: &Self) -> bool {
534 self.digest() == other.digest()
535 }
536}
537
538impl fmt::Display for VerifiedBlock {
539 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
540 write!(f, "{}", self.reference())
541 }
542}
543
544impl fmt::Debug for VerifiedBlock {
545 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
546 write!(
547 f,
548 "{:?}({}ms;{:?};{}t;{}c)",
549 self.reference(),
550 self.timestamp_ms(),
551 self.ancestors(),
552 self.transactions().len(),
553 self.commit_votes().len(),
554 )
555 }
556}
557
558#[derive(Clone, Debug)]
563pub(crate) struct ExtendedBlock {
564 pub block: VerifiedBlock,
565 pub excluded_ancestors: Vec<BlockRef>,
566}
567
568pub(crate) fn genesis_blocks(context: &Context) -> Vec<VerifiedBlock> {
571 context
572 .committee
573 .authorities()
574 .map(|(authority_index, _)| {
575 let signed_block = SignedBlock::new_genesis(Block::V1(BlockV1::genesis_block(
576 context,
577 authority_index,
578 )));
579 let serialized = signed_block
580 .serialize()
581 .expect("Genesis block serialization failed.");
582 VerifiedBlock::new_verified(signed_block, serialized)
584 })
585 .collect::<Vec<VerifiedBlock>>()
586}
587
588#[derive(Clone)]
590pub struct TestBlock {
591 block: BlockV1,
592}
593
594impl TestBlock {
595 pub fn new(round: Round, author: u32) -> Self {
596 Self {
597 block: BlockV1 {
598 round,
599 author: AuthorityIndex::new_for_test(author),
600 ..Default::default()
601 },
602 }
603 }
604
605 pub fn set_epoch(mut self, epoch: Epoch) -> Self {
606 self.block.epoch = epoch;
607 self
608 }
609
610 pub fn set_round(mut self, round: Round) -> Self {
611 self.block.round = round;
612 self
613 }
614
615 pub fn set_author(mut self, author: AuthorityIndex) -> Self {
616 self.block.author = author;
617 self
618 }
619
620 pub fn set_timestamp_ms(mut self, timestamp_ms: BlockTimestampMs) -> Self {
621 self.block.timestamp_ms = timestamp_ms;
622 self
623 }
624
625 pub fn set_ancestors(mut self, ancestors: Vec<BlockRef>) -> Self {
626 self.block.ancestors = ancestors;
627 self
628 }
629
630 pub fn set_transactions(mut self, transactions: Vec<Transaction>) -> Self {
631 self.block.transactions = transactions;
632 self
633 }
634
635 pub fn set_commit_votes(mut self, commit_votes: Vec<CommitVote>) -> Self {
636 self.block.commit_votes = commit_votes;
637 self
638 }
639
640 pub fn build(self) -> Block {
641 Block::V1(self.block)
642 }
643}
644
645#[derive(Clone, Serialize, Deserialize, Debug)]
647pub struct MisbehaviorReport {
648 pub target: AuthorityIndex,
649 pub proof: MisbehaviorProof,
650}
651
652#[derive(Clone, Serialize, Deserialize, Debug)]
655pub enum MisbehaviorProof {
656 InvalidBlock(BlockRef),
657}
658
659#[cfg(test)]
663mod tests {
664 use std::sync::Arc;
665
666 use fastcrypto::error::FastCryptoError;
667
668 use crate::{
669 BlockAPI,
670 block::{SignedBlock, TestBlock, genesis_blocks},
671 context::Context,
672 error::ConsensusError,
673 };
674
675 #[tokio::test]
676 async fn test_sign_and_verify() {
677 let (context, key_pairs) = Context::new_for_test(4);
678 let context = Arc::new(context);
679
680 let block = TestBlock::new(10, 2).build();
682
683 let author_two_key = &key_pairs[2].1;
685 let signed_block = SignedBlock::new(block, author_two_key).expect("Shouldn't fail signing");
686
687 let result = signed_block.verify_signature(&context);
689 assert!(result.is_ok());
690
691 let block = TestBlock::new(10, 2).build();
693 let author_one_key = &key_pairs[1].1;
694 let signed_block = SignedBlock::new(block, author_one_key).expect("Shouldn't fail signing");
695
696 let result = signed_block.verify_signature(&context);
698 match result.err().unwrap() {
699 ConsensusError::SignatureVerificationFailure(err) => {
700 assert_eq!(err, FastCryptoError::InvalidSignature);
701 }
702 err => panic!("Unexpected error: {err:?}"),
703 }
704 }
705
706 #[tokio::test]
707 async fn test_genesis_blocks() {
708 let (context, _) = Context::new_for_test(4);
709 const TIMESTAMP_MS: u64 = 1000;
710 let context = Arc::new(context.with_epoch_start_timestamp_ms(TIMESTAMP_MS));
711 let blocks = genesis_blocks(&context);
712 for (i, block) in blocks.into_iter().enumerate() {
713 assert_eq!(block.author().value(), i);
714 assert_eq!(block.round(), 0);
715 assert_eq!(block.timestamp_ms(), TIMESTAMP_MS);
716 }
717 }
718}