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}