1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
// Copyright (c) 2021-2024 Espresso Systems (espressosys.com)
// This file is part of the HotShot repository.
// You should have received a copy of the MIT License
// along with the HotShot repository. If not, see <https://mit-license.org/>.
//! Composite trait for node behavior
//!
//! This module defines the [`NodeImplementation`] trait, which is a composite trait used for
//! describing the overall behavior of a node, as a composition of implementations of the node trait.
use std::{
fmt::{Debug, Display},
hash::Hash,
ops::{self, Deref, Sub},
sync::Arc,
time::Duration,
};
use async_trait::async_trait;
use committable::Committable;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use url::Url;
use vbs::version::StaticVersionType;
use super::{
auction_results_provider::AuctionResultsProvider,
block_contents::{BlockHeader, TestableBlock, Transaction},
network::{
AsyncGenerator, ConnectedNetwork, NetworkReliability, TestableNetworkingImplementation,
},
signature_key::BuilderSignatureKey,
states::TestableState,
storage::Storage,
ValidatedState,
};
use crate::{
data::{Leaf2, TestableLeaf},
traits::{
election::Membership, signature_key::SignatureKey, states::InstanceState, BlockPayload,
},
};
/// This trait guarantees that a particular type has urls that can be extracted from it. This trait
/// essentially ensures that the results returned by the [`AuctionResultsProvider`] trait includes a
/// list of urls for the builders that HotShot must request from.
pub trait HasUrls {
/// Returns the builder url associated with the datatype
fn urls(&self) -> Vec<Url>;
}
/// Node implementation aggregate trait
///
/// This trait exists to collect multiple behavior implementations into one type, to allow
/// `HotShot` to avoid annoying numbers of type arguments and type patching.
///
/// It is recommended you implement this trait on a zero sized type, as `HotShot`does not actually
/// store or keep a reference to any value implementing this trait.
pub trait NodeImplementation<TYPES: NodeType>:
Send + Sync + Clone + Eq + Hash + 'static + Serialize + for<'de> Deserialize<'de>
{
/// The underlying network type
type Network: ConnectedNetwork<TYPES::SignatureKey>;
/// Storage for DA layer interactions
type Storage: Storage<TYPES>;
/// The auction results type for Solver interactions
type AuctionResultsProvider: AuctionResultsProvider<TYPES>;
}
/// extra functions required on a node implementation to be usable by hotshot-testing
#[allow(clippy::type_complexity)]
#[async_trait]
pub trait TestableNodeImplementation<TYPES: NodeType>: NodeImplementation<TYPES> {
/// Creates random transaction if possible
/// otherwise panics
/// `padding` is the bytes of padding to add to the transaction
fn state_create_random_transaction(
state: Option<&TYPES::ValidatedState>,
rng: &mut dyn rand::RngCore,
padding: u64,
) -> <TYPES::BlockPayload as BlockPayload<TYPES>>::Transaction;
/// Creates random transaction if possible
/// otherwise panics
/// `padding` is the bytes of padding to add to the transaction
fn leaf_create_random_transaction(
leaf: &Leaf2<TYPES>,
rng: &mut dyn rand::RngCore,
padding: u64,
) -> <TYPES::BlockPayload as BlockPayload<TYPES>>::Transaction;
/// generate a genesis block
fn block_genesis() -> TYPES::BlockPayload;
/// the number of transactions in a block
fn txn_count(block: &TYPES::BlockPayload) -> u64;
/// Generate the communication channels for testing
fn gen_networks(
expected_node_count: usize,
num_bootstrap: usize,
da_committee_size: usize,
reliability_config: Option<Box<dyn NetworkReliability>>,
secondary_network_delay: Duration,
) -> AsyncGenerator<Arc<Self::Network>>;
}
#[async_trait]
impl<TYPES: NodeType, I: NodeImplementation<TYPES>> TestableNodeImplementation<TYPES> for I
where
TYPES::ValidatedState: TestableState<TYPES>,
TYPES::BlockPayload: TestableBlock<TYPES>,
I::Network: TestableNetworkingImplementation<TYPES>,
{
fn state_create_random_transaction(
state: Option<&TYPES::ValidatedState>,
rng: &mut dyn rand::RngCore,
padding: u64,
) -> <TYPES::BlockPayload as BlockPayload<TYPES>>::Transaction {
<TYPES::ValidatedState as TestableState<TYPES>>::create_random_transaction(
state, rng, padding,
)
}
fn leaf_create_random_transaction(
leaf: &Leaf2<TYPES>,
rng: &mut dyn rand::RngCore,
padding: u64,
) -> <TYPES::BlockPayload as BlockPayload<TYPES>>::Transaction {
Leaf2::create_random_transaction(leaf, rng, padding)
}
fn block_genesis() -> TYPES::BlockPayload {
<TYPES::BlockPayload as TestableBlock<TYPES>>::genesis()
}
fn txn_count(block: &TYPES::BlockPayload) -> u64 {
<TYPES::BlockPayload as TestableBlock<TYPES>>::txn_count(block)
}
fn gen_networks(
expected_node_count: usize,
num_bootstrap: usize,
da_committee_size: usize,
reliability_config: Option<Box<dyn NetworkReliability>>,
secondary_network_delay: Duration,
) -> AsyncGenerator<Arc<Self::Network>> {
<I::Network as TestableNetworkingImplementation<TYPES>>::generator(
expected_node_count,
num_bootstrap,
0,
da_committee_size,
reliability_config.clone(),
secondary_network_delay,
)
}
}
/// Trait for time compatibility needed for reward collection
pub trait ConsensusTime:
PartialOrd
+ Ord
+ Send
+ Sync
+ Debug
+ Clone
+ Copy
+ Hash
+ Deref<Target = u64>
+ serde::Serialize
+ for<'de> serde::Deserialize<'de>
+ ops::AddAssign<u64>
+ ops::Add<u64, Output = Self>
+ Sub<u64, Output = Self>
+ 'static
+ Committable
{
/// Create a new instance of this time unit at time number 0
#[must_use]
fn genesis() -> Self {
Self::new(0)
}
/// Create a new instance of this time unit
fn new(val: u64) -> Self;
/// Get the u64 format of time
fn u64(&self) -> u64;
}
/// Trait with all the type definitions that are used in the current hotshot setup.
pub trait NodeType:
Clone
+ Copy
+ Debug
+ Hash
+ PartialEq
+ Eq
+ PartialOrd
+ Ord
+ Default
+ serde::Serialize
+ for<'de> Deserialize<'de>
+ Send
+ Sync
+ 'static
{
/// The time type that this hotshot setup is using.
///
/// This should be the same `Time` that `ValidatedState::Time` is using.
type View: ConsensusTime + Display;
/// Same as above but for epoch.
type Epoch: ConsensusTime + Display;
/// constant for epoch height
const EPOCH_HEIGHT: u64;
/// The AuctionSolverResult is a type that holds the data associated with a particular solver
/// run, for a particular view.
type AuctionResult: Debug
+ HasUrls
+ DeserializeOwned
+ Default
+ PartialEq
+ Eq
+ Clone
+ Send
+ Sync;
/// The block header type that this hotshot setup is using.
type BlockHeader: BlockHeader<Self>;
/// The block type that this hotshot setup is using.
///
/// This should be the same block that `ValidatedState::BlockPayload` is using.
type BlockPayload: BlockPayload<
Self,
Instance = Self::InstanceState,
Transaction = Self::Transaction,
ValidatedState = Self::ValidatedState,
>;
/// The signature key that this hotshot setup is using.
type SignatureKey: SignatureKey;
/// The transaction type that this hotshot setup is using.
///
/// This should be equal to `BlockPayload::Transaction`
type Transaction: Transaction;
/// The instance-level state type that this hotshot setup is using.
type InstanceState: InstanceState;
/// The validated state type that this hotshot setup is using.
type ValidatedState: ValidatedState<Self, Instance = Self::InstanceState, Time = Self::View>;
/// Membership used for this implementation
type Membership: Membership<Self>;
/// The type builder uses to sign its messages
type BuilderSignatureKey: BuilderSignatureKey;
}
/// Version information for HotShot
pub trait Versions: Clone + Copy + Debug + Send + Sync + 'static {
/// The base version of HotShot this node is instantiated with.
type Base: StaticVersionType;
/// The version of HotShot this node may be upgraded to. Set equal to `Base` to disable upgrades.
type Upgrade: StaticVersionType;
/// The hash for the upgrade.
const UPGRADE_HASH: [u8; 32];
/// The version at which to switch over to marketplace logic
type Marketplace: StaticVersionType;
/// The version at which to switch over to epochs logic
type Epochs: StaticVersionType;
}