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
415macro_rules! field {
417 ($obj:ident.$name:ident) => {
418 match $obj {
419 Self::V1(data) => &data.$name,
420 Self::V2(data) => &data.$name,
421 Self::V3(data) => &data.$name,
422 Self::V4(data) => &data.$name,
423 Self::V5(data) => &data.$name,
424 }
425 };
426}
427
428macro_rules! field_mut {
429 ($obj:ident.$name:ident) => {
430 match $obj {
431 Self::V1(data) => &mut data.$name,
432 Self::V2(data) => &mut data.$name,
433 Self::V3(data) => &mut data.$name,
434 Self::V4(data) => &mut data.$name,
435 Self::V5(data) => &mut data.$name,
436 }
437 };
438}
439
440impl Header {
441 #[allow(clippy::too_many_arguments)]
442 fn from_info(
443 payload_commitment: VidCommitment,
444 builder_commitment: BuilderCommitment,
445 ns_table: NsTable,
446 parent_leaf: &Leaf2,
447 mut l1: L1Snapshot,
448 l1_deposits: &[FeeInfo],
449 builder_fee: Vec<BuilderFee<SeqTypes>>,
450 mut timestamp: u64,
451 mut timestamp_millis: u64,
452 mut state: ValidatedState,
453 chain_config: ChainConfig,
454 version: Version,
455 reward_distributor: Option<RewardDistributor>,
456 next_stake_table_hash: Option<StakeTableHash>,
457 ) -> anyhow::Result<Self> {
458 ensure!(
459 version.major == 0,
460 "Invalid major version {}",
461 version.major
462 );
463
464 let parent_header = parent_leaf.block_header();
466 let height = parent_header.height() + 1;
467
468 if timestamp < parent_header.timestamp() {
472 tracing::warn!(
473 "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
474 parent_header.timestamp()
475 );
476 timestamp = parent_header.timestamp();
477 }
478
479 if timestamp_millis < parent_header.timestamp_millis() {
480 tracing::warn!(
481 "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
482 parent_header.timestamp_millis()
483 );
484 timestamp_millis = parent_header.timestamp_millis();
485 }
486
487 if l1.head < parent_header.l1_head() {
490 tracing::warn!(
491 "L1 head {} behind parent {}, L1 client may be lagging",
492 l1.head,
493 parent_header.l1_head()
494 );
495 l1.head = parent_header.l1_head();
496 }
497 if l1.finalized < parent_header.l1_finalized() {
498 tracing::warn!(
499 "L1 finalized {:?} behind parent {:?}, L1 client may be lagging",
500 l1.finalized,
501 parent_header.l1_finalized()
502 );
503 l1.finalized = parent_header.l1_finalized();
504 }
505
506 if let Some(l1_block) = &l1.finalized {
509 let l1_timestamp = l1_block.timestamp.to::<u64>();
510 if timestamp < l1_timestamp {
511 tracing::warn!(
512 "Espresso timestamp {timestamp} behind L1 timestamp {l1_timestamp}, local \
513 clock may be out of sync"
514 );
515 timestamp = l1_timestamp;
516 }
517
518 let l1_timestamp_millis = l1_timestamp * 1_000;
519
520 if timestamp_millis < l1_timestamp_millis {
521 tracing::warn!(
522 "Espresso timestamp_millis {timestamp_millis} behind L1 timestamp \
523 {l1_timestamp_millis}, local clock may be out of sync"
524 );
525 timestamp_millis = l1_timestamp_millis;
526 }
527 }
528
529 state
530 .block_merkle_tree
531 .push(parent_header.commit())
532 .context("missing blocks frontier")?;
533 let block_merkle_tree_root = state.block_merkle_tree.commitment();
534
535 for fee_info in l1_deposits {
537 state
538 .insert_fee_deposit(*fee_info)
539 .context(format!("missing fee account {}", fee_info.account()))?;
540 }
541
542 for BuilderFee {
544 fee_account,
545 fee_signature,
546 fee_amount,
547 } in &builder_fee
548 {
549 ensure!(
550 fee_account.validate_fee_signature(fee_signature, *fee_amount, &ns_table)
551 || fee_account.validate_fee_signature_with_vid_commitment(
552 fee_signature,
553 *fee_amount,
554 &ns_table,
555 &payload_commitment
556 ),
557 "invalid builder signature"
558 );
559
560 let fee_info = FeeInfo::new(*fee_account, *fee_amount);
561 state
562 .charge_fee(fee_info, chain_config.fee_recipient)
563 .context(format!("invalid builder fee {fee_info:?}"))?;
564 }
565
566 let fee_info = FeeInfo::from_builder_fees(builder_fee.clone());
567
568 let builder_signature: Vec<BuilderSignature> =
569 builder_fee.iter().map(|e| e.fee_signature).collect();
570
571 let fee_merkle_tree_root = state.fee_merkle_tree.commitment();
572
573 let header = match (version.major, version.minor) {
574 (0, 1) => Self::V1(v0_1::Header {
575 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
576 chain_config,
577 )),
578 height,
579 timestamp,
580 l1_head: l1.head,
581 l1_finalized: l1.finalized,
582 payload_commitment,
583 builder_commitment,
584 ns_table,
585 block_merkle_tree_root,
586 fee_merkle_tree_root,
587 fee_info: fee_info[0],
588 builder_signature: builder_signature.first().copied(),
589 }),
590 (0, 2) => Self::V2(v0_2::Header {
591 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
592 chain_config,
593 )),
594 height,
595 timestamp,
596 l1_head: l1.head,
597 l1_finalized: l1.finalized,
598 payload_commitment,
599 builder_commitment,
600 ns_table,
601 block_merkle_tree_root,
602 fee_merkle_tree_root,
603 fee_info: fee_info[0],
604 builder_signature: builder_signature.first().copied(),
605 }),
606 (0, 3) => Self::V3(v0_3::Header {
607 chain_config: chain_config.into(),
608 height,
609 timestamp,
610 l1_head: l1.head,
611 l1_finalized: l1.finalized,
612 payload_commitment,
613 builder_commitment,
614 ns_table,
615 block_merkle_tree_root,
616 fee_merkle_tree_root,
617 reward_merkle_tree_root: state.reward_merkle_tree_v1.commitment(),
618 fee_info: fee_info[0],
619 builder_signature: builder_signature.first().copied(),
620 }),
621 (0, 4) => Self::V4(v0_4::Header {
622 chain_config: chain_config.into(),
623 height,
624 timestamp,
625 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
626 l1_head: l1.head,
627 l1_finalized: l1.finalized,
628 payload_commitment,
629 builder_commitment,
630 ns_table,
631 block_merkle_tree_root,
632 fee_merkle_tree_root,
633 reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
634 fee_info: fee_info[0],
635 builder_signature: builder_signature.first().copied(),
636 total_reward_distributed: reward_distributor
637 .map(|r| r.total_distributed())
638 .unwrap_or_default(),
639 next_stake_table_hash,
640 }),
641 (0, 5) => Self::V5(v0_5::Header {
642 chain_config: chain_config.into(),
643 height,
644 timestamp,
645 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
646 l1_head: l1.head,
647 l1_finalized: l1.finalized,
648 payload_commitment,
649 builder_commitment,
650 ns_table,
651 block_merkle_tree_root,
652 fee_merkle_tree_root,
653 reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
654 fee_info: fee_info[0],
655 builder_signature: builder_signature.first().copied(),
656 total_reward_distributed: reward_distributor
657 .map(|r| r.total_distributed())
658 .unwrap_or_default(),
659 next_stake_table_hash,
660 }),
661 _ => panic!("invalid version: {version}"),
665 };
666 Ok(header)
667 }
668
669 async fn get_chain_config(
670 validated_state: &ValidatedState,
671 instance_state: &NodeState,
672 ) -> anyhow::Result<ChainConfig> {
673 let validated_cf = validated_state.chain_config;
674 let instance_cf = instance_state.chain_config;
675
676 if validated_cf.commit() == instance_cf.commit() {
677 return Ok(instance_cf);
678 }
679
680 match validated_cf.resolve() {
681 Some(cf) => Ok(cf),
682 None => {
683 tracing::info!("fetching chain config {} from peers", validated_cf.commit());
684
685 instance_state
686 .state_catchup
687 .as_ref()
688 .fetch_chain_config(validated_cf.commit())
689 .await
690 },
691 }
692 }
693}
694
695impl Header {
696 pub fn chain_config(&self) -> v0_3::ResolvableChainConfig {
698 match self {
699 Self::V1(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
700 Self::V2(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
701 Self::V3(fields) => fields.chain_config,
702 Self::V4(fields) => fields.chain_config,
703 Self::V5(fields) => fields.chain_config,
704 }
705 }
706
707 pub fn height(&self) -> u64 {
708 *field!(self.height)
709 }
710
711 pub fn height_mut(&mut self) -> &mut u64 {
712 &mut *field_mut!(self.height)
713 }
714
715 pub fn timestamp_internal(&self) -> u64 {
716 match self {
717 Self::V1(fields) => fields.timestamp,
718 Self::V2(fields) => fields.timestamp,
719 Self::V3(fields) => fields.timestamp,
720 Self::V4(fields) => fields.timestamp,
721 Self::V5(fields) => fields.timestamp,
722 }
723 }
724
725 pub fn timestamp_millis_internal(&self) -> u64 {
726 match self {
727 Self::V1(fields) => fields.timestamp * 1_000,
728 Self::V2(fields) => fields.timestamp * 1_000,
729 Self::V3(fields) => fields.timestamp * 1_000,
730 Self::V4(fields) => fields.timestamp_millis.u64(),
731 Self::V5(fields) => fields.timestamp_millis.u64(),
732 }
733 }
734
735 pub fn set_timestamp(&mut self, timestamp: u64, timestamp_millis: u64) {
736 match self {
737 Self::V1(fields) => {
738 fields.timestamp = timestamp;
739 },
740 Self::V2(fields) => {
741 fields.timestamp = timestamp;
742 },
743 Self::V3(fields) => {
744 fields.timestamp = timestamp;
745 },
746 Self::V4(fields) => {
747 fields.timestamp = timestamp;
748 fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
749 },
750 Self::V5(fields) => {
751 fields.timestamp = timestamp;
752 fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
753 },
754 };
755 }
756
757 pub fn l1_head(&self) -> u64 {
782 *field!(self.l1_head)
783 }
784
785 pub fn l1_head_mut(&mut self) -> &mut u64 {
786 &mut *field_mut!(self.l1_head)
787 }
788
789 pub fn l1_finalized(&self) -> Option<L1BlockInfo> {
804 *field!(self.l1_finalized)
805 }
806
807 pub fn l1_finalized_mut(&mut self) -> &mut Option<L1BlockInfo> {
808 &mut *field_mut!(self.l1_finalized)
809 }
810
811 pub fn payload_commitment(&self) -> VidCommitment {
812 *field!(self.payload_commitment)
813 }
814
815 pub fn payload_commitment_mut(&mut self) -> &mut VidCommitment {
816 &mut *field_mut!(self.payload_commitment)
817 }
818
819 pub fn builder_commitment(&self) -> &BuilderCommitment {
820 field!(self.builder_commitment)
821 }
822
823 pub fn builder_commitment_mut(&mut self) -> &mut BuilderCommitment {
824 &mut *field_mut!(self.builder_commitment)
825 }
826
827 pub fn ns_table(&self) -> &NsTable {
828 field!(self.ns_table)
829 }
830
831 pub fn block_merkle_tree_root(&self) -> BlockMerkleCommitment {
833 *field!(self.block_merkle_tree_root)
834 }
835
836 pub fn block_merkle_tree_root_mut(&mut self) -> &mut BlockMerkleCommitment {
837 &mut *field_mut!(self.block_merkle_tree_root)
838 }
839
840 pub fn fee_merkle_tree_root(&self) -> FeeMerkleCommitment {
842 *field!(self.fee_merkle_tree_root)
843 }
844
845 pub fn fee_merkle_tree_root_mut(&mut self) -> &mut FeeMerkleCommitment {
846 &mut *field_mut!(self.fee_merkle_tree_root)
847 }
848
849 pub fn fee_info(&self) -> Vec<FeeInfo> {
851 match self {
852 Self::V1(fields) => vec![fields.fee_info],
853 Self::V2(fields) => vec![fields.fee_info],
854 Self::V3(fields) => vec![fields.fee_info],
855 Self::V4(fields) => vec![fields.fee_info],
856 Self::V5(fields) => vec![fields.fee_info],
857 }
858 }
859
860 pub fn reward_merkle_tree_root(
861 &self,
862 ) -> Either<RewardMerkleCommitmentV1, RewardMerkleCommitmentV2> {
863 let empty_reward_merkle_tree = RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT);
864 match self {
865 Self::V1(_) => Either::Left(empty_reward_merkle_tree.commitment()),
866 Self::V2(_) => Either::Left(empty_reward_merkle_tree.commitment()),
867 Self::V3(fields) => Either::Left(fields.reward_merkle_tree_root),
868 Self::V4(fields) => Either::Right(fields.reward_merkle_tree_root),
869 Self::V5(fields) => Either::Right(fields.reward_merkle_tree_root),
870 }
871 }
872
873 pub fn builder_signature(&self) -> Vec<BuilderSignature> {
882 match self {
883 Self::V1(fields) => fields.builder_signature.as_slice().to_vec(),
888 Self::V2(fields) => fields.builder_signature.as_slice().to_vec(),
889 Self::V3(fields) => fields.builder_signature.as_slice().to_vec(),
890 Self::V4(fields) => fields.builder_signature.as_slice().to_vec(),
891 Self::V5(fields) => fields.builder_signature.as_slice().to_vec(),
892 }
893 }
894
895 pub fn total_reward_distributed(&self) -> Option<RewardAmount> {
896 match self {
897 Self::V1(_) | Self::V2(_) | Self::V3(_) => None,
898 Self::V4(fields) | Self::V5(fields) => Some(fields.total_reward_distributed),
899 }
900 }
901}
902
903#[derive(Debug, Error)]
904#[error("Invalid Block Header {msg}")]
905pub struct InvalidBlockHeader {
906 msg: String,
907}
908impl InvalidBlockHeader {
909 fn new(msg: String) -> Self {
910 Self { msg }
911 }
912}
913
914impl From<anyhow::Error> for InvalidBlockHeader {
915 fn from(err: anyhow::Error) -> Self {
916 Self::new(format!("{err:#}"))
917 }
918}
919
920impl BlockHeader<SeqTypes> for Header {
921 type Error = InvalidBlockHeader;
922
923 #[tracing::instrument(
924 skip_all,
925 fields(
926 node_id = instance_state.node_id,
927 view = ?parent_leaf.view_number(),
928 height = parent_leaf.block_header().height(),
929 ),
930 )]
931 #[tracing::instrument(
932 skip_all,
933 fields(
934 height = parent_leaf.block_header().block_number() + 1,
935 parent_view = ?parent_leaf.view_number(),
936 payload_commitment,
937 version,
938 )
939 )]
940 async fn new(
941 parent_state: &ValidatedState,
942 instance_state: &NodeState,
943 parent_leaf: &Leaf2,
944 payload_commitment: VidCommitment,
945 builder_commitment: BuilderCommitment,
946 metadata: <<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
947 builder_fee: BuilderFee<SeqTypes>,
948 version: Version,
949 view_number: u64,
950 ) -> Result<Self, Self::Error> {
951 tracing::info!("preparing to propose legacy header");
952
953 let height = parent_leaf.height();
954 let view = parent_leaf.view_number();
955
956 let mut validated_state = parent_state.clone();
957
958 let chain_config = if version > instance_state.current_version {
959 match instance_state.upgrades.get(&version) {
960 Some(upgrade) => match upgrade.upgrade_type {
961 UpgradeType::Fee { chain_config } => chain_config,
962 UpgradeType::Epoch { chain_config } => chain_config,
963 UpgradeType::DrbAndHeader { chain_config } => chain_config,
964 UpgradeType::Da { chain_config } => chain_config,
965 },
966 None => Header::get_chain_config(&validated_state, instance_state).await?,
967 }
968 } else {
969 Header::get_chain_config(&validated_state, instance_state).await?
970 };
971
972 validated_state.chain_config = chain_config.into();
973
974 let l1_snapshot = instance_state.l1_client.snapshot().await;
976 let l1_deposits = if let (Some(addr), Some(block_info)) =
978 (chain_config.fee_contract, l1_snapshot.finalized)
979 {
980 instance_state
981 .l1_client
982 .get_finalized_deposits(
983 addr,
984 parent_leaf
985 .block_header()
986 .l1_finalized()
987 .map(|block_info| block_info.number),
988 block_info.number,
989 )
990 .await
991 } else {
992 vec![]
993 };
994 let missing_accounts = parent_state.forgotten_accounts(
998 [builder_fee.fee_account, chain_config.fee_recipient]
999 .into_iter()
1000 .chain(l1_deposits.iter().map(|info| info.account())),
1001 );
1002 if !missing_accounts.is_empty() {
1003 tracing::warn!(
1004 height,
1005 ?view,
1006 ?missing_accounts,
1007 "fetching missing accounts from peers"
1008 );
1009
1010 let missing_account_proofs = instance_state
1012 .state_catchup
1013 .as_ref()
1014 .fetch_accounts(
1015 instance_state,
1016 height,
1017 view,
1018 parent_state.fee_merkle_tree.commitment(),
1019 missing_accounts,
1020 )
1021 .await?;
1022
1023 for proof in missing_account_proofs.iter() {
1025 proof
1026 .remember(&mut validated_state.fee_merkle_tree)
1027 .context("remembering fee account")?;
1028 }
1029 }
1030
1031 if validated_state.need_to_fetch_blocks_mt_frontier() {
1033 tracing::warn!(height, ?view, "fetching block frontier from peers");
1034 instance_state
1035 .state_catchup
1036 .as_ref()
1037 .remember_blocks_merkle_tree(
1038 instance_state,
1039 height,
1040 view,
1041 &mut validated_state.block_merkle_tree,
1042 )
1043 .await
1044 .context("remembering block proof")?;
1045 }
1046
1047 let mut rewards = None;
1048 if version >= EpochVersion::version() {
1049 rewards = distribute_block_reward(
1050 instance_state,
1051 &mut validated_state,
1052 parent_leaf,
1053 ViewNumber::new(view_number),
1054 version,
1055 )
1056 .await?;
1057 };
1058
1059 let mut next_stake_table_hash = None;
1060
1061 if version >= DrbAndHeaderUpgradeVersion::version() {
1062 let epoch_height = instance_state
1063 .epoch_height
1064 .context("epoch height not in instance state")?;
1065 if is_ge_epoch_root(height + 1, epoch_height) {
1066 let coordinator = instance_state.coordinator.clone();
1067 let first_epoch = {
1068 coordinator
1069 .membership()
1070 .read()
1071 .await
1072 .first_epoch()
1073 .context("The first epoch was not set.")?
1074 };
1075
1076 let epoch = EpochNumber::new(epoch_from_block_number(height + 1, epoch_height));
1077
1078 if epoch > first_epoch + 1 {
1080 let epoch_membership = coordinator
1081 .stake_table_for_epoch(Some(epoch + 1))
1082 .await
1083 .map_err(|e| anyhow::anyhow!("failed to get epoch membership: {e}"))?;
1084 next_stake_table_hash = Some(
1085 epoch_membership
1086 .stake_table_hash()
1087 .await
1088 .context("failed to get next stake table hash")?,
1089 );
1090 }
1091 }
1092 }
1093
1094 let now = OffsetDateTime::now_utc();
1095
1096 let timestamp = now.unix_timestamp() as u64;
1097 let timestamp_millis = TimestampMillis::from_time(&now).u64();
1098
1099 Ok(Self::from_info(
1100 payload_commitment,
1101 builder_commitment,
1102 metadata,
1103 parent_leaf,
1104 l1_snapshot,
1105 &l1_deposits,
1106 vec![builder_fee],
1107 timestamp,
1108 timestamp_millis,
1109 validated_state,
1110 chain_config,
1111 version,
1112 rewards,
1113 next_stake_table_hash,
1114 )?)
1115 }
1116
1117 fn genesis<V: Versions>(
1118 instance_state: &NodeState,
1119 payload: <SeqTypes as NodeType>::BlockPayload,
1120 metadata: &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
1121 ) -> Self {
1122 let payload_bytes = payload.encode();
1123 let builder_commitment = payload.builder_commitment(metadata);
1124
1125 let vid_commitment_version = instance_state.genesis_version;
1126
1127 let payload_commitment = vid_commitment::<V>(
1128 &payload_bytes,
1129 &metadata.encode(),
1130 GENESIS_VID_NUM_STORAGE_NODES,
1131 vid_commitment_version,
1132 );
1133
1134 let ValidatedState {
1135 fee_merkle_tree,
1136 block_merkle_tree,
1137 reward_merkle_tree_v1,
1138 reward_merkle_tree_v2,
1139 ..
1140 } = ValidatedState::genesis(instance_state).0;
1141 let block_merkle_tree_root = block_merkle_tree.commitment();
1142 let fee_merkle_tree_root = fee_merkle_tree.commitment();
1143 let reward_merkle_tree_root = reward_merkle_tree_v2.commitment();
1144
1145 let time = instance_state.genesis_header.timestamp;
1146
1147 let timestamp = time.unix_timestamp();
1148 let timestamp_millis = time.unix_timestamp_millis();
1149
1150 Self::create(
1153 instance_state.genesis_header.chain_config,
1154 0,
1155 timestamp,
1156 timestamp_millis,
1157 instance_state
1158 .l1_genesis
1159 .map(|block| block.number)
1160 .unwrap_or_default(),
1161 instance_state.l1_genesis,
1162 payload_commitment,
1163 builder_commitment.clone(),
1164 metadata.clone(),
1165 fee_merkle_tree_root,
1166 block_merkle_tree_root,
1167 reward_merkle_tree_v1.commitment(),
1168 reward_merkle_tree_root,
1169 vec![FeeInfo::genesis()],
1170 vec![],
1171 None,
1172 instance_state.genesis_version,
1173 None,
1174 )
1175 }
1176
1177 fn timestamp(&self) -> u64 {
1178 self.timestamp_internal()
1179 }
1180
1181 fn timestamp_millis(&self) -> u64 {
1182 self.timestamp_millis_internal()
1183 }
1184
1185 fn block_number(&self) -> u64 {
1186 self.height()
1187 }
1188
1189 fn version(&self) -> Version {
1190 self.version()
1191 }
1192
1193 fn payload_commitment(&self) -> VidCommitment {
1194 self.payload_commitment()
1195 }
1196
1197 fn metadata(
1198 &self,
1199 ) -> &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata {
1200 self.ns_table()
1201 }
1202
1203 fn builder_commitment(&self) -> BuilderCommitment {
1205 self.builder_commitment().clone()
1206 }
1207
1208 fn get_light_client_state(
1209 &self,
1210 view: <SeqTypes as NodeType>::View,
1211 ) -> anyhow::Result<LightClientState> {
1212 let mut block_comm_root_bytes = vec![];
1213 self.block_merkle_tree_root()
1214 .serialize_compressed(&mut block_comm_root_bytes)?;
1215
1216 Ok(LightClientState {
1217 view_number: view.u64(),
1218 block_height: self.height(),
1219 block_comm_root: hotshot_types::light_client::hash_bytes_to_field(
1220 &block_comm_root_bytes,
1221 )?,
1222 })
1223 }
1224
1225 fn auth_root(&self) -> anyhow::Result<B256> {
1226 match self {
1227 Header::V1(_) | Header::V2(_) | Header::V3(_) => Ok(B256::ZERO),
1228 Header::V4(header) | Header::V5(header) => {
1229 let placeholder_1 = B256::ZERO;
1231 let placeholder_2 = B256::ZERO;
1232 let placeholder_3 = B256::ZERO;
1233 let placeholder_4 = B256::ZERO;
1234 let placeholder_5 = B256::ZERO;
1235 let placeholder_6 = B256::ZERO;
1236 let placeholder_7 = B256::ZERO;
1237
1238 let mut hasher = Keccak256::new();
1239
1240 let digest = header.reward_merkle_tree_root.digest();
1242 hasher.update(digest.0);
1243 hasher.update(placeholder_1);
1244 hasher.update(placeholder_2);
1245 hasher.update(placeholder_3);
1246 hasher.update(placeholder_4);
1247 hasher.update(placeholder_5);
1248 hasher.update(placeholder_6);
1249 hasher.update(placeholder_7);
1250
1251 Ok(hasher.finalize())
1252 },
1253 }
1254 }
1255}
1256
1257impl QueryableHeader<SeqTypes> for Header {
1258 type NamespaceId = NamespaceId;
1259 type NamespaceIndex = NsIndex;
1260
1261 fn namespace_id(&self, i: &NsIndex) -> Option<NamespaceId> {
1262 self.ns_table().read_ns_id(i)
1263 }
1264
1265 fn namespace_size(&self, i: &NsIndex, payload_size: usize) -> u64 {
1266 self.ns_table()
1267 .ns_range(i, &PayloadByteLen(payload_size))
1268 .byte_len()
1269 .0 as u64
1270 }
1271}
1272
1273impl ExplorerHeader<SeqTypes> for Header {
1274 type BalanceAmount = FeeAmount;
1275 type WalletAddress = Vec<FeeAccount>;
1276 type ProposerId = Vec<FeeAccount>;
1277
1278 fn proposer_id(&self) -> Self::ProposerId {
1280 self.fee_info().accounts()
1281 }
1282
1283 fn fee_info_account(&self) -> Self::WalletAddress {
1284 self.fee_info().accounts()
1285 }
1286
1287 fn fee_info_balance(&self) -> Self::BalanceAmount {
1288 self.fee_info().amount().unwrap()
1290 }
1291
1292 fn reward_balance(&self) -> Self::BalanceAmount {
1298 FeeAmount::from(0)
1299 }
1300
1301 fn namespace_ids(&self) -> Vec<NamespaceId> {
1302 self.ns_table()
1303 .iter()
1304 .map(|i| self.ns_table().read_ns_id_unchecked(&i))
1305 .collect()
1306 }
1307}
1308
1309#[cfg(test)]
1310mod test_headers {
1311 use std::sync::Arc;
1312
1313 use alloy::{
1314 node_bindings::Anvil,
1315 primitives::{Address, U256},
1316 };
1317 use hotshot_query_service::testing::mocks::MockVersions;
1318 use hotshot_types::traits::signature_key::BuilderSignatureKey;
1319 use v0_1::{BlockMerkleTree, FeeMerkleTree, L1Client};
1320 use vbs::{bincode_serializer::BincodeSerializer, version::StaticVersion, BinarySerializer};
1321
1322 use super::*;
1323 use crate::{
1324 eth_signature_key::EthKeyPair,
1325 mock::MockStateCatchup,
1326 v0_3::{RewardAccountV1, RewardAmount, REWARD_MERKLE_TREE_V1_HEIGHT},
1327 v0_4::{RewardAccountV2, RewardMerkleTreeV2, REWARD_MERKLE_TREE_V2_HEIGHT},
1328 Leaf,
1329 };
1330
1331 #[derive(Debug, Default)]
1332 #[must_use]
1333 struct TestCase {
1334 parent_timestamp: u64,
1336 parent_timestamp_millis: u64,
1337 parent_l1_head: u64,
1338 parent_l1_finalized: Option<L1BlockInfo>,
1339
1340 l1_head: u64,
1342 l1_finalized: Option<L1BlockInfo>,
1343 timestamp: u64,
1344 timestamp_millis: u64,
1345 l1_deposits: Vec<FeeInfo>,
1346
1347 expected_timestamp: u64,
1349 expected_timestamp_millis: u64,
1350 expected_l1_head: u64,
1351 expected_l1_finalized: Option<L1BlockInfo>,
1352 }
1353
1354 impl TestCase {
1355 async fn run(self) {
1356 assert!(self.expected_timestamp >= self.parent_timestamp);
1358 assert!(self.expected_timestamp_millis >= self.parent_timestamp_millis);
1359 assert!(self.expected_l1_head >= self.parent_l1_head);
1360 assert!(self.expected_l1_finalized >= self.parent_l1_finalized);
1361
1362 let genesis = GenesisForTest::default().await;
1363 let mut parent = genesis.header.clone();
1364 parent.set_timestamp(self.parent_timestamp, self.parent_timestamp_millis);
1365 *parent.l1_head_mut() = self.parent_l1_head;
1366 *parent.l1_finalized_mut() = self.parent_l1_finalized;
1367
1368 let mut parent_leaf = genesis.leaf.clone();
1369 *parent_leaf.block_header_mut() = parent.clone();
1370
1371 let block_merkle_tree =
1372 BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap();
1373
1374 let fee_info = FeeInfo::genesis();
1375 let fee_merkle_tree = FeeMerkleTree::from_kv_set(
1376 20,
1377 Vec::from([(fee_info.account(), fee_info.amount())]),
1378 )
1379 .unwrap();
1380
1381 let reward_account_v1 = RewardAccountV1::default();
1382 let reward_account = RewardAccountV2::default();
1383 let reward_amount = RewardAmount::default();
1384 let reward_merkle_tree_v2 =
1385 RewardMerkleTreeV2::from_kv_set(20, Vec::from([(reward_account, reward_amount)]))
1386 .unwrap();
1387
1388 let reward_merkle_tree_v1 = RewardMerkleTreeV1::from_kv_set(
1389 20,
1390 Vec::from([(reward_account_v1, reward_amount)]),
1391 )
1392 .unwrap();
1393
1394 let mut validated_state = ValidatedState {
1395 block_merkle_tree: block_merkle_tree.clone(),
1396 fee_merkle_tree,
1397 reward_merkle_tree_v2,
1398 reward_merkle_tree_v1,
1399 chain_config: genesis.instance_state.chain_config.into(),
1400 };
1401
1402 let (fee_account, fee_key) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
1403 let fee_amount = 0;
1404 let fee_signature =
1405 FeeAccount::sign_fee(&fee_key, fee_amount, &genesis.ns_table).unwrap();
1406
1407 let header = Header::from_info(
1408 genesis.header.payload_commitment(),
1409 genesis.header.builder_commitment().clone(),
1410 genesis.ns_table,
1411 &parent_leaf,
1412 L1Snapshot {
1413 head: self.l1_head,
1414 finalized: self.l1_finalized,
1415 },
1416 &self.l1_deposits,
1417 vec![BuilderFee {
1418 fee_account,
1419 fee_amount,
1420 fee_signature,
1421 }],
1422 self.timestamp,
1423 self.timestamp_millis,
1424 validated_state.clone(),
1425 genesis.instance_state.chain_config,
1426 Version { major: 0, minor: 1 },
1427 None,
1428 None,
1429 )
1430 .unwrap();
1431 assert_eq!(header.height(), parent.height() + 1);
1432 assert_eq!(header.timestamp(), self.expected_timestamp);
1433 assert_eq!(header.timestamp_millis(), self.expected_timestamp_millis);
1434 assert_eq!(header.l1_head(), self.expected_l1_head);
1435 assert_eq!(header.l1_finalized(), self.expected_l1_finalized);
1436
1437 for fee_info in self.l1_deposits {
1439 validated_state.insert_fee_deposit(fee_info).unwrap();
1440 }
1441 assert_eq!(
1442 validated_state.fee_merkle_tree.commitment(),
1443 header.fee_merkle_tree_root(),
1444 );
1445
1446 assert_eq!(
1447 block_merkle_tree,
1448 BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap()
1449 );
1450 }
1451 }
1452
1453 fn l1_block(number: u64) -> L1BlockInfo {
1454 L1BlockInfo {
1455 number,
1456 ..Default::default()
1457 }
1458 }
1459
1460 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1461 async fn test_new_header() {
1462 TestCase::default().run().await
1464 }
1465
1466 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1467 async fn test_new_header_advance_timestamp() {
1468 TestCase {
1469 timestamp: 1,
1470 timestamp_millis: 1_000,
1471 expected_timestamp: 1,
1472 expected_timestamp_millis: 1_000,
1473 ..Default::default()
1474 }
1475 .run()
1476 .await
1477 }
1478
1479 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1480 async fn test_new_header_advance_l1_block() {
1481 TestCase {
1482 parent_l1_head: 0,
1483 parent_l1_finalized: Some(l1_block(0)),
1484
1485 l1_head: 1,
1486 l1_finalized: Some(l1_block(1)),
1487
1488 expected_l1_head: 1,
1489 expected_l1_finalized: Some(l1_block(1)),
1490
1491 ..Default::default()
1492 }
1493 .run()
1494 .await
1495 }
1496
1497 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1498 async fn test_new_header_advance_l1_finalized_from_none() {
1499 TestCase {
1500 l1_finalized: Some(l1_block(1)),
1501 expected_l1_finalized: Some(l1_block(1)),
1502 ..Default::default()
1503 }
1504 .run()
1505 .await
1506 }
1507
1508 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1509 async fn test_new_header_timestamp_behind_finalized_l1_block() {
1510 let l1_finalized = Some(L1BlockInfo {
1511 number: 1,
1512 timestamp: U256::from(1),
1513 ..Default::default()
1514 });
1515 TestCase {
1516 l1_head: 1,
1517 l1_finalized,
1518 timestamp: 0,
1519 timestamp_millis: 0,
1520
1521 expected_l1_head: 1,
1522 expected_l1_finalized: l1_finalized,
1523 expected_timestamp: 1,
1524 expected_timestamp_millis: 1_000,
1525
1526 ..Default::default()
1527 }
1528 .run()
1529 .await
1530 }
1531
1532 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1533 async fn test_new_header_timestamp_behind() {
1534 TestCase {
1535 parent_timestamp: 1,
1536 parent_timestamp_millis: 1_000,
1537 timestamp: 0,
1538 timestamp_millis: 0,
1539 expected_timestamp: 1,
1540 expected_timestamp_millis: 1_000,
1541
1542 ..Default::default()
1543 }
1544 .run()
1545 .await
1546 }
1547
1548 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1549 async fn test_new_header_l1_head_behind() {
1550 TestCase {
1551 parent_l1_head: 1,
1552 l1_head: 0,
1553 expected_l1_head: 1,
1554
1555 ..Default::default()
1556 }
1557 .run()
1558 .await
1559 }
1560
1561 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1562 async fn test_new_header_l1_finalized_behind_some() {
1563 TestCase {
1564 parent_l1_finalized: Some(l1_block(1)),
1565 l1_finalized: Some(l1_block(0)),
1566 expected_l1_finalized: Some(l1_block(1)),
1567
1568 ..Default::default()
1569 }
1570 .run()
1571 .await
1572 }
1573
1574 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1575 async fn test_new_header_l1_finalized_behind_none() {
1576 TestCase {
1577 parent_l1_finalized: Some(l1_block(0)),
1578 l1_finalized: None,
1579 expected_l1_finalized: Some(l1_block(0)),
1580
1581 ..Default::default()
1582 }
1583 .run()
1584 .await
1585 }
1586
1587 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1588 async fn test_new_header_deposits_one() {
1589 TestCase {
1590 l1_deposits: vec![FeeInfo::new(Address::default(), 1)],
1591 ..Default::default()
1592 }
1593 .run()
1594 .await
1595 }
1596
1597 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1598 async fn test_new_header_deposits_many() {
1599 TestCase {
1600 l1_deposits: [
1601 (Address::default(), 1),
1602 (Address::default(), 2),
1603 (Address::random(), 3),
1604 ]
1605 .iter()
1606 .map(|(address, amount)| FeeInfo::new(*address, *amount))
1607 .collect(),
1608 ..Default::default()
1609 }
1610 .run()
1611 .await
1612 }
1613
1614 struct GenesisForTest {
1615 pub instance_state: NodeState,
1616 pub validated_state: ValidatedState,
1617 pub leaf: Leaf2,
1618 pub header: Header,
1619 pub ns_table: NsTable,
1620 }
1621
1622 impl GenesisForTest {
1623 async fn default() -> Self {
1624 let instance_state = NodeState::mock();
1625 let validated_state = ValidatedState::genesis(&instance_state).0;
1626 let leaf: Leaf2 = Leaf::genesis::<MockVersions>(&validated_state, &instance_state)
1627 .await
1628 .into();
1629 let header = leaf.block_header().clone();
1630 let ns_table = leaf.block_payload().unwrap().ns_table().clone();
1631 Self {
1632 instance_state,
1633 validated_state,
1634 leaf,
1635 header,
1636 ns_table,
1637 }
1638 }
1639 }
1640
1641 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1642 async fn test_proposal_validation_success() {
1643 let anvil = Anvil::new().block_time(1u64).spawn();
1644 let mut genesis_state = NodeState::mock()
1645 .with_l1(L1Client::new(vec![anvil.endpoint_url()]).expect("Failed to create L1 client"))
1646 .with_current_version(StaticVersion::<0, 1>::version());
1647
1648 let genesis = GenesisForTest::default().await;
1649
1650 let mut parent_state = genesis.validated_state.clone();
1651
1652 let mut block_merkle_tree = parent_state.block_merkle_tree.clone();
1653 let fee_merkle_tree = parent_state.fee_merkle_tree.clone();
1654
1655 block_merkle_tree.push(genesis.header.commit()).unwrap();
1657 let block_merkle_tree_root = block_merkle_tree.commitment();
1658 let fee_merkle_tree_root = fee_merkle_tree.commitment();
1659 parent_state.block_merkle_tree = block_merkle_tree.clone();
1660 parent_state.fee_merkle_tree = fee_merkle_tree.clone();
1661
1662 let mut parent_header = genesis.header.clone();
1663 *parent_header.block_merkle_tree_root_mut() = block_merkle_tree_root;
1664 *parent_header.fee_merkle_tree_root_mut() = fee_merkle_tree_root;
1665
1666 let mut parent_leaf = genesis.leaf.clone();
1667 *parent_leaf.block_header_mut() = parent_header.clone();
1668
1669 let forgotten_state = parent_state.forget();
1671 genesis_state.state_catchup = Arc::new(MockStateCatchup::from_iter([(
1672 parent_leaf.view_number(),
1673 Arc::new(parent_state.clone()),
1674 )]));
1675 let key_pair = EthKeyPair::for_test();
1680 let fee_amount = 0u64;
1681 let payload_commitment = parent_header.payload_commitment();
1682 let builder_commitment = parent_header.builder_commitment();
1683 let ns_table = genesis.ns_table;
1684 let fee_signature = FeeAccount::sign_fee(&key_pair, fee_amount, &ns_table).unwrap();
1685 let builder_fee = BuilderFee {
1686 fee_amount,
1687 fee_account: key_pair.fee_account(),
1688 fee_signature,
1689 };
1690 let proposal = Header::new(
1691 &forgotten_state,
1692 &genesis_state,
1693 &parent_leaf,
1694 payload_commitment,
1695 builder_commitment.clone(),
1696 ns_table,
1697 builder_fee,
1698 StaticVersion::<0, 1>::version(),
1699 *parent_leaf.view_number() + 1,
1700 )
1701 .await
1702 .unwrap();
1703
1704 let mut proposal_state = parent_state.clone();
1705 for fee_info in genesis_state
1706 .l1_client
1707 .get_finalized_deposits(Address::default(), None, 0)
1708 .await
1709 {
1710 proposal_state.insert_fee_deposit(fee_info).unwrap();
1711 }
1712
1713 let mut block_merkle_tree = proposal_state.block_merkle_tree.clone();
1714 block_merkle_tree.push(proposal.commit()).unwrap();
1715
1716 let _proposal_state = proposal_state
1717 .apply_header(
1718 &genesis_state,
1719 &genesis_state.state_catchup,
1720 &parent_leaf,
1721 &proposal,
1722 StaticVersion::<0, 1>::version(),
1723 parent_leaf.view_number() + 1,
1724 )
1725 .await
1726 .unwrap()
1727 .0;
1728
1729 }
1742
1743 #[test_log::test]
1744 fn verify_builder_signature() {
1745 let message = ";)";
1747 let mut commitment = [0u8; 32];
1748 commitment[..message.len()].copy_from_slice(message.as_bytes());
1749
1750 let key = FeeAccount::generated_from_seed_indexed([0; 32], 0).1;
1751 let signature = FeeAccount::sign_builder_message(&key, &commitment).unwrap();
1752 assert!(key
1753 .fee_account()
1754 .validate_builder_signature(&signature, &commitment));
1755 }
1756
1757 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1758 async fn test_versioned_header_serialization() {
1759 let genesis = GenesisForTest::default().await;
1760 let header = genesis.header.clone();
1761 let ns_table = genesis.ns_table;
1762
1763 let (fee_account, _) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
1764
1765 let v1_header = Header::create(
1766 genesis.instance_state.chain_config,
1767 1,
1768 2,
1769 2_000_000_000,
1770 3,
1771 Default::default(),
1772 header.payload_commitment(),
1773 header.builder_commitment().clone(),
1774 ns_table.clone(),
1775 header.fee_merkle_tree_root(),
1776 header.block_merkle_tree_root(),
1777 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
1778 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
1779 }),
1780 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
1781 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
1782 }),
1783 vec![FeeInfo {
1784 amount: 0.into(),
1785 account: fee_account,
1786 }],
1787 Default::default(),
1788 None,
1789 Version { major: 0, minor: 1 },
1790 None,
1791 );
1792
1793 let serialized = serde_json::to_string(&v1_header).unwrap();
1794 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1795 assert_eq!(v1_header, deserialized);
1796
1797 let v2_header = Header::create(
1798 genesis.instance_state.chain_config,
1799 1,
1800 2,
1801 2_000_000_000,
1802 3,
1803 Default::default(),
1804 header.payload_commitment(),
1805 header.builder_commitment().clone(),
1806 ns_table.clone(),
1807 header.fee_merkle_tree_root(),
1808 header.block_merkle_tree_root(),
1809 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
1810 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
1811 }),
1812 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
1813 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
1814 }),
1815 vec![FeeInfo {
1816 amount: 0.into(),
1817 account: fee_account,
1818 }],
1819 Default::default(),
1820 None,
1821 Version { major: 0, minor: 2 },
1822 None,
1823 );
1824
1825 let serialized = serde_json::to_string(&v2_header).unwrap();
1826 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1827 assert_eq!(v2_header, deserialized);
1828
1829 let v3_header = Header::create(
1830 genesis.instance_state.chain_config,
1831 1,
1832 2,
1833 2_000_000_000,
1834 3,
1835 Default::default(),
1836 header.payload_commitment(),
1837 header.builder_commitment().clone(),
1838 ns_table.clone(),
1839 header.fee_merkle_tree_root(),
1840 header.block_merkle_tree_root(),
1841 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
1842 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
1843 }),
1844 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
1845 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
1846 }),
1847 vec![FeeInfo {
1848 amount: 0.into(),
1849 account: fee_account,
1850 }],
1851 Default::default(),
1852 None,
1853 Version { major: 0, minor: 3 },
1854 None,
1855 );
1856
1857 let serialized = serde_json::to_string(&v3_header).unwrap();
1858 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
1859 assert_eq!(v3_header, deserialized);
1860
1861 let v1_bytes = BincodeSerializer::<StaticVersion<0, 1>>::serialize(&v1_header).unwrap();
1862 let deserialized: Header =
1863 BincodeSerializer::<StaticVersion<0, 1>>::deserialize(&v1_bytes).unwrap();
1864 assert_eq!(v1_header, deserialized);
1865
1866 let v2_bytes = BincodeSerializer::<StaticVersion<0, 2>>::serialize(&v2_header).unwrap();
1867 let deserialized: Header =
1868 BincodeSerializer::<StaticVersion<0, 2>>::deserialize(&v2_bytes).unwrap();
1869 assert_eq!(v2_header, deserialized);
1870
1871 let v3_bytes = BincodeSerializer::<StaticVersion<0, 3>>::serialize(&v3_header).unwrap();
1872 let deserialized: Header =
1873 BincodeSerializer::<StaticVersion<0, 3>>::deserialize(&v3_bytes).unwrap();
1874 assert_eq!(v3_header, deserialized);
1875 }
1876}