cfx_executor/internal_contract/impls/
pos.rs

1use bls_signatures::{
2    sigma_protocol::{decode_answer, decode_commit, verify},
3    Error as CryptoError, PublicKey, Serialize,
4};
5use tiny_keccak::{Hasher, Keccak};
6
7use cfx_parameters::{
8    internal_contract_addresses::POS_REGISTER_CONTRACT_ADDRESS,
9    staking::POS_VOTE_PRICE,
10};
11use cfx_statedb::Result as DbResult;
12use cfx_types::{Address, BigEndianHash, H256, U256};
13use cfx_vm_types::{self as vm, ActionParams};
14use pow_types::StakingEvent::{self, IncreaseStake, Register, Retire};
15use primitives::log_entry::LogEntry;
16use solidity_abi::ABIDecodable;
17
18use crate::internal_bail;
19
20use super::super::{
21    components::{InternalRefContext, SolidityEventTrait},
22    contracts::pos::{IncreaseStakeEvent, RegisterEvent, RetireEvent},
23};
24
25use self::entries::*;
26
27pub struct IndexStatus {
28    pub registered: u64,
29    pub unlocked: u64,
30}
31
32impl From<U256> for IndexStatus {
33    fn from(input: U256) -> Self {
34        IndexStatus {
35            registered: input.0[0],
36            unlocked: input.0[1],
37        }
38    }
39}
40
41impl IndexStatus {
42    #[allow(unused)]
43    pub fn inc_unlocked(&mut self, number: u64) -> Result<(), &'static str> {
44        match self.unlocked.checked_add(number) {
45            None => Err("u64 overflow"),
46            Some(answer) if answer > self.registered => {
47                Err("The unlocked votes exceeds registered votes")
48            }
49            Some(answer) => {
50                self.unlocked = answer;
51                Ok(())
52            }
53        }
54    }
55
56    pub fn set_unlocked(&mut self, number: u64) { self.unlocked = number; }
57
58    pub fn locked(&self) -> u64 { self.registered - self.unlocked }
59}
60
61impl Into<U256> for IndexStatus {
62    fn into(self) -> U256 { U256([self.registered, self.unlocked, 0, 0]) }
63}
64
65type Bytes = Vec<u8>;
66
67#[inline]
68fn address_to_u256(value: Address) -> U256 { H256::from(value).into_uint() }
69
70#[inline]
71fn u256_to_address(value: &U256) -> Address {
72    let addr: H256 = BigEndianHash::from_uint(value);
73    Address::from(addr)
74}
75
76fn decode_bls_pubkey(bls_pubkey: Bytes) -> Result<PublicKey, CryptoError> {
77    PublicKey::from_bytes(bls_pubkey.as_slice())
78}
79
80fn verify_bls_pubkey(
81    bls_pubkey: Bytes, bls_proof: [Bytes; 2], legacy: bool,
82) -> Result<Option<Bytes>, CryptoError> {
83    let pubkey = decode_bls_pubkey(bls_pubkey)?;
84    let commit = decode_commit(bls_proof[0].as_slice())?;
85    let answer = decode_answer(bls_proof[1].as_slice())?;
86    let verified_pubkey = if verify(pubkey.clone(), commit, answer, legacy) {
87        let mut serialized_pubkey: Vec<u8> = Vec::new();
88        pubkey
89            .write_bytes(&mut serialized_pubkey)
90            .expect("Write to `Vec<u8>` should never fail");
91        Some(serialized_pubkey)
92    } else {
93        None
94    };
95    Ok(verified_pubkey)
96}
97
98fn update_vote_power(
99    identifier: H256, sender: Address, vote_power: u64, initialize_mode: bool,
100    params: &ActionParams, context: &mut InternalRefContext,
101) -> vm::Result<()> {
102    let status: IndexStatus = context
103        .storage_at(params, &index_entry(&identifier))?
104        .into();
105
106    if !initialize_mode && status.registered == 0 {
107        internal_bail!("uninitialized identifier");
108    }
109    if initialize_mode && status.registered != 0 {
110        internal_bail!("identifier has already been initialized");
111    }
112
113    let votes = status
114        .locked()
115        .checked_add(vote_power)
116        .ok_or(vm::Error::InternalContract("locked votes overflow".into()))?;
117    if context.state.staking_balance(&sender)? < *POS_VOTE_PRICE * votes {
118        internal_bail!("Not enough staking balance");
119    }
120
121    let mut status = status;
122    status.registered = status.registered.checked_add(vote_power).ok_or(
123        vm::Error::InternalContract("registered index overflow".into()),
124    )?;
125    context
126        .state
127        .add_total_pos_staking(*POS_VOTE_PRICE * vote_power);
128
129    IncreaseStakeEvent::log(&identifier, &vote_power, params, context)?;
130    context.set_storage(params, index_entry(&identifier), status.into())?;
131    Ok(())
132}
133
134fn is_identifier_changeable(
135    sender: Address, params: &ActionParams, context: &mut InternalRefContext,
136) -> DbResult<bool> {
137    let identifier = address_to_identifier(sender, params, context)?;
138    if identifier.is_zero() {
139        return Ok(true);
140    }
141    let status = get_status(identifier, params, context)?;
142    Ok(status.registered == status.unlocked)
143}
144
145pub fn register(
146    identifier: H256, sender: Address, vote_power: u64, bls_pubkey: Bytes,
147    vrf_pubkey: Bytes, bls_proof: [Bytes; 2], param: &ActionParams,
148    context: &mut InternalRefContext,
149) -> vm::Result<()> {
150    if vote_power == 0 {
151        internal_bail!("vote_power should be none zero");
152    }
153
154    if !is_identifier_changeable(sender, param, context)? {
155        internal_bail!("can not change identifier");
156    }
157
158    let verified_bls_pubkey = match verify_bls_pubkey(
159        bls_pubkey,
160        bls_proof,
161        !context.spec.cip_sigma_fix,
162    ) {
163        Err(e) => {
164            internal_bail!("Crypto decoding error {:?}", e);
165        }
166        Ok(None) => {
167            internal_bail!("Can not verify bls pubkey");
168        }
169        Ok(Some(key)) => key,
170    };
171
172    let mut hasher = Keccak::v256();
173    hasher.update(verified_bls_pubkey.as_slice());
174    hasher.update(vrf_pubkey.as_slice());
175    let mut computed_identifier = H256::default();
176    hasher.finalize(computed_identifier.as_bytes_mut());
177
178    if computed_identifier != identifier {
179        internal_bail!("Inconsistent identifier");
180    }
181    if identifier_to_address(identifier, param, context)? != Address::zero() {
182        internal_bail!("identifier has already been registered");
183    }
184
185    RegisterEvent::log(
186        &identifier,
187        &(verified_bls_pubkey, vrf_pubkey),
188        param,
189        context,
190    )?;
191
192    context.set_storage(
193        param,
194        address_entry(&identifier),
195        address_to_u256(sender),
196    )?;
197    context.set_storage(
198        param,
199        identifier_entry(&sender),
200        identifier.into_uint(),
201    )?;
202    update_vote_power(
203        identifier, sender, vote_power, /* allow_uninitialized */ true,
204        param, context,
205    )
206}
207
208pub fn increase_stake(
209    sender: Address, vote_power: u64, params: &ActionParams,
210    context: &mut InternalRefContext,
211) -> vm::Result<()> {
212    if vote_power == 0 {
213        internal_bail!("vote_power should be none zero");
214    }
215
216    let identifier = address_to_identifier(sender, params, context)?;
217
218    if identifier.is_zero() {
219        internal_bail!("The sender has not register a PoS identifier");
220    }
221
222    update_vote_power(
223        identifier, sender, vote_power, /* allow_uninitialized */ false,
224        params, context,
225    )
226}
227
228pub fn retire(
229    sender: Address, votes: u64, params: &ActionParams,
230    context: &mut InternalRefContext,
231) -> vm::Result<()> {
232    let identifier = address_to_identifier(sender, params, context)?;
233
234    if identifier.is_zero() {
235        internal_bail!("The sender has not register a PoS identifier");
236    }
237
238    let status: IndexStatus = context
239        .storage_at(params, &index_entry(&identifier))?
240        .into();
241
242    if status.locked() == 0 {
243        internal_bail!("The PoS account is fully unlocked");
244    }
245
246    RetireEvent::log(&identifier, &votes, params, context)?;
247    Ok(())
248}
249
250pub fn get_status(
251    identifier: H256, params: &ActionParams, context: &mut InternalRefContext,
252) -> DbResult<IndexStatus> {
253    Ok(context
254        .storage_at(params, &index_entry(&identifier))?
255        .into())
256}
257
258pub fn identifier_to_address(
259    identifier: H256, params: &ActionParams, context: &mut InternalRefContext,
260) -> DbResult<Address> {
261    Ok(u256_to_address(
262        &context.storage_at(params, &address_entry(&identifier))?,
263    ))
264}
265
266pub fn address_to_identifier(
267    address: Address, params: &ActionParams, context: &mut InternalRefContext,
268) -> DbResult<H256> {
269    Ok(BigEndianHash::from_uint(
270        &context.storage_at(params, &identifier_entry(&address))?,
271    ))
272}
273
274pub fn decode_register_info(event: &LogEntry) -> Option<StakingEvent> {
275    if event.address != POS_REGISTER_CONTRACT_ADDRESS {
276        return None;
277    }
278
279    match event.topics.first().expect("First topic is event sig") {
280        sig if sig == &RegisterEvent::EVENT_SIG => {
281            let identifier =
282                event.topics.get(1).expect("Second topic is identifier");
283            let (verified_bls_pubkey, vrf_pubkey) =
284                <(Bytes, Bytes)>::abi_decode(&event.data).unwrap();
285            Some(Register(*identifier, verified_bls_pubkey, vrf_pubkey))
286        }
287        sig if sig == &IncreaseStakeEvent::EVENT_SIG => {
288            let identifier =
289                event.topics.get(1).expect("Second topic is identifier");
290            let power = u64::abi_decode(&event.data).unwrap();
291            Some(IncreaseStake(*identifier, power))
292        }
293        sig if sig == &RetireEvent::EVENT_SIG => {
294            let identifier =
295                event.topics.get(1).expect("Second topic is identifier");
296            let votes = u64::abi_decode(&event.data).unwrap();
297            Some(Retire(*identifier, votes))
298        }
299        _ => unreachable!(),
300    }
301}
302
303pub fn make_staking_events(logs: &[LogEntry]) -> Vec<StakingEvent> {
304    logs.iter().filter_map(decode_register_info).collect()
305}
306
307pub mod entries {
308    use super::*;
309
310    pub type StorageEntryKey = Vec<u8>;
311
312    fn prefix_and_hash(prefix: u64, data: &[u8]) -> StorageEntryKey {
313        let mut hasher = Keccak::v256();
314        hasher.update(&prefix.to_be_bytes());
315        hasher.update(data);
316        let mut hash = H256::default();
317        hasher.finalize(hash.as_bytes_mut());
318        hash.as_bytes().to_vec()
319    }
320
321    #[inline]
322    pub fn index_entry(identifier: &H256) -> StorageEntryKey {
323        prefix_and_hash(0, identifier.as_bytes())
324    }
325
326    #[inline]
327    pub fn address_entry(identifier: &H256) -> StorageEntryKey {
328        prefix_and_hash(1, identifier.as_bytes())
329    }
330
331    #[inline]
332    pub fn identifier_entry(sender: &Address) -> StorageEntryKey {
333        prefix_and_hash(2, sender.as_bytes())
334    }
335}