1use std::{borrow::Borrow, collections::HashSet, iter::once, str::FromStr};
2
3use alloy::primitives::{
4 utils::{parse_units, ParseUnits},
5 Address, B256, U256,
6};
7use anyhow::{bail, ensure, Context};
8use ark_serialize::{
9 CanonicalDeserialize, CanonicalSerialize, Compress, Read, SerializationError, Valid, Validate,
10};
11use hotshot::types::BLSPubKey;
12use hotshot_contract_adapter::reward::RewardProofSiblings;
13use hotshot_types::{
14 data::{EpochNumber, ViewNumber},
15 traits::{election::Membership, node_implementation::ConsensusTime},
16 utils::epoch_from_block_number,
17};
18use jf_merkle_tree_compat::{
19 prelude::MerkleNode, ForgetableMerkleTreeScheme, ForgetableUniversalMerkleTreeScheme,
20 LookupResult, MerkleTreeScheme, PersistentUniversalMerkleTreeScheme, ToTraversalPath,
21 UniversalMerkleTreeScheme,
22};
23use num_traits::CheckedSub;
24use sequencer_utils::{
25 impl_serde_from_string_or_integer, impl_to_fixed_bytes, ser::FromStringOrInteger,
26};
27use vbs::version::StaticVersionType;
28
29use super::{
30 v0_3::{RewardAmount, Validator, COMMISSION_BASIS_POINTS},
31 v0_4::{
32 RewardAccountProofV2, RewardAccountQueryDataV2, RewardAccountV2, RewardMerkleCommitmentV2,
33 RewardMerkleProofV2, RewardMerkleTreeV2,
34 },
35 Leaf2, NodeState, ValidatedState,
36};
37use crate::{
38 eth_signature_key::EthKeyPair,
39 v0_3::{
40 RewardAccountProofV1, RewardAccountV1, RewardMerkleCommitmentV1, RewardMerkleProofV1,
41 RewardMerkleTreeV1,
42 },
43 v0_4::{Delta, REWARD_MERKLE_TREE_V2_ARITY, REWARD_MERKLE_TREE_V2_HEIGHT},
44 DrbAndHeaderUpgradeVersion, EpochVersion, FeeAccount,
45};
46
47impl_serde_from_string_or_integer!(RewardAmount);
48impl_to_fixed_bytes!(RewardAmount, U256);
49
50impl From<u64> for RewardAmount {
51 fn from(amt: u64) -> Self {
52 Self(U256::from(amt))
53 }
54}
55
56impl CheckedSub for RewardAmount {
57 fn checked_sub(&self, v: &Self) -> Option<Self> {
58 self.0.checked_sub(v.0).map(RewardAmount)
59 }
60}
61
62impl FromStr for RewardAmount {
63 type Err = <U256 as FromStr>::Err;
64
65 fn from_str(s: &str) -> Result<Self, Self::Err> {
66 Ok(Self(s.parse()?))
67 }
68}
69
70impl FromStringOrInteger for RewardAmount {
71 type Binary = U256;
72 type Integer = u64;
73
74 fn from_binary(b: Self::Binary) -> anyhow::Result<Self> {
75 Ok(Self(b))
76 }
77
78 fn from_integer(i: Self::Integer) -> anyhow::Result<Self> {
79 Ok(i.into())
80 }
81
82 fn from_string(s: String) -> anyhow::Result<Self> {
83 if let Some(s) = s.strip_prefix("0x") {
86 return Ok(Self(s.parse()?));
87 }
88
89 let (base, unit) = s
91 .split_once(char::is_whitespace)
92 .unwrap_or((s.as_str(), "wei"));
93 match parse_units(base, unit)? {
94 ParseUnits::U256(n) => Ok(Self(n)),
95 ParseUnits::I256(_) => bail!("amount cannot be negative"),
96 }
97 }
98
99 fn to_binary(&self) -> anyhow::Result<Self::Binary> {
100 Ok(self.0)
101 }
102
103 fn to_string(&self) -> anyhow::Result<String> {
104 Ok(format!("{self}"))
105 }
106}
107
108impl RewardAmount {
109 pub fn as_u64(&self) -> Option<u64> {
110 if self.0 <= U256::from(u64::MAX) {
111 Some(self.0.to::<u64>())
112 } else {
113 None
114 }
115 }
116}
117
118impl From<[u8; 20]> for RewardAccountV1 {
119 fn from(bytes: [u8; 20]) -> Self {
120 Self(Address::from(bytes))
121 }
122}
123
124impl AsRef<[u8]> for RewardAccountV1 {
125 fn as_ref(&self) -> &[u8] {
126 self.0.as_slice()
127 }
128}
129
130impl<const ARITY: usize> ToTraversalPath<ARITY> for RewardAccountV1 {
131 fn to_traversal_path(&self, height: usize) -> Vec<usize> {
132 self.0
133 .as_slice()
134 .iter()
135 .take(height)
136 .map(|i| *i as usize)
137 .collect()
138 }
139}
140
141impl RewardAccountV2 {
142 pub fn address(&self) -> Address {
144 self.0
145 }
146 pub fn as_bytes(&self) -> &[u8] {
148 self.0.as_slice()
149 }
150 pub fn to_fixed_bytes(self) -> [u8; 20] {
152 self.0.into_array()
153 }
154 pub fn test_key_pair() -> EthKeyPair {
155 EthKeyPair::from_mnemonic(
156 "test test test test test test test test test test test junk",
157 0u32,
158 )
159 .unwrap()
160 }
161}
162
163impl RewardAccountV1 {
164 pub fn address(&self) -> Address {
166 self.0
167 }
168 pub fn as_bytes(&self) -> &[u8] {
170 self.0.as_slice()
171 }
172 pub fn to_fixed_bytes(self) -> [u8; 20] {
174 self.0.into_array()
175 }
176 pub fn test_key_pair() -> EthKeyPair {
177 EthKeyPair::from_mnemonic(
178 "test test test test test test test test test test test junk",
179 0u32,
180 )
181 .unwrap()
182 }
183}
184
185impl FromStr for RewardAccountV2 {
186 type Err = anyhow::Error;
187
188 fn from_str(s: &str) -> Result<Self, Self::Err> {
189 Ok(Self(s.parse()?))
190 }
191}
192
193impl FromStr for RewardAccountV1 {
194 type Err = anyhow::Error;
195
196 fn from_str(s: &str) -> Result<Self, Self::Err> {
197 Ok(Self(s.parse()?))
198 }
199}
200
201impl Valid for RewardAmount {
202 fn check(&self) -> Result<(), SerializationError> {
203 Ok(())
204 }
205}
206
207impl Valid for RewardAccountV2 {
208 fn check(&self) -> Result<(), SerializationError> {
209 Ok(())
210 }
211}
212
213impl Valid for RewardAccountV1 {
214 fn check(&self) -> Result<(), SerializationError> {
215 Ok(())
216 }
217}
218
219impl CanonicalSerialize for RewardAmount {
220 fn serialize_with_mode<W: std::io::prelude::Write>(
221 &self,
222 mut writer: W,
223 _compress: Compress,
224 ) -> Result<(), SerializationError> {
225 Ok(writer.write_all(&self.to_fixed_bytes())?)
226 }
227
228 fn serialized_size(&self, _compress: Compress) -> usize {
229 core::mem::size_of::<U256>()
230 }
231}
232impl CanonicalDeserialize for RewardAmount {
233 fn deserialize_with_mode<R: Read>(
234 mut reader: R,
235 _compress: Compress,
236 _validate: Validate,
237 ) -> Result<Self, SerializationError> {
238 let mut bytes = [0u8; core::mem::size_of::<U256>()];
239 reader.read_exact(&mut bytes)?;
240 let value = U256::from_le_slice(&bytes);
241 Ok(Self(value))
242 }
243}
244
245impl CanonicalSerialize for RewardAccountV2 {
246 fn serialize_with_mode<W: std::io::prelude::Write>(
247 &self,
248 mut writer: W,
249 _compress: Compress,
250 ) -> Result<(), SerializationError> {
251 Ok(writer.write_all(self.0.as_slice())?)
252 }
253
254 fn serialized_size(&self, _compress: Compress) -> usize {
255 core::mem::size_of::<Address>()
256 }
257}
258impl CanonicalDeserialize for RewardAccountV2 {
259 fn deserialize_with_mode<R: Read>(
260 mut reader: R,
261 _compress: Compress,
262 _validate: Validate,
263 ) -> Result<Self, SerializationError> {
264 let mut bytes = [0u8; core::mem::size_of::<Address>()];
265 reader.read_exact(&mut bytes)?;
266 let value = Address::from_slice(&bytes);
267 Ok(Self(value))
268 }
269}
270
271impl CanonicalSerialize for RewardAccountV1 {
272 fn serialize_with_mode<W: std::io::prelude::Write>(
273 &self,
274 mut writer: W,
275 _compress: Compress,
276 ) -> Result<(), SerializationError> {
277 Ok(writer.write_all(self.0.as_slice())?)
278 }
279
280 fn serialized_size(&self, _compress: Compress) -> usize {
281 core::mem::size_of::<Address>()
282 }
283}
284impl CanonicalDeserialize for RewardAccountV1 {
285 fn deserialize_with_mode<R: Read>(
286 mut reader: R,
287 _compress: Compress,
288 _validate: Validate,
289 ) -> Result<Self, SerializationError> {
290 let mut bytes = [0u8; core::mem::size_of::<Address>()];
291 reader.read_exact(&mut bytes)?;
292 let value = Address::from_slice(&bytes);
293 Ok(Self(value))
294 }
295}
296
297impl From<[u8; 20]> for RewardAccountV2 {
298 fn from(bytes: [u8; 20]) -> Self {
299 Self(Address::from(bytes))
300 }
301}
302
303impl AsRef<[u8]> for RewardAccountV2 {
304 fn as_ref(&self) -> &[u8] {
305 self.0.as_slice()
306 }
307}
308
309impl<const ARITY: usize> ToTraversalPath<ARITY> for RewardAccountV2 {
310 fn to_traversal_path(&self, height: usize) -> Vec<usize> {
311 let mut result = vec![0; height];
312
313 let mut value = U256::from_be_slice(self.0.as_slice());
315
316 for item in result.iter_mut().take(height) {
318 let digit = (value % U256::from(ARITY)).to::<usize>();
319 *item = digit;
320 value /= U256::from(ARITY);
321 }
322
323 result
324 }
325}
326
327impl RewardAccountProofV2 {
328 pub fn presence(
329 pos: FeeAccount,
330 proof: <RewardMerkleTreeV2 as MerkleTreeScheme>::MembershipProof,
331 ) -> Self {
332 Self {
333 account: pos.into(),
334 proof: RewardMerkleProofV2::Presence(proof),
335 }
336 }
337
338 pub fn absence(
339 pos: RewardAccountV2,
340 proof: <RewardMerkleTreeV2 as UniversalMerkleTreeScheme>::NonMembershipProof,
341 ) -> Self {
342 Self {
343 account: pos.into(),
344 proof: RewardMerkleProofV2::Absence(proof),
345 }
346 }
347
348 pub fn prove(tree: &RewardMerkleTreeV2, account: Address) -> Option<(Self, U256)> {
349 match tree.universal_lookup(RewardAccountV2(account)) {
350 LookupResult::Ok(balance, proof) => Some((
351 Self {
352 account,
353 proof: RewardMerkleProofV2::Presence(proof),
354 },
355 balance.0,
356 )),
357 LookupResult::NotFound(proof) => Some((
358 Self {
359 account,
360 proof: RewardMerkleProofV2::Absence(proof),
361 },
362 U256::ZERO,
363 )),
364 LookupResult::NotInMemory => None,
365 }
366 }
367
368 pub fn verify(&self, comm: &RewardMerkleCommitmentV2) -> anyhow::Result<U256> {
369 match &self.proof {
370 RewardMerkleProofV2::Presence(proof) => {
371 ensure!(
372 RewardMerkleTreeV2::verify(comm, RewardAccountV2(self.account), proof)?.is_ok(),
373 "invalid proof"
374 );
375 Ok(proof
376 .elem()
377 .context("presence proof is missing account balance")?
378 .0)
379 },
380 RewardMerkleProofV2::Absence(proof) => {
381 let tree = RewardMerkleTreeV2::from_commitment(comm);
382 ensure!(
383 RewardMerkleTreeV2::non_membership_verify(
384 tree.commitment(),
385 RewardAccountV2(self.account),
386 proof
387 )?,
388 "invalid proof"
389 );
390 Ok(U256::ZERO)
391 },
392 }
393 }
394
395 pub fn remember(&self, tree: &mut RewardMerkleTreeV2) -> anyhow::Result<()> {
396 match &self.proof {
397 RewardMerkleProofV2::Presence(proof) => {
398 tree.remember(
399 RewardAccountV2(self.account),
400 proof
401 .elem()
402 .context("presence proof is missing account balance")?,
403 proof,
404 )?;
405 Ok(())
406 },
407 RewardMerkleProofV2::Absence(proof) => {
408 tree.non_membership_remember(RewardAccountV2(self.account), proof)?;
409 Ok(())
410 },
411 }
412 }
413}
414
415impl TryInto<RewardProofSiblings> for RewardAccountProofV2 {
416 type Error = anyhow::Error;
417
418 fn try_into(self) -> anyhow::Result<RewardProofSiblings> {
423 let proof = if let RewardMerkleProofV2::Presence(proof) = &self.proof {
425 proof
426 } else {
427 bail!("only presence proofs supported")
428 };
429
430 let path = ToTraversalPath::<REWARD_MERKLE_TREE_V2_ARITY>::to_traversal_path(
431 &RewardAccountV2(self.account),
432 REWARD_MERKLE_TREE_V2_HEIGHT,
433 );
434
435 if path.len() != REWARD_MERKLE_TREE_V2_HEIGHT {
436 bail!("Invalid proof: unexpected path length: {}", path.len());
437 };
438
439 let siblings: [B256; REWARD_MERKLE_TREE_V2_HEIGHT] = proof
440 .proof
441 .iter()
442 .enumerate()
443 .skip(1) .filter_map(|(level_idx, node)| match node {
445 MerkleNode::Branch { children, .. } => {
446 let path_direction = path
448 .get(level_idx - 1)
449 .copied()
450 .expect("exists");
451 let sibling_idx = if path_direction == 0 { 1 } else { 0 };
452 if sibling_idx >= children.len() {
453 panic!(
454 "Invalid proof: index={sibling_idx} length={}",
455 children.len()
456 );
457 };
458
459 match children[sibling_idx].as_ref() {
460 MerkleNode::Empty => Some(B256::ZERO),
461 MerkleNode::Leaf { value, .. } => {
462 let bytes = value.as_ref();
463 Some(B256::from_slice(bytes))
464 }
465 MerkleNode::Branch { value, .. } => {
466 let bytes = value.as_ref();
467 Some(B256::from_slice(bytes))
468 }
469 MerkleNode::ForgettenSubtree { value } => {
470 let bytes = value.as_ref();
471 Some(B256::from_slice(bytes))
472 }
473 }
474 }
475 _ => None,
476 })
477 .collect::<Vec<B256>>().try_into().map_err(|err: Vec<_>| {
478 panic!("Invalid proof length: {:?}, this should never happen", err.len())
479 })
480 .unwrap();
481
482 Ok(siblings.into())
483 }
484}
485
486impl RewardAccountProofV1 {
487 pub fn presence(
488 pos: FeeAccount,
489 proof: <RewardMerkleTreeV1 as MerkleTreeScheme>::MembershipProof,
490 ) -> Self {
491 Self {
492 account: pos.into(),
493 proof: RewardMerkleProofV1::Presence(proof),
494 }
495 }
496
497 pub fn absence(
498 pos: RewardAccountV1,
499 proof: <RewardMerkleTreeV1 as UniversalMerkleTreeScheme>::NonMembershipProof,
500 ) -> Self {
501 Self {
502 account: pos.into(),
503 proof: RewardMerkleProofV1::Absence(proof),
504 }
505 }
506
507 pub fn prove(tree: &RewardMerkleTreeV1, account: Address) -> Option<(Self, U256)> {
508 match tree.universal_lookup(RewardAccountV1(account)) {
509 LookupResult::Ok(balance, proof) => Some((
510 Self {
511 account,
512 proof: RewardMerkleProofV1::Presence(proof),
513 },
514 balance.0,
515 )),
516 LookupResult::NotFound(proof) => Some((
517 Self {
518 account,
519 proof: RewardMerkleProofV1::Absence(proof),
520 },
521 U256::ZERO,
522 )),
523 LookupResult::NotInMemory => None,
524 }
525 }
526
527 pub fn verify(&self, comm: &RewardMerkleCommitmentV1) -> anyhow::Result<U256> {
528 match &self.proof {
529 RewardMerkleProofV1::Presence(proof) => {
530 ensure!(
531 RewardMerkleTreeV1::verify(comm, RewardAccountV1(self.account), proof)?.is_ok(),
532 "invalid proof"
533 );
534 Ok(proof
535 .elem()
536 .context("presence proof is missing account balance")?
537 .0)
538 },
539 RewardMerkleProofV1::Absence(proof) => {
540 let tree = RewardMerkleTreeV1::from_commitment(comm);
541 ensure!(
542 RewardMerkleTreeV1::non_membership_verify(
543 tree.commitment(),
544 RewardAccountV1(self.account),
545 proof
546 )?,
547 "invalid proof"
548 );
549 Ok(U256::ZERO)
550 },
551 }
552 }
553
554 pub fn remember(&self, tree: &mut RewardMerkleTreeV1) -> anyhow::Result<()> {
555 match &self.proof {
556 RewardMerkleProofV1::Presence(proof) => {
557 tree.remember(
558 RewardAccountV1(self.account),
559 proof
560 .elem()
561 .context("presence proof is missing account balance")?,
562 proof,
563 )?;
564 Ok(())
565 },
566 RewardMerkleProofV1::Absence(proof) => {
567 tree.non_membership_remember(RewardAccountV1(self.account), proof)?;
568 Ok(())
569 },
570 }
571 }
572}
573
574impl From<(RewardAccountProofV2, U256)> for RewardAccountQueryDataV2 {
575 fn from((proof, balance): (RewardAccountProofV2, U256)) -> Self {
576 Self { balance, proof }
577 }
578}
579
580#[derive(Clone, Debug)]
581pub struct ComputedRewards {
582 leader_address: Address,
583 leader_commission: RewardAmount,
585 delegators: Vec<(Address, RewardAmount)>,
587}
588
589impl ComputedRewards {
590 pub fn new(
591 delegators: Vec<(Address, RewardAmount)>,
592 leader_address: Address,
593 leader_commission: RewardAmount,
594 ) -> Self {
595 Self {
596 delegators,
597 leader_address,
598 leader_commission,
599 }
600 }
601
602 pub fn leader_commission(&self) -> &RewardAmount {
603 &self.leader_commission
604 }
605
606 pub fn delegators(&self) -> &Vec<(Address, RewardAmount)> {
607 &self.delegators
608 }
609
610 pub fn all_rewards(self) -> Vec<(Address, RewardAmount)> {
612 self.delegators
613 .into_iter()
614 .chain(once((self.leader_address, self.leader_commission)))
615 .collect()
616 }
617}
618
619pub struct RewardDistributor {
620 validator: Validator<BLSPubKey>,
621 block_reward: RewardAmount,
622 total_distributed: RewardAmount,
623}
624
625impl RewardDistributor {
626 pub fn new(
627 validator: Validator<BLSPubKey>,
628 block_reward: RewardAmount,
629 total_distributed: RewardAmount,
630 ) -> Self {
631 Self {
632 validator,
633 block_reward,
634 total_distributed,
635 }
636 }
637
638 pub fn validator(&self) -> Validator<BLSPubKey> {
639 self.validator.clone()
640 }
641
642 pub fn block_reward(&self) -> RewardAmount {
643 self.block_reward
644 }
645
646 pub fn total_distributed(&self) -> RewardAmount {
647 self.total_distributed
648 }
649
650 pub fn update_rewards_delta(&self, delta: &mut Delta) -> anyhow::Result<()> {
651 delta
653 .rewards_delta
654 .insert(RewardAccountV2(self.validator().account));
655 delta.rewards_delta.extend(
656 self.validator()
657 .delegators
658 .keys()
659 .map(|d| RewardAccountV2(*d)),
660 );
661
662 Ok(())
663 }
664
665 fn update_reward_balance<P>(
666 tree: &mut P,
667 account: &P::Index,
668 amount: P::Element,
669 ) -> anyhow::Result<()>
670 where
671 P: PersistentUniversalMerkleTreeScheme,
672 P: MerkleTreeScheme<Element = RewardAmount>,
673 P::Index: Borrow<<P as MerkleTreeScheme>::Index> + std::fmt::Display,
674 {
675 let mut err = None;
676 *tree = tree.persistent_update_with(account.clone(), |balance| {
677 let balance = balance.copied();
678 match balance.unwrap_or_default().0.checked_add(amount.0) {
679 Some(updated) => Some(updated.into()),
680 None => {
681 err = Some(format!("overflowed reward balance for account {account}"));
682 balance
683 },
684 }
685 })?;
686
687 if let Some(error) = err {
688 tracing::warn!(error);
689 bail!(error)
690 }
691
692 Ok(())
693 }
694
695 pub fn apply_rewards(
696 &mut self,
697 version: vbs::version::Version,
698 state: &mut ValidatedState,
699 ) -> anyhow::Result<()> {
700 let computed_rewards = self.compute_rewards()?;
701
702 if version <= EpochVersion::version() {
703 for (address, reward) in computed_rewards.all_rewards() {
704 Self::update_reward_balance(
705 &mut state.reward_merkle_tree_v1,
706 &RewardAccountV1(address),
707 reward,
708 )?;
709 tracing::debug!(%address, %reward, "applied v1 rewards");
710 }
711 } else {
712 for (address, reward) in computed_rewards.all_rewards() {
713 Self::update_reward_balance(
714 &mut state.reward_merkle_tree_v2,
715 &RewardAccountV2(address),
716 reward,
717 )?;
718 tracing::debug!(%address, %reward, "applied v2 rewards");
719 }
720 }
721
722 self.total_distributed += self.block_reward();
723
724 Ok(())
725 }
726
727 pub fn compute_rewards(&self) -> anyhow::Result<ComputedRewards> {
735 ensure!(
736 self.validator.commission <= COMMISSION_BASIS_POINTS,
737 "commission must not exceed {COMMISSION_BASIS_POINTS}"
738 );
739
740 let mut rewards = Vec::new();
741
742 let total_reward = self.block_reward.0;
743 let delegators_ratio_basis_points = U256::from(COMMISSION_BASIS_POINTS)
744 .checked_sub(U256::from(self.validator.commission))
745 .context("overflow")?;
746 let delegators_reward = delegators_ratio_basis_points
747 .checked_mul(total_reward)
748 .context("overflow")?;
749
750 let total_stake = self.validator.stake;
752 let mut delegators_total_reward_distributed = U256::from(0);
753 for (delegator_address, delegator_stake) in &self.validator.delegators {
754 let delegator_reward = RewardAmount::from(
755 (delegator_stake
756 .checked_mul(delegators_reward)
757 .context("overflow")?
758 .checked_div(total_stake)
759 .context("overflow")?)
760 .checked_div(U256::from(COMMISSION_BASIS_POINTS))
761 .context("overflow")?,
762 );
763
764 delegators_total_reward_distributed += delegator_reward.0;
765
766 rewards.push((*delegator_address, delegator_reward));
767 }
768
769 let leader_commission = total_reward
770 .checked_sub(delegators_total_reward_distributed)
771 .context("overflow")?;
772
773 Ok(ComputedRewards::new(
774 rewards,
775 self.validator.account,
776 leader_commission.into(),
777 ))
778 }
779}
780
781pub async fn distribute_block_reward(
788 instance_state: &NodeState,
789 validated_state: &mut ValidatedState,
790 parent_leaf: &Leaf2,
791 view_number: ViewNumber,
792 version: vbs::version::Version,
793) -> anyhow::Result<Option<RewardDistributor>> {
794 let height = parent_leaf.height() + 1;
795
796 let epoch_height = instance_state
797 .epoch_height
798 .context("epoch height not found")?;
799 let epoch = EpochNumber::new(epoch_from_block_number(height, epoch_height));
800 let coordinator = instance_state.coordinator.clone();
801 let first_epoch = {
802 coordinator
803 .membership()
804 .read()
805 .await
806 .first_epoch()
807 .context("The first epoch was not set.")?
808 };
809
810 if epoch <= first_epoch + 1 {
813 return Ok(None);
814 }
815
816 let leader = get_leader_and_fetch_missing_rewards(
820 instance_state,
821 validated_state,
822 parent_leaf,
823 view_number,
824 )
825 .await?;
826
827 let parent_header = parent_leaf.block_header();
828
829 let mut previously_distributed = parent_header.total_reward_distributed().unwrap_or_default();
831
832 let block_reward = if version >= DrbAndHeaderUpgradeVersion::version() {
834 instance_state
835 .block_reward(EpochNumber::new(*epoch))
836 .await
837 .with_context(|| format!("block reward is None for epoch {epoch}"))?
838 } else {
839 instance_state.fixed_block_reward().await?
840 };
841
842 if version >= DrbAndHeaderUpgradeVersion::version()
847 && parent_header.version() == EpochVersion::version()
848 {
849 ensure!(
850 instance_state.epoch_start_block != 0,
851 "epoch_start_block is zero"
852 );
853
854 let fixed_block_reward = instance_state.fixed_block_reward().await?;
855
856 let first_reward_block = (*first_epoch + 1) * epoch_height + 1;
862 if height > first_reward_block {
866 let blocks = height.checked_sub(first_reward_block).with_context(|| {
869 format!("height ({height}) - first_reward_block ({first_reward_block}) underflowed")
870 })?;
871 previously_distributed = U256::from(blocks)
872 .checked_mul(fixed_block_reward.0)
873 .with_context(|| {
874 format!(
875 "overflow during total_distributed calculation: blocks={blocks}, \
876 fixed_block_reward={}",
877 fixed_block_reward.0
878 )
879 })?
880 .into();
881 }
882 }
883
884 if block_reward.0.is_zero() {
885 tracing::info!("block reward is zero. height={height}. epoch={epoch}");
886 return Ok(None);
887 }
888
889 let mut reward_distributor =
890 RewardDistributor::new(leader, block_reward, previously_distributed);
891
892 reward_distributor.apply_rewards(version, validated_state)?;
893
894 Ok(Some(reward_distributor))
895}
896
897pub async fn get_leader_and_fetch_missing_rewards(
898 instance_state: &NodeState,
899 validated_state: &mut ValidatedState,
900 parent_leaf: &Leaf2,
901 view: ViewNumber,
902) -> anyhow::Result<Validator<BLSPubKey>> {
903 let parent_height = parent_leaf.height();
904 let parent_view = parent_leaf.view_number();
905 let new_height = parent_height + 1;
906
907 let epoch_height = instance_state
908 .epoch_height
909 .context("epoch height not found")?;
910 if epoch_height == 0 {
911 bail!("epoch height is 0. can not catchup reward accounts");
912 }
913 let epoch = EpochNumber::new(epoch_from_block_number(new_height, epoch_height));
914
915 let coordinator = instance_state.coordinator.clone();
916
917 let epoch_membership = coordinator.membership_for_epoch(Some(epoch)).await?;
918 let membership = epoch_membership.coordinator.membership().read().await;
919
920 let leader: BLSPubKey = membership
921 .leader(view, Some(epoch))
922 .context(format!("leader for epoch {epoch:?} not found"))?;
923
924 let validator = membership
925 .get_validator_config(&epoch, leader)
926 .context("validator not found")?;
927 drop(membership);
928
929 let mut reward_accounts = HashSet::new();
930 reward_accounts.insert(validator.account.into());
931 let delegators = validator
932 .delegators
933 .keys()
934 .cloned()
935 .map(|a| a.into())
936 .collect::<Vec<RewardAccountV2>>();
937
938 reward_accounts.extend(delegators.clone());
939
940 let parent_header = parent_leaf.block_header();
941
942 if parent_header.version() <= EpochVersion::version() {
943 let accts: HashSet<_> = reward_accounts
944 .into_iter()
945 .map(RewardAccountV1::from)
946 .collect();
947 let missing_reward_accts = validated_state.forgotten_reward_accounts_v1(accts);
948
949 if !missing_reward_accts.is_empty() {
950 tracing::warn!(
951 parent_height,
952 ?parent_view,
953 ?missing_reward_accts,
954 "fetching missing v1 reward accounts from peers"
955 );
956
957 let missing_account_proofs = instance_state
958 .state_catchup
959 .fetch_reward_accounts_v1(
960 instance_state,
961 parent_height,
962 parent_view,
963 validated_state.reward_merkle_tree_v1.commitment(),
964 missing_reward_accts,
965 )
966 .await?;
967
968 for proof in missing_account_proofs.iter() {
969 proof
970 .remember(&mut validated_state.reward_merkle_tree_v1)
971 .expect("proof previously verified");
972 }
973 }
974 } else {
975 let missing_reward_accts = validated_state.forgotten_reward_accounts_v2(reward_accounts);
976
977 if !missing_reward_accts.is_empty() {
978 tracing::warn!(
979 parent_height,
980 ?parent_view,
981 ?missing_reward_accts,
982 "fetching missing reward accounts from peers"
983 );
984
985 let missing_account_proofs = instance_state
986 .state_catchup
987 .fetch_reward_accounts_v2(
988 instance_state,
989 parent_height,
990 parent_view,
991 validated_state.reward_merkle_tree_v2.commitment(),
992 missing_reward_accts,
993 )
994 .await?;
995
996 for proof in missing_account_proofs.iter() {
997 proof
998 .remember(&mut validated_state.reward_merkle_tree_v2)
999 .expect("proof previously verified");
1000 }
1001 }
1002 }
1003
1004 Ok(validator)
1005}
1006
1007#[cfg(test)]
1008pub mod tests {
1009
1010 use super::*;
1011
1012 #[test]
1015 fn test_reward_calculation_sanity_checks() {
1016 let validator = Validator::mock();
1021 let mut distributor = RewardDistributor::new(
1022 validator,
1023 RewardAmount(U256::from(1902000000000000000_u128)),
1024 U256::ZERO.into(),
1025 );
1026 let rewards = distributor.compute_rewards().unwrap();
1027 let total = |rewards: ComputedRewards| {
1028 rewards
1029 .all_rewards()
1030 .iter()
1031 .fold(U256::ZERO, |acc, (_, r)| acc + r.0)
1032 };
1033 assert_eq!(total(rewards.clone()), distributor.block_reward.0);
1034
1035 distributor.validator.commission = 0;
1036 let rewards = distributor.compute_rewards().unwrap();
1037 assert_eq!(total(rewards.clone()), distributor.block_reward.0);
1038
1039 distributor.validator.commission = 10000;
1040 let rewards = distributor.compute_rewards().unwrap();
1041 assert_eq!(total(rewards.clone()), distributor.block_reward.0);
1042 let leader_commission = rewards.leader_commission();
1043 assert_eq!(*leader_commission, distributor.block_reward);
1044
1045 distributor.validator.commission = 10001;
1046 assert!(distributor
1047 .compute_rewards()
1048 .err()
1049 .unwrap()
1050 .to_string()
1051 .contains("must not exceed"));
1052 }
1053
1054 #[test]
1055 fn test_compute_rewards_validator_commission() {
1056 let validator = Validator::mock();
1057 let mut distributor = RewardDistributor::new(
1058 validator.clone(),
1059 RewardAmount(U256::from(1902000000000000000_u128)),
1060 U256::ZERO.into(),
1061 );
1062 distributor.validator.commission = 0;
1063
1064 let rewards = distributor.compute_rewards().unwrap();
1065
1066 let leader_commission = rewards.leader_commission();
1067 let percentage =
1068 leader_commission.0 * U256::from(COMMISSION_BASIS_POINTS) / distributor.block_reward.0;
1069 assert_eq!(percentage, U256::ZERO);
1070
1071 distributor.validator.commission = 300;
1073
1074 let rewards = distributor.compute_rewards().unwrap();
1075 let leader_commission = rewards.leader_commission();
1076 let percentage =
1077 leader_commission.0 * U256::from(COMMISSION_BASIS_POINTS) / distributor.block_reward.0;
1078 println!("percentage: {percentage:?}");
1079 assert_eq!(percentage, U256::from(300));
1080
1081 distributor.validator.commission = 10000;
1083
1084 let rewards = distributor.compute_rewards().unwrap();
1085 let leader_commission = rewards.leader_commission();
1086 assert_eq!(*leader_commission, distributor.block_reward);
1087 }
1088}