1use std::fmt;
2
3use alloy::primitives::{Keccak256, B256};
4use anyhow::{ensure, Context};
5use ark_serialize::CanonicalSerialize;
6use committable::{Commitment, Committable, RawCommitmentBuilder};
7use either::Either;
8use hotshot_query_service::{availability::QueryableHeader, explorer::ExplorerHeader};
9use hotshot_types::{
10 data::{vid_commitment, EpochNumber, VidCommitment, ViewNumber},
11 light_client::LightClientState,
12 traits::{
13 block_contents::{BlockHeader, BuilderFee, GENESIS_VID_NUM_STORAGE_NODES},
14 node_implementation::{ConsensusTime, NodeType, Versions},
15 signature_key::BuilderSignatureKey,
16 BlockPayload, EncodeBytes, ValidatedState as _,
17 },
18 utils::{epoch_from_block_number, is_ge_epoch_root, BuilderCommitment},
19};
20use jf_merkle_tree_compat::{AppendableMerkleTreeScheme, MerkleCommitment, MerkleTreeScheme};
21use serde::{
22 de::{self, MapAccess, SeqAccess, Visitor},
23 Deserialize, Deserializer, Serialize, Serializer,
24};
25use serde_json::{Map, Value};
26use thiserror::Error;
27use time::OffsetDateTime;
28use vbs::version::{StaticVersionType, Version};
29
30use super::{
31 instance_state::NodeState, state::ValidatedState, v0_1::IterableFeeInfo, v0_3::ChainConfig,
32};
33use crate::{
34 eth_signature_key::BuilderSignature,
35 v0::{
36 header::{EitherOrVersion, VersionedHeader},
37 impls::{distribute_block_reward, reward::RewardDistributor, StakeTableHash},
38 },
39 v0_1::{self},
40 v0_2,
41 v0_3::{
42 self, RewardAmount, RewardMerkleCommitmentV1, RewardMerkleTreeV1,
43 REWARD_MERKLE_TREE_V1_HEIGHT,
44 },
45 v0_4::{self, RewardMerkleCommitmentV2},
46 v0_5, BlockMerkleCommitment, DrbAndHeaderUpgradeVersion, EpochVersion, FeeAccount, FeeAmount,
47 FeeInfo, FeeMerkleCommitment, Header, L1BlockInfo, L1Snapshot, Leaf2, NamespaceId, NsIndex,
48 NsTable, PayloadByteLen, SeqTypes, TimestampMillis, UpgradeType,
49};
50
51impl v0_1::Header {
52 pub(crate) fn commit(&self) -> Commitment<Header> {
53 let mut bmt_bytes = vec![];
54 self.block_merkle_tree_root
55 .serialize_with_mode(&mut bmt_bytes, ark_serialize::Compress::Yes)
56 .unwrap();
57 let mut fmt_bytes = vec![];
58 self.fee_merkle_tree_root
59 .serialize_with_mode(&mut fmt_bytes, ark_serialize::Compress::Yes)
60 .unwrap();
61
62 RawCommitmentBuilder::new(&Self::tag())
63 .field("chain_config", self.chain_config.commit())
64 .u64_field("height", self.height)
65 .u64_field("timestamp", self.timestamp)
66 .u64_field("l1_head", self.l1_head)
67 .optional("l1_finalized", &self.l1_finalized)
68 .constant_str("payload_commitment")
69 .fixed_size_bytes(self.payload_commitment.as_ref())
70 .constant_str("builder_commitment")
71 .fixed_size_bytes(self.builder_commitment.as_ref())
72 .field("ns_table", self.ns_table.commit())
73 .var_size_field("block_merkle_tree_root", &bmt_bytes)
74 .var_size_field("fee_merkle_tree_root", &fmt_bytes)
75 .field("fee_info", self.fee_info.commit())
76 .finalize()
77 }
78}
79
80impl Committable for Header {
81 fn commit(&self) -> Commitment<Self> {
82 match self {
83 Self::V1(header) => header.commit(),
84 Self::V2(fields) => RawCommitmentBuilder::new(&Self::tag())
85 .u64_field("version_major", 0)
86 .u64_field("version_minor", 2)
87 .field("fields", fields.commit())
88 .finalize(),
89 Self::V3(fields) => RawCommitmentBuilder::new(&Self::tag())
90 .u64_field("version_major", 0)
91 .u64_field("version_minor", 3)
92 .field("fields", fields.commit())
93 .finalize(),
94 Self::V4(fields) => RawCommitmentBuilder::new(&Self::tag())
95 .u64_field("version_major", 0)
96 .u64_field("version_minor", 4)
97 .field("fields", fields.commit())
98 .finalize(),
99 Self::V5(fields) => RawCommitmentBuilder::new(&Self::tag())
100 .u64_field("version_major", 0)
101 .u64_field("version_minor", 5)
102 .field("fields", fields.commit())
103 .finalize(),
104 }
105 }
106
107 fn tag() -> String {
108 "BLOCK".into()
111 }
112}
113
114impl Serialize for Header {
115 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116 where
117 S: Serializer,
118 {
119 match self {
120 Self::V1(header) => header.serialize(serializer),
121 Self::V2(fields) => VersionedHeader {
122 version: EitherOrVersion::Version(Version { major: 0, minor: 2 }),
123 fields: fields.clone(),
124 }
125 .serialize(serializer),
126 Self::V3(fields) => VersionedHeader {
127 version: EitherOrVersion::Version(Version { major: 0, minor: 3 }),
128 fields: fields.clone(),
129 }
130 .serialize(serializer),
131 Self::V4(fields) => VersionedHeader {
132 version: EitherOrVersion::Version(Version { major: 0, minor: 4 }),
133 fields: fields.clone(),
134 }
135 .serialize(serializer),
136 Self::V5(fields) => VersionedHeader {
137 version: EitherOrVersion::Version(Version { major: 0, minor: 5 }),
138 fields: fields.clone(),
139 }
140 .serialize(serializer),
141 }
142 }
143}
144
145impl<'de> Deserialize<'de> for Header {
146 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
147 where
148 D: Deserializer<'de>,
149 {
150 struct HeaderVisitor;
151
152 impl<'de> Visitor<'de> for HeaderVisitor {
153 type Value = Header;
154
155 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
156 formatter.write_str("Header")
157 }
158
159 fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
160 where
161 V: SeqAccess<'de>,
162 {
163 let chain_config_or_version: EitherOrVersion = seq
164 .next_element()?
165 .ok_or_else(|| de::Error::missing_field("chain_config"))?;
166
167 match chain_config_or_version {
168 EitherOrVersion::Left(cfg) => Ok(Header::V1(
171 v0_1::Header::deserialize_with_chain_config(cfg.into(), seq)?,
172 )),
173 EitherOrVersion::Right(commit) => Ok(Header::V1(
174 v0_1::Header::deserialize_with_chain_config(commit.into(), seq)?,
175 )),
176 EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2(
179 seq.next_element()?
180 .ok_or_else(|| de::Error::missing_field("fields"))?,
181 )),
182 EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3(
183 seq.next_element()?
184 .ok_or_else(|| de::Error::missing_field("fields"))?,
185 )),
186 EitherOrVersion::Version(Version { major: 0, minor: 4 }) => Ok(Header::V4(
187 seq.next_element()?
188 .ok_or_else(|| de::Error::missing_field("fields"))?,
189 )),
190 EitherOrVersion::Version(Version { major: 0, minor: 5 }) => Ok(Header::V5(
191 seq.next_element()?
192 .ok_or_else(|| de::Error::missing_field("fields"))?,
193 )),
194 EitherOrVersion::Version(v) => {
195 Err(serde::de::Error::custom(format!("invalid version {v:?}")))
196 },
197 }
198 }
199
200 fn visit_map<V>(self, mut map: V) -> Result<Header, V::Error>
201 where
202 V: MapAccess<'de>,
203 {
204 let mut serde_map: Map<String, Value> = Map::new();
206
207 while let Some(key) = map.next_key::<String>()? {
208 serde_map.insert(key.trim().to_owned(), map.next_value()?);
209 }
210
211 if let Some(v) = serde_map.get("version") {
212 let fields = serde_map
213 .get("fields")
214 .ok_or_else(|| de::Error::missing_field("fields"))?;
215
216 let version = serde_json::from_value::<EitherOrVersion>(v.clone())
217 .map_err(de::Error::custom)?;
218 let result = match version {
219 EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2(
220 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
221 )),
222 EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3(
223 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
224 )),
225 EitherOrVersion::Version(Version { major: 0, minor: 4 }) => Ok(Header::V4(
226 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
227 )),
228 EitherOrVersion::Version(Version { major: 0, minor: 5 }) => Ok(Header::V5(
229 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
230 )),
231 EitherOrVersion::Version(v) => {
232 Err(de::Error::custom(format!("invalid version {v:?}")))
233 },
234 chain_config => Err(de::Error::custom(format!(
235 "expected version, found chain_config {chain_config:?}"
236 ))),
237 };
238 return result;
239 }
240
241 Ok(Header::V1(
242 serde_json::from_value(serde_map.into()).map_err(de::Error::custom)?,
243 ))
244 }
245 }
246
247 let fields: &[&str] = &[
262 "fields",
263 "chain_config",
264 "version",
265 "height",
266 "timestamp",
267 "l1_head",
268 "l1_finalized",
269 "payload_commitment",
270 "builder_commitment",
271 "ns_table",
272 "block_merkle_tree_root",
273 "fee_merkle_tree_root",
274 "fee_info",
275 "builder_signature",
276 ];
277
278 deserializer.deserialize_struct("Header", fields, HeaderVisitor)
279 }
280}
281
282impl Header {
283 pub fn version(&self) -> Version {
284 match self {
285 Self::V1(_) => Version { major: 0, minor: 1 },
286 Self::V2(_) => Version { major: 0, minor: 2 },
287 Self::V3(_) => Version { major: 0, minor: 3 },
288 Self::V4(_) => Version { major: 0, minor: 4 },
289 Self::V5(_) => Version { major: 0, minor: 5 },
290 }
291 }
292 #[allow(clippy::too_many_arguments)]
293 pub(crate) fn create(
294 chain_config: ChainConfig,
295 height: u64,
296 timestamp: u64,
297 timestamp_millis: u64,
298 l1_head: u64,
299 l1_finalized: Option<L1BlockInfo>,
300 payload_commitment: VidCommitment,
301 builder_commitment: BuilderCommitment,
302 ns_table: NsTable,
303 fee_merkle_tree_root: FeeMerkleCommitment,
304 block_merkle_tree_root: BlockMerkleCommitment,
305 reward_merkle_tree_root_v1: RewardMerkleCommitmentV1,
306 reward_merkle_tree_root_v2: RewardMerkleCommitmentV2,
307 fee_info: Vec<FeeInfo>,
308 builder_signature: Vec<BuilderSignature>,
309 total_reward_distributed: Option<RewardAmount>,
310 version: Version,
311 next_stake_table_hash: Option<StakeTableHash>,
312 ) -> Self {
313 assert!(!fee_info.is_empty(), "Invalid fee_info length: 0");
315
316 match (version.major, version.minor) {
317 (0, 1) => Self::V1(v0_1::Header {
318 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
319 chain_config,
320 )),
321 height,
322 timestamp,
323 l1_head,
324 l1_finalized,
325 payload_commitment,
326 builder_commitment,
327 ns_table,
328 block_merkle_tree_root,
329 fee_merkle_tree_root,
330 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
332 }),
333 (0, 2) => Self::V2(v0_2::Header {
334 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
335 chain_config,
336 )),
337 height,
338 timestamp,
339 l1_head,
340 l1_finalized,
341 payload_commitment,
342 builder_commitment,
343 ns_table,
344 block_merkle_tree_root,
345 fee_merkle_tree_root,
346 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
348 }),
349 (0, 3) => Self::V3(v0_3::Header {
350 chain_config: chain_config.into(),
351 height,
352 timestamp,
353 l1_head,
354 l1_finalized,
355 payload_commitment,
356 builder_commitment,
357 ns_table,
358 block_merkle_tree_root,
359 fee_merkle_tree_root,
360 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
362 reward_merkle_tree_root: reward_merkle_tree_root_v1,
363 }),
364 (0, 4) => Self::V4(v0_4::Header {
365 chain_config: chain_config.into(),
366 height,
367 timestamp,
368 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
369 l1_head,
370 l1_finalized,
371 payload_commitment,
372 builder_commitment,
373 ns_table,
374 block_merkle_tree_root,
375 fee_merkle_tree_root,
376 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
378 reward_merkle_tree_root: reward_merkle_tree_root_v2,
379 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
380 next_stake_table_hash,
381 }),
382 (0, 5) => Self::V5(v0_5::Header {
383 chain_config: chain_config.into(),
384 height,
385 timestamp,
386 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
387 l1_head,
388 l1_finalized,
389 payload_commitment,
390 builder_commitment,
391 ns_table,
392 block_merkle_tree_root,
393 fee_merkle_tree_root,
394 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
396 reward_merkle_tree_root: reward_merkle_tree_root_v2,
397 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
398 next_stake_table_hash,
399 }),
400 _ => panic!("invalid version: {version}"),
404 }
405 }
406
407 pub fn next_stake_table_hash(&self) -> Option<StakeTableHash> {
408 match self {
409 Self::V4(fields) | Self::V5(fields) => fields.next_stake_table_hash,
410 _ => None,
411 }
412 }
413
414 pub fn set_next_stake_table_hash(&mut self, hash: StakeTableHash) -> bool {
415 match self {
416 Self::V4(fields) | Self::V5(fields) => {
417 fields.next_stake_table_hash = Some(hash);
418 true
419 },
420 _ => false,
421 }
422 }
423}
424
425macro_rules! field {
427 ($obj:ident.$name:ident) => {
428 match $obj {
429 Self::V1(data) => &data.$name,
430 Self::V2(data) => &data.$name,
431 Self::V3(data) => &data.$name,
432 Self::V4(data) => &data.$name,
433 Self::V5(data) => &data.$name,
434 }
435 };
436}
437
438macro_rules! field_mut {
439 ($obj:ident.$name:ident) => {
440 match $obj {
441 Self::V1(data) => &mut data.$name,
442 Self::V2(data) => &mut data.$name,
443 Self::V3(data) => &mut data.$name,
444 Self::V4(data) => &mut data.$name,
445 Self::V5(data) => &mut data.$name,
446 }
447 };
448}
449
450impl Header {
451 #[allow(clippy::too_many_arguments)]
452 fn from_info(
453 payload_commitment: VidCommitment,
454 builder_commitment: BuilderCommitment,
455 ns_table: NsTable,
456 parent_leaf: &Leaf2,
457 mut l1: L1Snapshot,
458 l1_deposits: &[FeeInfo],
459 builder_fee: Vec<BuilderFee<SeqTypes>>,
460 mut timestamp: u64,
461 mut timestamp_millis: u64,
462 mut state: ValidatedState,
463 chain_config: ChainConfig,
464 version: Version,
465 reward_distributor: Option<RewardDistributor>,
466 next_stake_table_hash: Option<StakeTableHash>,
467 ) -> anyhow::Result<Self> {
468 ensure!(
469 version.major == 0,
470 "Invalid major version {}",
471 version.major
472 );
473
474 let parent_header = parent_leaf.block_header();
476 let height = parent_header.height() + 1;
477
478 if timestamp < parent_header.timestamp() {
482 tracing::warn!(
483 "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
484 parent_header.timestamp()
485 );
486 timestamp = parent_header.timestamp();
487 }
488
489 if timestamp_millis < parent_header.timestamp_millis() {
490 tracing::warn!(
491 "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
492 parent_header.timestamp_millis()
493 );
494 timestamp_millis = parent_header.timestamp_millis();
495 }
496
497 if l1.head < parent_header.l1_head() {
500 tracing::warn!(
501 "L1 head {} behind parent {}, L1 client may be lagging",
502 l1.head,
503 parent_header.l1_head()
504 );
505 l1.head = parent_header.l1_head();
506 }
507 if l1.finalized < parent_header.l1_finalized() {
508 tracing::warn!(
509 "L1 finalized {:?} behind parent {:?}, L1 client may be lagging",
510 l1.finalized,
511 parent_header.l1_finalized()
512 );
513 l1.finalized = parent_header.l1_finalized();
514 }
515
516 if let Some(l1_block) = &l1.finalized {
519 let l1_timestamp = l1_block.timestamp.to::<u64>();
520 if timestamp < l1_timestamp {
521 tracing::warn!(
522 "Espresso timestamp {timestamp} behind L1 timestamp {l1_timestamp}, local \
523 clock may be out of sync"
524 );
525 timestamp = l1_timestamp;
526 }
527
528 let l1_timestamp_millis = l1_timestamp * 1_000;
529
530 if timestamp_millis < l1_timestamp_millis {
531 tracing::warn!(
532 "Espresso timestamp_millis {timestamp_millis} behind L1 timestamp \
533 {l1_timestamp_millis}, local clock may be out of sync"
534 );
535 timestamp_millis = l1_timestamp_millis;
536 }
537 }
538
539 state
540 .block_merkle_tree
541 .push(parent_header.commit())
542 .context("missing blocks frontier")?;
543 let block_merkle_tree_root = state.block_merkle_tree.commitment();
544
545 for fee_info in l1_deposits {
547 state
548 .insert_fee_deposit(*fee_info)
549 .context(format!("missing fee account {}", fee_info.account()))?;
550 }
551
552 for BuilderFee {
554 fee_account,
555 fee_signature,
556 fee_amount,
557 } in &builder_fee
558 {
559 ensure!(
560 fee_account.validate_fee_signature(fee_signature, *fee_amount, &ns_table)
561 || fee_account.validate_fee_signature_with_vid_commitment(
562 fee_signature,
563 *fee_amount,
564 &ns_table,
565 &payload_commitment
566 ),
567 "invalid builder signature"
568 );
569
570 let fee_info = FeeInfo::new(*fee_account, *fee_amount);
571 state
572 .charge_fee(fee_info, chain_config.fee_recipient)
573 .context(format!("invalid builder fee {fee_info:?}"))?;
574 }
575
576 let fee_info = FeeInfo::from_builder_fees(builder_fee.clone());
577
578 let builder_signature: Vec<BuilderSignature> =
579 builder_fee.iter().map(|e| e.fee_signature).collect();
580
581 let fee_merkle_tree_root = state.fee_merkle_tree.commitment();
582
583 let header = match (version.major, version.minor) {
584 (0, 1) => Self::V1(v0_1::Header {
585 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
586 chain_config,
587 )),
588 height,
589 timestamp,
590 l1_head: l1.head,
591 l1_finalized: l1.finalized,
592 payload_commitment,
593 builder_commitment,
594 ns_table,
595 block_merkle_tree_root,
596 fee_merkle_tree_root,
597 fee_info: fee_info[0],
598 builder_signature: builder_signature.first().copied(),
599 }),
600 (0, 2) => Self::V2(v0_2::Header {
601 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
602 chain_config,
603 )),
604 height,
605 timestamp,
606 l1_head: l1.head,
607 l1_finalized: l1.finalized,
608 payload_commitment,
609 builder_commitment,
610 ns_table,
611 block_merkle_tree_root,
612 fee_merkle_tree_root,
613 fee_info: fee_info[0],
614 builder_signature: builder_signature.first().copied(),
615 }),
616 (0, 3) => Self::V3(v0_3::Header {
617 chain_config: chain_config.into(),
618 height,
619 timestamp,
620 l1_head: l1.head,
621 l1_finalized: l1.finalized,
622 payload_commitment,
623 builder_commitment,
624 ns_table,
625 block_merkle_tree_root,
626 fee_merkle_tree_root,
627 reward_merkle_tree_root: state.reward_merkle_tree_v1.commitment(),
628 fee_info: fee_info[0],
629 builder_signature: builder_signature.first().copied(),
630 }),
631 (0, 4) => Self::V4(v0_4::Header {
632 chain_config: chain_config.into(),
633 height,
634 timestamp,
635 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
636 l1_head: l1.head,
637 l1_finalized: l1.finalized,
638 payload_commitment,
639 builder_commitment,
640 ns_table,
641 block_merkle_tree_root,
642 fee_merkle_tree_root,
643 reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
644 fee_info: fee_info[0],
645 builder_signature: builder_signature.first().copied(),
646 total_reward_distributed: reward_distributor
647 .map(|r| r.total_distributed())
648 .unwrap_or_default(),
649 next_stake_table_hash,
650 }),
651 (0, 5) => Self::V5(v0_5::Header {
652 chain_config: chain_config.into(),
653 height,
654 timestamp,
655 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
656 l1_head: l1.head,
657 l1_finalized: l1.finalized,
658 payload_commitment,
659 builder_commitment,
660 ns_table,
661 block_merkle_tree_root,
662 fee_merkle_tree_root,
663 reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
664 fee_info: fee_info[0],
665 builder_signature: builder_signature.first().copied(),
666 total_reward_distributed: reward_distributor
667 .map(|r| r.total_distributed())
668 .unwrap_or_default(),
669 next_stake_table_hash,
670 }),
671 _ => panic!("invalid version: {version}"),
675 };
676 Ok(header)
677 }
678
679 async fn get_chain_config(
680 validated_state: &ValidatedState,
681 instance_state: &NodeState,
682 ) -> anyhow::Result<ChainConfig> {
683 let validated_cf = validated_state.chain_config;
684 let instance_cf = instance_state.chain_config;
685
686 if validated_cf.commit() == instance_cf.commit() {
687 return Ok(instance_cf);
688 }
689
690 match validated_cf.resolve() {
691 Some(cf) => Ok(cf),
692 None => {
693 tracing::info!("fetching chain config {} from peers", validated_cf.commit());
694
695 instance_state
696 .state_catchup
697 .as_ref()
698 .fetch_chain_config(validated_cf.commit())
699 .await
700 },
701 }
702 }
703}
704
705impl Header {
706 pub fn chain_config(&self) -> v0_3::ResolvableChainConfig {
708 match self {
709 Self::V1(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
710 Self::V2(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
711 Self::V3(fields) => fields.chain_config,
712 Self::V4(fields) => fields.chain_config,
713 Self::V5(fields) => fields.chain_config,
714 }
715 }
716
717 pub fn height(&self) -> u64 {
718 *field!(self.height)
719 }
720
721 pub fn height_mut(&mut self) -> &mut u64 {
722 &mut *field_mut!(self.height)
723 }
724
725 pub fn timestamp_internal(&self) -> u64 {
726 match self {
727 Self::V1(fields) => fields.timestamp,
728 Self::V2(fields) => fields.timestamp,
729 Self::V3(fields) => fields.timestamp,
730 Self::V4(fields) => fields.timestamp,
731 Self::V5(fields) => fields.timestamp,
732 }
733 }
734
735 pub fn timestamp_millis_internal(&self) -> u64 {
736 match self {
737 Self::V1(fields) => fields.timestamp * 1_000,
738 Self::V2(fields) => fields.timestamp * 1_000,
739 Self::V3(fields) => fields.timestamp * 1_000,
740 Self::V4(fields) => fields.timestamp_millis.u64(),
741 Self::V5(fields) => fields.timestamp_millis.u64(),
742 }
743 }
744
745 pub fn set_timestamp(&mut self, timestamp: u64, timestamp_millis: u64) {
746 match self {
747 Self::V1(fields) => {
748 fields.timestamp = timestamp;
749 },
750 Self::V2(fields) => {
751 fields.timestamp = timestamp;
752 },
753 Self::V3(fields) => {
754 fields.timestamp = timestamp;
755 },
756 Self::V4(fields) => {
757 fields.timestamp = timestamp;
758 fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
759 },
760 Self::V5(fields) => {
761 fields.timestamp = timestamp;
762 fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
763 },
764 };
765 }
766
767 pub fn l1_head(&self) -> u64 {
792 *field!(self.l1_head)
793 }
794
795 pub fn l1_head_mut(&mut self) -> &mut u64 {
796 &mut *field_mut!(self.l1_head)
797 }
798
799 pub fn l1_finalized(&self) -> Option<L1BlockInfo> {
814 *field!(self.l1_finalized)
815 }
816
817 pub fn l1_finalized_mut(&mut self) -> &mut Option<L1BlockInfo> {
818 &mut *field_mut!(self.l1_finalized)
819 }
820
821 pub fn payload_commitment(&self) -> VidCommitment {
822 *field!(self.payload_commitment)
823 }
824
825 pub fn payload_commitment_mut(&mut self) -> &mut VidCommitment {
826 &mut *field_mut!(self.payload_commitment)
827 }
828
829 pub fn builder_commitment(&self) -> &BuilderCommitment {
830 field!(self.builder_commitment)
831 }
832
833 pub fn builder_commitment_mut(&mut self) -> &mut BuilderCommitment {
834 &mut *field_mut!(self.builder_commitment)
835 }
836
837 pub fn ns_table(&self) -> &NsTable {
838 field!(self.ns_table)
839 }
840
841 pub fn ns_table_mut(&mut self) -> &mut NsTable {
842 &mut *field_mut!(self.ns_table)
843 }
844
845 pub fn block_merkle_tree_root(&self) -> BlockMerkleCommitment {
847 *field!(self.block_merkle_tree_root)
848 }
849
850 pub fn block_merkle_tree_root_mut(&mut self) -> &mut BlockMerkleCommitment {
851 &mut *field_mut!(self.block_merkle_tree_root)
852 }
853
854 pub fn fee_merkle_tree_root(&self) -> FeeMerkleCommitment {
856 *field!(self.fee_merkle_tree_root)
857 }
858
859 pub fn fee_merkle_tree_root_mut(&mut self) -> &mut FeeMerkleCommitment {
860 &mut *field_mut!(self.fee_merkle_tree_root)
861 }
862
863 pub fn fee_info(&self) -> Vec<FeeInfo> {
865 match self {
866 Self::V1(fields) => vec![fields.fee_info],
867 Self::V2(fields) => vec![fields.fee_info],
868 Self::V3(fields) => vec![fields.fee_info],
869 Self::V4(fields) => vec![fields.fee_info],
870 Self::V5(fields) => vec![fields.fee_info],
871 }
872 }
873
874 pub fn reward_merkle_tree_root(
875 &self,
876 ) -> Either<RewardMerkleCommitmentV1, RewardMerkleCommitmentV2> {
877 let empty_reward_merkle_tree = RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT);
878 match self {
879 Self::V1(_) => Either::Left(empty_reward_merkle_tree.commitment()),
880 Self::V2(_) => Either::Left(empty_reward_merkle_tree.commitment()),
881 Self::V3(fields) => Either::Left(fields.reward_merkle_tree_root),
882 Self::V4(fields) => Either::Right(fields.reward_merkle_tree_root),
883 Self::V5(fields) => Either::Right(fields.reward_merkle_tree_root),
884 }
885 }
886
887 pub fn builder_signature(&self) -> Vec<BuilderSignature> {
896 match self {
897 Self::V1(fields) => fields.builder_signature.as_slice().to_vec(),
902 Self::V2(fields) => fields.builder_signature.as_slice().to_vec(),
903 Self::V3(fields) => fields.builder_signature.as_slice().to_vec(),
904 Self::V4(fields) => fields.builder_signature.as_slice().to_vec(),
905 Self::V5(fields) => fields.builder_signature.as_slice().to_vec(),
906 }
907 }
908
909 pub fn total_reward_distributed(&self) -> Option<RewardAmount> {
910 match self {
911 Self::V1(_) | Self::V2(_) | Self::V3(_) => None,
912 Self::V4(fields) | Self::V5(fields) => Some(fields.total_reward_distributed),
913 }
914 }
915}
916
917#[derive(Debug, Error)]
918#[error("Invalid Block Header {msg}")]
919pub struct InvalidBlockHeader {
920 msg: String,
921}
922impl InvalidBlockHeader {
923 fn new(msg: String) -> Self {
924 Self { msg }
925 }
926}
927
928impl From<anyhow::Error> for InvalidBlockHeader {
929 fn from(err: anyhow::Error) -> Self {
930 Self::new(format!("{err:#}"))
931 }
932}
933
934impl BlockHeader<SeqTypes> for Header {
935 type Error = InvalidBlockHeader;
936
937 #[tracing::instrument(
938 skip_all,
939 fields(
940 node_id = instance_state.node_id,
941 view = ?parent_leaf.view_number(),
942 height = parent_leaf.block_header().height(),
943 ),
944 )]
945 #[tracing::instrument(
946 skip_all,
947 fields(
948 height = parent_leaf.block_header().block_number() + 1,
949 parent_view = ?parent_leaf.view_number(),
950 payload_commitment,
951 version,
952 )
953 )]
954 async fn new(
955 parent_state: &ValidatedState,
956 instance_state: &NodeState,
957 parent_leaf: &Leaf2,
958 payload_commitment: VidCommitment,
959 builder_commitment: BuilderCommitment,
960 metadata: <<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
961 builder_fee: BuilderFee<SeqTypes>,
962 version: Version,
963 view_number: u64,
964 ) -> Result<Self, Self::Error> {
965 tracing::info!("preparing to propose legacy header");
966
967 let height = parent_leaf.height();
968 let view = parent_leaf.view_number();
969
970 let mut validated_state = parent_state.clone();
971
972 let chain_config = if version > instance_state.current_version {
973 match instance_state.upgrades.get(&version) {
974 Some(upgrade) => match upgrade.upgrade_type {
975 UpgradeType::Fee { chain_config } => chain_config,
976 UpgradeType::Epoch { chain_config } => chain_config,
977 UpgradeType::DrbAndHeader { chain_config } => chain_config,
978 UpgradeType::Da { chain_config } => chain_config,
979 },
980 None => Header::get_chain_config(&validated_state, instance_state).await?,
981 }
982 } else {
983 Header::get_chain_config(&validated_state, instance_state).await?
984 };
985
986 validated_state.chain_config = chain_config.into();
987
988 let l1_snapshot = instance_state.l1_client.snapshot().await;
990 let l1_deposits = if let (Some(addr), Some(block_info)) =
992 (chain_config.fee_contract, l1_snapshot.finalized)
993 {
994 instance_state
995 .l1_client
996 .get_finalized_deposits(
997 addr,
998 parent_leaf
999 .block_header()
1000 .l1_finalized()
1001 .map(|block_info| block_info.number),
1002 block_info.number,
1003 )
1004 .await
1005 } else {
1006 vec![]
1007 };
1008 let missing_accounts = parent_state.forgotten_accounts(
1012 [builder_fee.fee_account, chain_config.fee_recipient]
1013 .into_iter()
1014 .chain(l1_deposits.iter().map(|info| info.account())),
1015 );
1016 if !missing_accounts.is_empty() {
1017 tracing::warn!(
1018 height,
1019 ?view,
1020 ?missing_accounts,
1021 "fetching missing accounts from peers"
1022 );
1023
1024 let missing_account_proofs = instance_state
1026 .state_catchup
1027 .as_ref()
1028 .fetch_accounts(
1029 instance_state,
1030 height,
1031 view,
1032 parent_state.fee_merkle_tree.commitment(),
1033 missing_accounts,
1034 )
1035 .await?;
1036
1037 for proof in missing_account_proofs.iter() {
1039 proof
1040 .remember(&mut validated_state.fee_merkle_tree)
1041 .context("remembering fee account")?;
1042 }
1043 }
1044
1045 if validated_state.need_to_fetch_blocks_mt_frontier() {
1047 tracing::warn!(height, ?view, "fetching block frontier from peers");
1048 instance_state
1049 .state_catchup
1050 .as_ref()
1051 .remember_blocks_merkle_tree(
1052 instance_state,
1053 height,
1054 view,
1055 &mut validated_state.block_merkle_tree,
1056 )
1057 .await
1058 .context("remembering block proof")?;
1059 }
1060
1061 let mut rewards = None;
1062 if version >= EpochVersion::version() {
1063 rewards = distribute_block_reward(
1064 instance_state,
1065 &mut validated_state,
1066 parent_leaf,
1067 ViewNumber::new(view_number),
1068 version,
1069 )
1070 .await?;
1071 };
1072
1073 let mut next_stake_table_hash = None;
1074
1075 if version >= DrbAndHeaderUpgradeVersion::version() {
1076 let epoch_height = instance_state
1077 .epoch_height
1078 .context("epoch height not in instance state")?;
1079 if is_ge_epoch_root(height + 1, epoch_height) {
1080 let coordinator = instance_state.coordinator.clone();
1081 let first_epoch = {
1082 coordinator
1083 .membership()
1084 .read()
1085 .await
1086 .first_epoch()
1087 .context("The first epoch was not set.")?
1088 };
1089
1090 let epoch = EpochNumber::new(epoch_from_block_number(height + 1, epoch_height));
1091
1092 if epoch > first_epoch {
1094 let epoch_membership = coordinator
1095 .stake_table_for_epoch(Some(epoch + 1))
1096 .await
1097 .map_err(|e| anyhow::anyhow!("failed to get epoch membership: {e}"))?;
1098 next_stake_table_hash = Some(
1099 epoch_membership
1100 .stake_table_hash()
1101 .await
1102 .context("failed to get next stake table hash")?,
1103 );
1104 }
1105 }
1106 }
1107
1108 let now = OffsetDateTime::now_utc();
1109
1110 let timestamp = now.unix_timestamp() as u64;
1111 let timestamp_millis = TimestampMillis::from_time(&now).u64();
1112
1113 Ok(Self::from_info(
1114 payload_commitment,
1115 builder_commitment,
1116 metadata,
1117 parent_leaf,
1118 l1_snapshot,
1119 &l1_deposits,
1120 vec![builder_fee],
1121 timestamp,
1122 timestamp_millis,
1123 validated_state,
1124 chain_config,
1125 version,
1126 rewards,
1127 next_stake_table_hash,
1128 )?)
1129 }
1130
1131 fn genesis<V: Versions>(
1132 instance_state: &NodeState,
1133 payload: <SeqTypes as NodeType>::BlockPayload,
1134 metadata: &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
1135 ) -> Self {
1136 let payload_bytes = payload.encode();
1137 let builder_commitment = payload.builder_commitment(metadata);
1138
1139 let vid_commitment_version = instance_state.genesis_version;
1140
1141 let payload_commitment = vid_commitment::<V>(
1142 &payload_bytes,
1143 &metadata.encode(),
1144 GENESIS_VID_NUM_STORAGE_NODES,
1145 vid_commitment_version,
1146 );
1147
1148 let ValidatedState {
1149 fee_merkle_tree,
1150 block_merkle_tree,
1151 reward_merkle_tree_v1,
1152 reward_merkle_tree_v2,
1153 ..
1154 } = ValidatedState::genesis(instance_state).0;
1155 let block_merkle_tree_root = block_merkle_tree.commitment();
1156 let fee_merkle_tree_root = fee_merkle_tree.commitment();
1157 let reward_merkle_tree_root = reward_merkle_tree_v2.commitment();
1158
1159 let time = instance_state.genesis_header.timestamp;
1160
1161 let timestamp = time.unix_timestamp();
1162 let timestamp_millis = time.unix_timestamp_millis();
1163
1164 Self::create(
1167 instance_state.genesis_header.chain_config,
1168 0,
1169 timestamp,
1170 timestamp_millis,
1171 instance_state
1172 .l1_genesis
1173 .map(|block| block.number)
1174 .unwrap_or_default(),
1175 instance_state.l1_genesis,
1176 payload_commitment,
1177 builder_commitment.clone(),
1178 metadata.clone(),
1179 fee_merkle_tree_root,
1180 block_merkle_tree_root,
1181 reward_merkle_tree_v1.commitment(),
1182 reward_merkle_tree_root,
1183 vec![FeeInfo::genesis()],
1184 vec![],
1185 None,
1186 instance_state.genesis_version,
1187 None,
1188 )
1189 }
1190
1191 fn timestamp(&self) -> u64 {
1192 self.timestamp_internal()
1193 }
1194
1195 fn timestamp_millis(&self) -> u64 {
1196 self.timestamp_millis_internal()
1197 }
1198
1199 fn block_number(&self) -> u64 {
1200 self.height()
1201 }
1202
1203 fn version(&self) -> Version {
1204 self.version()
1205 }
1206
1207 fn payload_commitment(&self) -> VidCommitment {
1208 self.payload_commitment()
1209 }
1210
1211 fn metadata(
1212 &self,
1213 ) -> &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata {
1214 self.ns_table()
1215 }
1216
1217 fn builder_commitment(&self) -> BuilderCommitment {
1219 self.builder_commitment().clone()
1220 }
1221
1222 fn get_light_client_state(
1223 &self,
1224 view: <SeqTypes as NodeType>::View,
1225 ) -> anyhow::Result<LightClientState> {
1226 let mut block_comm_root_bytes = vec![];
1227 self.block_merkle_tree_root()
1228 .serialize_compressed(&mut block_comm_root_bytes)?;
1229
1230 Ok(LightClientState {
1231 view_number: view.u64(),
1232 block_height: self.height(),
1233 block_comm_root: hotshot_types::light_client::hash_bytes_to_field(
1234 &block_comm_root_bytes,
1235 )?,
1236 })
1237 }
1238
1239 fn auth_root(&self) -> anyhow::Result<B256> {
1240 match self {
1241 Header::V1(_) | Header::V2(_) | Header::V3(_) => Ok(B256::ZERO),
1242 Header::V4(header) | Header::V5(header) => {
1243 let placeholder_1 = B256::ZERO;
1245 let placeholder_2 = B256::ZERO;
1246 let placeholder_3 = B256::ZERO;
1247 let placeholder_4 = B256::ZERO;
1248 let placeholder_5 = B256::ZERO;
1249 let placeholder_6 = B256::ZERO;
1250 let placeholder_7 = B256::ZERO;
1251
1252 let mut hasher = Keccak256::new();
1253
1254 let digest = header.reward_merkle_tree_root.digest();
1256 hasher.update(digest.0);
1257 hasher.update(placeholder_1);
1258 hasher.update(placeholder_2);
1259 hasher.update(placeholder_3);
1260 hasher.update(placeholder_4);
1261 hasher.update(placeholder_5);
1262 hasher.update(placeholder_6);
1263 hasher.update(placeholder_7);
1264
1265 Ok(hasher.finalize())
1266 },
1267 }
1268 }
1269}
1270
1271impl QueryableHeader<SeqTypes> for Header {
1272 type NamespaceId = NamespaceId;
1273 type NamespaceIndex = NsIndex;
1274
1275 fn namespace_id(&self, i: &NsIndex) -> Option<NamespaceId> {
1276 self.ns_table().read_ns_id(i)
1277 }
1278
1279 fn namespace_size(&self, i: &NsIndex, payload_size: usize) -> u64 {
1280 self.ns_table()
1281 .ns_range(i, &PayloadByteLen(payload_size))
1282 .byte_len()
1283 .0 as u64
1284 }
1285}
1286
1287impl ExplorerHeader<SeqTypes> for Header {
1288 type BalanceAmount = FeeAmount;
1289 type WalletAddress = Vec<FeeAccount>;
1290 type ProposerId = Vec<FeeAccount>;
1291
1292 fn proposer_id(&self) -> Self::ProposerId {
1294 self.fee_info().accounts()
1295 }
1296
1297 fn fee_info_account(&self) -> Self::WalletAddress {
1298 self.fee_info().accounts()
1299 }
1300
1301 fn fee_info_balance(&self) -> Self::BalanceAmount {
1302 self.fee_info().amount().unwrap()
1304 }
1305
1306 fn reward_balance(&self) -> Self::BalanceAmount {
1312 FeeAmount::from(0)
1313 }
1314
1315 fn namespace_ids(&self) -> Vec<NamespaceId> {
1316 self.ns_table()
1317 .iter()
1318 .map(|i| self.ns_table().read_ns_id_unchecked(&i))
1319 .collect()
1320 }
1321}
1322
1323#[cfg(test)]
1324mod test_headers {
1325 use std::sync::Arc;
1326
1327 use alloy::{
1328 node_bindings::Anvil,
1329 primitives::{Address, U256},
1330 };
1331 use hotshot_query_service::testing::mocks::MockVersions;
1332 use hotshot_types::traits::signature_key::BuilderSignatureKey;
1333 use v0_1::{BlockMerkleTree, FeeMerkleTree, L1Client};
1334 use vbs::{bincode_serializer::BincodeSerializer, version::StaticVersion, BinarySerializer};
1335
1336 use super::*;
1337 use crate::{
1338 eth_signature_key::EthKeyPair,
1339 mock::MockStateCatchup,
1340 v0_3::{RewardAccountV1, RewardAmount, REWARD_MERKLE_TREE_V1_HEIGHT},
1341 v0_4::{RewardAccountV2, RewardMerkleTreeV2, REWARD_MERKLE_TREE_V2_HEIGHT},
1342 Leaf,
1343 };
1344
1345 #[derive(Debug, Default)]
1346 #[must_use]
1347 struct TestCase {
1348 parent_timestamp: u64,
1350 parent_timestamp_millis: u64,
1351 parent_l1_head: u64,
1352 parent_l1_finalized: Option<L1BlockInfo>,
1353
1354 l1_head: u64,
1356 l1_finalized: Option<L1BlockInfo>,
1357 timestamp: u64,
1358 timestamp_millis: u64,
1359 l1_deposits: Vec<FeeInfo>,
1360
1361 expected_timestamp: u64,
1363 expected_timestamp_millis: u64,
1364 expected_l1_head: u64,
1365 expected_l1_finalized: Option<L1BlockInfo>,
1366 }
1367
1368 impl TestCase {
1369 async fn run(self) {
1370 assert!(self.expected_timestamp >= self.parent_timestamp);
1372 assert!(self.expected_timestamp_millis >= self.parent_timestamp_millis);
1373 assert!(self.expected_l1_head >= self.parent_l1_head);
1374 assert!(self.expected_l1_finalized >= self.parent_l1_finalized);
1375
1376 let genesis = GenesisForTest::default().await;
1377 let mut parent = genesis.header.clone();
1378 parent.set_timestamp(self.parent_timestamp, self.parent_timestamp_millis);
1379 *parent.l1_head_mut() = self.parent_l1_head;
1380 *parent.l1_finalized_mut() = self.parent_l1_finalized;
1381
1382 let mut parent_leaf = genesis.leaf.clone();
1383 *parent_leaf.block_header_mut() = parent.clone();
1384
1385 let block_merkle_tree =
1386 BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap();
1387
1388 let fee_info = FeeInfo::genesis();
1389 let fee_merkle_tree = FeeMerkleTree::from_kv_set(
1390 20,
1391 Vec::from([(fee_info.account(), fee_info.amount())]),
1392 )
1393 .unwrap();
1394
1395 let reward_account_v1 = RewardAccountV1::default();
1396 let reward_account = RewardAccountV2::default();
1397 let reward_amount = RewardAmount::default();
1398 let reward_merkle_tree_v2 =
1399 RewardMerkleTreeV2::from_kv_set(20, Vec::from([(reward_account, reward_amount)]))
1400 .unwrap();
1401
1402 let reward_merkle_tree_v1 = RewardMerkleTreeV1::from_kv_set(
1403 20,
1404 Vec::from([(reward_account_v1, reward_amount)]),
1405 )
1406 .unwrap();
1407
1408 let mut validated_state = ValidatedState {
1409 block_merkle_tree: block_merkle_tree.clone(),
1410 fee_merkle_tree,
1411 reward_merkle_tree_v2,
1412 reward_merkle_tree_v1,
1413 chain_config: genesis.instance_state.chain_config.into(),
1414 };
1415
1416 let (fee_account, fee_key) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
1417 let fee_amount = 0;
1418 let fee_signature =
1419 FeeAccount::sign_fee(&fee_key, fee_amount, &genesis.ns_table).unwrap();
1420
1421 let header = Header::from_info(
1422 genesis.header.payload_commitment(),
1423 genesis.header.builder_commitment().clone(),
1424 genesis.ns_table,
1425 &parent_leaf,
1426 L1Snapshot {
1427 head: self.l1_head,
1428 finalized: self.l1_finalized,
1429 },
1430 &self.l1_deposits,
1431 vec![BuilderFee {
1432 fee_account,
1433 fee_amount,
1434 fee_signature,
1435 }],
1436 self.timestamp,
1437 self.timestamp_millis,
1438 validated_state.clone(),
1439 genesis.instance_state.chain_config,
1440 Version { major: 0, minor: 1 },
1441 None,
1442 None,
1443 )
1444 .unwrap();
1445 assert_eq!(header.height(), parent.height() + 1);
1446 assert_eq!(header.timestamp(), self.expected_timestamp);
1447 assert_eq!(header.timestamp_millis(), self.expected_timestamp_millis);
1448 assert_eq!(header.l1_head(), self.expected_l1_head);
1449 assert_eq!(header.l1_finalized(), self.expected_l1_finalized);
1450
1451 for fee_info in self.l1_deposits {
1453 validated_state.insert_fee_deposit(fee_info).unwrap();
1454 }
1455 assert_eq!(
1456 validated_state.fee_merkle_tree.commitment(),
1457 header.fee_merkle_tree_root(),
1458 );
1459
1460 assert_eq!(
1461 block_merkle_tree,
1462 BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap()
1463 );
1464 }
1465 }
1466
1467 fn l1_block(number: u64) -> L1BlockInfo {
1468 L1BlockInfo {
1469 number,
1470 ..Default::default()
1471 }
1472 }
1473
1474 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1475 async fn test_new_header() {
1476 TestCase::default().run().await
1478 }
1479
1480 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1481 async fn test_new_header_advance_timestamp() {
1482 TestCase {
1483 timestamp: 1,
1484 timestamp_millis: 1_000,
1485 expected_timestamp: 1,
1486 expected_timestamp_millis: 1_000,
1487 ..Default::default()
1488 }
1489 .run()
1490 .await
1491 }
1492
1493 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1494 async fn test_new_header_advance_l1_block() {
1495 TestCase {
1496 parent_l1_head: 0,
1497 parent_l1_finalized: Some(l1_block(0)),
1498
1499 l1_head: 1,
1500 l1_finalized: Some(l1_block(1)),
1501
1502 expected_l1_head: 1,
1503 expected_l1_finalized: Some(l1_block(1)),
1504
1505 ..Default::default()
1506 }
1507 .run()
1508 .await
1509 }
1510
1511 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1512 async fn test_new_header_advance_l1_finalized_from_none() {
1513 TestCase {
1514 l1_finalized: Some(l1_block(1)),
1515 expected_l1_finalized: Some(l1_block(1)),
1516 ..Default::default()
1517 }
1518 .run()
1519 .await
1520 }
1521
1522 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1523 async fn test_new_header_timestamp_behind_finalized_l1_block() {
1524 let l1_finalized = Some(L1BlockInfo {
1525 number: 1,
1526 timestamp: U256::from(1),
1527 ..Default::default()
1528 });
1529 TestCase {
1530 l1_head: 1,
1531 l1_finalized,
1532 timestamp: 0,
1533 timestamp_millis: 0,
1534
1535 expected_l1_head: 1,
1536 expected_l1_finalized: l1_finalized,
1537 expected_timestamp: 1,
1538 expected_timestamp_millis: 1_000,
1539
1540 ..Default::default()
1541 }
1542 .run()
1543 .await
1544 }
1545
1546 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1547 async fn test_new_header_timestamp_behind() {
1548 TestCase {
1549 parent_timestamp: 1,
1550 parent_timestamp_millis: 1_000,
1551 timestamp: 0,
1552 timestamp_millis: 0,
1553 expected_timestamp: 1,
1554 expected_timestamp_millis: 1_000,
1555
1556 ..Default::default()
1557 }
1558 .run()
1559 .await
1560 }
1561
1562 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1563 async fn test_new_header_l1_head_behind() {
1564 TestCase {
1565 parent_l1_head: 1,
1566 l1_head: 0,
1567 expected_l1_head: 1,
1568
1569 ..Default::default()
1570 }
1571 .run()
1572 .await
1573 }
1574
1575 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1576 async fn test_new_header_l1_finalized_behind_some() {
1577 TestCase {
1578 parent_l1_finalized: Some(l1_block(1)),
1579 l1_finalized: Some(l1_block(0)),
1580 expected_l1_finalized: Some(l1_block(1)),
1581
1582 ..Default::default()
1583 }
1584 .run()
1585 .await
1586 }
1587
1588 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1589 async fn test_new_header_l1_finalized_behind_none() {
1590 TestCase {
1591 parent_l1_finalized: Some(l1_block(0)),
1592 l1_finalized: None,
1593 expected_l1_finalized: Some(l1_block(0)),
1594
1595 ..Default::default()
1596 }
1597 .run()
1598 .await
1599 }
1600
1601 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1602 async fn test_new_header_deposits_one() {
1603 TestCase {
1604 l1_deposits: vec![FeeInfo::new(Address::default(), 1)],
1605 ..Default::default()
1606 }
1607 .run()
1608 .await
1609 }
1610
1611 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1612 async fn test_new_header_deposits_many() {
1613 TestCase {
1614 l1_deposits: [
1615 (Address::default(), 1),
1616 (Address::default(), 2),
1617 (Address::random(), 3),
1618 ]
1619 .iter()
1620 .map(|(address, amount)| FeeInfo::new(*address, *amount))
1621 .collect(),
1622 ..Default::default()
1623 }
1624 .run()
1625 .await
1626 }
1627
1628 struct GenesisForTest {
1629 pub instance_state: NodeState,
1630 pub validated_state: ValidatedState,
1631 pub leaf: Leaf2,
1632 pub header: Header,
1633 pub ns_table: NsTable,
1634 }
1635
1636 impl GenesisForTest {
1637 async fn default() -> Self {
1638 let instance_state = NodeState::mock();
1639 let validated_state = ValidatedState::genesis(&instance_state).0;
1640 let leaf: Leaf2 = Leaf::genesis::<MockVersions>(&validated_state, &instance_state)
1641 .await
1642 .into();
1643 let header = leaf.block_header().clone();
1644 let ns_table = leaf.block_payload().unwrap().ns_table().clone();
1645 Self {
1646 instance_state,
1647 validated_state,
1648 leaf,
1649 header,
1650 ns_table,
1651 }
1652 }
1653 }
1654
1655 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1656 async fn test_proposal_validation_success() {
1657 let anvil = Anvil::new().block_time(1u64).spawn();
1658 let mut genesis_state = NodeState::mock()
1659 .with_l1(L1Client::new(vec![anvil.endpoint_url()]).expect("Failed to create L1 client"))
1660 .with_current_version(StaticVersion::<0, 1>::version());
1661
1662 let genesis = GenesisForTest::default().await;
1663
1664 let mut parent_state = genesis.validated_state.clone();
1665
1666 let mut block_merkle_tree = parent_state.block_merkle_tree.clone();
1667 let fee_merkle_tree = parent_state.fee_merkle_tree.clone();
1668
1669 block_merkle_tree.push(genesis.header.commit()).unwrap();
1671 let block_merkle_tree_root = block_merkle_tree.commitment();
1672 let fee_merkle_tree_root = fee_merkle_tree.commitment();
1673 parent_state.block_merkle_tree = block_merkle_tree.clone();
1674 parent_state.fee_merkle_tree = fee_merkle_tree.clone();
1675
1676 let mut parent_header = genesis.header.clone();
1677 *parent_header.block_merkle_tree_root_mut() = block_merkle_tree_root;
1678 *parent_header.fee_merkle_tree_root_mut() = fee_merkle_tree_root;
1679
1680 let mut parent_leaf = genesis.leaf.clone();
1681 *parent_leaf.block_header_mut() = parent_header.clone();
1682
1683 let forgotten_state = parent_state.forget();
1685 genesis_state.state_catchup = Arc::new(MockStateCatchup::from_iter([(
1686 parent_leaf.view_number(),
1687 Arc::new(parent_state.clone()),
1688 )]));
1689 let key_pair = EthKeyPair::for_test();
1694 let fee_amount = 0u64;
1695 let payload_commitment = parent_header.payload_commitment();
1696 let builder_commitment = parent_header.builder_commitment();
1697 let ns_table = genesis.ns_table;
1698 let fee_signature = FeeAccount::sign_fee(&key_pair, fee_amount, &ns_table).unwrap();
1699 let builder_fee = BuilderFee {
1700 fee_amount,
1701 fee_account: key_pair.fee_account(),
1702 fee_signature,
1703 };
1704 let proposal = Header::new(
1705 &forgotten_state,
1706 &genesis_state,
1707 &parent_leaf,
1708 payload_commitment,
1709 builder_commitment.clone(),
1710 ns_table,
1711 builder_fee,
1712 StaticVersion::<0, 1>::version(),
1713 *parent_leaf.view_number() + 1,
1714 )
1715 .await
1716 .unwrap();
1717
1718 let mut proposal_state = parent_state.clone();
1719 for fee_info in genesis_state
1720 .l1_client
1721 .get_finalized_deposits(Address::default(), None, 0)
1722 .await
1723 {
1724 proposal_state.insert_fee_deposit(fee_info).unwrap();
1725 }
1726
1727 let mut block_merkle_tree = proposal_state.block_merkle_tree.clone();
1728 block_merkle_tree.push(proposal.commit()).unwrap();
1729
1730 let _proposal_state = proposal_state
1731 .apply_header(
1732 &genesis_state,
1733 &genesis_state.state_catchup,
1734 &parent_leaf,
1735 &proposal,
1736 StaticVersion::<0, 1>::version(),
1737 parent_leaf.view_number() + 1,
1738 )
1739 .await
1740 .unwrap()
1741 .0;
1742
1743 }
1756
1757 #[test_log::test]
1758 fn verify_builder_signature() {
1759 let message = ";)";
1761 let mut commitment = [0u8; 32];
1762 commitment[..message.len()].copy_from_slice(message.as_bytes());
1763
1764 let key = FeeAccount::generated_from_seed_indexed([0; 32], 0).1;
1765 let signature = FeeAccount::sign_builder_message(&key, &commitment).unwrap();
1766 assert!(key
1767 .fee_account()
1768 .validate_builder_signature(&signature, &commitment));
1769 }
1770
1771 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1772 async fn test_versioned_header_serialization() {
1773 let genesis = GenesisForTest::default().await;
1774 let header = genesis.header.clone();
1775 let ns_table = genesis.ns_table;
1776
1777 let (fee_account, _) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
1778
1779 let v1_header = Header::create(
1780 genesis.instance_state.chain_config,
1781 1,
1782 2,
1783 2_000_000_000,
1784 3,
1785 Default::default(),
1786 header.payload_commitment(),
1787 header.builder_commitment().clone(),
1788 ns_table.clone(),
1789 header.fee_merkle_tree_root(),
1790 header.block_merkle_tree_root(),
1791 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
1792 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
1793 }),
1794 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
1795 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
1796 }),
1797 vec![FeeInfo {
1798 amount: 0.into(),
1799 account: fee_account,
1800 }],
1801 Default::default(),
1802 None,
1803 Version { major: 0, minor: 1 },
1804 None,
1805 );
1806
1807 let serialized = serde_json::to_string(&v1_header).unwrap();
1808 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1809 assert_eq!(v1_header, deserialized);
1810
1811 let v2_header = Header::create(
1812 genesis.instance_state.chain_config,
1813 1,
1814 2,
1815 2_000_000_000,
1816 3,
1817 Default::default(),
1818 header.payload_commitment(),
1819 header.builder_commitment().clone(),
1820 ns_table.clone(),
1821 header.fee_merkle_tree_root(),
1822 header.block_merkle_tree_root(),
1823 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
1824 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
1825 }),
1826 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
1827 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
1828 }),
1829 vec![FeeInfo {
1830 amount: 0.into(),
1831 account: fee_account,
1832 }],
1833 Default::default(),
1834 None,
1835 Version { major: 0, minor: 2 },
1836 None,
1837 );
1838
1839 let serialized = serde_json::to_string(&v2_header).unwrap();
1840 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1841 assert_eq!(v2_header, deserialized);
1842
1843 let v3_header = Header::create(
1844 genesis.instance_state.chain_config,
1845 1,
1846 2,
1847 2_000_000_000,
1848 3,
1849 Default::default(),
1850 header.payload_commitment(),
1851 header.builder_commitment().clone(),
1852 ns_table.clone(),
1853 header.fee_merkle_tree_root(),
1854 header.block_merkle_tree_root(),
1855 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
1856 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
1857 }),
1858 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
1859 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
1860 }),
1861 vec![FeeInfo {
1862 amount: 0.into(),
1863 account: fee_account,
1864 }],
1865 Default::default(),
1866 None,
1867 Version { major: 0, minor: 3 },
1868 None,
1869 );
1870
1871 let serialized = serde_json::to_string(&v3_header).unwrap();
1872 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1873 assert_eq!(v3_header, deserialized);
1874
1875 let v1_bytes = BincodeSerializer::<StaticVersion<0, 1>>::serialize(&v1_header).unwrap();
1876 let deserialized: Header =
1877 BincodeSerializer::<StaticVersion<0, 1>>::deserialize(&v1_bytes).unwrap();
1878 assert_eq!(v1_header, deserialized);
1879
1880 let v2_bytes = BincodeSerializer::<StaticVersion<0, 2>>::serialize(&v2_header).unwrap();
1881 let deserialized: Header =
1882 BincodeSerializer::<StaticVersion<0, 2>>::deserialize(&v2_bytes).unwrap();
1883 assert_eq!(v2_header, deserialized);
1884
1885 let v3_bytes = BincodeSerializer::<StaticVersion<0, 3>>::serialize(&v3_header).unwrap();
1886 let deserialized: Header =
1887 BincodeSerializer::<StaticVersion<0, 3>>::deserialize(&v3_bytes).unwrap();
1888 assert_eq!(v3_header, deserialized);
1889 }
1890}