hotshot_types/traits/block_contents.rs
1// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
2// This file is part of the HotShot repository.
3
4// You should have received a copy of the MIT License
5// along with the HotShot repository. If not, see <https://mit-license.org/>.
6
7//! Abstraction over the contents of a block
8//!
9//! This module provides the [`Transaction`], [`BlockPayload`], and [`BlockHeader`] traits, which
10//! describe the behaviors that a block is expected to have.
11
12use std::{
13 error::Error,
14 fmt::{Debug, Display},
15 future::Future,
16 hash::Hash,
17 sync::Arc,
18};
19
20use alloy::primitives::FixedBytes;
21use async_trait::async_trait;
22use committable::{Commitment, Committable};
23use serde::{de::DeserializeOwned, Deserialize, Serialize};
24use vbs::version::Version;
25
26use super::{node_implementation::Versions, signature_key::BuilderSignatureKey};
27use crate::{
28 data::{Leaf2, VidCommitment},
29 light_client::LightClientState,
30 traits::{node_implementation::NodeType, states::InstanceState, ValidatedState},
31 utils::BuilderCommitment,
32};
33
34/// Trait for structures that need to be unambiguously encoded as bytes.
35pub trait EncodeBytes {
36 /// Encode `&self`
37 fn encode(&self) -> Arc<[u8]>;
38}
39
40/// Abstraction over any type of transaction. Used by [`BlockPayload`].
41pub trait Transaction:
42 Clone + Serialize + DeserializeOwned + Debug + PartialEq + Eq + Sync + Send + Committable + Hash
43{
44 /// The function to estimate the transaction size
45 /// It takes in the transaction itself and a boolean indicating if the transaction adds a new namespace
46 /// Since each new namespace adds overhead
47 /// just ignore this parameter by default and use it when needed
48 fn minimum_block_size(&self) -> u64;
49}
50
51/// Abstraction over the full contents of a block
52///
53/// This trait encapsulates the behaviors that the transactions of a block must have in order to be
54/// used by consensus
55/// * Must have a predefined error type ([`BlockPayload::Error`])
56/// * Must have a transaction type that can be compared for equality, serialized and serialized,
57/// sent between threads, and can have a hash produced of it
58/// * Must be hashable
59#[async_trait]
60pub trait BlockPayload<TYPES: NodeType>:
61 Serialize
62 + Clone
63 + Debug
64 + Display
65 + Hash
66 + PartialEq
67 + Eq
68 + Send
69 + Sync
70 + DeserializeOwned
71 + EncodeBytes
72{
73 /// The error type for this type of block
74 type Error: Error + Debug + Send + Sync + Serialize + DeserializeOwned;
75
76 /// The type of the instance-level state this state is associated with
77 type Instance: InstanceState;
78 /// The type of the transitions we are applying
79 type Transaction: Transaction + Serialize + DeserializeOwned;
80 /// Validated State
81 type ValidatedState: ValidatedState<TYPES>;
82 /// Data created during block building which feeds into the block header
83 type Metadata: Clone
84 + Debug
85 + DeserializeOwned
86 + Eq
87 + Hash
88 + Send
89 + Sync
90 + Serialize
91 + EncodeBytes;
92
93 /// Build a payload and associated metadata with the transactions.
94 /// This function is asynchronous because it may need to request updated state from the peers via GET requests.
95 /// # Errors
96 /// If the transaction length conversion fails.
97 async fn from_transactions(
98 transactions: impl IntoIterator<Item = Self::Transaction> + Send,
99 validated_state: &Self::ValidatedState,
100 instance_state: &Self::Instance,
101 ) -> Result<(Self, Self::Metadata), Self::Error>;
102
103 /// Build a payload with the encoded transaction bytes, metadata,
104 /// and the associated number of VID storage nodes
105 fn from_bytes(encoded_transactions: &[u8], metadata: &Self::Metadata) -> Self;
106
107 /// Build the payload and metadata for genesis/null block.
108 fn empty() -> (Self, Self::Metadata);
109
110 /// List of transaction commitments.
111 fn transaction_commitments(
112 &self,
113 metadata: &Self::Metadata,
114 ) -> Vec<Commitment<Self::Transaction>> {
115 self.transactions(metadata).map(|tx| tx.commit()).collect()
116 }
117
118 /// Number of transactions in the block.
119 fn num_transactions(&self, metadata: &Self::Metadata) -> usize {
120 self.transactions(metadata).count()
121 }
122
123 /// Generate commitment that builders use to sign block options.
124 fn builder_commitment(&self, metadata: &Self::Metadata) -> BuilderCommitment;
125
126 /// Get the transactions in the payload.
127 fn transactions<'a>(
128 &'a self,
129 metadata: &'a Self::Metadata,
130 ) -> impl 'a + Iterator<Item = Self::Transaction>;
131
132 /// Get the number of bytes of transactions in the payload.
133 fn txn_bytes(&self) -> usize;
134}
135
136/// extra functions required on block to be usable by hotshot-testing
137pub trait TestableBlock<TYPES: NodeType>: BlockPayload<TYPES> + Debug {
138 /// generate a genesis block
139 fn genesis() -> Self;
140
141 /// the number of transactions in this block
142 fn txn_count(&self) -> u64;
143}
144
145/// The number of storage nodes to use when computing the genesis VID commitment.
146///
147/// The number of storage nodes for the genesis VID commitment is arbitrary, since we don't actually
148/// do dispersal for the genesis block. For simplicity and performance, we use 1.
149pub const GENESIS_VID_NUM_STORAGE_NODES: usize = 1;
150
151#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
152/// Information about builder fee for proposed block
153pub struct BuilderFee<TYPES: NodeType> {
154 /// Proposed fee amount
155 pub fee_amount: u64,
156 /// Account authorizing the fee.
157 pub fee_account: TYPES::BuilderSignatureKey,
158 /// Signature over fee amount by `fee_account`.
159 pub fee_signature: <TYPES::BuilderSignatureKey as BuilderSignatureKey>::BuilderSignature,
160}
161
162/// Header of a block, which commits to a [`BlockPayload`].
163pub trait BlockHeader<TYPES: NodeType>:
164 Serialize + Clone + Debug + Hash + PartialEq + Eq + Send + Sync + DeserializeOwned + Committable
165{
166 /// Error type for this type of block header
167 type Error: Error + Debug + Send + Sync;
168
169 /// Build a header with the parent validate state, instance-level state, parent leaf, payload
170 /// and builder commitments, and metadata. This is only used in pre-marketplace versions
171 #[allow(clippy::too_many_arguments)]
172 fn new(
173 parent_state: &TYPES::ValidatedState,
174 instance_state: &<TYPES::ValidatedState as ValidatedState<TYPES>>::Instance,
175 parent_leaf: &Leaf2<TYPES>,
176 payload_commitment: VidCommitment,
177 builder_commitment: BuilderCommitment,
178 metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
179 builder_fee: BuilderFee<TYPES>,
180 version: Version,
181 view_number: u64,
182 ) -> impl Future<Output = Result<Self, Self::Error>> + Send;
183
184 /// Build the genesis header, payload, and metadata.
185 fn genesis<V: Versions>(
186 instance_state: &<TYPES::ValidatedState as ValidatedState<TYPES>>::Instance,
187 payload: TYPES::BlockPayload,
188 metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
189 ) -> Self;
190
191 /// Get the block number.
192 fn block_number(&self) -> u64;
193
194 /// Get the timestamp.
195 fn timestamp(&self) -> u64;
196
197 /// Get the timestamp in milliseconds.
198 fn timestamp_millis(&self) -> u64;
199
200 /// Get the payload commitment.
201 fn payload_commitment(&self) -> VidCommitment;
202
203 /// Get the metadata.
204 fn metadata(&self) -> &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata;
205
206 /// Get the builder commitment
207 fn builder_commitment(&self) -> BuilderCommitment;
208
209 /// Get the light client state
210 fn get_light_client_state(&self, view: TYPES::View) -> anyhow::Result<LightClientState>;
211
212 /// Returns the `auth_root` value for versions >= V4 (`DrbAndHeaderUpgrade`).
213 ///
214 /// For versions < V4, this will return `None`.
215 ///
216 /// The `auth_root` is a 32-byte hash calculated using the reward Merkle tree
217 /// digest and other values.
218 /// It is used by the reward claim contract to verify the reward claim
219 fn auth_root(&self) -> anyhow::Result<FixedBytes<32>>;
220}