cfx_executor/state/state_object/
staking.rs

1use super::{RequireFields, State};
2use crate::{
3    internal_contract::{
4        get_settled_param_vote_count, get_settled_pos_staking_for_votes,
5        settle_current_votes, storage_point_prop,
6    },
7    return_if, try_loaded,
8};
9use cfx_parameters::{
10    consensus::ONE_UCFX_IN_DRIP,
11    consensus_internal::MINING_REWARD_TANZANITE_IN_UCFX,
12};
13use cfx_statedb::{
14    global_params::{
15        AccumulateInterestRate, InterestRate, PowBaseReward, TotalStaking,
16    },
17    Result as DbResult,
18};
19use cfx_types::{Address, AddressSpaceUtil, U256};
20
21// Staking balance
22
23impl State {
24    pub fn staking_balance(&self, address: &Address) -> DbResult<U256> {
25        let acc = try_loaded!(self.read_native_account_lock(address));
26        Ok(*acc.staking_balance())
27    }
28
29    pub fn withdrawable_staking_balance(
30        &self, address: &Address, current_block_number: u64,
31    ) -> DbResult<U256> {
32        let acc = try_loaded!(self.read_account_ext_lock(
33            &address.with_native_space(),
34            RequireFields::VoteStakeList,
35        ));
36        Ok(acc.withdrawable_staking_balance(current_block_number))
37    }
38
39    pub fn locked_staking_balance_at_block_number(
40        &self, address: &Address, block_number: u64,
41    ) -> DbResult<U256> {
42        let acc = try_loaded!(self.read_account_ext_lock(
43            &address.with_native_space(),
44            RequireFields::VoteStakeList,
45        ));
46        Ok(acc.staking_balance()
47            - acc.withdrawable_staking_balance(block_number))
48    }
49
50    pub fn vote_stake_list_length(&self, address: &Address) -> DbResult<usize> {
51        let acc = try_loaded!(self.read_account_ext_lock(
52            &address.with_native_space(),
53            RequireFields::VoteStakeList
54        ));
55        Ok(acc.vote_stake_list().len())
56    }
57
58    pub fn vote_lock(
59        &mut self, address: &Address, amount: &U256, unlock_block_number: u64,
60    ) -> DbResult<()> {
61        return_if!(amount.is_zero());
62
63        self.write_account_ext_lock(
64            &address.with_native_space(),
65            RequireFields::VoteStakeList,
66        )?
67        .vote_lock(*amount, unlock_block_number);
68        Ok(())
69    }
70
71    pub fn remove_expired_vote_stake_info(
72        &mut self, address: &Address, current_block_number: u64,
73    ) -> DbResult<()> {
74        let mut account = self.write_native_account_lock(&address)?;
75        account.cache_ext_fields(false, true, &self.db)?;
76        account.remove_expired_vote_stake_info(current_block_number);
77        Ok(())
78    }
79
80    pub fn deposit_list_length(&self, address: &Address) -> DbResult<usize> {
81        let acc = try_loaded!(self.read_account_ext_lock(
82            &address.with_native_space(),
83            RequireFields::DepositList
84        ));
85        Ok(acc.deposit_list().len())
86    }
87
88    pub fn deposit(
89        &mut self, address: &Address, amount: &U256, current_block_number: u64,
90        cip_97: bool,
91    ) -> DbResult<()> {
92        return_if!(amount.is_zero());
93
94        let acc_interest_rate =
95            self.global_stat.get::<AccumulateInterestRate>();
96        self.write_account_ext_lock(
97            &address.with_native_space(),
98            RequireFields::DepositList,
99        )?
100        .deposit(
101            *amount,
102            acc_interest_rate,
103            current_block_number,
104            cip_97,
105        );
106        *self.global_stat.val::<TotalStaking>() += *amount;
107        Ok(())
108    }
109
110    pub fn withdraw(
111        &mut self, address: &Address, amount: &U256, cip_97: bool,
112    ) -> DbResult<U256> {
113        return_if!(amount.is_zero());
114
115        let accumulated_interest_rate =
116            self.global_stat.get::<AccumulateInterestRate>();
117        let interest = self
118            .write_account_ext_lock(
119                &address.with_native_space(),
120                RequireFields::DepositList,
121            )?
122            .withdraw(*amount, accumulated_interest_rate, cip_97);
123
124        // the interest will be put in balance.
125        self.add_total_issued(interest);
126        *self.global_stat.val::<TotalStaking>() -= *amount;
127        Ok(interest)
128    }
129}
130
131pub fn initialize_or_update_dao_voted_params(
132    state: &mut State, cip105: bool,
133) -> DbResult<()> {
134    let vote_count = get_settled_param_vote_count(state).expect("db error");
135    debug!(
136        "initialize_or_update_dao_voted_params: vote_count={:?}",
137        vote_count
138    );
139    debug!(
140        "before pos interest: {} base_reward:{}",
141        state.global_stat.refr::<InterestRate>(),
142        state.global_stat.refr::<PowBaseReward>(),
143    );
144
145    // If pos_staking has not been set before, this will be zero and the
146    // vote count will always be sufficient, so we do not need to
147    // check if CIP105 is enabled here.
148    let pos_staking_for_votes = get_settled_pos_staking_for_votes(state)?;
149    // If the internal contract is just initialized, all votes are zero and
150    // the parameters remain unchanged.
151    *state.global_stat.val::<InterestRate>() =
152        vote_count.pos_reward_interest.compute_next_params(
153            state.global_stat.get::<InterestRate>(),
154            pos_staking_for_votes,
155        );
156
157    // Initialize or update PoW base reward.
158    let pow_base_reward = state.global_stat.val::<PowBaseReward>();
159    if !pow_base_reward.is_zero() {
160        *pow_base_reward = vote_count
161            .pow_base_reward
162            .compute_next_params(*pow_base_reward, pos_staking_for_votes);
163    } else {
164        *pow_base_reward =
165            (MINING_REWARD_TANZANITE_IN_UCFX * ONE_UCFX_IN_DRIP).into();
166    }
167
168    // Only write storage_collateral_refund_ratio if it has been set in the
169    // db. This keeps the state unchanged before cip107 is enabled.
170    // TODO: better way in check if cip107 encabled.
171    let old_storage_point_prop =
172        state.get_system_storage(&storage_point_prop())?;
173    if !old_storage_point_prop.is_zero() {
174        debug!("old_storage_point_prop: {}", old_storage_point_prop);
175        state.set_system_storage(
176            storage_point_prop().to_vec(),
177            vote_count.storage_point_prop.compute_next_params(
178                old_storage_point_prop,
179                pos_staking_for_votes,
180            ),
181        )?;
182    }
183
184    let old_base_fee_prop = state.get_base_price_prop();
185    if !old_base_fee_prop.is_zero() {
186        state.set_base_fee_prop(
187            vote_count
188                .base_fee_prop
189                .compute_next_params(old_base_fee_prop, pos_staking_for_votes),
190        )
191    }
192    debug!(
193        "pos interest: {} base_reward: {}",
194        state.global_stat.refr::<InterestRate>(),
195        state.global_stat.refr::<PowBaseReward>()
196    );
197
198    settle_current_votes(state, cip105)?;
199
200    Ok(())
201}