1use std::{collections::HashSet, fmt};
2
3use alloy::primitives::{B256, Keccak256};
4use anyhow::{Context, bail, ensure};
5use ark_serialize::CanonicalSerialize;
6use base64::{Engine, prelude::BASE64_STANDARD};
7use committable::{Commitment, Committable, RawCommitmentBuilder};
8use either::Either;
9use hotshot_query_service_types::{
10 HeightIndexed, availability::QueryableHeader, explorer::traits::ExplorerHeader,
11};
12#[cfg(feature = "node")]
13use hotshot_types::utils::is_ge_epoch_root;
14use hotshot_types::{
15 data::{EpochNumber, VidCommitment, ViewNumber, vid_commitment},
16 light_client::LightClientState,
17 traits::{
18 BlockPayload, EncodeBytes, ValidatedState as _,
19 block_contents::{BlockHeader, BuilderFee, GENESIS_VID_NUM_STORAGE_NODES},
20 election::{Membership, MembershipSnapshot},
21 node_implementation::NodeType,
22 signature_key::BuilderSignatureKey,
23 },
24 utils::{BuilderCommitment, epoch_from_block_number, is_last_block},
25};
26use jf_merkle_tree_compat::{AppendableMerkleTreeScheme, MerkleCommitment, MerkleTreeScheme};
27use serde::{
28 Deserialize, Deserializer, Serialize, Serializer,
29 de::{self, MapAccess, SeqAccess, Visitor},
30};
31use serde_json::{Map, Value};
32use thiserror::Error;
33#[cfg(feature = "node")]
34use time::OffsetDateTime;
35use vbs::version::Version;
36use versions::EPOCH_REWARD_VERSION;
37#[cfg(feature = "node")]
38use versions::{DRB_AND_HEADER_UPGRADE_VERSION, EPOCH_VERSION};
39
40use super::{
41 instance_state::NodeState, state::ValidatedState, v0_1::IterableFeeInfo, v0_3::ChainConfig,
42};
43use crate::{
44 BlockMerkleCommitment, FeeAccount, FeeAmount, FeeInfo, FeeMerkleCommitment, Header,
45 L1BlockInfo, L1Snapshot, Leaf2, NamespaceId, NsIndex, NsTable, PayloadByteLen, SeqTypes,
46 TimestampMillis,
47 eth_signature_key::BuilderSignature,
48 v0::{
49 header::{EitherOrVersion, VersionedHeader},
50 impls::StakeTableHash,
51 },
52 v0_1::{self},
53 v0_2,
54 v0_3::{
55 self, REWARD_MERKLE_TREE_V1_HEIGHT, RewardAmount, RewardMerkleCommitmentV1,
56 RewardMerkleTreeV1,
57 },
58 v0_4::{self, RewardAccountV2, RewardMerkleCommitmentV2},
59 v0_5::{self, LeaderCounts, MAX_VALIDATORS},
60 v0_6::{self},
61 v0_7,
62};
63#[cfg(feature = "node")]
64use crate::{UpgradeType, v0::impls::distribute_block_reward};
65
66impl v0_1::Header {
67 pub(crate) fn commit(&self) -> Commitment<Header> {
68 let mut bmt_bytes = vec![];
69 self.block_merkle_tree_root
70 .serialize_with_mode(&mut bmt_bytes, ark_serialize::Compress::Yes)
71 .unwrap();
72 let mut fmt_bytes = vec![];
73 self.fee_merkle_tree_root
74 .serialize_with_mode(&mut fmt_bytes, ark_serialize::Compress::Yes)
75 .unwrap();
76
77 RawCommitmentBuilder::new(&Self::tag())
78 .field("chain_config", self.chain_config.commit())
79 .u64_field("height", self.height)
80 .u64_field("timestamp", self.timestamp)
81 .u64_field("l1_head", self.l1_head)
82 .optional("l1_finalized", &self.l1_finalized)
83 .constant_str("payload_commitment")
84 .fixed_size_bytes(self.payload_commitment.as_ref())
85 .constant_str("builder_commitment")
86 .fixed_size_bytes(self.builder_commitment.as_ref())
87 .field("ns_table", self.ns_table.commit())
88 .var_size_field("block_merkle_tree_root", &bmt_bytes)
89 .var_size_field("fee_merkle_tree_root", &fmt_bytes)
90 .field("fee_info", self.fee_info.commit())
91 .finalize()
92 }
93}
94
95impl Committable for Header {
96 fn commit(&self) -> Commitment<Self> {
97 match self {
98 Self::V1(header) => header.commit(),
99 Self::V2(fields) => RawCommitmentBuilder::new(&Self::tag())
100 .u64_field("version_major", 0)
101 .u64_field("version_minor", 2)
102 .field("fields", fields.commit())
103 .finalize(),
104 Self::V3(fields) => RawCommitmentBuilder::new(&Self::tag())
105 .u64_field("version_major", 0)
106 .u64_field("version_minor", 3)
107 .field("fields", fields.commit())
108 .finalize(),
109 Self::V4(fields) => RawCommitmentBuilder::new(&Self::tag())
110 .u64_field("version_major", 0)
111 .u64_field("version_minor", 4)
112 .field("fields", fields.commit())
113 .finalize(),
114 Self::V5(fields) => RawCommitmentBuilder::new(&Self::tag())
115 .u64_field("version_major", 0)
116 .u64_field("version_minor", 5)
117 .field("fields", fields.commit())
118 .finalize(),
119 Self::V6(fields) => RawCommitmentBuilder::new(&Self::tag())
120 .u64_field("version_major", 0)
121 .u64_field("version_minor", 6)
122 .field("fields", fields.commit())
123 .finalize(),
124 Self::V7(fields) => RawCommitmentBuilder::new(&Self::tag())
125 .u64_field("version_major", 0)
126 .u64_field("version_minor", 7)
127 .field("fields", fields.commit())
128 .finalize(),
129 }
130 }
131
132 fn tag() -> String {
133 "BLOCK".into()
136 }
137}
138
139impl Serialize for Header {
140 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141 where
142 S: Serializer,
143 {
144 match self {
145 Self::V1(header) => header.serialize(serializer),
146 Self::V2(fields) => VersionedHeader {
147 version: EitherOrVersion::Version(Version { major: 0, minor: 2 }),
148 fields: fields.clone(),
149 }
150 .serialize(serializer),
151 Self::V3(fields) => VersionedHeader {
152 version: EitherOrVersion::Version(Version { major: 0, minor: 3 }),
153 fields: fields.clone(),
154 }
155 .serialize(serializer),
156 Self::V4(fields) => VersionedHeader {
157 version: EitherOrVersion::Version(Version { major: 0, minor: 4 }),
158 fields: fields.clone(),
159 }
160 .serialize(serializer),
161 Self::V5(fields) => VersionedHeader {
162 version: EitherOrVersion::Version(Version { major: 0, minor: 5 }),
163 fields: fields.clone(),
164 }
165 .serialize(serializer),
166 Self::V6(fields) => VersionedHeader {
167 version: EitherOrVersion::Version(Version { major: 0, minor: 6 }),
168 fields: fields.clone(),
169 }
170 .serialize(serializer),
171 Self::V7(fields) => VersionedHeader {
172 version: EitherOrVersion::Version(Version { major: 0, minor: 7 }),
173 fields: fields.clone(),
174 }
175 .serialize(serializer),
176 }
177 }
178}
179
180impl<'de> Deserialize<'de> for Header {
181 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182 where
183 D: Deserializer<'de>,
184 {
185 struct HeaderVisitor;
186
187 impl<'de> Visitor<'de> for HeaderVisitor {
188 type Value = Header;
189
190 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
191 formatter.write_str("Header")
192 }
193
194 fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
195 where
196 V: SeqAccess<'de>,
197 {
198 let chain_config_or_version: EitherOrVersion = seq
199 .next_element()?
200 .ok_or_else(|| de::Error::missing_field("chain_config"))?;
201
202 match chain_config_or_version {
203 EitherOrVersion::Left(cfg) => Ok(Header::V1(
206 v0_1::Header::deserialize_with_chain_config(cfg.into(), seq)?,
207 )),
208 EitherOrVersion::Right(commit) => Ok(Header::V1(
209 v0_1::Header::deserialize_with_chain_config(commit.into(), seq)?,
210 )),
211 EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2(
214 seq.next_element()?
215 .ok_or_else(|| de::Error::missing_field("fields"))?,
216 )),
217 EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3(
218 seq.next_element()?
219 .ok_or_else(|| de::Error::missing_field("fields"))?,
220 )),
221 EitherOrVersion::Version(Version { major: 0, minor: 4 }) => Ok(Header::V4(
222 seq.next_element()?
223 .ok_or_else(|| de::Error::missing_field("fields"))?,
224 )),
225 EitherOrVersion::Version(Version { major: 0, minor: 5 }) => Ok(Header::V5(
226 seq.next_element()?
227 .ok_or_else(|| de::Error::missing_field("fields"))?,
228 )),
229 EitherOrVersion::Version(Version { major: 0, minor: 6 }) => Ok(Header::V6(
230 seq.next_element()?
231 .ok_or_else(|| de::Error::missing_field("fields"))?,
232 )),
233 EitherOrVersion::Version(Version { major: 0, minor: 7 }) => Ok(Header::V7(
234 seq.next_element()?
235 .ok_or_else(|| de::Error::missing_field("fields"))?,
236 )),
237 EitherOrVersion::Version(v) => {
238 Err(serde::de::Error::custom(format!("invalid version {v:?}")))
239 },
240 }
241 }
242
243 fn visit_map<V>(self, mut map: V) -> Result<Header, V::Error>
244 where
245 V: MapAccess<'de>,
246 {
247 let mut serde_map: Map<String, Value> = Map::new();
249
250 while let Some(key) = map.next_key::<String>()? {
251 serde_map.insert(key.trim().to_owned(), map.next_value()?);
252 }
253
254 if let Some(v) = serde_map.get("version") {
255 let fields = serde_map
256 .get("fields")
257 .ok_or_else(|| de::Error::missing_field("fields"))?;
258
259 let version = serde_json::from_value::<EitherOrVersion>(v.clone())
260 .map_err(de::Error::custom)?;
261 let result = match version {
262 EitherOrVersion::Version(Version { major: 0, minor: 2 }) => Ok(Header::V2(
263 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
264 )),
265 EitherOrVersion::Version(Version { major: 0, minor: 3 }) => Ok(Header::V3(
266 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
267 )),
268 EitherOrVersion::Version(Version { major: 0, minor: 4 }) => Ok(Header::V4(
269 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
270 )),
271 EitherOrVersion::Version(Version { major: 0, minor: 5 }) => Ok(Header::V5(
272 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
273 )),
274 EitherOrVersion::Version(Version { major: 0, minor: 6 }) => Ok(Header::V6(
275 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
276 )),
277 EitherOrVersion::Version(Version { major: 0, minor: 7 }) => Ok(Header::V7(
278 serde_json::from_value(fields.clone()).map_err(de::Error::custom)?,
279 )),
280 EitherOrVersion::Version(v) => {
281 Err(de::Error::custom(format!("invalid version {v:?}")))
282 },
283 chain_config => Err(de::Error::custom(format!(
284 "expected version, found chain_config {chain_config:?}"
285 ))),
286 };
287 return result;
288 }
289
290 Ok(Header::V1(
291 serde_json::from_value(serde_map.into()).map_err(de::Error::custom)?,
292 ))
293 }
294 }
295
296 let fields: &[&str] = &[
311 "fields",
312 "chain_config",
313 "version",
314 "height",
315 "timestamp",
316 "l1_head",
317 "l1_finalized",
318 "payload_commitment",
319 "builder_commitment",
320 "ns_table",
321 "block_merkle_tree_root",
322 "fee_merkle_tree_root",
323 "fee_info",
324 "builder_signature",
325 ];
326
327 deserializer.deserialize_struct("Header", fields, HeaderVisitor)
328 }
329}
330
331impl Header {
332 pub fn version(&self) -> Version {
333 match self {
334 Self::V1(_) => Version { major: 0, minor: 1 },
335 Self::V2(_) => Version { major: 0, minor: 2 },
336 Self::V3(_) => Version { major: 0, minor: 3 },
337 Self::V4(_) => Version { major: 0, minor: 4 },
338 Self::V5(_) => Version { major: 0, minor: 5 },
339 Self::V6(_) => Version { major: 0, minor: 6 },
340 Self::V7(_) => Version { major: 0, minor: 7 },
341 }
342 }
343 #[allow(clippy::too_many_arguments)]
344 pub(crate) fn create(
345 chain_config: ChainConfig,
346 height: u64,
347 timestamp: u64,
348 timestamp_millis: u64,
349 l1_head: u64,
350 l1_finalized: Option<L1BlockInfo>,
351 payload_commitment: VidCommitment,
352 builder_commitment: BuilderCommitment,
353 ns_table: NsTable,
354 fee_merkle_tree_root: FeeMerkleCommitment,
355 block_merkle_tree_root: BlockMerkleCommitment,
356 reward_merkle_tree_root_v1: RewardMerkleCommitmentV1,
357 reward_merkle_tree_root_v2: RewardMerkleCommitmentV2,
358 fee_info: Vec<FeeInfo>,
359 builder_signature: Vec<BuilderSignature>,
360 total_reward_distributed: Option<RewardAmount>,
361 version: Version,
362 next_stake_table_hash: Option<StakeTableHash>,
363 leader_counts: Option<LeaderCounts>,
364 ) -> Self {
365 assert!(!fee_info.is_empty(), "Invalid fee_info length: 0");
367
368 match (version.major, version.minor) {
369 (0, 1) => Self::V1(v0_1::Header {
370 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
371 chain_config,
372 )),
373 height,
374 timestamp,
375 l1_head,
376 l1_finalized,
377 payload_commitment,
378 builder_commitment,
379 ns_table,
380 block_merkle_tree_root,
381 fee_merkle_tree_root,
382 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
384 }),
385 (0, 2) => Self::V2(v0_2::Header {
386 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
387 chain_config,
388 )),
389 height,
390 timestamp,
391 l1_head,
392 l1_finalized,
393 payload_commitment,
394 builder_commitment,
395 ns_table,
396 block_merkle_tree_root,
397 fee_merkle_tree_root,
398 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
400 }),
401 (0, 3) => Self::V3(v0_3::Header {
402 chain_config: chain_config.into(),
403 height,
404 timestamp,
405 l1_head,
406 l1_finalized,
407 payload_commitment,
408 builder_commitment,
409 ns_table,
410 block_merkle_tree_root,
411 fee_merkle_tree_root,
412 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
414 reward_merkle_tree_root: reward_merkle_tree_root_v1,
415 }),
416 (0, 4) => Self::V4(v0_4::Header {
417 chain_config: chain_config.into(),
418 height,
419 timestamp,
420 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
421 l1_head,
422 l1_finalized,
423 payload_commitment,
424 builder_commitment,
425 ns_table,
426 block_merkle_tree_root,
427 fee_merkle_tree_root,
428 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
430 reward_merkle_tree_root: reward_merkle_tree_root_v2,
431 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
432 next_stake_table_hash,
433 }),
434 (0, 5) => Self::V5(v0_5::Header {
435 chain_config: chain_config.into(),
436 height,
437 timestamp,
438 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
439 l1_head,
440 l1_finalized,
441 payload_commitment,
442 builder_commitment,
443 ns_table,
444 block_merkle_tree_root,
445 fee_merkle_tree_root,
446 fee_info: fee_info[0], builder_signature: builder_signature.first().copied(),
448 reward_merkle_tree_root: reward_merkle_tree_root_v2,
449 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
450 next_stake_table_hash,
451 leader_counts: leader_counts.expect("leader_counts required for V5 header"),
452 }),
453 (0, 6) => Self::V6(v0_6::Header {
454 chain_config: chain_config.into(),
455 height,
456 timestamp,
457 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
458 l1_head,
459 l1_finalized,
460 payload_commitment,
461 builder_commitment,
462 ns_table,
463 block_merkle_tree_root,
464 fee_merkle_tree_root,
465 fee_info: fee_info[0],
466 builder_signature: builder_signature.first().copied(),
467 reward_merkle_tree_root: reward_merkle_tree_root_v2,
468 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
469 next_stake_table_hash,
470 leader_counts: leader_counts.expect("leader_counts required for V6 header"),
471 }),
472 (0, 7) => Self::V7(v0_7::Header {
474 chain_config: chain_config.into(),
475 height,
476 timestamp,
477 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
478 l1_head,
479 l1_finalized,
480 payload_commitment,
481 ns_table,
482 block_merkle_tree_root,
483 fee_merkle_tree_root,
484 fee_info: fee_info[0],
485 builder_signature: builder_signature.first().copied(),
486 reward_merkle_tree_root: reward_merkle_tree_root_v2,
487 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
488 next_stake_table_hash,
489 leader_counts: leader_counts.expect("leader_counts required for V7 header"),
490 }),
491 _ => panic!("invalid version: {version}"),
495 }
496 }
497
498 pub fn next_stake_table_hash(&self) -> Option<StakeTableHash> {
499 match self {
500 Self::V4(fields) => fields.next_stake_table_hash,
501 Self::V5(fields) | Self::V6(fields) => fields.next_stake_table_hash,
502 Self::V7(fields) => fields.next_stake_table_hash,
503 _ => None,
504 }
505 }
506
507 pub fn leader_counts(&self) -> Option<&LeaderCounts> {
510 match self {
511 Self::V5(fields) | Self::V6(fields) => Some(&fields.leader_counts),
512 Self::V7(fields) => Some(&fields.leader_counts),
513 _ => None,
514 }
515 }
516
517 pub fn set_next_stake_table_hash(&mut self, hash: StakeTableHash) -> bool {
518 match self {
519 Self::V4(fields) => {
520 fields.next_stake_table_hash = Some(hash);
521 true
522 },
523 Self::V5(fields) | Self::V6(fields) => {
524 fields.next_stake_table_hash = Some(hash);
525 true
526 },
527 Self::V7(fields) => {
528 fields.next_stake_table_hash = Some(hash);
529 true
530 },
531 _ => false,
532 }
533 }
534}
535
536macro_rules! field {
538 ($obj:ident.$name:ident) => {
539 match $obj {
540 Self::V1(data) => &data.$name,
541 Self::V2(data) => &data.$name,
542 Self::V3(data) => &data.$name,
543 Self::V4(data) => &data.$name,
544 Self::V5(data) => &data.$name,
545 Self::V6(data) => &data.$name,
546 Self::V7(data) => &data.$name,
547 }
548 };
549}
550
551macro_rules! field_mut {
552 ($obj:ident.$name:ident) => {
553 match $obj {
554 Self::V1(data) => &mut data.$name,
555 Self::V2(data) => &mut data.$name,
556 Self::V3(data) => &mut data.$name,
557 Self::V4(data) => &mut data.$name,
558 Self::V5(data) => &mut data.$name,
559 Self::V6(data) => &mut data.$name,
560 Self::V7(data) => &mut data.$name,
561 }
562 };
563}
564
565impl Header {
566 #[cfg_attr(not(feature = "node"), allow(dead_code))]
567 #[allow(clippy::too_many_arguments)]
568 fn from_info(
569 payload_commitment: VidCommitment,
570 builder_commitment: BuilderCommitment,
571 ns_table: NsTable,
572 parent_leaf: &Leaf2,
573 mut l1: L1Snapshot,
574 l1_deposits: &[FeeInfo],
575 builder_fee: Vec<BuilderFee<SeqTypes>>,
576 mut timestamp: u64,
577 mut timestamp_millis: u64,
578 mut state: ValidatedState,
579 chain_config: ChainConfig,
580 version: Version,
581 total_reward_distributed: Option<RewardAmount>,
582 next_stake_table_hash: Option<StakeTableHash>,
583 leader_counts: Option<LeaderCounts>,
584 ) -> anyhow::Result<Self> {
585 ensure!(
586 version.major == 0,
587 "Invalid major version {}",
588 version.major
589 );
590
591 let parent_header = parent_leaf.block_header();
593 let height = parent_header.height() + 1;
594
595 if timestamp < parent_header.timestamp() {
599 tracing::warn!(
600 "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
601 parent_header.timestamp()
602 );
603 timestamp = parent_header.timestamp();
604 }
605
606 if timestamp_millis < parent_header.timestamp_millis() {
607 tracing::warn!(
608 "Espresso timestamp {timestamp} behind parent {}, local clock may be out of sync",
609 parent_header.timestamp_millis()
610 );
611 timestamp_millis = parent_header.timestamp_millis();
612 }
613
614 if l1.head < parent_header.l1_head() {
617 tracing::warn!(
618 "L1 head {} behind parent {}, L1 client may be lagging",
619 l1.head,
620 parent_header.l1_head()
621 );
622 l1.head = parent_header.l1_head();
623 }
624 if l1.finalized < parent_header.l1_finalized() {
625 tracing::warn!(
626 "L1 finalized {:?} behind parent {:?}, L1 client may be lagging",
627 l1.finalized,
628 parent_header.l1_finalized()
629 );
630 l1.finalized = parent_header.l1_finalized();
631 }
632
633 if let Some(l1_block) = &l1.finalized {
636 let l1_timestamp = l1_block.timestamp.to::<u64>();
637 if timestamp < l1_timestamp {
638 tracing::warn!(
639 "Espresso timestamp {timestamp} behind L1 timestamp {l1_timestamp}, local \
640 clock may be out of sync"
641 );
642 timestamp = l1_timestamp;
643 }
644
645 let l1_timestamp_millis = l1_timestamp * 1_000;
646
647 if timestamp_millis < l1_timestamp_millis {
648 tracing::warn!(
649 "Espresso timestamp_millis {timestamp_millis} behind L1 timestamp \
650 {l1_timestamp_millis}, local clock may be out of sync"
651 );
652 timestamp_millis = l1_timestamp_millis;
653 }
654 }
655
656 state
657 .block_merkle_tree
658 .push(parent_header.commit())
659 .context("missing blocks frontier")?;
660 let block_merkle_tree_root = state.block_merkle_tree.commitment();
661
662 for fee_info in l1_deposits {
664 state
665 .insert_fee_deposit(*fee_info)
666 .context(format!("missing fee account {}", fee_info.account()))?;
667 }
668
669 if version < versions::NEW_PROTOCOL_VERSION {
671 for BuilderFee {
672 fee_account,
673 fee_signature,
674 fee_amount,
675 } in &builder_fee
676 {
677 ensure!(
678 fee_account.validate_fee_signature(fee_signature, *fee_amount, &ns_table)
679 || fee_account.validate_fee_signature_with_vid_commitment(
680 fee_signature,
681 *fee_amount,
682 &ns_table,
683 &payload_commitment
684 ),
685 "invalid builder signature"
686 );
687
688 let fee_info = FeeInfo::new(*fee_account, *fee_amount);
689 state
690 .charge_fee(fee_info, chain_config.fee_recipient)
691 .context(format!("invalid builder fee {fee_info:?}"))?;
692 }
693 }
694
695 let fee_info = FeeInfo::from_builder_fees(builder_fee.clone());
696
697 let builder_signature: Vec<BuilderSignature> =
698 builder_fee.iter().map(|e| e.fee_signature).collect();
699
700 let fee_merkle_tree_root = state.fee_merkle_tree.commitment();
701
702 let header = match (version.major, version.minor) {
703 (0, 1) => Self::V1(v0_1::Header {
704 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
705 chain_config,
706 )),
707 height,
708 timestamp,
709 l1_head: l1.head,
710 l1_finalized: l1.finalized,
711 payload_commitment,
712 builder_commitment,
713 ns_table,
714 block_merkle_tree_root,
715 fee_merkle_tree_root,
716 fee_info: fee_info[0],
717 builder_signature: builder_signature.first().copied(),
718 }),
719 (0, 2) => Self::V2(v0_2::Header {
720 chain_config: v0_1::ResolvableChainConfig::from(v0_1::ChainConfig::from(
721 chain_config,
722 )),
723 height,
724 timestamp,
725 l1_head: l1.head,
726 l1_finalized: l1.finalized,
727 payload_commitment,
728 builder_commitment,
729 ns_table,
730 block_merkle_tree_root,
731 fee_merkle_tree_root,
732 fee_info: fee_info[0],
733 builder_signature: builder_signature.first().copied(),
734 }),
735 (0, 3) => Self::V3(v0_3::Header {
736 chain_config: chain_config.into(),
737 height,
738 timestamp,
739 l1_head: l1.head,
740 l1_finalized: l1.finalized,
741 payload_commitment,
742 builder_commitment,
743 ns_table,
744 block_merkle_tree_root,
745 fee_merkle_tree_root,
746 reward_merkle_tree_root: state.reward_merkle_tree_v1.commitment(),
747 fee_info: fee_info[0],
748 builder_signature: builder_signature.first().copied(),
749 }),
750 (0, 4) => Self::V4(v0_4::Header {
751 chain_config: chain_config.into(),
752 height,
753 timestamp,
754 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
755 l1_head: l1.head,
756 l1_finalized: l1.finalized,
757 payload_commitment,
758 builder_commitment,
759 ns_table,
760 block_merkle_tree_root,
761 fee_merkle_tree_root,
762 reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
763 fee_info: fee_info[0],
764 builder_signature: builder_signature.first().copied(),
765 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
766 next_stake_table_hash,
767 }),
768 (0, 5) => Self::V5(v0_5::Header {
769 chain_config: chain_config.into(),
770 height,
771 timestamp,
772 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
773 l1_head: l1.head,
774 l1_finalized: l1.finalized,
775 payload_commitment,
776 builder_commitment,
777 ns_table,
778 block_merkle_tree_root,
779 fee_merkle_tree_root,
780 reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
781 fee_info: fee_info[0],
782 builder_signature: builder_signature.first().copied(),
783 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
784 next_stake_table_hash,
785 leader_counts: leader_counts.expect("leader_counts is required for V5 headers"),
786 }),
787 (0, 6) => Self::V6(v0_6::Header {
788 chain_config: chain_config.into(),
789 height,
790 timestamp,
791 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
792 l1_head: l1.head,
793 l1_finalized: l1.finalized,
794 payload_commitment,
795 builder_commitment,
796 ns_table,
797 block_merkle_tree_root,
798 fee_merkle_tree_root,
799 reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
800 fee_info: fee_info[0],
801 builder_signature: builder_signature.first().copied(),
802 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
803 next_stake_table_hash,
804 leader_counts: leader_counts.expect("leader_counts is required for V6 headers"),
805 }),
806 (0, 7) => Self::V7(v0_7::Header {
808 chain_config: chain_config.into(),
809 height,
810 timestamp,
811 timestamp_millis: TimestampMillis::from_millis(timestamp_millis),
812 l1_head: l1.head,
813 l1_finalized: l1.finalized,
814 payload_commitment,
815 ns_table,
816 block_merkle_tree_root,
817 fee_merkle_tree_root,
818 reward_merkle_tree_root: state.reward_merkle_tree_v2.commitment(),
819 fee_info: fee_info[0],
820 builder_signature: builder_signature.first().copied(),
821 total_reward_distributed: total_reward_distributed.unwrap_or_default(),
822 next_stake_table_hash,
823 leader_counts: leader_counts.expect("leader_counts is required for V7 headers"),
824 }),
825 _ => panic!("invalid version: {version}"),
829 };
830 Ok(header)
831 }
832
833 pub fn calculate_leader_counts(
840 parent_header: &Header,
841 height: u64,
842 leader_index: usize,
843 epoch_height: u64,
844 ) -> LeaderCounts {
845 let mut leader_counts = [0u16; MAX_VALIDATORS];
846
847 let parent_counts = parent_header.leader_counts();
849
850 let is_epoch_start = is_last_block(height.saturating_sub(1), epoch_height);
852
853 if is_epoch_start || parent_counts.is_none() {
854 leader_counts[leader_index] = 1;
855 } else if let Some(parent_counts) = parent_counts {
856 leader_counts = *parent_counts;
857 leader_counts[leader_index] += 1;
858 }
859
860 leader_counts
861 }
862
863 pub async fn get_leader_index(
871 version: Version,
872 height: u64,
873 view_number: u64,
874 instance_state: &NodeState,
875 ) -> anyhow::Result<Option<usize>> {
876 if version < EPOCH_REWARD_VERSION {
878 return Ok(None);
879 }
880
881 let epoch_height = instance_state
882 .epoch_height
883 .context("epoch height not in instance state for V6")?;
884 let epoch = EpochNumber::new(epoch_from_block_number(height, epoch_height));
885
886 let coordinator = instance_state.coordinator.clone();
887 coordinator
888 .membership_for_epoch(Some(epoch))
889 .map_err(|e| anyhow::anyhow!("failed to get epoch membership: {e}"))?;
890
891 let snapshot = coordinator
893 .membership()
894 .snapshot(epoch)
895 .with_context(|| format!("no committee for epoch {epoch:?}"))?;
896
897 let leader = snapshot
898 .leader(ViewNumber::new(view_number))
899 .with_context(|| format!("leader for epoch {epoch:?} not found"))?;
900
901 let index = snapshot.validator_index(&leader).with_context(|| {
902 format!("Leader {leader} not found in stake table for epoch {epoch}")
903 })?;
904
905 Ok(Some(index))
906 }
907
908 pub async fn handle_epoch_rewards(
935 height: u64,
936 leader_counts: &LeaderCounts,
937 instance_state: &NodeState,
938 validated_state: &mut ValidatedState,
939 header_root: Option<RewardMerkleCommitmentV2>,
940 ) -> anyhow::Result<(RewardAmount, HashSet<RewardAccountV2>)> {
941 let epoch_height = instance_state
942 .epoch_height
943 .context("epoch_height not configured")?;
944 ensure!(epoch_height > 0, "epoch_height must be > 0");
945 let epoch = EpochNumber::new(epoch_from_block_number(height, epoch_height));
946 let prev_epoch = EpochNumber::new(*epoch - 1);
947 let coordinator = instance_state.coordinator.clone();
948 let first_epoch = coordinator.membership().first_epoch();
949
950 if epoch <= first_epoch + 1 {
952 return Ok((RewardAmount::default(), HashSet::new()));
953 }
954
955 let mut reward_calculator = instance_state.epoch_rewards_calculator.lock().await;
956
957 if epoch > first_epoch + 2 && !reward_calculator.is_calculating(prev_epoch) {
960 tracing::info!(%epoch, %prev_epoch, "triggering catchup reward calculation");
961 reward_calculator.spawn_background_task(
962 prev_epoch,
963 epoch_height,
964 validated_state.reward_merkle_tree_v2.clone(),
965 instance_state.clone(),
966 coordinator.clone(),
967 None,
968 );
969 }
970
971 if !is_last_block(height, epoch_height) {
972 return Ok((RewardAmount::default(), HashSet::new()));
973 }
974
975 tracing::info!(%height, %epoch, %prev_epoch, "epoch boundary: applying rewards");
976
977 let result = reward_calculator.get_result(prev_epoch).await.transpose()?;
980
981 let (epoch_rewards_applied, changed_accounts) = if let Some(result) = result {
982 tracing::info!(
983 %epoch,
984 prev_epoch = %result.epoch,
985 total = %result.total_distributed.0,
986 "applying epoch rewards"
987 );
988 validated_state.reward_merkle_tree_v2 = result.reward_tree.clone();
989 (result.total_distributed, result.changed_accounts)
990 } else if prev_epoch <= first_epoch + 1 {
991 (RewardAmount::default(), HashSet::new())
993 } else {
994 tracing::warn!(
996 %epoch,
997 %prev_epoch,
998 "missing epoch rewards at boundary, spawning calculation now"
999 );
1000
1001 reward_calculator.spawn_background_task(
1002 prev_epoch,
1003 epoch_height,
1004 validated_state.reward_merkle_tree_v2.clone(),
1005 instance_state.clone(),
1006 coordinator.clone(),
1007 None,
1008 );
1009
1010 let result = reward_calculator
1011 .get_result(prev_epoch)
1012 .await
1013 .context(format!("no pending reward task for epoch {prev_epoch}"))?
1014 .context(format!(
1015 "failed to calculate missing rewards for epoch {prev_epoch}"
1016 ))?;
1017
1018 tracing::info!(
1019 %epoch,
1020 %prev_epoch,
1021 total = %result.total_distributed.0,
1022 "applied delayed epoch rewards"
1023 );
1024
1025 validated_state.reward_merkle_tree_v2 = result.reward_tree.clone();
1026 (result.total_distributed, result.changed_accounts)
1027 };
1028
1029 let calculated_root = validated_state.reward_merkle_tree_v2.commitment();
1031 if let Some(header_root) = header_root
1032 && calculated_root != header_root
1033 {
1034 reward_calculator.discard(prev_epoch);
1037 bail!(
1038 "reward merkle tree root mismatch, using new merkle tree. Header root: \
1039 {header_root}, Calculated root: {calculated_root}"
1040 );
1041 }
1042
1043 reward_calculator.spawn_background_task(
1046 epoch,
1047 epoch_height,
1048 validated_state.reward_merkle_tree_v2.clone(),
1049 instance_state.clone(),
1050 coordinator,
1051 Some(*leader_counts),
1052 );
1053
1054 Ok((epoch_rewards_applied, changed_accounts))
1055 }
1056
1057 #[cfg_attr(not(feature = "node"), allow(dead_code))]
1058 async fn get_chain_config(
1059 validated_state: &ValidatedState,
1060 instance_state: &NodeState,
1061 ) -> anyhow::Result<ChainConfig> {
1062 let validated_cf = validated_state.chain_config;
1063 let instance_cf = instance_state.chain_config;
1064
1065 if validated_cf.commit() == instance_cf.commit() {
1066 return Ok(instance_cf);
1067 }
1068
1069 match validated_cf.resolve() {
1070 Some(cf) => Ok(cf),
1071 None => {
1072 tracing::info!("fetching chain config {} from peers", validated_cf.commit());
1073
1074 instance_state
1075 .state_catchup
1076 .as_ref()
1077 .fetch_chain_config(validated_cf.commit())
1078 .await
1079 },
1080 }
1081 }
1082}
1083
1084impl Header {
1085 pub fn chain_config(&self) -> v0_3::ResolvableChainConfig {
1087 match self {
1088 Self::V1(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
1089 Self::V2(fields) => v0_3::ResolvableChainConfig::from(&fields.chain_config),
1090 Self::V3(fields) => fields.chain_config,
1091 Self::V4(fields) => fields.chain_config,
1092 Self::V5(fields) | Self::V6(fields) => fields.chain_config,
1093 Self::V7(fields) => fields.chain_config,
1094 }
1095 }
1096
1097 pub fn height(&self) -> u64 {
1098 *field!(self.height)
1099 }
1100
1101 pub fn height_mut(&mut self) -> &mut u64 {
1102 &mut *field_mut!(self.height)
1103 }
1104
1105 pub fn timestamp_internal(&self) -> u64 {
1106 match self {
1107 Self::V1(fields) => fields.timestamp,
1108 Self::V2(fields) => fields.timestamp,
1109 Self::V3(fields) => fields.timestamp,
1110 Self::V4(fields) => fields.timestamp,
1111 Self::V5(fields) | Self::V6(fields) => fields.timestamp,
1112 Self::V7(fields) => fields.timestamp,
1113 }
1114 }
1115
1116 pub fn timestamp_millis_internal(&self) -> u64 {
1117 match self {
1118 Self::V1(fields) => fields.timestamp * 1_000,
1119 Self::V2(fields) => fields.timestamp * 1_000,
1120 Self::V3(fields) => fields.timestamp * 1_000,
1121 Self::V4(fields) => fields.timestamp_millis.u64(),
1122 Self::V5(fields) | Self::V6(fields) => fields.timestamp_millis.u64(),
1123 Self::V7(fields) => fields.timestamp_millis.u64(),
1124 }
1125 }
1126
1127 pub fn set_timestamp(&mut self, timestamp: u64, timestamp_millis: u64) {
1128 match self {
1129 Self::V1(fields) => {
1130 fields.timestamp = timestamp;
1131 },
1132 Self::V2(fields) => {
1133 fields.timestamp = timestamp;
1134 },
1135 Self::V3(fields) => {
1136 fields.timestamp = timestamp;
1137 },
1138 Self::V4(fields) => {
1139 fields.timestamp = timestamp;
1140 fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
1141 },
1142 Self::V5(fields) | Self::V6(fields) => {
1143 fields.timestamp = timestamp;
1144 fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
1145 },
1146 Self::V7(fields) => {
1147 fields.timestamp = timestamp;
1148 fields.timestamp_millis = TimestampMillis::from_millis(timestamp_millis);
1149 },
1150 };
1151 }
1152
1153 pub fn l1_head(&self) -> u64 {
1182 *field!(self.l1_head)
1183 }
1184
1185 pub fn l1_head_mut(&mut self) -> &mut u64 {
1186 &mut *field_mut!(self.l1_head)
1187 }
1188
1189 pub fn l1_finalized(&self) -> Option<L1BlockInfo> {
1204 *field!(self.l1_finalized)
1205 }
1206
1207 pub fn l1_finalized_mut(&mut self) -> &mut Option<L1BlockInfo> {
1208 &mut *field_mut!(self.l1_finalized)
1209 }
1210
1211 pub fn payload_commitment(&self) -> VidCommitment {
1212 *field!(self.payload_commitment)
1213 }
1214
1215 pub fn payload_commitment_mut(&mut self) -> &mut VidCommitment {
1216 &mut *field_mut!(self.payload_commitment)
1217 }
1218
1219 pub fn builder_commitment(&self) -> Option<&BuilderCommitment> {
1221 match self {
1222 Self::V1(data) => Some(&data.builder_commitment),
1223 Self::V2(data) => Some(&data.builder_commitment),
1224 Self::V3(data) => Some(&data.builder_commitment),
1225 Self::V4(data) => Some(&data.builder_commitment),
1226 Self::V5(data) | Self::V6(data) => Some(&data.builder_commitment),
1227 Self::V7(_) => None,
1228 }
1229 }
1230
1231 pub fn ns_table(&self) -> &NsTable {
1232 field!(self.ns_table)
1233 }
1234
1235 pub fn ns_table_mut(&mut self) -> &mut NsTable {
1236 &mut *field_mut!(self.ns_table)
1237 }
1238
1239 pub fn block_merkle_tree_root(&self) -> BlockMerkleCommitment {
1241 *field!(self.block_merkle_tree_root)
1242 }
1243
1244 pub fn block_merkle_tree_root_mut(&mut self) -> &mut BlockMerkleCommitment {
1245 &mut *field_mut!(self.block_merkle_tree_root)
1246 }
1247
1248 pub fn fee_merkle_tree_root(&self) -> FeeMerkleCommitment {
1250 *field!(self.fee_merkle_tree_root)
1251 }
1252
1253 pub fn fee_merkle_tree_root_mut(&mut self) -> &mut FeeMerkleCommitment {
1254 &mut *field_mut!(self.fee_merkle_tree_root)
1255 }
1256
1257 pub fn fee_info(&self) -> Vec<FeeInfo> {
1259 match self {
1260 Self::V1(fields) => vec![fields.fee_info],
1261 Self::V2(fields) => vec![fields.fee_info],
1262 Self::V3(fields) => vec![fields.fee_info],
1263 Self::V4(fields) => vec![fields.fee_info],
1264 Self::V5(fields) | Self::V6(fields) => vec![fields.fee_info],
1265 Self::V7(fields) => vec![fields.fee_info],
1266 }
1267 }
1268
1269 pub fn reward_merkle_tree_root(
1270 &self,
1271 ) -> Either<RewardMerkleCommitmentV1, RewardMerkleCommitmentV2> {
1272 let empty_reward_merkle_tree = RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT);
1273 match self {
1274 Self::V1(_) => Either::Left(empty_reward_merkle_tree.commitment()),
1275 Self::V2(_) => Either::Left(empty_reward_merkle_tree.commitment()),
1276 Self::V3(fields) => Either::Left(fields.reward_merkle_tree_root),
1277 Self::V4(fields) => Either::Right(fields.reward_merkle_tree_root),
1278 Self::V5(fields) | Self::V6(fields) => Either::Right(fields.reward_merkle_tree_root),
1279 Self::V7(fields) => Either::Right(fields.reward_merkle_tree_root),
1280 }
1281 }
1282
1283 pub fn builder_signature(&self) -> Vec<BuilderSignature> {
1292 match self {
1293 Self::V1(fields) => fields.builder_signature.as_slice().to_vec(),
1298 Self::V2(fields) => fields.builder_signature.as_slice().to_vec(),
1299 Self::V3(fields) => fields.builder_signature.as_slice().to_vec(),
1300 Self::V4(fields) => fields.builder_signature.as_slice().to_vec(),
1301 Self::V5(fields) | Self::V6(fields) => fields.builder_signature.as_slice().to_vec(),
1302 Self::V7(fields) => fields.builder_signature.as_slice().to_vec(),
1303 }
1304 }
1305
1306 pub fn total_reward_distributed(&self) -> Option<RewardAmount> {
1307 match self {
1308 Self::V1(_) | Self::V2(_) | Self::V3(_) => None,
1309 Self::V4(fields) => Some(fields.total_reward_distributed),
1310 Self::V5(fields) | Self::V6(fields) => Some(fields.total_reward_distributed),
1311 Self::V7(fields) => Some(fields.total_reward_distributed),
1312 }
1313 }
1314}
1315
1316#[derive(Debug, Error)]
1317#[error("Invalid Block Header {msg}")]
1318pub struct InvalidBlockHeader {
1319 msg: String,
1320}
1321impl InvalidBlockHeader {
1322 fn new(msg: String) -> Self {
1323 Self { msg }
1324 }
1325}
1326
1327impl From<anyhow::Error> for InvalidBlockHeader {
1328 fn from(err: anyhow::Error) -> Self {
1329 Self::new(format!("{err:#}"))
1330 }
1331}
1332
1333const L1_HEAD_MARGIN: u64 = 3;
1336
1337fn proposal_l1_head(local_head: u64, parent_l1_head: u64, finalized: Option<u64>) -> u64 {
1342 local_head
1343 .saturating_sub(L1_HEAD_MARGIN)
1344 .max(parent_l1_head)
1345 .max(finalized.unwrap_or(0))
1346}
1347
1348impl L1Snapshot {
1349 fn for_proposal(&self, parent_l1_head: u64) -> Self {
1352 if self.head < parent_l1_head {
1354 tracing::warn!(
1355 local = self.head,
1356 parent = parent_l1_head,
1357 "local L1 head behind parent, L1 client may be lagging"
1358 );
1359 }
1360 Self {
1361 head: proposal_l1_head(self.head, parent_l1_head, self.finalized.map(|f| f.number)),
1362 ..*self
1363 }
1364 }
1365}
1366
1367impl BlockHeader<SeqTypes> for Header {
1368 type Error = InvalidBlockHeader;
1369
1370 #[tracing::instrument(
1371 skip_all,
1372 fields(
1373 node_id = instance_state.node_id,
1374 view = ?parent_leaf.view_number(),
1375 height = parent_leaf.block_header().height(),
1376 ),
1377 )]
1378 #[tracing::instrument(
1379 skip_all,
1380 fields(
1381 height = parent_leaf.block_header().block_number() + 1,
1382 parent_view = ?parent_leaf.view_number(),
1383 payload_commitment,
1384 version,
1385 )
1386 )]
1387 #[cfg_attr(not(feature = "node"), allow(unused_variables))]
1388 async fn new(
1389 parent_state: &ValidatedState,
1390 instance_state: &NodeState,
1391 parent_leaf: &Leaf2,
1392 payload_commitment: VidCommitment,
1393 builder_commitment: BuilderCommitment,
1394 metadata: <<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
1395 builder_fee: BuilderFee<SeqTypes>,
1396 version: Version,
1397 view_number: u64,
1398 ) -> Result<Self, Self::Error> {
1399 #[cfg(not(feature = "node"))]
1401 {
1402 unimplemented!("proposing a block header requires the node feature");
1403 }
1404 #[cfg(feature = "node")]
1405 {
1406 tracing::info!("preparing to propose header");
1407
1408 let height = parent_leaf.height();
1409 let view = parent_leaf.view_number();
1410
1411 let mut validated_state = parent_state.clone();
1412
1413 let chain_config = if version > instance_state.current_version {
1414 match instance_state.upgrades.get(&version) {
1415 Some(upgrade) => match upgrade.upgrade_type {
1416 UpgradeType::Fee { chain_config } => chain_config,
1417 UpgradeType::Epoch { chain_config } => chain_config,
1418 UpgradeType::DrbAndHeader { chain_config } => chain_config,
1419 UpgradeType::NewProtocol { chain_config } => chain_config,
1420 UpgradeType::EpochReward { chain_config } => chain_config,
1421 UpgradeType::LargeBlock { chain_config } => chain_config,
1422 },
1423 None => Header::get_chain_config(&validated_state, instance_state).await?,
1424 }
1425 } else {
1426 Header::get_chain_config(&validated_state, instance_state).await?
1427 };
1428
1429 validated_state.chain_config = chain_config.into();
1430
1431 let l1_snapshot = instance_state
1433 .l1_client
1434 .snapshot()
1435 .await
1436 .for_proposal(parent_leaf.block_header().l1_head());
1437 let l1_deposits = if version >= versions::NEW_PROTOCOL_VERSION {
1439 Vec::new()
1440 } else if let (Some(addr), Some(block_info)) =
1441 (chain_config.fee_contract, l1_snapshot.finalized)
1442 {
1443 instance_state
1444 .l1_client
1445 .get_finalized_deposits(
1446 addr,
1447 parent_leaf
1448 .block_header()
1449 .l1_finalized()
1450 .map(|block_info| block_info.number),
1451 block_info.number,
1452 )
1453 .await
1454 } else {
1455 vec![]
1456 };
1457 let missing_accounts = parent_state.forgotten_accounts(
1461 [builder_fee.fee_account, chain_config.fee_recipient]
1462 .into_iter()
1463 .chain(l1_deposits.iter().map(|info| info.account())),
1464 );
1465 if !missing_accounts.is_empty() {
1466 tracing::warn!(
1467 height,
1468 ?view,
1469 ?missing_accounts,
1470 "fetching missing accounts from peers"
1471 );
1472
1473 let missing_account_proofs = instance_state
1475 .state_catchup
1476 .as_ref()
1477 .fetch_accounts(
1478 instance_state,
1479 height,
1480 view,
1481 parent_state.fee_merkle_tree.commitment(),
1482 missing_accounts,
1483 )
1484 .await?;
1485
1486 for proof in missing_account_proofs.iter() {
1488 proof
1489 .remember(&mut validated_state.fee_merkle_tree)
1490 .context("remembering fee account")?;
1491 }
1492 }
1493
1494 if validated_state.need_to_fetch_blocks_mt_frontier() {
1496 tracing::warn!(height, ?view, "fetching block frontier from peers");
1497 instance_state
1498 .state_catchup
1499 .as_ref()
1500 .remember_blocks_merkle_tree(
1501 instance_state,
1502 height,
1503 view,
1504 &mut validated_state.block_merkle_tree,
1505 )
1506 .await
1507 .context("remembering block proof")?;
1508 }
1509
1510 let (leader_counts, total_reward_distributed) = if version >= EPOCH_REWARD_VERSION {
1512 let epoch_height = instance_state
1513 .epoch_height
1514 .context("epoch_height not configured for V6")?;
1515 let new_height = height + 1;
1517 let leader_index =
1518 Header::get_leader_index(version, new_height, view_number, instance_state)
1519 .await?
1520 .context("leader_index must be present for V6")?;
1521
1522 let leader_counts = Header::calculate_leader_counts(
1523 parent_leaf.block_header(),
1524 new_height,
1525 leader_index,
1526 epoch_height,
1527 );
1528
1529 let (epoch_rewards_applied, _changed_accounts) = Header::handle_epoch_rewards(
1530 new_height,
1531 &leader_counts,
1532 instance_state,
1533 &mut validated_state,
1534 None,
1535 )
1536 .await?;
1537
1538 let parent_total = parent_leaf
1542 .block_header()
1543 .total_reward_distributed()
1544 .unwrap_or_default();
1545
1546 (
1547 Some(leader_counts),
1548 Some(RewardAmount(parent_total.0 + epoch_rewards_applied.0)),
1549 )
1550 } else if version >= EPOCH_VERSION {
1551 let total = distribute_block_reward(
1553 instance_state,
1554 &mut validated_state,
1555 parent_leaf,
1556 ViewNumber::new(view_number),
1557 version,
1558 )
1559 .await?
1560 .map(|r| r.total_distributed());
1561
1562 (None, total)
1563 } else {
1564 (None, None)
1565 };
1566
1567 let mut next_stake_table_hash = None;
1568
1569 if version >= DRB_AND_HEADER_UPGRADE_VERSION {
1570 let epoch_height = instance_state
1571 .epoch_height
1572 .context("epoch height not in instance state")?;
1573 if is_ge_epoch_root(height + 1, epoch_height) {
1574 let coordinator = instance_state.coordinator.clone();
1575 let first_epoch = coordinator.membership().first_epoch();
1576
1577 let epoch = EpochNumber::new(epoch_from_block_number(height + 1, epoch_height));
1578
1579 if epoch > first_epoch {
1581 let epoch_membership = coordinator
1582 .stake_table_for_epoch(Some(epoch + 1))
1583 .map_err(|e| {
1584 anyhow::anyhow!("failed to get epoch membership: {e}")
1585 })?;
1586 next_stake_table_hash = Some(
1587 epoch_membership
1588 .stake_table_hash()
1589 .context("failed to get next stake table hash")?,
1590 );
1591 }
1592 }
1593 }
1594
1595 let now = OffsetDateTime::now_utc();
1596
1597 let timestamp = now.unix_timestamp() as u64;
1598 let timestamp_millis = TimestampMillis::from_time(&now).u64();
1599
1600 Ok(Self::from_info(
1601 payload_commitment,
1602 builder_commitment,
1603 metadata,
1604 parent_leaf,
1605 l1_snapshot,
1606 &l1_deposits,
1607 vec![builder_fee],
1608 timestamp,
1609 timestamp_millis,
1610 validated_state,
1611 chain_config,
1612 version,
1613 total_reward_distributed,
1614 next_stake_table_hash,
1615 leader_counts,
1616 )?)
1617 }
1618 }
1619
1620 fn genesis(
1621 instance_state: &NodeState,
1622 payload: <SeqTypes as NodeType>::BlockPayload,
1623 metadata: &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata,
1624 _: Version,
1625 ) -> Self {
1626 let payload_bytes = payload.encode();
1627 let builder_commitment = payload.builder_commitment(metadata);
1628
1629 let vid_commitment_version = instance_state.genesis_version;
1630
1631 let payload_commitment = vid_commitment(
1632 &payload_bytes,
1633 &metadata.encode(),
1634 GENESIS_VID_NUM_STORAGE_NODES,
1635 vid_commitment_version,
1636 );
1637
1638 let ValidatedState {
1639 fee_merkle_tree,
1640 block_merkle_tree,
1641 reward_merkle_tree_v1,
1642 reward_merkle_tree_v2,
1643 ..
1644 } = ValidatedState::genesis(instance_state).0;
1645 let block_merkle_tree_root = block_merkle_tree.commitment();
1646 let fee_merkle_tree_root = fee_merkle_tree.commitment();
1647 let reward_merkle_tree_root = reward_merkle_tree_v2.commitment();
1648
1649 let time = instance_state.genesis_header.timestamp;
1650
1651 let timestamp = time.unix_timestamp();
1652 let timestamp_millis = time.unix_timestamp_millis();
1653
1654 Self::create(
1657 instance_state.genesis_header.chain_config,
1658 0,
1659 timestamp,
1660 timestamp_millis,
1661 instance_state
1662 .l1_genesis
1663 .map(|block| block.number)
1664 .unwrap_or_default(),
1665 instance_state.l1_genesis,
1666 payload_commitment,
1667 builder_commitment.clone(),
1668 metadata.clone(),
1669 fee_merkle_tree_root,
1670 block_merkle_tree_root,
1671 reward_merkle_tree_v1.commitment(),
1672 reward_merkle_tree_root,
1673 vec![FeeInfo::genesis()],
1674 vec![],
1675 None,
1676 instance_state.genesis_version,
1677 None,
1678 Some([0; 100]),
1679 )
1680 }
1681
1682 fn timestamp(&self) -> u64 {
1683 self.timestamp_internal()
1684 }
1685
1686 fn timestamp_millis(&self) -> u64 {
1687 self.timestamp_millis_internal()
1688 }
1689
1690 fn block_number(&self) -> u64 {
1691 self.height()
1692 }
1693
1694 fn version(&self) -> Version {
1695 self.version()
1696 }
1697
1698 fn payload_commitment(&self) -> VidCommitment {
1699 self.payload_commitment()
1700 }
1701
1702 fn metadata(
1703 &self,
1704 ) -> &<<SeqTypes as NodeType>::BlockPayload as BlockPayload<SeqTypes>>::Metadata {
1705 self.ns_table()
1706 }
1707
1708 fn builder_commitment(&self) -> BuilderCommitment {
1711 self.builder_commitment()
1712 .cloned()
1713 .unwrap_or_else(BuilderCommitment::empty)
1714 }
1715
1716 fn get_light_client_state(&self, view: ViewNumber) -> anyhow::Result<LightClientState> {
1717 let mut block_comm_root_bytes = vec![];
1718 self.block_merkle_tree_root()
1719 .serialize_compressed(&mut block_comm_root_bytes)?;
1720
1721 Ok(LightClientState {
1722 view_number: view.u64(),
1723 block_height: self.height(),
1724 block_comm_root: hotshot_types::light_client::hash_bytes_to_field(
1725 &block_comm_root_bytes,
1726 )?,
1727 })
1728 }
1729
1730 fn auth_root(&self) -> anyhow::Result<B256> {
1731 let Either::Right(reward_merkle_tree_root) = self.reward_merkle_tree_root() else {
1733 return Ok(B256::ZERO);
1734 };
1735
1736 let placeholder_1 = B256::ZERO;
1738 let placeholder_2 = B256::ZERO;
1739 let placeholder_3 = B256::ZERO;
1740 let placeholder_4 = B256::ZERO;
1741 let placeholder_5 = B256::ZERO;
1742 let placeholder_6 = B256::ZERO;
1743 let placeholder_7 = B256::ZERO;
1744
1745 let mut hasher = Keccak256::new();
1746
1747 let digest = reward_merkle_tree_root.digest();
1749 hasher.update(digest.0);
1750 hasher.update(placeholder_1);
1751 hasher.update(placeholder_2);
1752 hasher.update(placeholder_3);
1753 hasher.update(placeholder_4);
1754 hasher.update(placeholder_5);
1755 hasher.update(placeholder_6);
1756 hasher.update(placeholder_7);
1757
1758 Ok(hasher.finalize())
1759 }
1760}
1761
1762impl HeightIndexed for Header {
1763 fn height(&self) -> u64 {
1764 self.height()
1765 }
1766}
1767
1768impl QueryableHeader<SeqTypes> for Header {
1769 type NamespaceId = NamespaceId;
1770 type NamespaceIndex = NsIndex;
1771
1772 fn namespace_id(&self, i: &NsIndex) -> Option<NamespaceId> {
1773 self.ns_table().read_ns_id(i)
1774 }
1775
1776 fn namespace_size(&self, i: &NsIndex, payload_size: usize) -> u64 {
1777 self.ns_table()
1778 .ns_range(i, &PayloadByteLen(payload_size))
1779 .byte_len()
1780 .0 as u64
1781 }
1782
1783 fn ns_table(&self) -> String {
1784 BASE64_STANDARD.encode(&self.ns_table().bytes)
1785 }
1786}
1787
1788impl ExplorerHeader<SeqTypes> for Header {
1789 type BalanceAmount = FeeAmount;
1790 type WalletAddress = Vec<FeeAccount>;
1791 type ProposerId = Vec<FeeAccount>;
1792
1793 fn proposer_id(&self) -> Self::ProposerId {
1795 self.fee_info().accounts()
1796 }
1797
1798 fn fee_info_account(&self) -> Self::WalletAddress {
1799 self.fee_info().accounts()
1800 }
1801
1802 fn fee_info_balance(&self) -> Self::BalanceAmount {
1803 self.fee_info().amount().unwrap()
1805 }
1806
1807 fn reward_balance(&self) -> Self::BalanceAmount {
1813 FeeAmount::from(0)
1814 }
1815
1816 fn namespace_ids(&self) -> Vec<NamespaceId> {
1817 self.ns_table()
1818 .iter()
1819 .map(|i| self.ns_table().read_ns_id_unchecked(&i))
1820 .collect()
1821 }
1822}
1823
1824#[cfg(test)]
1825mod test_headers {
1826 use std::sync::Arc;
1827
1828 use alloy::{
1829 node_bindings::Anvil,
1830 primitives::{Address, U256},
1831 };
1832 use hotshot_query_service::testing::mocks::MOCK_UPGRADE;
1833 use hotshot_types::traits::signature_key::BuilderSignatureKey;
1834 use v0_1::{BlockMerkleTree, FeeMerkleTree, L1Client};
1835 use vbs::{BinarySerializer, bincode_serializer::BincodeSerializer, version::StaticVersion};
1836 use versions::version;
1837
1838 use super::*;
1839 use crate::{
1840 Leaf,
1841 eth_signature_key::EthKeyPair,
1842 mock::MockStateCatchup,
1843 v0_3::{REWARD_MERKLE_TREE_V1_HEIGHT, RewardAccountV1, RewardAmount},
1844 v0_4::{REWARD_MERKLE_TREE_V2_HEIGHT, RewardAccountV2, RewardMerkleTreeV2},
1845 };
1846
1847 #[derive(Debug, Default)]
1848 #[must_use]
1849 struct TestCase {
1850 parent_timestamp: u64,
1852 parent_timestamp_millis: u64,
1853 parent_l1_head: u64,
1854 parent_l1_finalized: Option<L1BlockInfo>,
1855
1856 l1_head: u64,
1858 l1_finalized: Option<L1BlockInfo>,
1859 timestamp: u64,
1860 timestamp_millis: u64,
1861 l1_deposits: Vec<FeeInfo>,
1862
1863 expected_timestamp: u64,
1865 expected_timestamp_millis: u64,
1866 expected_l1_head: u64,
1867 expected_l1_finalized: Option<L1BlockInfo>,
1868 }
1869
1870 impl TestCase {
1871 async fn run(self) {
1872 assert!(self.expected_timestamp >= self.parent_timestamp);
1874 assert!(self.expected_timestamp_millis >= self.parent_timestamp_millis);
1875 assert!(self.expected_l1_head >= self.parent_l1_head);
1876 assert!(self.expected_l1_finalized >= self.parent_l1_finalized);
1877
1878 let genesis = GenesisForTest::default().await;
1879 let mut parent = genesis.header.clone();
1880 parent.set_timestamp(self.parent_timestamp, self.parent_timestamp_millis);
1881 *parent.l1_head_mut() = self.parent_l1_head;
1882 *parent.l1_finalized_mut() = self.parent_l1_finalized;
1883
1884 let mut parent_leaf = genesis.leaf.clone();
1885 *parent_leaf.block_header_mut() = parent.clone();
1886
1887 let block_merkle_tree =
1888 BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap();
1889
1890 let fee_info = FeeInfo::genesis();
1891 let fee_merkle_tree = FeeMerkleTree::from_kv_set(
1892 20,
1893 Vec::from([(fee_info.account(), fee_info.amount())]),
1894 )
1895 .unwrap();
1896
1897 let reward_account_v1 = RewardAccountV1::default();
1898 let reward_account = RewardAccountV2::default();
1899 let reward_amount = RewardAmount::default();
1900 let reward_merkle_tree_v2 =
1901 RewardMerkleTreeV2::from_kv_set(20, Vec::from([(reward_account, reward_amount)]))
1902 .unwrap();
1903
1904 let reward_merkle_tree_v1 = RewardMerkleTreeV1::from_kv_set(
1905 20,
1906 Vec::from([(reward_account_v1, reward_amount)]),
1907 )
1908 .unwrap();
1909
1910 let mut validated_state = ValidatedState {
1911 block_merkle_tree: block_merkle_tree.clone(),
1912 fee_merkle_tree,
1913 reward_merkle_tree_v2,
1914 reward_merkle_tree_v1,
1915 chain_config: genesis.instance_state.chain_config.into(),
1916 };
1917
1918 let (fee_account, fee_key) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
1919 let fee_amount = 0;
1920 let fee_signature =
1921 FeeAccount::sign_fee(&fee_key, fee_amount, &genesis.ns_table).unwrap();
1922
1923 let header = Header::from_info(
1924 genesis.header.payload_commitment(),
1925 genesis.header.builder_commitment().unwrap().clone(),
1926 genesis.ns_table,
1927 &parent_leaf,
1928 L1Snapshot {
1929 head: self.l1_head,
1930 finalized: self.l1_finalized,
1931 },
1932 &self.l1_deposits,
1933 vec![BuilderFee {
1934 fee_account,
1935 fee_amount,
1936 fee_signature,
1937 }],
1938 self.timestamp,
1939 self.timestamp_millis,
1940 validated_state.clone(),
1941 genesis.instance_state.chain_config,
1942 version(0, 1),
1943 None, None, None, )
1947 .unwrap();
1948 assert_eq!(header.height(), parent.height() + 1);
1949 assert_eq!(header.timestamp(), self.expected_timestamp);
1950 assert_eq!(header.timestamp_millis(), self.expected_timestamp_millis);
1951 assert_eq!(header.l1_head(), self.expected_l1_head);
1952 assert_eq!(header.l1_finalized(), self.expected_l1_finalized);
1953
1954 for fee_info in self.l1_deposits {
1956 validated_state.insert_fee_deposit(fee_info).unwrap();
1957 }
1958 assert_eq!(
1959 validated_state.fee_merkle_tree.commitment(),
1960 header.fee_merkle_tree_root(),
1961 );
1962
1963 assert_eq!(
1964 block_merkle_tree,
1965 BlockMerkleTree::from_elems(Some(32), Vec::<Commitment<Header>>::new()).unwrap()
1966 );
1967 }
1968 }
1969
1970 fn l1_block(number: u64) -> L1BlockInfo {
1971 L1BlockInfo {
1972 number,
1973 ..Default::default()
1974 }
1975 }
1976
1977 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1978 async fn test_new_header() {
1979 TestCase::default().run().await
1981 }
1982
1983 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1984 async fn test_new_header_advance_timestamp() {
1985 TestCase {
1986 timestamp: 1,
1987 timestamp_millis: 1_000,
1988 expected_timestamp: 1,
1989 expected_timestamp_millis: 1_000,
1990 ..Default::default()
1991 }
1992 .run()
1993 .await
1994 }
1995
1996 #[test_log::test(tokio::test(flavor = "multi_thread"))]
1997 async fn test_new_header_advance_l1_block() {
1998 TestCase {
1999 parent_l1_head: 0,
2000 parent_l1_finalized: Some(l1_block(0)),
2001
2002 l1_head: 1,
2003 l1_finalized: Some(l1_block(1)),
2004
2005 expected_l1_head: 1,
2006 expected_l1_finalized: Some(l1_block(1)),
2007
2008 ..Default::default()
2009 }
2010 .run()
2011 .await
2012 }
2013
2014 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2015 async fn test_new_header_advance_l1_finalized_from_none() {
2016 TestCase {
2017 l1_finalized: Some(l1_block(1)),
2018 expected_l1_finalized: Some(l1_block(1)),
2019 ..Default::default()
2020 }
2021 .run()
2022 .await
2023 }
2024
2025 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2026 async fn test_new_header_timestamp_behind_finalized_l1_block() {
2027 let l1_finalized = Some(L1BlockInfo {
2028 number: 1,
2029 timestamp: U256::from(1),
2030 ..Default::default()
2031 });
2032 TestCase {
2033 l1_head: 1,
2034 l1_finalized,
2035 timestamp: 0,
2036 timestamp_millis: 0,
2037
2038 expected_l1_head: 1,
2039 expected_l1_finalized: l1_finalized,
2040 expected_timestamp: 1,
2041 expected_timestamp_millis: 1_000,
2042
2043 ..Default::default()
2044 }
2045 .run()
2046 .await
2047 }
2048
2049 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2050 async fn test_new_header_timestamp_behind() {
2051 TestCase {
2052 parent_timestamp: 1,
2053 parent_timestamp_millis: 1_000,
2054 timestamp: 0,
2055 timestamp_millis: 0,
2056 expected_timestamp: 1,
2057 expected_timestamp_millis: 1_000,
2058
2059 ..Default::default()
2060 }
2061 .run()
2062 .await
2063 }
2064
2065 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2066 async fn test_new_header_l1_head_behind() {
2067 TestCase {
2068 parent_l1_head: 1,
2069 l1_head: 0,
2070 expected_l1_head: 1,
2071
2072 ..Default::default()
2073 }
2074 .run()
2075 .await
2076 }
2077
2078 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2079 async fn test_new_header_l1_finalized_behind_some() {
2080 TestCase {
2081 parent_l1_finalized: Some(l1_block(1)),
2082 l1_finalized: Some(l1_block(0)),
2083 expected_l1_finalized: Some(l1_block(1)),
2084
2085 ..Default::default()
2086 }
2087 .run()
2088 .await
2089 }
2090
2091 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2092 async fn test_new_header_l1_finalized_behind_none() {
2093 TestCase {
2094 parent_l1_finalized: Some(l1_block(0)),
2095 l1_finalized: None,
2096 expected_l1_finalized: Some(l1_block(0)),
2097
2098 ..Default::default()
2099 }
2100 .run()
2101 .await
2102 }
2103
2104 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2105 async fn test_new_header_deposits_one() {
2106 TestCase {
2107 l1_deposits: vec![FeeInfo::new(Address::default(), 1)],
2108 ..Default::default()
2109 }
2110 .run()
2111 .await
2112 }
2113
2114 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2115 async fn test_new_header_deposits_many() {
2116 TestCase {
2117 l1_deposits: [
2118 (Address::default(), 1),
2119 (Address::default(), 2),
2120 (Address::random(), 3),
2121 ]
2122 .iter()
2123 .map(|(address, amount)| FeeInfo::new(*address, *amount))
2124 .collect(),
2125 ..Default::default()
2126 }
2127 .run()
2128 .await
2129 }
2130
2131 struct GenesisForTest {
2132 pub instance_state: NodeState,
2133 pub validated_state: ValidatedState,
2134 pub leaf: Leaf2,
2135 pub header: Header,
2136 pub ns_table: NsTable,
2137 }
2138
2139 impl GenesisForTest {
2140 async fn default() -> Self {
2141 let instance_state = NodeState::mock();
2142 let validated_state = ValidatedState::genesis(&instance_state).0;
2143 let leaf: Leaf2 = Leaf::genesis(&validated_state, &instance_state, MOCK_UPGRADE.base)
2144 .await
2145 .into();
2146 let header = leaf.block_header().clone();
2147 let ns_table = leaf.block_payload().unwrap().ns_table().clone();
2148 Self {
2149 instance_state,
2150 validated_state,
2151 leaf,
2152 header,
2153 ns_table,
2154 }
2155 }
2156 }
2157
2158 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2159 async fn test_proposal_validation_success() {
2160 let anvil = Anvil::new().block_time(1u64).spawn();
2161 let mut genesis_state = NodeState::mock()
2162 .with_l1(L1Client::new(vec![anvil.endpoint_url()]).expect("Failed to create L1 client"))
2163 .with_current_version(version(0, 1));
2164
2165 let genesis = GenesisForTest::default().await;
2166
2167 let mut parent_state = genesis.validated_state.clone();
2168
2169 let mut block_merkle_tree = parent_state.block_merkle_tree.clone();
2170 let fee_merkle_tree = parent_state.fee_merkle_tree.clone();
2171
2172 block_merkle_tree.push(genesis.header.commit()).unwrap();
2174 let block_merkle_tree_root = block_merkle_tree.commitment();
2175 let fee_merkle_tree_root = fee_merkle_tree.commitment();
2176 parent_state.block_merkle_tree = block_merkle_tree.clone();
2177 parent_state.fee_merkle_tree = fee_merkle_tree.clone();
2178
2179 let mut parent_header = genesis.header.clone();
2180 *parent_header.block_merkle_tree_root_mut() = block_merkle_tree_root;
2181 *parent_header.fee_merkle_tree_root_mut() = fee_merkle_tree_root;
2182
2183 let mut parent_leaf = genesis.leaf.clone();
2184 *parent_leaf.block_header_mut() = parent_header.clone();
2185
2186 let forgotten_state = parent_state.forget();
2188 genesis_state.state_catchup = Arc::new(MockStateCatchup::from_iter([(
2189 parent_leaf.view_number(),
2190 Arc::new(parent_state.clone()),
2191 )]));
2192 let key_pair = EthKeyPair::for_test();
2197 let fee_amount = 0u64;
2198 let payload_commitment = parent_header.payload_commitment();
2199 let builder_commitment = parent_header.builder_commitment().unwrap().clone();
2200 let ns_table = genesis.ns_table;
2201 let fee_signature = FeeAccount::sign_fee(&key_pair, fee_amount, &ns_table).unwrap();
2202 let builder_fee = BuilderFee {
2203 fee_amount,
2204 fee_account: key_pair.fee_account(),
2205 fee_signature,
2206 };
2207 let proposal = Header::new(
2208 &forgotten_state,
2209 &genesis_state,
2210 &parent_leaf,
2211 payload_commitment,
2212 builder_commitment.clone(),
2213 ns_table,
2214 builder_fee,
2215 version(0, 1),
2216 *parent_leaf.view_number() + 1,
2217 )
2218 .await
2219 .unwrap();
2220
2221 let mut proposal_state = parent_state.clone();
2222 for fee_info in genesis_state
2223 .l1_client
2224 .get_finalized_deposits(Address::default(), None, 0)
2225 .await
2226 {
2227 proposal_state.insert_fee_deposit(fee_info).unwrap();
2228 }
2229
2230 let mut block_merkle_tree = proposal_state.block_merkle_tree.clone();
2231 block_merkle_tree.push(proposal.commit()).unwrap();
2232
2233 let _proposal_state = proposal_state
2234 .apply_header(
2235 &genesis_state,
2236 &genesis_state.state_catchup,
2237 &parent_leaf,
2238 &proposal,
2239 version(0, 1),
2240 parent_leaf.view_number() + 1,
2241 )
2242 .await
2243 .unwrap()
2244 .0;
2245
2246 }
2259
2260 #[test_log::test]
2261 fn verify_builder_signature() {
2262 let message = ";)";
2264 let mut commitment = [0u8; 32];
2265 commitment[..message.len()].copy_from_slice(message.as_bytes());
2266
2267 let key = FeeAccount::generated_from_seed_indexed([0; 32], 0).1;
2268 let signature = FeeAccount::sign_builder_message(&key, &commitment).unwrap();
2269 assert!(
2270 key.fee_account()
2271 .validate_builder_signature(&signature, &commitment)
2272 );
2273 }
2274
2275 #[test_log::test(tokio::test(flavor = "multi_thread"))]
2276 async fn test_versioned_header_serialization() {
2277 let genesis = GenesisForTest::default().await;
2278 let header = genesis.header.clone();
2279 let ns_table = genesis.ns_table;
2280
2281 let (fee_account, _) = FeeAccount::generated_from_seed_indexed([0; 32], 0);
2282
2283 let v1_header = Header::create(
2284 genesis.instance_state.chain_config,
2285 1,
2286 2,
2287 2_000_000_000,
2288 3,
2289 Default::default(),
2290 header.payload_commitment(),
2291 header.builder_commitment().unwrap().clone(),
2292 ns_table.clone(),
2293 header.fee_merkle_tree_root(),
2294 header.block_merkle_tree_root(),
2295 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
2296 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
2297 }),
2298 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
2299 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
2300 }),
2301 vec![FeeInfo {
2302 amount: 0.into(),
2303 account: fee_account,
2304 }],
2305 Default::default(),
2306 None,
2307 version(0, 1),
2308 None,
2309 None, );
2311
2312 let serialized = serde_json::to_string(&v1_header).unwrap();
2313 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
2314 assert_eq!(v1_header, deserialized);
2315
2316 let v2_header = Header::create(
2317 genesis.instance_state.chain_config,
2318 1,
2319 2,
2320 2_000_000_000,
2321 3,
2322 Default::default(),
2323 header.payload_commitment(),
2324 header.builder_commitment().unwrap().clone(),
2325 ns_table.clone(),
2326 header.fee_merkle_tree_root(),
2327 header.block_merkle_tree_root(),
2328 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
2329 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
2330 }),
2331 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
2332 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
2333 }),
2334 vec![FeeInfo {
2335 amount: 0.into(),
2336 account: fee_account,
2337 }],
2338 Default::default(),
2339 None,
2340 version(0, 2),
2341 None,
2342 None, );
2344
2345 let serialized = serde_json::to_string(&v2_header).unwrap();
2346 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
2347 assert_eq!(v2_header, deserialized);
2348
2349 let v3_header = Header::create(
2350 genesis.instance_state.chain_config,
2351 1,
2352 2,
2353 2_000_000_000,
2354 3,
2355 Default::default(),
2356 header.payload_commitment(),
2357 header.builder_commitment().unwrap().clone(),
2358 ns_table.clone(),
2359 header.fee_merkle_tree_root(),
2360 header.block_merkle_tree_root(),
2361 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
2362 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
2363 }),
2364 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
2365 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
2366 }),
2367 vec![FeeInfo {
2368 amount: 0.into(),
2369 account: fee_account,
2370 }],
2371 Default::default(),
2372 None,
2373 version(0, 3),
2374 None,
2375 None, );
2377
2378 let serialized = serde_json::to_string(&v3_header).unwrap();
2379 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
2380 assert_eq!(v3_header, deserialized);
2381
2382 let v1_bytes = BincodeSerializer::<StaticVersion<0, 1>>::serialize(&v1_header).unwrap();
2383 let deserialized: Header =
2384 BincodeSerializer::<StaticVersion<0, 1>>::deserialize(&v1_bytes).unwrap();
2385 assert_eq!(v1_header, deserialized);
2386
2387 let v2_bytes = BincodeSerializer::<StaticVersion<0, 2>>::serialize(&v2_header).unwrap();
2388 let deserialized: Header =
2389 BincodeSerializer::<StaticVersion<0, 2>>::deserialize(&v2_bytes).unwrap();
2390 assert_eq!(v2_header, deserialized);
2391
2392 let v3_bytes = BincodeSerializer::<StaticVersion<0, 3>>::serialize(&v3_header).unwrap();
2393 let deserialized: Header =
2394 BincodeSerializer::<StaticVersion<0, 3>>::deserialize(&v3_bytes).unwrap();
2395 assert_eq!(v3_header, deserialized);
2396
2397 let v7_header = Header::create(
2398 genesis.instance_state.chain_config,
2399 1,
2400 2,
2401 2_000_000_000,
2402 3,
2403 Default::default(),
2404 header.payload_commitment(),
2405 header.builder_commitment().unwrap().clone(),
2406 ns_table.clone(),
2407 header.fee_merkle_tree_root(),
2408 header.block_merkle_tree_root(),
2409 header.reward_merkle_tree_root().left().unwrap_or_else(|| {
2410 RewardMerkleTreeV1::new(REWARD_MERKLE_TREE_V1_HEIGHT).commitment()
2411 }),
2412 header.reward_merkle_tree_root().right().unwrap_or_else(|| {
2413 RewardMerkleTreeV2::new(REWARD_MERKLE_TREE_V2_HEIGHT).commitment()
2414 }),
2415 vec![FeeInfo {
2416 amount: 0.into(),
2417 account: fee_account,
2418 }],
2419 Default::default(),
2420 None,
2421 version(0, 7),
2422 None,
2423 Some([0; MAX_VALIDATORS]),
2424 );
2425 assert_eq!(v7_header.version(), version(0, 7));
2426
2427 let serialized = serde_json::to_string(&v7_header).unwrap();
2428 let deserialized: Header = serde_json::from_str(&serialized).unwrap();
2429 assert_eq!(v7_header, deserialized);
2430
2431 let v7_bytes = BincodeSerializer::<StaticVersion<0, 7>>::serialize(&v7_header).unwrap();
2432 let deserialized: Header =
2433 BincodeSerializer::<StaticVersion<0, 7>>::deserialize(&v7_bytes).unwrap();
2434 assert_eq!(v7_header, deserialized);
2435 }
2436
2437 #[test]
2438 fn test_proposal_l1_head() {
2439 let cases = [
2441 (100, 50, None, 97),
2443 (100, 99, None, 99),
2445 (100, 120, None, 120),
2447 (100, 100, None, 100),
2449 (2, 0, None, 0),
2451 (10, 0, Some(8), 8),
2454 (10, 5, Some(3), 7),
2456 (10, 9, Some(9), 9),
2458 ];
2459 for (local_head, parent_l1_head, finalized, expected) in cases {
2460 assert_eq!(
2461 proposal_l1_head(local_head, parent_l1_head, finalized),
2462 expected,
2463 "local_head={local_head}, parent_l1_head={parent_l1_head}, finalized={finalized:?}"
2464 );
2465 }
2466 }
2467
2468 #[test]
2469 fn test_proposal_l1_head_bounds() {
2470 for local_head in 0..=10 {
2471 for parent_l1_head in 0..=local_head {
2472 for finalized in 0..=local_head {
2473 let result = proposal_l1_head(local_head, parent_l1_head, Some(finalized));
2474 assert!(result >= parent_l1_head);
2475 assert!(result >= finalized);
2476 assert!(result <= local_head.max(parent_l1_head));
2477 }
2478 }
2479 }
2480 }
2481}