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 = if version + 1 == current_voting_version {
224 SETTLED_VOTES_ENTRIES.as_ref()
225 } else if version == current_voting_version {
226 CURRENT_VOTES_ENTRIES.as_ref()
227 } else {
228 internal_bail!(
229 "Unsupport version {} (current {})",
230 version,
231 current_voting_version
232 );
233 };
234
235 let mut answer = vec![];
236 for x in 0..params_index_max(context.spec) {
237 let slot_entry = &votes_entries[x];
238 answer.push(Vote {
239 index: x as u16,
240 votes: [
241 state.get_system_storage(
242 slot_entry[OPTION_UNCHANGE_INDEX as usize].as_ref(),
243 )?,
244 state.get_system_storage(
245 slot_entry[OPTION_INCREASE_INDEX as usize].as_ref(),
246 )?,
247 state.get_system_storage(
248 slot_entry[OPTION_DECREASE_INDEX as usize].as_ref(),
249 )?,
250 ],
251 });
252 }
253
254 Ok(answer)
255}
256
257pub fn pos_stake_for_votes(
258 version: u64, context: &mut InternalRefContext,
259) -> vm::Result<U256> {
260 let current_voting_version = (context.env.number
261 - context.spec.cip94_activation_block_number)
262 / context.spec.params_dao_vote_period
263 + 1;
264
265 let state = &context.state;
266 let pos_stake_entry = if version + 1 == current_voting_version {
267 settled_pos_staking_for_votes()
268 } else if version == current_voting_version {
269 current_pos_staking_for_votes()
270 } else {
271 internal_bail!(
272 "Unsupport version {} (current {})",
273 version,
274 current_voting_version
275 );
276 };
277 Ok(state.get_system_storage(&pos_stake_entry)?)
278}
279
280lazy_static! {
281 static ref CURRENT_VOTES_ENTRIES: [[[u8; 32]; OPTION_INDEX_MAX]; PARAMETER_INDEX_MAX] = {
282 let mut answer: [[[u8; 32]; OPTION_INDEX_MAX]; PARAMETER_INDEX_MAX] =
283 Default::default();
284 for index in 0..PARAMETER_INDEX_MAX {
285 for opt_index in 0..OPTION_INDEX_MAX {
286 answer[index][opt_index] =
287 system_storage_key::current_votes(index, opt_index);
288 }
289 }
290 answer
291 };
292 static ref SETTLED_VOTES_ENTRIES: [[[u8; 32]; OPTION_INDEX_MAX]; PARAMETER_INDEX_MAX] = {
293 let mut answer: [[[u8; 32]; OPTION_INDEX_MAX]; PARAMETER_INDEX_MAX] =
294 Default::default();
295 for index in 0..PARAMETER_INDEX_MAX {
296 for opt_index in 0..OPTION_INDEX_MAX {
297 answer[index][opt_index] =
298 system_storage_key::settled_votes(index, opt_index);
299 }
300 }
301 answer
302 };
303}
304
305#[derive(Clone, Copy, Debug, Default)]
306pub struct ParamVoteCount {
307 unchange: U256,
308 increase: U256,
309 decrease: U256,
310}
311
312impl ParamVoteCount {
313 pub fn new(unchange: U256, increase: U256, decrease: U256) -> Self {
314 Self {
315 unchange,
316 increase,
317 decrease,
318 }
319 }
320
321 pub fn from_state<U: AsRef<[u8]>>(
322 state: &State, slot_entry: &[U; 3],
323 ) -> DbResult<Self> {
324 Ok(ParamVoteCount {
325 unchange: state.get_system_storage(
326 slot_entry[OPTION_UNCHANGE_INDEX as usize].as_ref(),
327 )?,
328 increase: state.get_system_storage(
329 &slot_entry[OPTION_INCREASE_INDEX as usize].as_ref(),
330 )?,
331 decrease: state.get_system_storage(
332 &slot_entry[OPTION_DECREASE_INDEX as usize].as_ref(),
333 )?,
334 })
335 }
336
337 pub fn compute_next_params(
338 &self, old_value: U256, pos_staking_for_votes: U256,
339 ) -> U256 {
340 if self.should_update(pos_staking_for_votes) {
341 let answer = self.compute_next_params_inner(old_value);
342 let min_value = U256::from(256u64);
344 let max_value = U256::one() << 192usize;
345 if answer < min_value {
346 min_value
347 } else if answer > max_value {
348 max_value
349 } else {
350 answer
351 }
352 } else {
353 debug!("params unchanged with pos token {}", pos_staking_for_votes);
354 old_value
355 }
356 }
357
358 fn compute_next_params_inner(&self, old_value: U256) -> U256 {
359 let total = self.unchange + self.increase + self.decrease;
361
362 if total == U256::zero() || self.increase == self.decrease {
363 return old_value;
365 } else if self.increase == total {
366 return old_value * 2u64;
367 } else if self.decrease == total {
368 return old_value / 2u64;
369 };
370
371 let weight = if self.increase > self.decrease {
372 self.increase - self.decrease
373 } else {
374 self.decrease - self.increase
375 };
376 let increase = self.increase > self.decrease;
377
378 let frac_power = (U512::from(weight) << 64u64) / U512::from(total);
379 assert!(frac_power < (U512::one() << 64u64));
380 let frac_power = frac_power.as_u64();
381
382 let ratio = power_two_fractional(frac_power, increase, 96);
383 let new_value = (U512::from(old_value) * U512::from(ratio)) >> 96u64;
384
385 if new_value > (U512::one() << 192u64) {
386 return U256::one() << 192u64;
387 } else {
388 return new_value.try_into().unwrap();
389 }
390 }
391
392 fn should_update(&self, pos_staking_for_votes: U256) -> bool {
393 (self.decrease + self.increase + self.unchange)
394 >= pos_staking_for_votes * DAO_MIN_VOTE_PERCENTAGE / 100
395 }
396}
397
398#[derive(Clone, Copy, Debug, Default)]
399pub struct AllParamsVoteCount {
400 pub pow_base_reward: ParamVoteCount,
401 pub pos_reward_interest: ParamVoteCount,
402 pub storage_point_prop: ParamVoteCount,
403 pub base_fee_prop: ParamVoteCount,
404}
405
406pub fn get_settled_param_vote_count(
409 state: &State,
410) -> DbResult<AllParamsVoteCount> {
411 let pow_base_reward = ParamVoteCount::from_state(
412 state,
413 &SETTLED_VOTES_ENTRIES[POW_BASE_REWARD_INDEX as usize],
414 )?;
415 let pos_reward_interest = ParamVoteCount::from_state(
416 state,
417 &SETTLED_VOTES_ENTRIES[POS_REWARD_INTEREST_RATE_INDEX as usize],
418 )?;
419 let storage_point_prop = ParamVoteCount::from_state(
420 state,
421 &SETTLED_VOTES_ENTRIES[STORAGE_POINT_PROP_INDEX as usize],
422 )?;
423 let base_fee_prop = ParamVoteCount::from_state(
424 state,
425 &SETTLED_VOTES_ENTRIES[BASEFEE_PROP_INDEX as usize],
426 )?;
427 Ok(AllParamsVoteCount {
428 pow_base_reward,
429 pos_reward_interest,
430 storage_point_prop,
431 base_fee_prop,
432 })
433}
434
435pub fn get_settled_pos_staking_for_votes(state: &State) -> DbResult<U256> {
436 state.get_system_storage(&settled_pos_staking_for_votes())
437}
438
439pub fn settle_current_votes(state: &mut State, cip105: bool) -> DbResult<()> {
442 for index in 0..PARAMETER_INDEX_MAX {
446 for opt_index in 0..OPTION_INDEX_MAX {
447 let vote_count = state
448 .get_system_storage(&CURRENT_VOTES_ENTRIES[index][opt_index])?;
449 state.set_system_storage(
450 SETTLED_VOTES_ENTRIES[index][opt_index].to_vec(),
451 vote_count,
452 )?;
453 state.set_system_storage(
454 CURRENT_VOTES_ENTRIES[index][opt_index].to_vec(),
455 U256::zero(),
456 )?;
457 }
458 }
459 if cip105 {
460 let pos_staking =
461 state.get_system_storage(¤t_pos_staking_for_votes())?;
462 state.set_system_storage(
463 settled_pos_staking_for_votes().to_vec(),
464 pos_staking,
465 )?;
466 state.set_system_storage(
467 current_pos_staking_for_votes().to_vec(),
468 state.total_pos_staking_tokens(),
469 )?;
470 }
471 Ok(())
472}
473
474pub fn params_index_max(spec: &Spec) -> usize {
475 let mut max = PARAMETER_INDEX_MAX;
476 if !spec.cip1559 {
477 max -= 1;
478 }
479 if !spec.cip107 {
480 max -= 1;
481 }
482 max
483}
484
485mod storage_key {
495 use cfx_types::{Address, BigEndianHash, H256, U256};
496
497 use super::super::super::components::storage_layout::*;
498
499 const VOTES_SLOT: usize = 0;
500
501 pub fn versions(address: &Address) -> [u8; 32] {
503 let base = U256::from(VOTES_SLOT);
505
506 let address_slot = mapping_slot(base, H256::from(*address).into_uint());
508
509 let version_slot = address_slot;
511
512 return u256_to_array(version_slot);
513 }
514
515 pub fn votes(
516 address: &Address, index: usize, opt_index: usize,
517 ) -> [u8; 32] {
518 const TOPIC_OFFSET: [usize; 4] = [1, 2, 3, 4];
519
520 let base = U256::from(VOTES_SLOT);
522
523 let address_slot = mapping_slot(base, H256::from(*address).into_uint());
525
526 let topic_slot = address_slot + TOPIC_OFFSET[index];
528
529 let topic_slot = dynamic_slot(topic_slot);
531
532 let opt_slot = array_slot(topic_slot, opt_index, 1);
534
535 return u256_to_array(opt_slot);
536 }
537}
538
539mod system_storage_key {
551 use cfx_parameters::internal_contract_addresses::PARAMS_CONTROL_CONTRACT_ADDRESS;
552 use cfx_types::U256;
553
554 use super::super::super::{
555 components::storage_layout::*, contracts::system_storage::base_slot,
556 };
557
558 const CURRENT_VOTES_SLOT: usize = 0;
559 const SETTLED_VOTES_SLOT: usize = 1;
560 const CURRENT_POS_STAKING_SLOT: usize = 2;
561 const SETTLED_POS_STAKING_SLOT: usize = 3;
562 const STORAGE_POINT_PROP_SLOT: usize = 4;
563
564 fn vote_stats(base: U256, index: usize, opt_index: usize) -> U256 {
565 let topic_slot = base + index;
567
568 let topic_slot = dynamic_slot(topic_slot);
570
571 return array_slot(topic_slot, opt_index, 1);
573 }
574
575 pub(super) fn current_votes(index: usize, opt_index: usize) -> [u8; 32] {
576 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
578 + U256::from(CURRENT_VOTES_SLOT);
579
580 let base = dynamic_slot(base);
582
583 u256_to_array(vote_stats(base, index, opt_index))
584 }
585
586 pub(super) fn settled_votes(index: usize, opt_index: usize) -> [u8; 32] {
587 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
589 + U256::from(SETTLED_VOTES_SLOT);
590
591 let base = dynamic_slot(base);
593
594 u256_to_array(vote_stats(base, index, opt_index))
595 }
596
597 pub(super) fn current_pos_staking_for_votes() -> [u8; 32] {
598 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
600 + U256::from(CURRENT_POS_STAKING_SLOT);
601 u256_to_array(base)
602 }
603
604 pub(super) fn settled_pos_staking_for_votes() -> [u8; 32] {
605 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
607 + U256::from(SETTLED_POS_STAKING_SLOT);
608 u256_to_array(base)
609 }
610
611 pub fn storage_point_prop() -> [u8; 32] {
612 let base = base_slot(PARAMS_CONTROL_CONTRACT_ADDRESS)
614 + U256::from(STORAGE_POINT_PROP_SLOT);
615 u256_to_array(base)
616 }
617}