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