Skip to main content

tendermint_machine/
blockchain.rs

1use core::{future::Future, num::NonZero};
2
3use crate::{Validator, SignatureScheme, ValidatorSet, CommitFor, SlashReasonFor};
4
5/// A block's number.
6///
7/// The genesis block is considered to have number `0` where following blocks' numbers increase
8/// incrementally by one.
9///
10/// As this library never works with the genesis block, as it's constant and does not undergo the
11/// consensus process, this library represents block numbers via [`NonZero`]. This is complete to
12/// all block numbers this library has to consider.
13#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
14#[cfg_attr(feature = "alloc", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
15#[repr(transparent)]
16pub struct BlockNumber(pub(crate) NonZero<u64>);
17impl BlockNumber {
18  pub(crate) const ONE: Self = Self(NonZero::new(1).unwrap());
19}
20impl From<BlockNumber> for u64 {
21  fn from(block_number: BlockNumber) -> Self {
22    u64::from(block_number.0)
23  }
24}
25impl From<NonZero<u64>> for BlockNumber {
26  fn from(block_number: NonZero<u64>) -> Self {
27    Self(block_number)
28  }
29}
30/// A round's number.
31///
32/// Tendermint describes this as starting from `0`, yet we start it from `1`. This has two
33/// benefits:
34/// 1) It allows representing `None` (our representation of what the paper describes as `-1`) as
35///    `0`, saving a byte in memory and encoding
36/// 2) It slightly simplifies calculating timeouts
37#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
38#[cfg_attr(feature = "alloc", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
39#[repr(transparent)]
40pub struct RoundNumber(pub(crate) NonZero<u64>);
41impl RoundNumber {
42  pub(crate) const ONE: Self = Self(NonZero::new(1).unwrap());
43}
44impl From<RoundNumber> for u64 {
45  fn from(round_number: RoundNumber) -> Self {
46    u64::from(round_number.0)
47  }
48}
49
50/// A representation of a block's hash.
51///
52/// This is effectively a trait alias where a potential representation of a block's hash is any
53/// type which satisfies all of these bounds, and this is implemented for all such types.
54///
55/// The [`BorshSerialize`] implementation MUST be infallible if the underlying writer is
56/// infallible. The [`BorshDeserialize`] implementation MUST be infallible if it is deserializing a
57/// value which was successfully serialized, from a well-formed reader.
58pub trait BlockHash: Clone + Copy + PartialEq + Eq + AsRef<[u8]> {}
59impl<H: Clone + Copy + PartialEq + Eq + AsRef<[u8]>> BlockHash for H {}
60
61/// The block was invalid.
62#[derive(Clone, Copy, Debug)]
63pub struct InvalidBlock;
64
65/// A view over the application layer's block.
66///
67/// The [`BorshSerialize`] implementation MUST be infallible if the underlying writer is
68/// infallible. The [`BorshDeserialize`] implementation MUST be infallible if it is deserializing a
69/// value which was successfully serialized, from a well-formed reader.
70pub trait Block: Clone {
71  // The type representing a block's hash.
72  ///
73  /// The `AsRef<[u8]>` implementation MUST return a slice with a consistent length for _any_
74  /// value, meaning it's _constant_.
75  ///
76  /// The [`BorshSerialize`] implementation MUST be infallible if the underlying writer is
77  /// infallible. The [`BorshDeserialize`] implementation MUST be infallible if it is deserializing
78  /// a value which was successfully serialized, from a well-formed reader.
79  type Hash: BlockHash;
80
81  /// The block's hash.
82  ///
83  /// This MUST be cryptographically binding to the entirety of the block.
84  ///
85  /// For two blocks, `b1` and `b2`, `b1 == b2` MUST equal `b1.hash() == b2.hash()`, except with
86  /// negligible probability (per the binding property).
87  ///
88  /// This MUST always return the same hash for a value upon each call.
89  fn hash(&self) -> Self::Hash;
90}
91
92/// A view of the application layer's blockchain.
93pub trait Blockchain {
94  /// The type used to identify validators.
95  ///
96  /// It is RECOMMENDED to use [`u16`] to identify validators.
97  type Validator: Validator;
98  /// The type used to define the validator set.
99  type ValidatorSet: ?Sized + ValidatorSet<Validator = Self::Validator>;
100  /// The signature scheme used by validators.
101  type SignatureScheme: ?Sized + SignatureScheme<Validator = Self::Validator>;
102
103  /// The type used to represent the genesis of this blockchain.
104  ///
105  /// This DOES NOT need to satisfy any cryptographic binding properties and is solely used for
106  /// domain-separation purposes. The slice this may be taken as a reference to MUST be consistent
107  /// across calls and MUST have a length less than or equal to [`u8::MAX`].
108  type Genesis: AsRef<[u8]>;
109
110  /// The block type.
111  type Block: Block;
112
113  /// The future for a block proposal.
114  ///
115  /// This allows the proposal to not be _immediately_ ready, such as if the blockchain is still
116  /// syncing, where this future will be occasionally polled (likely infrequently) for if the
117  /// proposal is ready. It is RECOMMENDED to literally instantiate it with
118  /// [`futures_channel::oneshot::Receiver`] or similar.
119  type BlockProposal: Future<Output = Self::Block>;
120
121  /// The genesis ID for this blockchain.
122  ///
123  /// This MUST be consistent for the lifetime of this blockchain and unique across blockchains.
124  fn genesis(&self) -> &Self::Genesis;
125
126  /// The validator set's definition.
127  ///
128  /// This MUST be consistent for the lifetime of this blockchain.
129  fn validator_set(&self) -> &Self::ValidatorSet;
130
131  /// The signature scheme for this blockchain.
132  fn signature_scheme(&self) -> &Self::SignatureScheme;
133
134  /// Validate a block.
135  ///
136  /// This SHOULD NOT cause any mutations to the blockchain. This solely validates a potential
137  /// candidate with minimal context passed and no call pattern guaranteed. The proposer is
138  /// specified in case the blockchain wishes to record a slash for this block, if it is
139  /// fundamentally invalid.
140  ///
141  /// This DOES NOT have to be cancel-safe.
142  // TODO: Don't just provide the proposer, but enough to build evidence sufficient to convince
143  // someone else (the signed proposal message)?
144  // TODO: `&mut self`?
145  fn validate(
146    &self,
147    proposer: Self::Validator,
148    block: &Self::Block,
149  ) -> impl Send + Future<Output = Result<(), InvalidBlock>>;
150
151  /// Add a block, returning a future for the proposal for the next block.
152  ///
153  /// It's possible a block, which was never validated or even failed validation, may be passed
154  /// here if a supermajority of validators did consider it valid and created a verified commit for
155  /// it. This deviates from the paper which will have a local node refuse to decide on a block it
156  /// considers invalid. This library acknowledges the validators did decide on it, leaving
157  /// handling of it to the application layer, and outside of this scope. If this block is invalid,
158  /// it's a break in the assumptions required for Tendermint's soundness.
159  ///
160  /// The proposal for the next block is returned as a [`Future`]. This allows the proposal to not
161  /// be ready _yet_, such as if the blockchain is still syncing, where this future will be
162  /// occasionally polled (likely infrequently) for if the proposal is ready. It likely SHOULD be
163  /// literally instantiated with [`futures_channel::oneshot::Receiver`] or similar.
164  ///
165  /// This MAY be called multiple times if the process reboots before the Tendermint process
166  /// commits its advanced state. The underlying blockchain MUST be able to handle being asked to
167  /// add a block which it already added.
168  ///
169  /// This DOES NOT have to be cancel-safe.
170  fn add_block(
171    &mut self,
172    block: Self::Block,
173    commit: CommitFor<Self>,
174  ) -> impl Send + Future<Output = Self::BlockProposal>;
175
176  /// Record a missed proposal.
177  ///
178  /// This is a non-`async` function as this is expected to be implemented in a non-blocking
179  /// fashion as to not hold up the consensus process.
180  ///
181  /// A missed proposal is LIKELY something which should cause the validator to be slashed.
182  /// However, there is no evidence for a missed proposal as it may have never been sent, or may
183  /// have simply not been received by the local view (which may be the one faulty). For this
184  /// reason, it's explicitly distinguished from a slash.
185  // TODO: `&mut self`?
186  fn missed_proposal(&self, proposer: Self::Validator);
187
188  /// Slash a validator.
189  ///
190  /// This is a non-`async` function as this is expected to be implemented in a non-blocking
191  /// fashion as to not hold up the consensus process.
192  ///
193  /// The actual recording of this slash and its interpretation is left entirely to the application
194  /// layer. All slashes have the necessary evidence and can be verified by this library.
195  // TODO: `&mut self`?
196  fn slash(&self, validator: Self::Validator, slash_reason: SlashReasonFor<Self>);
197}