1use std::{
8 fmt::{Debug, Display},
9 mem::size_of,
10 sync::Arc,
11};
12
13use alloy::primitives::FixedBytes;
14use async_trait::async_trait;
15use committable::{Commitment, Committable, RawCommitmentBuilder};
16use derive_more::Display;
17use hotshot_types::{
18 data::{BlockError, Leaf2, VidCommitment, ViewNumber, vid_commitment},
19 light_client::LightClientState,
20 traits::{
21 BlockPayload, ValidatedState,
22 block_contents::{
23 BlockHeader, BuilderFee, EncodeBytes, GENESIS_VID_NUM_STORAGE_NODES, TestableBlock,
24 Transaction,
25 },
26 node_implementation::NodeType,
27 },
28 utils::BuilderCommitment,
29};
30use rand::{Rng, thread_rng};
31use serde::{Deserialize, Serialize};
32use sha3::{Digest, Keccak256};
33use thiserror::Error;
34use time::OffsetDateTime;
35use vbs::version::Version;
36
37use crate::{
38 node_types::TestTypes,
39 state_types::{TestInstanceState, TestValidatedState},
40 testable_delay::{DelayConfig, SupportedTraitTypesForAsyncDelay, TestableDelay},
41};
42
43#[derive(Default, PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Debug)]
45#[serde(try_from = "Vec<u8>")]
46pub struct TestTransaction(Vec<u8>);
47
48#[derive(Debug, Error)]
49pub enum TransactionError {
50 #[error("Transaction too long")]
51 TransactionTooLong,
52}
53
54impl TryFrom<Vec<u8>> for TestTransaction {
55 type Error = TransactionError;
56
57 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
58 Self::try_new(value).ok_or(TransactionError::TransactionTooLong)
59 }
60}
61
62impl TestTransaction {
63 pub fn new(bytes: Vec<u8>) -> Self {
68 Self::try_new(bytes).expect("Vector too long")
69 }
70
71 pub fn try_new(bytes: Vec<u8>) -> Option<Self> {
75 if u32::try_from(bytes.len()).is_err() {
76 None
77 } else {
78 Some(Self(bytes))
79 }
80 }
81
82 pub fn bytes(&self) -> &Vec<u8> {
84 &self.0
85 }
86
87 pub fn into_bytes(self) -> Vec<u8> {
89 self.0
90 }
91
92 pub fn encode(transactions: &[Self]) -> Vec<u8> {
97 let mut encoded = Vec::new();
98
99 for txn in transactions {
100 let txn_size = u32::try_from(txn.0.len())
103 .expect("Invalid transaction length")
104 .to_le_bytes();
105
106 encoded.extend(txn_size);
108 encoded.extend(&txn.0);
109 }
110
111 encoded
112 }
113}
114
115impl Committable for TestTransaction {
116 fn commit(&self) -> Commitment<Self> {
117 let builder = committable::RawCommitmentBuilder::new("Txn Comm");
118 let mut hasher = Keccak256::new();
119 hasher.update(&self.0);
120 let generic_array = hasher.finalize();
121 builder.generic_byte_array(&generic_array).finalize()
122 }
123
124 fn tag() -> String {
125 "TEST_TXN".to_string()
126 }
127}
128
129impl Transaction for TestTransaction {
130 fn minimum_block_size(&self) -> u64 {
131 self.0.len() as u64
133 }
134}
135
136#[derive(PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Debug)]
138pub struct TestBlockPayload {
139 pub transactions: Vec<TestTransaction>,
141}
142
143impl TestBlockPayload {
144 #[must_use]
149 pub fn genesis() -> Self {
150 TestBlockPayload {
151 transactions: vec![],
152 }
153 }
154}
155
156impl Display for TestBlockPayload {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 write!(f, "BlockPayload #txns={}", self.transactions.len())
159 }
160}
161
162impl<TYPES: NodeType> TestableBlock<TYPES> for TestBlockPayload {
163 fn genesis() -> Self {
164 Self::genesis()
165 }
166
167 fn txn_count(&self) -> u64 {
168 self.transactions.len() as u64
169 }
170}
171
172#[derive(
173 Debug, Display, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash,
174)]
175#[display("{{num_transactions:{num_transactions}}}")]
176pub struct TestMetadata {
177 pub num_transactions: u64,
178}
179
180impl EncodeBytes for TestMetadata {
181 fn encode(&self) -> Arc<[u8]> {
182 Arc::new([])
183 }
184}
185
186impl EncodeBytes for TestBlockPayload {
187 fn encode(&self) -> Arc<[u8]> {
188 TestTransaction::encode(&self.transactions).into()
189 }
190}
191
192#[async_trait]
193impl<TYPES: NodeType> BlockPayload<TYPES> for TestBlockPayload {
194 type Error = BlockError;
195 type Instance = TestInstanceState;
196 type Transaction = TestTransaction;
197 type Metadata = TestMetadata;
198 type ValidatedState = TestValidatedState;
199
200 async fn from_transactions(
201 transactions: impl IntoIterator<Item = Self::Transaction> + Send,
202 _validated_state: &Self::ValidatedState,
203 _instance_state: &Self::Instance,
204 ) -> Result<(Self, Self::Metadata), Self::Error> {
205 let txns_vec: Vec<TestTransaction> = transactions.into_iter().collect();
206 let metadata = TestMetadata {
207 num_transactions: txns_vec.len() as u64,
208 };
209 Ok((
210 Self {
211 transactions: txns_vec,
212 },
213 metadata,
214 ))
215 }
216
217 fn from_bytes(encoded_transactions: &[u8], _metadata: &Self::Metadata) -> Self {
218 let mut transactions = Vec::new();
219 let mut current_index = 0;
220 while current_index < encoded_transactions.len() {
221 let txn_start_index = current_index + size_of::<u32>();
223 let mut txn_len_bytes = [0; size_of::<u32>()];
224 txn_len_bytes.copy_from_slice(&encoded_transactions[current_index..txn_start_index]);
225 let txn_len: usize = u32::from_le_bytes(txn_len_bytes) as usize;
226
227 let next_index = txn_start_index + txn_len;
229 transactions.push(TestTransaction(
230 encoded_transactions[txn_start_index..next_index].to_vec(),
231 ));
232 current_index = next_index;
233 }
234
235 Self { transactions }
236 }
237
238 fn empty() -> (Self, Self::Metadata) {
239 (
240 Self::genesis(),
241 TestMetadata {
242 num_transactions: 0,
243 },
244 )
245 }
246
247 fn builder_commitment(&self, _metadata: &Self::Metadata) -> BuilderCommitment {
248 let mut digest = sha2::Sha256::new();
249 for txn in &self.transactions {
250 digest.update(&txn.0);
251 }
252 BuilderCommitment::from_raw_digest(digest.finalize())
253 }
254
255 fn transactions<'a>(
256 &'a self,
257 _metadata: &'a Self::Metadata,
258 ) -> impl 'a + Iterator<Item = Self::Transaction> {
259 self.transactions.iter().cloned()
260 }
261
262 fn txn_bytes(&self) -> usize {
263 self.transactions.iter().map(|tx| tx.0.len()).sum()
264 }
265}
266
267#[derive(PartialEq, Eq, Hash, Clone, Debug, Deserialize, Serialize)]
269pub struct TestBlockHeader {
270 pub block_number: u64,
272 pub payload_commitment: VidCommitment,
274 pub builder_commitment: BuilderCommitment,
276 pub metadata: TestMetadata,
278 pub timestamp: u64,
280 pub timestamp_millis: u64,
282 pub random: u64,
284 pub version: Version,
286}
287
288impl TestBlockHeader {
289 pub fn new<TYPES: NodeType<BlockHeader = Self>>(
290 parent_leaf: &Leaf2<TYPES>,
291 payload_commitment: VidCommitment,
292 builder_commitment: BuilderCommitment,
293 metadata: TestMetadata,
294 version: Version,
295 ) -> Self {
296 let parent = parent_leaf.block_header();
297
298 let time = OffsetDateTime::now_utc();
299
300 let mut timestamp = time.unix_timestamp() as u64;
301 let mut timestamp_millis = (time.unix_timestamp_nanos() / 1_000_000) as u64;
302
303 if timestamp < parent.timestamp {
304 timestamp = parent.timestamp;
306 }
307
308 if timestamp_millis < parent.timestamp_millis {
309 timestamp_millis = parent.timestamp_millis;
311 }
312
313 let random = thread_rng().gen_range(0..=u64::MAX);
314
315 Self {
316 block_number: parent.block_number + 1,
317 payload_commitment,
318 builder_commitment,
319 metadata,
320 timestamp,
321 timestamp_millis,
322 random,
323 version,
324 }
325 }
326}
327
328impl<
329 TYPES: NodeType<
330 BlockHeader = Self,
331 BlockPayload = TestBlockPayload,
332 InstanceState = TestInstanceState,
333 >,
334> BlockHeader<TYPES> for TestBlockHeader
335{
336 type Error = std::convert::Infallible;
337
338 async fn new(
339 _parent_state: &TYPES::ValidatedState,
340 instance_state: &<TYPES::ValidatedState as ValidatedState<TYPES>>::Instance,
341 parent_leaf: &Leaf2<TYPES>,
342 payload_commitment: VidCommitment,
343 builder_commitment: BuilderCommitment,
344 metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
345 _builder_fee: BuilderFee<TYPES>,
346 version: Version,
347 _view_number: u64,
348 ) -> Result<Self, Self::Error> {
349 Self::run_delay_settings_from_config(&instance_state.delay_config).await;
350 Ok(Self::new(
351 parent_leaf,
352 payload_commitment,
353 builder_commitment,
354 metadata,
355 version,
356 ))
357 }
358
359 fn genesis(
360 _instance_state: &<TYPES::ValidatedState as ValidatedState<TYPES>>::Instance,
361 payload: TYPES::BlockPayload,
362 metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
363 version: Version,
364 ) -> Self {
365 let builder_commitment =
366 <TestBlockPayload as BlockPayload<TYPES>>::builder_commitment(&payload, metadata);
367
368 let payload_bytes = payload.encode();
369 let payload_commitment = vid_commitment(
370 &payload_bytes,
371 &metadata.encode(),
372 GENESIS_VID_NUM_STORAGE_NODES,
373 version,
374 );
375
376 Self {
377 block_number: 0,
378 payload_commitment,
379 builder_commitment,
380 metadata: *metadata,
381 timestamp: 0,
382 timestamp_millis: 0,
383 random: 0,
384 version,
385 }
386 }
387
388 fn block_number(&self) -> u64 {
389 self.block_number
390 }
391
392 fn payload_commitment(&self) -> VidCommitment {
393 self.payload_commitment
394 }
395
396 fn metadata(&self) -> &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata {
397 &self.metadata
398 }
399
400 fn builder_commitment(&self) -> BuilderCommitment {
401 self.builder_commitment.clone()
402 }
403
404 fn version(&self) -> Version {
405 self.version
406 }
407
408 fn get_light_client_state(&self, view: ViewNumber) -> anyhow::Result<LightClientState> {
409 LightClientState::new(
410 view.u64(),
411 self.block_number,
412 self.payload_commitment.as_ref(),
413 )
414 }
415
416 fn auth_root(&self) -> anyhow::Result<FixedBytes<32>> {
417 Ok(FixedBytes::from([0u8; 32]))
418 }
419
420 fn timestamp(&self) -> u64 {
421 self.timestamp
422 }
423
424 fn timestamp_millis(&self) -> u64 {
425 self.timestamp_millis
426 }
427}
428
429impl Committable for TestBlockHeader {
430 fn commit(&self) -> Commitment<Self> {
431 RawCommitmentBuilder::new("Header Comm")
432 .u64_field(
433 "block number",
434 <TestBlockHeader as BlockHeader<TestTypes>>::block_number(self),
435 )
436 .constant_str("payload commitment")
437 .fixed_size_bytes(
438 <TestBlockHeader as BlockHeader<TestTypes>>::payload_commitment(self).as_ref(),
439 )
440 .finalize()
441 }
442
443 fn tag() -> String {
444 "TEST_HEADER".to_string()
445 }
446}
447
448#[async_trait]
449impl TestableDelay for TestBlockHeader {
450 async fn run_delay_settings_from_config(delay_config: &DelayConfig) {
451 if let Some(settings) =
452 delay_config.get_setting(&SupportedTraitTypesForAsyncDelay::BlockHeader)
453 {
454 Self::handle_async_delay(settings).await;
455 }
456 }
457}