cfx_executor/internal_contract/impls/
pos.rs1use 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, 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, 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}