espresso_types/v0/v0_4/
state.rs

1use std::collections::HashSet;
2
3use alloy::primitives::{Address, U256};
4use derive_more::{Display, From, Into};
5use hotshot_contract_adapter::reward::{RewardAuthData, RewardAuthRootInputs, RewardClaimInput};
6use jf_merkle_tree_compat::{
7    universal_merkle_tree::UniversalMerkleTree, MerkleTreeScheme, UniversalMerkleTreeScheme,
8};
9use serde::{Deserialize, Serialize};
10
11use super::FeeAccount;
12use crate::{
13    v0::sparse_mt::{Keccak256Hasher, KeccakNode},
14    v0_3::{RewardAccountV1, RewardAmount},
15};
16
17pub const REWARD_MERKLE_TREE_V2_HEIGHT: usize = 160;
18pub const REWARD_MERKLE_TREE_V2_ARITY: usize = 2;
19
20pub type RewardMerkleCommitmentV2 = <RewardMerkleTreeV2 as MerkleTreeScheme>::Commitment;
21
22pub type RewardMerkleTreeV2 = UniversalMerkleTree<
23    RewardAmount,
24    Keccak256Hasher,
25    RewardAccountV2,
26    REWARD_MERKLE_TREE_V2_ARITY,
27    KeccakNode,
28>;
29// New Type for `Address` in order to implement `CanonicalSerialize` and
30// `CanonicalDeserialize`
31// This is the same as `RewardAccountV1` but the `ToTraversal` trait implementation
32// for this type is different
33#[derive(
34    Default,
35    Hash,
36    Copy,
37    Clone,
38    Debug,
39    Display,
40    Deserialize,
41    Serialize,
42    PartialEq,
43    Eq,
44    PartialOrd,
45    Ord,
46    From,
47    Into,
48)]
49#[display("{_0}")]
50pub struct RewardAccountV2(pub Address);
51
52impl From<RewardAccountV2> for RewardAccountV1 {
53    fn from(account: RewardAccountV2) -> Self {
54        RewardAccountV1(account.0)
55    }
56}
57
58impl From<RewardAccountV1> for RewardAccountV2 {
59    fn from(account: RewardAccountV1) -> Self {
60        RewardAccountV2(account.0)
61    }
62}
63
64/// A proof of the balance of an account in the fee ledger.
65///
66/// If the account of interest does not exist in the fee state, this is a Merkle non-membership
67/// proof, and the balance is implicitly zero. Otherwise, this is a normal Merkle membership proof.
68#[derive(Clone, Debug, Deserialize, Serialize)]
69pub struct RewardAccountProofV2 {
70    pub account: Address,
71    pub proof: RewardMerkleProofV2,
72}
73
74#[derive(Clone, Debug, Deserialize, Serialize)]
75pub enum RewardMerkleProofV2 {
76    Presence(<RewardMerkleTreeV2 as MerkleTreeScheme>::MembershipProof),
77    Absence(<RewardMerkleTreeV2 as UniversalMerkleTreeScheme>::NonMembershipProof),
78}
79
80#[derive(Clone, Debug, Serialize, Deserialize)]
81pub struct RewardAccountQueryDataV2 {
82    pub balance: U256,
83    pub proof: RewardAccountProofV2,
84}
85
86#[derive(Debug, thiserror::Error)]
87pub enum RewardClaimError {
88    #[error("Zero reward balance")]
89    ZeroRewardError,
90    #[error("Failed to convert proof: {0}")]
91    ProofConversionError(#[from] anyhow::Error),
92}
93
94impl RewardAccountQueryDataV2 {
95    pub fn to_reward_claim_input(
96        self,
97        auth_root_inputs: RewardAuthRootInputs,
98    ) -> Result<RewardClaimInput, RewardClaimError> {
99        if self.balance == U256::ZERO {
100            return Err(RewardClaimError::ZeroRewardError);
101        }
102
103        let account_proof = match self.proof.proof {
104            RewardMerkleProofV2::Presence(_) => self.proof,
105            RewardMerkleProofV2::Absence(_) => {
106                return Err(RewardClaimError::ZeroRewardError);
107            },
108        };
109
110        Ok(RewardClaimInput {
111            lifetime_rewards: self.balance,
112            auth_data: RewardAuthData::new(account_proof.try_into()?, auth_root_inputs).into(),
113        })
114    }
115}
116
117#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
118pub struct Delta {
119    pub fees_delta: HashSet<FeeAccount>,
120    pub rewards_delta: HashSet<RewardAccountV2>,
121}