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