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