cfx_executor/state/state_object/
pos.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use super::State;
6use crate::{
7    internal_contract::{pos_internal_entries, IndexStatus},
8    return_if,
9};
10use cfx_math::sqrt_u256;
11use cfx_parameters::{
12    internal_contract_addresses::POS_REGISTER_CONTRACT_ADDRESS, staking::*,
13};
14use cfx_statedb::{
15    global_params::{
16        DistributablePoSInterest, InterestRate, LastDistributeBlock,
17        TotalPosStaking,
18    },
19    Result as DbResult,
20};
21use cfx_types::{Address, AddressSpaceUtil, BigEndianHash, H256, U256};
22use diem_types::term_state::MAX_TERM_POINTS;
23
24impl State {
25    pub fn inc_distributable_pos_interest(
26        &mut self, current_block_number: u64,
27    ) -> DbResult<()> {
28        assert!(self.checkpoints.get_mut().is_empty());
29
30        let next_distribute_block =
31            self.global_stat.refr::<LastDistributeBlock>().as_u64()
32                + BLOCKS_PER_HOUR;
33
34        return_if!(current_block_number > next_distribute_block);
35        return_if!(self.global_stat.refr::<TotalPosStaking>().is_zero());
36
37        let total_circulating_tokens = self.total_circulating_tokens()?;
38        let total_pos_staking_tokens =
39            self.global_stat.refr::<TotalPosStaking>();
40
41        // The `interest_amount` exactly equals to the floor of
42        // pos_amount * 4% / blocks_per_year / sqrt(pos_amount/total_issued)
43        let interest_rate_per_block = self.global_stat.refr::<InterestRate>();
44        let interest_amount = sqrt_u256(
45            total_circulating_tokens
46                * *total_pos_staking_tokens
47                * *interest_rate_per_block
48                * *interest_rate_per_block,
49        ) / (BLOCKS_PER_YEAR
50            * INVERSE_INTEREST_RATE
51            * INITIAL_INTEREST_RATE_PER_BLOCK.as_u64());
52        *self.global_stat.val::<DistributablePoSInterest>() += interest_amount;
53
54        Ok(())
55    }
56
57    pub fn pos_locked_staking(&self, address: &Address) -> DbResult<U256> {
58        let identifier = BigEndianHash::from_uint(&self.storage_at(
59            &POS_REGISTER_CONTRACT_ADDRESS.with_native_space(),
60            &pos_internal_entries::identifier_entry(address),
61        )?);
62        let current_value: IndexStatus = self
63            .storage_at(
64                &POS_REGISTER_CONTRACT_ADDRESS.with_native_space(),
65                &pos_internal_entries::index_entry(&identifier),
66            )?
67            .into();
68        Ok(*POS_VOTE_PRICE * current_value.locked())
69    }
70
71    pub fn add_pos_interest(
72        &mut self, address: &Address, interest: &U256,
73    ) -> DbResult<()> {
74        let address = address.with_native_space();
75        self.add_total_issued(*interest);
76        self.add_balance(&address, interest)?;
77        self.write_account_or_new_lock(&address)?
78            .record_interest_receive(interest);
79        Ok(())
80    }
81}
82
83/// Distribute PoS interest to the PoS committee according to their reward
84/// points. Return the rewarded PoW accounts and their rewarded
85/// interest.
86pub fn distribute_pos_interest<'a, I>(
87    state: &mut State, pos_points: I, current_block_number: u64,
88) -> DbResult<Vec<(Address, H256, U256)>>
89where I: Iterator<Item = (&'a H256, u64)> + 'a {
90    assert!(state.checkpoints.get_mut().is_empty());
91
92    let distributable_pos_interest = state.distributable_pos_interest();
93
94    let mut account_rewards = Vec::new();
95    for (identifier, points) in pos_points {
96        let address_value = state.storage_at(
97            &POS_REGISTER_CONTRACT_ADDRESS.with_native_space(),
98            &pos_internal_entries::address_entry(&identifier),
99        )?;
100        let address = Address::from(H256::from_uint(&address_value));
101        let interest = distributable_pos_interest * points / MAX_TERM_POINTS;
102        account_rewards.push((address, *identifier, interest));
103        state.add_pos_interest(&address, &interest)?;
104    }
105    state.reset_pos_distribute_info(current_block_number);
106
107    Ok(account_rewards)
108}
109
110pub fn update_pos_status(
111    state: &mut State, identifier: H256, number: u64,
112) -> DbResult<()> {
113    let old_value = state.storage_at(
114        &POS_REGISTER_CONTRACT_ADDRESS.with_native_space(),
115        &pos_internal_entries::index_entry(&identifier),
116    )?;
117    assert!(
118        !old_value.is_zero(),
119        "If an identifier is unlocked, its index information must be non-zero"
120    );
121    let mut status: IndexStatus = old_value.into();
122    let new_unlocked = number - status.unlocked;
123    status.set_unlocked(number);
124    // .expect("Incorrect unlock information");
125    state
126        .write_native_account_lock(&POS_REGISTER_CONTRACT_ADDRESS)?
127        .change_storage_value(
128            &state.db,
129            &pos_internal_entries::index_entry(&identifier),
130            status.into(),
131        )?;
132    state.sub_total_pos_staking(*POS_VOTE_PRICE * new_unlocked);
133    Ok(())
134}