Skip to content

Commit

Permalink
wip: add support for pool update
Browse files Browse the repository at this point in the history
  • Loading branch information
vincenthz committed Dec 10, 2019
1 parent 95aef81 commit 530e7dc
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 74 deletions.
3 changes: 2 additions & 1 deletion chain-impl-mockchain/src/certificate/mod.rs
Expand Up @@ -9,7 +9,8 @@ use crate::transaction::{Payload, PayloadData, PayloadSlice};
pub use delegation::{OwnerStakeDelegation, StakeDelegation};
pub use pool::{
GenesisPraosLeaderHash, IndexSignatures, ManagementThreshold, PoolId, PoolOwnersSigned,
PoolPermissions, PoolRegistration, PoolRetirement, PoolSignature, PoolUpdate,
PoolPermissions, PoolRegistration, PoolRegistrationHash, PoolRetirement, PoolSignature,
PoolUpdate,
};

pub enum CertificateSlice<'a> {
Expand Down
12 changes: 4 additions & 8 deletions chain-impl-mockchain/src/certificate/test.rs
@@ -1,6 +1,5 @@
use super::*;
use crate::accounting::account::DelegationType;
use crate::leadership::genesis::GenesisPraosLeader;
use crate::rewards::TaxType;
use chain_core::mempack::{ReadBuf, Readable};
use chain_crypto::{testing, Ed25519};
Expand All @@ -21,16 +20,13 @@ impl Arbitrary for PoolRetirement {
impl Arbitrary for PoolUpdate {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let pool_id = Arbitrary::arbitrary(g);
let start_validity = DurationSeconds::from(u64::arbitrary(g)).into();
let prev = GenesisPraosLeader::arbitrary(g);
let updated_keys = GenesisPraosLeader::arbitrary(g);
let previous_keys = prev.digest();
let last_pool_reg_hash = Arbitrary::arbitrary(g);
let new_pool_reg = Arbitrary::arbitrary(g);

PoolUpdate {
pool_id,
start_validity,
previous_keys,
updated_keys,
last_pool_reg_hash,
new_pool_reg,
}
}
}
Expand Down
8 changes: 3 additions & 5 deletions chain-impl-mockchain/src/leadership/genesis/mod.rs
Expand Up @@ -205,7 +205,7 @@ mod tests {
use crate::header::HeaderId;
use crate::ledger::Ledger;
use crate::milli::Milli;
use crate::stake::{PoolStakeDistribution, PoolStakeInformation, PoolStakeTotal};
use crate::stake::{PoolStakeDistribution, PoolStakeInformation};
use crate::testing::{
builders::{GenesisPraosBlockBuilder, StakePoolBuilder},
ConfigBuilder, LedgerBuilder,
Expand Down Expand Up @@ -295,10 +295,8 @@ mod tests {
selection.distribution.to_pools.insert(
pool_id.clone(),
PoolStakeInformation {
total: PoolStakeTotal { total_stake: value },
stake_owners: PoolStakeDistribution {
accounts: HashMap::new(),
},
registration: None,
stake: PoolStakeDistribution::test_new_with_total_value(value),
},
);
}
Expand Down
3 changes: 2 additions & 1 deletion chain-impl-mockchain/src/ledger/check.rs
Expand Up @@ -133,7 +133,8 @@ pub(super) fn valid_pool_retirement_certificate(_: &certificate::PoolRetirement)
Ok(())
}

pub(super) fn valid_pool_update_certificate(_: &certificate::PoolUpdate) -> LedgerCheck {
pub(super) fn valid_pool_update_certificate(reg: &certificate::PoolUpdate) -> LedgerCheck {
valid_pool_registration_certificate(&reg.new_pool_reg)?;
Ok(())
}

Expand Down
57 changes: 40 additions & 17 deletions chain-impl-mockchain/src/ledger/ledger.rs
Expand Up @@ -157,6 +157,8 @@ custom_error! {
StakeDelegationSignatureFailed = "Stake Delegation payload signature failed",
PoolRetirementSignatureFailed = "Pool Retirement payload signature failed",
PoolUpdateSignatureFailed = "Pool update payload signature failed",
PoolUpdateLastHashDoesntMatch = "Pool update last known registration hash doesn't match",
PoolUpdateKeysUpdateNotAllowed = "Pool update doesnt currently allow keys update",
UpdateNotAllowedYet = "Update not yet allowed",
}

Expand Down Expand Up @@ -412,19 +414,12 @@ impl Ledger {
for (pool_id, pool_blocks) in leaders_log.iter() {
let pool_total_reward = reward_unit.parts.scale(*pool_blocks).unwrap();

match (
new_ledger
.delegation
.stake_pool_get(&pool_id)
.map(|reg| reg.clone()),
distribution.to_pools.get(pool_id),
) {
(Ok(pool_reg), Some(pool_distribution)) => {
match distribution.to_pools.get(pool_id) {
Some(pool_distribution) => {
new_ledger.distribute_poolid_rewards(
&mut rewards_info,
epoch,
&pool_id,
&pool_reg,
pool_total_reward,
pool_distribution,
)?;
Expand Down Expand Up @@ -454,10 +449,17 @@ impl Ledger {
reward_info: &mut EpochRewardsInfo,
epoch: Epoch,
pool_id: &PoolId,
reg: &certificate::PoolRegistration,
total_reward: Value,
distribution: &PoolStakeInformation,
) -> Result<(), Error> {
let reg = match distribution.registration {
None => {
self.pots.treasury_add(total_reward)?;
return Ok(());
}
Some(ref reg) => reg,
};

let distr = rewards::tax_cut(total_reward, &reg.rewards).unwrap();

reward_info.set_stake_pool(pool_id, distr.taxed, distr.after_tax);
Expand Down Expand Up @@ -511,8 +513,8 @@ impl Ledger {

// distribute the rest to delegators
let mut leftover_reward = distr.after_tax;
for (account, stake) in distribution.stake_owners.iter() {
let ps = PercentStake::new(*stake, distribution.total.total_stake);
for (account, stake) in distribution.stake.accounts.iter() {
let ps = PercentStake::new(*stake, distribution.stake.total);
let r = ps.scale_value(distr.after_tax);
leftover_reward = (leftover_reward - r).unwrap();
self.accounts = self
Expand Down Expand Up @@ -801,20 +803,41 @@ impl Ledger {
}

pub fn apply_pool_update<'a>(
self,
mut self,
auth_cert: &certificate::PoolUpdate,
bad: &TransactionBindingAuthData<'a>,
sig: certificate::PoolSignature,
) -> Result<Self, Error> {
check::valid_pool_update_certificate(auth_cert)?;
check::valid_pool_signature(&sig)?;

let reg = self.delegation.stake_pool_get(&auth_cert.pool_id)?;
if sig.verify(reg, bad) == Verification::Failed {
let state = self.delegation.stake_pool_get_state(&auth_cert.pool_id)?;

if auth_cert.last_pool_reg_hash != state.current_pool_registration_hash() {
return Err(Error::PoolUpdateLastHashDoesntMatch);
}

if sig.verify(&state.registration, bad) == Verification::Failed {
return Err(Error::PoolUpdateSignatureFailed);
}
// TODO do things
Err(Error::PoolUpdateNotAllowedYet)

let new_pool_reg = auth_cert.new_pool_reg.clone();

// Due to an internal limitation (latest pool registration are used by
// consensus), we don't allow cryptographic keys to be updated, although
// this is a check/error that can be removed without external changes
// once the internal limitation has been lifted.
if new_pool_reg.keys != state.registration.keys {
return Err(Error::PoolUpdateKeysUpdateNotAllowed);
}

let mut updated_state = state.clone();
updated_state.registration = Arc::new(new_pool_reg);

self.delegation
.stake_pool_set_state(&auth_cert.pool_id, updated_state)?;

Ok(self)
}

pub fn apply_stake_delegation(
Expand Down
26 changes: 25 additions & 1 deletion chain-impl-mockchain/src/stake/delegation.rs
@@ -1,4 +1,4 @@
use crate::certificate::{PoolId, PoolRegistration};
use crate::certificate::{PoolId, PoolRegistration, PoolRegistrationHash};
use crate::header::Epoch;
use crate::value::Value;
use imhamt::Hamt;
Expand Down Expand Up @@ -38,6 +38,7 @@ impl PoolLastRewards {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PoolState {
pub last_rewards: PoolLastRewards,
//pub current_rewards_params: ///
pub registration: Arc<PoolRegistration>,
}

Expand All @@ -48,6 +49,10 @@ impl PoolState {
registration: Arc::new(reg),
}
}

pub fn current_pool_registration_hash(&self) -> PoolRegistrationHash {
self.registration.to_id()
}
}

impl Debug for PoolsState {
Expand Down Expand Up @@ -107,6 +112,25 @@ impl PoolsState {
.map_or_else(|| false, |_| true)
}

pub fn stake_pool_get_state(&self, pool_id: &PoolId) -> Result<&PoolState, PoolError> {
self.stake_pools
.lookup(pool_id)
.ok_or(PoolError::NotFound(pool_id.clone()))
}

pub fn stake_pool_set_state(
&mut self,
pool_id: &PoolId,
pool_state: PoolState,
) -> Result<(), PoolError> {
self.stake_pools = self
.stake_pools
.replace(pool_id, pool_state)
.map(|r| r.0)
.map_err(|_| PoolError::NotFound(pool_id.clone()))?;
Ok(())
}

pub fn stake_pool_get(&self, pool_id: &PoolId) -> Result<&PoolRegistration, PoolError> {
self.stake_pools
.lookup(pool_id)
Expand Down
88 changes: 47 additions & 41 deletions chain-impl-mockchain/src/stake/distribution.rs
@@ -1,7 +1,13 @@
use super::stake::Stake;
use crate::{account, accounting::account::DelegationType, certificate::PoolId, utxo};
use crate::{
account,
accounting::account::DelegationType,
certificate::{PoolId, PoolRegistration},
utxo,
};
use chain_addr::{Address, Kind};
use std::collections::{hash_map, HashMap};
use std::sync::Arc;

use super::delegation::PoolsState;

Expand All @@ -22,38 +28,42 @@ pub struct StakeDistribution {

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PoolStakeInformation {
pub total: PoolStakeTotal,
pub stake_owners: PoolStakeDistribution,
pub registration: Option<Arc<PoolRegistration>>,
pub stake: PoolStakeDistribution,
}

impl PoolStakeInformation {
pub fn add_value(&mut self, id: &account::Identifier, s: Stake) {
self.stake_owners
.accounts
.entry(id.clone())
.and_modify(|c| *c += s)
.or_insert(s);
self.total.total_stake = self.total.total_stake + s;
self.stake.add(id.clone(), s)
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PoolStakeTotal {
pub total_stake: Stake,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PoolStakeDistribution {
pub total: Stake,
pub accounts: HashMap<account::Identifier, Stake>,
}

impl PoolStakeDistribution {
pub fn new() -> Self {
Self {
total: Stake::zero(),
accounts: HashMap::new(),
}
}

pub fn test_new_with_total_value(s: Stake) -> Self {
Self {
total: s,
accounts: HashMap::new(),
}
}

pub fn add(&mut self, id: account::Identifier, s: Stake) {
self.accounts.entry(id).and_modify(|c| *c += s).or_insert(s);
self.total += s;
}

pub fn to_total(&self) -> Stake {
Stake::sum(self.accounts.values().copied())
}
Expand All @@ -79,11 +89,11 @@ impl StakeDistribution {

/// Return the total stake held by the eligible stake pools.
pub fn total_stake(&self) -> Stake {
Stake::sum(self.to_pools.iter().map(|(_, pool)| pool.total.total_stake))
Stake::sum(self.to_pools.iter().map(|(_, pool)| pool.stake.total))
}

pub fn get_stake_for(&self, poolid: &PoolId) -> Option<Stake> {
self.to_pools.get(poolid).map(|psd| psd.total.total_stake)
self.to_pools.get(poolid).map(|psd| psd.stake.total)
}

pub fn get_distribution(&self, pool_id: &PoolId) -> Option<&PoolStakeInformation> {
Expand Down Expand Up @@ -148,22 +158,18 @@ pub fn get_distribution(
) -> StakeDistribution {
use std::iter::FromIterator;

let p0 = PoolStakeInformation {
total: PoolStakeTotal {
total_stake: Stake::zero(),
},
stake_owners: PoolStakeDistribution::new(),
};

let mut distribution = StakeDistribution {
unassigned: Stake::zero(),
dangling: Stake::zero(),
to_pools: HashMap::from_iter(
dstate
.stake_pools
.iter()
.map(|(id, _)| (id.clone(), p0.clone())),
),
to_pools: HashMap::from_iter(dstate.stake_pools.iter().map(|(id, pool_state)| {
(
id.clone(),
PoolStakeInformation {
registration: Some(pool_state.registration.clone()),
stake: PoolStakeDistribution::new(),
},
)
})),
};

for (identifier, account_state) in accounts.iter() {
Expand Down Expand Up @@ -474,7 +480,7 @@ mod tests {
}

let pools_total_stake: Stake =
Stake::sum(distribution.to_pools.values().map(|x| x.total.total_stake));
Stake::sum(distribution.to_pools.values().map(|x| x.stake.total));
if pools_total_stake != stake_distribution_data.pools_total() {
return TestResult::error(format!(
"Wrong Unassigned value. expected: {} but got {}",
Expand All @@ -490,8 +496,7 @@ mod tests {

impl Arbitrary for CorrectDelegationType {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let option: u8 = u8::arbitrary(g) % 3u8;
let delegation_type = match option {
let delegation_type = match u8::arbitrary(g) % 3u8 {
0 => DelegationType::NonDelegated,
1 => {
let pool_id = PoolRegistration::arbitrary(g).to_id();
Expand All @@ -518,24 +523,25 @@ mod tests {

impl CorrectDelegationType {
pub fn get_pools(&self) -> HashMap<PoolId, PoolStakeInformation> {
let p0 = PoolStakeInformation {
total: PoolStakeTotal {
total_stake: Stake::zero(),
},
stake_owners: PoolStakeDistribution::new(),
};

match &self.0 {
DelegationType::NonDelegated => HashMap::new(),
DelegationType::Full(pool_id) => {
let mut pools = HashMap::new();
pools.insert(pool_id.clone(), p0.clone());
let information = PoolStakeInformation {
registration: None,
stake: PoolStakeDistribution::new(),
};
pools.insert(pool_id.clone(), information);
pools
}
DelegationType::Ratio(delegation_ratio) => {
let mut pools = HashMap::new();
for pool_id in delegation_ratio.pools().iter().cloned().map(|x| x.0) {
pools.insert(pool_id.clone(), p0.clone());
let information = PoolStakeInformation {
registration: None,
stake: PoolStakeDistribution::new(),
};
pools.insert(pool_id.clone(), information);
}
pools
}
Expand Down

0 comments on commit 530e7dc

Please sign in to comment.