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