1use std::convert::TryInto;
6
7use cfx_math::power_two_fractional;
8use cfx_parameters::consensus_internal::DAO_MIN_VOTE_PERCENTAGE;
9use cfx_statedb::Result as DbResult;
10use cfx_types::{Address, U256, U512};
11use cfx_vm_types::{self as vm, ActionParams, Spec};
12use lazy_static::lazy_static;
13
14use super::super::{
15 components::{InternalRefContext, SolidityEventTrait},
16 contracts::params_control::*,
17 impls::staking::get_vote_power,
18};
19use crate::{internal_bail, state::State};
20
21use self::system_storage_key::{
22 current_pos_staking_for_votes, settled_pos_staking_for_votes,
23};
24pub use system_storage_key::storage_point_prop;
25
26pub fn cast_vote(
27 address: Address, version: u64, votes: Vec<Vote>, params: &ActionParams,
28 context: &mut InternalRefContext,
29) -> vm::Result<()> {
30 let current_voting_version = (context.env.number
34 - context.spec.cip94_activation_block_number)
35 / context.spec.params_dao_vote_period
36 + 1;
37 if version != current_voting_version {
38 internal_bail!(
39 "vote version unmatch: current={} voted={}",
40 current_voting_version,
41 version
42 );
43 }
44 let old_version =
45 context.storage_at(params, &storage_key::versions(&address))?;
46 let is_new_vote = old_version.as_u64() != version;
47
48 let mut vote_counts = [None; PARAMETER_INDEX_MAX];
49 for vote in votes {
50 if vote.index >= params_index_max(context.spec) as u16 {
51 internal_bail!("invalid vote index or opt_index");
52 }
53 let entry = &mut vote_counts[vote.index as usize];
54 match entry {
55 None => {
56 *entry = Some(vote.votes);
57 }
58 Some(_) => {
59 internal_bail!(
60 "Parameter voted twice: vote.index={}",
61 vote.index
62 );
63 }
64 }
65 }
66 if is_new_vote {
67 for index in 0..params_index_max(context.spec) {
71 if vote_counts[index].is_none() {
72 vote_counts[index] = Some([U256::zero(); OPTION_INDEX_MAX]);
73 }
74 }
75 }
76
77 let vote_power = get_vote_power(
78 address,
79 U256::from(context.env.number),
80 context.env.number,
81 context.state,
82 )?;
83 debug!("vote_power:{}", vote_power);
84 for index in 0..params_index_max(context.spec) {
85 if vote_counts[index].is_none() {
86 continue;
87 }
88 let param_vote = vote_counts[index].unwrap();
89 let total_counts = param_vote[0]
90 .saturating_add(param_vote[1])
91 .saturating_add(param_vote[2]);
92 if total_counts > vote_power {
93 internal_bail!(
94 "not enough vote power: power={} votes={}",
95 vote_power,
96 total_counts
97 );
98 }
99 let mut old_votes = [U256::zero(); 3];
100 for opt_index in 0..OPTION_INDEX_MAX {
101 let vote_slot = storage_key::votes(&address, index, opt_index);
102 let old_vote = if is_new_vote {
103 U256::zero()
104 } else {
105 context.storage_at(params, &vote_slot)?
106 };
107 old_votes[opt_index] = old_vote;
108 debug!(
109 "index:{}, opt_index{}, old_vote: {}, new_vote: {}",
110 index, opt_index, old_vote, param_vote[opt_index]
111 );
112
113 if param_vote[opt_index] != old_vote {
115 let old_total_votes = context.state.get_system_storage(
116 &CURRENT_VOTES_ENTRIES[index][opt_index],
117 )?;
118 let new_total_votes =
121 old_total_votes + param_vote[opt_index] - old_vote;
122
123 debug!(
124 "old_total_vote: {}, new_total_vote:{}",
125 old_total_votes, new_total_votes
126 );
127 context.state.set_system_storage(
128 CURRENT_VOTES_ENTRIES[index][opt_index].to_vec(),
129 new_total_votes,
130 )?;
131 }
132
133 context.set_storage(
135 params,
136 vote_slot.to_vec(),
137 param_vote[opt_index],
138 )?;
139 }
140 if !is_new_vote {
141 RevokeEvent::log(
142 &(version, address, index as u16),
143 &old_votes,
144 params,
145 context,
146 )?;
147 }
148 VoteEvent::log(
149 &(version, address, index as u16),
150 &vote_counts[index].as_ref().unwrap(),
151 params,
152 context,
153 )?;
154 }
155 if is_new_vote {
156 context.set_storage(
157 params,
158 storage_key::versions(&address).to_vec(),
159 U256::from(version),
160 )?;
161 }
162 Ok(())
163}
164
165pub fn cast_vote_gas(length: usize, spec: &Spec) -> usize {
166 let version_gas =
167 2 * spec.cold_sload_gas + spec.sha3_gas + spec.sstore_reset_gas;
168
169 let io_gas_per_topic = 3
170 * (2 * spec.cold_sload_gas
171 + 2 * spec.sstore_reset_gas
172 + 2 * spec.sha3_gas);
173
174 let log_gas_per_topic = 2 * spec.log_gas
175 + 8 * spec.log_topic_gas
176 + 32 * 3 * 2 * spec.log_data_gas;
177
178 version_gas + length * (io_gas_per_topic + log_gas_per_topic)
179}
180
181pub fn read_vote(
182 address: Address, params: &ActionParams, context: &mut InternalRefContext,
183) -> vm::Result<Vec<Vote>> {
184 let current_voting_version = (context.env.number
185 - context.spec.cip94_activation_block_number)
186 / context.spec.params_dao_vote_period
187 + 1;
188 let version = context
189 .storage_at(params, &storage_key::versions(&address))?
190 .as_u64();
191 let deprecated_vote = version != current_voting_version;
192
193 let mut votes_list = Vec::new();
194 for index in 0..params_index_max(context.spec) {
195 let mut param_vote = [U256::zero(); OPTION_INDEX_MAX];
196 if !deprecated_vote {
197 for opt_index in 0..OPTION_INDEX_MAX {
198 let votes = context.storage_at(
199 params,
200 &storage_key::votes(&address, index, opt_index),
201 )?;
202 param_vote[opt_index] = votes;
203 }
204 }
205 votes_list.push(Vote {
206 index: index as u16,
207 votes: param_vote,
208 })
209 }
210 Ok(votes_list)
211}
212
213pub fn total_votes(
214 version: u64, context: &mut InternalRefContext,
215) -> vm::Result<Vec<Vote>> {
216 let current_voting_version = (context.env.number
217 - context.spec.cip94_activation_block_number)
218 / context.spec.params_dao_vote_period
219 + 1;
220
221 let state = &context.state;
222
223 let votes_entries =
224 if version.checked_add(1) == Some(current_voting_version) {
225 SETTLED_VOTES_ENTRIES.as_ref()
226 } else if version == current_voting_version {
227 CURRENT_VOTES_ENTRIES.as_ref()
228 } else {
229 internal_bail!(
230 "Unsupport version {} (current {})",
231 version,
232 current_voting_version
233 );
234 };
235
236 let mut answer = vec![];
237 for x in 0..params_index_max(context.spec) {
238 let slot_entry = &votes_entries[x];
239 answer.push(Vote {
240 index: x as u16,
241 votes: [
242 state.get_system_storage(
243 slot_entry[OPTION_UNCHANGE_INDEX as usize].as_ref(),
244 )?,
245 state.get_system_storage(
246 slot_entry[OPTION_INCREASE_INDEX as usize].as_ref(),
247 )?,
248 state.get_system_storage(
249 slot_entry[OPTION_DECREASE_INDEX as usize].as_ref(),
250 )?,
251 ],
252 });
253 }
254
255 Ok(answer)
256}
257
258pub fn pos_stake_for_votes(
259 version: u64, context: &mut InternalRefContext,
260) -> vm::Result<U256> {
261 let current_voting_version = (context.env.number
262 - context.spec.cip94_activation_block_number)
263 / context.spec.params_dao_vote_period
264 + 1;
265
266 let state = &context.state;
267 let pos_stake_entry =
268 if version.checked_add(1) == Some(current_voting_version) {
269 settled_pos_staking_for_votes()
270 } else if version == current_voting_version {
271 current_pos_staking_for_votes()
272 } else {
273 internal_bail!(
274 "Unsupport version {} (current {})",
275 version,
276 current_voting_version
277 );
278 };
279 Ok(state.get_system_storage(&pos_stake_entry)?)
280}
281
282lazy_static! {
283 static ref CURRENT_VOTES_ENTRIES: [[[u8; 32]; OPTION_INDEX_MAX]; PARAMETER_INDEX_MAX] = {
284 let mut answer: [[[u8; 32]; OPTION_INDEX_MAX]; PARAMETER_INDEX_MAX] =
285 Default::default();
286 for index in 0..PARAMETER_INDEX_MAX {
287 for opt_index in 0..OPTION_INDEX_MAX {
288 answer[index][opt_index] =
289 system_storage_key::current_votes(index, opt_index);
290 }
291 }
292 answer
293 };
294 static ref SETTLED_VOTES_ENTRIES: [[[u8; 32]; OPTION_INDEX_MAX]; PARAMETER_INDEX_MAX] = {
295 let mut answer: [[[u8; 32]; OPTION_INDEX_MAX]; PARAMETER_INDEX_MAX] =
296 Default::default();
297 for index in 0..PARAMETER_INDEX_MAX {
298 for opt_index in 0..OPTION_INDEX_MAX {
299 answer[index][opt_index] =
300 system_storage_key::settled_votes(index, opt_index);
301 }
302 }
303 answer
304 };
305}
306
307#[derive(Clone, Copy, Debug, Default)]
308pub struct ParamVoteCount {
309 unchange: U256,
310 increase: U256,
311 decrease: U256,
312}
313
314impl ParamVoteCount {
315 pub fn new(unchange: U256, increase: U256, decrease: U256) -> Self {
316 Self {
317 unchange,
318 increase,
319 decrease,
320 }
321 }
322
323 pub fn from_state<U: AsRef<[u8]>>(
324 state: &State, slot_entry: &[U; 3],
325 ) -> DbResult<Self> {
326 Ok(ParamVoteCount {
327 unchange: state.get_system_storage(
328 slot_entry[OPTION_UNCHANGE_INDEX as usize].as_ref(),
329 )?,
330 increase: state.get_system_storage(
331 &slot_entry[OPTION_INCREASE_INDEX as usize].as_ref(),
332 )?,
333 decrease: state.get_system_storage(
334 &slot_entry[OPTION_DECREASE_INDEX as usize].as_ref(),
335 )?,
336 })
337 }
338
339 pub fn compute_next_params(
340 &self, old_value: U256, pos_staking_for_votes: U256,
341 ) -> U256 {
342 if self.should_update(pos_staking_for_votes) {
343 let answer = self.compute_next_params_inner(old_value);
344 let min_value = U256::from(256u64);
346 let max_value = U256::one() << 192usize;
347 if answer < min_value {
348 min_value
349 } else if answer > max_value {
350 max_value
351 } else {
352 answer
353 }
354 } else {
355 debug!("params unchanged with pos token {}", pos_staking_for_votes);
356 old_value
357 }
358 }
359
360 fn compute_next_params_inner(&self, old_value: U256) -> U256 {
361 let total = self.unchange + self.increase + self.decrease;
363
364 if total == U256::zero() || self.increase == self.decrease {
365 return old_value;
367 } else if self.increase == total {
368 return old_value * 2u64;
369 } else if self.decrease == total {
370 return old_value / 2u64;
371 };
372
373 let weight = if self.increase > self.decrease {
374 self.increase - self.decrease
375 } else {
376 self.decrease - self.increase
377 };
378 let increase = self.increase > self.decrease;
379
380 let frac_power = (U512::from(weight) << 64u64) / U512::from(total);
381 assert!(frac_power < (U512::one() << 64u64));
382 let frac_power = frac_power.as_u64();
383
384 let ratio = power_two_fractional(frac_power, increase, 96);
385 let new_value = (U512::from(old_value) * U512::from(ratio)) >> 96u64;
386
387 if new_value > (U512::one() << 192u64) {
388 return U256::one() << 192u64;
389 } else {
390 return new_value.try_into().unwrap();
391 }
392 }
393
394 fn should_update(&self, pos_staking_for_votes: U256) -> bool {
395 (self.decrease + self.increase + self.unchange)
396 >= pos_staking_for_votes * DAO_MIN_VOTE_PERCENTAGE / 100
397 }
398}
399
400#[derive(Clone, Copy, Debug, Default)]
401pub struct AllParamsVoteCount {
402 pub pow_base_reward: ParamVoteCount,
403 pub pos_reward_interest: ParamVoteCount,
404 pub storage_point_prop: ParamVoteCount,
405 pub base_fee_prop: ParamVoteCount,
406}
407
408pub fn get_settled_param_vote_count(
411 state: &State,
412) -> DbResult<AllParamsVoteCount> {
413 let pow_base_reward = ParamVoteCount::from_state(
414 state,
415 &SETTLED_VOTES_ENTRIES[POW_BASE_REWARD_INDEX as usize],
416 )?;
417 let pos_reward_interest = ParamVoteCount::from_state(
418 state,
419 &SETTLED_VOTES_ENTRIES[POS_REWARD_INTEREST_RATE_INDEX as usize],
420 )?;
421 let storage_point_prop = ParamVoteCount::from_state(
422 state,
423 &SETTLED_VOTES_ENTRIES[STORAGE_POINT_PROP_INDEX as usize],
424 )?;
425 let base_fee_prop = ParamVoteCount::from_state(
426 state,
427 &SETTLED_VOTES_ENTRIES[BASEFEE_PROP_INDEX as usize],
428 )?;
429 Ok(AllParamsVoteCount {
430 pow_base_reward,
431 pos_reward_interest,
432 storage_point_prop,
433 base_fee_prop,
434 })
435}
436
437pub fn get_settled_pos_staking_for_votes(state: &State) -> DbResult<U256> {
438 state.get_system_storage(&settled_pos_staking_for_votes())
439}
440
441pub fn settle_current_votes(state: &mut State, cip105: bool) -> DbResult<()> {
444 for index in 0..PARAMETER_INDEX_MAX {
448 for opt_index in 0..OPTION_INDEX_MAX {
449 let vote_count = state
450 .get_system_storage(&CURRENT_VOTES_ENTRIES[index][opt_index])?;
451 state.set_system_storage(
452 SETTLED_VOTES_ENTRIES[index][opt_index].to_vec(),
453 vote_count,
454 )?;
455 state.set_system_storage(
456 CURRENT_VOTES_ENTRIES[index][opt_index].to_vec(),
457 U256::zero(),
458 )?;
459 }
460 }
461 if cip105 {
462 let pos_staking =
463 state.get_system_storage(¤t_pos_staking_for_votes())?;
464 state.set_system_storage(
465 settled_pos_staking_for_votes().to_vec(),
466 pos_staking,
467 )?;
468 state.set_system_storage(
469 current_pos_staking_for_votes().to_vec(),
470 state.total_pos_staking_tokens(),
471 )?;
472 }
473 Ok(())
474}
475
476pub fn params_index_max(spec: &Spec) -> usize {
477 let mut max = PARAMETER_INDEX_MAX;
478 if !spec.cip1559 {
479 max -= 1;
480 }
481 if !spec.cip107 {
482 max -= 1;
483 }
484 max
485}
486
487mod storage_key {
497 use cfx_types::{Address, BigEndianHash, H256, U256};
498
499 use super::super::super::components::storage_layout::*;
500
501 const VOTES_SLOT: usize = 0;
502
503 pub fn versions(address: &Address) -> [u8; 32] {
505 let base = U256::from(VOTES_SLOT);
507
508 let address_slot = mapping_slot(base, H256::from(*address).into_uint());
510
511 let version_slot = address_slot;
513
514 return u256_to_array(version_slot);
515 }
516
517 pub fn votes(
518 address: &Address, index: usize, opt_index: usize,
519 ) -> [u8; 32] {
520 const TOPIC_OFFSET: [usize; 4] = [1, 2, 3, 4];
521
522 let base = U256::from(VOTES_SLOT);
524
525 let address_slot = mapping_slot(base, H256::from(*address).into_uint());
527
528 let topic_slot = address_slot + TOPIC_OFFSET[index];
530
531 let topic_slot = dynamic_slot(topic_slot);
533
534 let opt_slot = array_slot(topic_slot, opt_index, 1);
536
537 return u256_to_array(opt_slot);
538 }
539}
540
541mod system_storage_key {
553 use cfx_parameters::internal_contract_addresses::PARAMS_CONTROL_CONTRACT_ADDRESS;
554 use cfx_types::U256;
555
556 use super::super::super::{
557 components::storage_layout::*, contracts::system_storage::base_slot,
558 };
559
560 const CURRENT_VOTES_SLOT: usize = 0;
561 const SETTLED_VOTES_SLOT: usize = 1;
562 const CURRENT_POS_STAKING_SLOT: usize = 2;
563 const SETTLED_POS_STAKING_SLOT: usize = 3;
564 const STORAGE_POINT_PROP_SLOT: usize = 4;
565
566 fn vote_stats(base: U256, index: usize, opt_index: usize) -> U256 {
567 let topic_slot = base + index;
569
570 let topic_slot = dynamic_slot(topic_slot);
572
573 return array_slot(topic_slot, opt_index, 1);
575 }
576
577 pub(super) fn current_votes(index: usize, opt_index: usize) -> [u8; 32] {
578 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
580 + U256::from(CURRENT_VOTES_SLOT);
581
582 let base = dynamic_slot(base);
584
585 u256_to_array(vote_stats(base, index, opt_index))
586 }
587
588 pub(super) fn settled_votes(index: usize, opt_index: usize) -> [u8; 32] {
589 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
591 + U256::from(SETTLED_VOTES_SLOT);
592
593 let base = dynamic_slot(base);
595
596 u256_to_array(vote_stats(base, index, opt_index))
597 }
598
599 pub(super) fn current_pos_staking_for_votes() -> [u8; 32] {
600 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
602 + U256::from(CURRENT_POS_STAKING_SLOT);
603 u256_to_array(base)
604 }
605
606 pub(super) fn settled_pos_staking_for_votes() -> [u8; 32] {
607 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
609 + U256::from(SETTLED_POS_STAKING_SLOT);
610 u256_to_array(base)
611 }
612
613 pub fn storage_point_prop() -> [u8; 32] {
614 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
616 + U256::from(STORAGE_POINT_PROP_SLOT);
617 u256_to_array(base)
618 }
619}