Skip to content

Commit 0432614

Browse files
authored
Merge pull request #4828 from fizyk20/fix-gsug-unbonds-2
Fix handling of unbonds in global-state-update-gen
2 parents c5262ed + 44ce6d0 commit 0432614

File tree

5 files changed

+144
-101
lines changed

5 files changed

+144
-101
lines changed

utils/global-state-update-gen/src/generic.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ fn update_auction_state<T: StateReader>(
120120

121121
let validators_diff = validators_diff(&old_snapshot, &new_snapshot);
122122

123+
let bids = state.get_bids();
124+
if slash {
125+
// zero the unbonds for the removed validators independently of set_bid; set_bid will take
126+
// care of zeroing the delegators if necessary
127+
for pub_key in &validators_diff.removed {
128+
if let Some(bid) = bids.get(pub_key) {
129+
state.remove_withdraws_and_unbonds_with_bonding_purse(bid.bonding_purse());
130+
}
131+
}
132+
}
133+
123134
add_and_remove_bids(
124135
state,
125136
&validators_diff,
@@ -128,10 +139,6 @@ fn update_auction_state<T: StateReader>(
128139
slash,
129140
);
130141

131-
if slash {
132-
state.remove_withdraws_and_unbonds(&validators_diff.removed);
133-
}
134-
135142
// We need to output the validators for the next era, which are contained in the first entry
136143
// in the snapshot.
137144
Some(

utils/global-state-update-gen/src/generic/state_reader.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use casper_engine_test_support::LmdbWasmTestBuilder;
22
use casper_types::{
33
account::{Account, AccountHash},
44
system::{
5-
auction::{Bids, UnbondingPurses, WithdrawPurses, SEIGNIORAGE_RECIPIENTS_SNAPSHOT_KEY},
5+
auction::{
6+
Bids, UnbondingPurses, WithdrawPurses, SEIGNIORAGE_RECIPIENTS_SNAPSHOT_KEY,
7+
UNBONDING_DELAY_KEY,
8+
},
69
mint::TOTAL_SUPPLY_KEY,
710
},
811
Key, StoredValue,
@@ -22,6 +25,8 @@ pub trait StateReader {
2225
fn get_withdraws(&mut self) -> WithdrawPurses;
2326

2427
fn get_unbonds(&mut self) -> UnbondingPurses;
28+
29+
fn get_unbonding_delay(&mut self) -> u64;
2530
}
2631

2732
impl<'a, T> StateReader for &'a mut T
@@ -55,6 +60,10 @@ where
5560
fn get_unbonds(&mut self) -> UnbondingPurses {
5661
T::get_unbonds(self)
5762
}
63+
64+
fn get_unbonding_delay(&mut self) -> u64 {
65+
T::get_unbonding_delay(self)
66+
}
5867
}
5968

6069
impl StateReader for LmdbWasmTestBuilder {
@@ -95,4 +104,22 @@ impl StateReader for LmdbWasmTestBuilder {
95104
fn get_unbonds(&mut self) -> UnbondingPurses {
96105
LmdbWasmTestBuilder::get_unbonds(self)
97106
}
107+
108+
fn get_unbonding_delay(&mut self) -> u64 {
109+
// Find the hash of the auction contract.
110+
let auction_contract_hash = self.get_system_auction_hash();
111+
112+
let unbonding_delay_key = self
113+
.get_contract(auction_contract_hash)
114+
.expect("auction should exist")
115+
.named_keys()[UNBONDING_DELAY_KEY];
116+
117+
self.query(unbonding_delay_key)
118+
.expect("should query")
119+
.as_cl_value()
120+
.cloned()
121+
.expect("should be cl value")
122+
.into_t()
123+
.expect("should be u64")
124+
}
98125
}

utils/global-state-update-gen/src/generic/state_tracker.rs

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
use std::{
22
cmp::Ordering,
3-
collections::{btree_map::Entry, BTreeMap, BTreeSet},
3+
collections::{btree_map::Entry, BTreeMap},
44
convert::TryFrom,
55
};
66

77
use rand::Rng;
88

99
use casper_types::{
1010
account::{Account, AccountHash},
11-
system::auction::{Bid, Bids, SeigniorageRecipientsSnapshot, UnbondingPurse},
11+
system::auction::{
12+
Bid, Bids, SeigniorageRecipientsSnapshot, UnbondingPurse, UnbondingPurses, WithdrawPurse,
13+
WithdrawPurses,
14+
},
1215
AccessRights, CLValue, Key, PublicKey, StoredValue, URef, U512,
1316
};
1417

@@ -21,6 +24,7 @@ pub struct StateTracker<T> {
2124
total_supply: U512,
2225
total_supply_key: Key,
2326
accounts_cache: BTreeMap<AccountHash, Account>,
27+
withdraws_cache: BTreeMap<AccountHash, Vec<WithdrawPurse>>,
2428
unbonds_cache: BTreeMap<AccountHash, Vec<UnbondingPurse>>,
2529
purses_cache: BTreeMap<URef, U512>,
2630
bids_cache: Option<Bids>,
@@ -46,6 +50,7 @@ impl<T: StateReader> StateTracker<T> {
4650
total_supply_key,
4751
total_supply: total_supply.into_t().expect("should be U512"),
4852
accounts_cache: BTreeMap::new(),
53+
withdraws_cache: BTreeMap::new(),
4954
unbonds_cache: BTreeMap::new(),
5055
purses_cache: BTreeMap::new(),
5156
bids_cache: None,
@@ -222,7 +227,12 @@ impl<T: StateReader> StateTracker<T> {
222227
pub fn set_bid(&mut self, public_key: PublicKey, bid: Bid, slash: bool) {
223228
let maybe_current_bid = self.get_bids().get(&public_key).cloned();
224229

225-
let new_amount = *bid.staked_amount();
230+
let account_hash = public_key.to_account_hash();
231+
let already_unbonded = self.already_unbonding_amount(&account_hash, &public_key);
232+
233+
// we need to put enough funds in the bonding purse to cover the staked amount and the
234+
// amount that will be getting unbonded in the future
235+
let new_amount = *bid.staked_amount() + already_unbonded;
226236
let old_amount = maybe_current_bid
227237
.as_ref()
228238
.map(|bid| self.get_purse_balance(*bid.bonding_purse()))
@@ -234,8 +244,6 @@ impl<T: StateReader> StateTracker<T> {
234244
.unwrap()
235245
.insert(public_key.clone(), bid.clone());
236246

237-
let account_hash = public_key.to_account_hash();
238-
239247
// Replace the bid (overwrite the previous bid, if any):
240248
self.write_entry(Key::Bid(account_hash), bid.clone().into());
241249

@@ -246,6 +254,8 @@ impl<T: StateReader> StateTracker<T> {
246254
if old_bid.bonding_purse() != bid.bonding_purse() {
247255
self.set_purse_balance(*old_bid.bonding_purse(), U512::zero());
248256
self.set_purse_balance(*bid.bonding_purse(), old_amount);
257+
// the old bonding purse gets zeroed - the unbonds will get invalid, anyway
258+
self.remove_withdraws_and_unbonds_with_bonding_purse(old_bid.bonding_purse());
249259
}
250260

251261
for (delegator_pub_key, delegator) in old_bid
@@ -271,6 +281,8 @@ impl<T: StateReader> StateTracker<T> {
271281
self.set_purse_balance(*new_delegator.bonding_purse(), old_amount);
272282
}
273283
self.set_purse_balance(*delegator.bonding_purse(), U512::zero());
284+
// the old bonding purse gets zeroed - remove the delegator's unbonds
285+
self.remove_withdraws_and_unbonds_with_bonding_purse(delegator.bonding_purse());
274286
} else {
275287
let amount = self.get_purse_balance(*delegator.bonding_purse());
276288
let already_unbonding =
@@ -288,48 +300,78 @@ impl<T: StateReader> StateTracker<T> {
288300
if (slash && new_amount != old_amount) || new_amount > old_amount {
289301
self.set_purse_balance(*bid.bonding_purse(), new_amount);
290302
} else if new_amount < old_amount {
291-
let already_unbonded = self.already_unbonding_amount(&account_hash, &public_key);
292303
self.create_unbonding_purse(
293304
*bid.bonding_purse(),
294305
&public_key,
295306
&public_key,
296-
old_amount - new_amount - already_unbonded,
307+
old_amount - new_amount,
297308
);
298309
}
299310

300311
for (delegator_public_key, delegator) in bid.delegators() {
301312
let old_amount = self.get_purse_balance(*delegator.bonding_purse());
302-
let new_amount = *delegator.staked_amount();
313+
let already_unbonded =
314+
self.already_unbonding_amount(&account_hash, delegator_public_key);
315+
let new_amount = *delegator.staked_amount() + already_unbonded;
303316
if (slash && new_amount != old_amount) || new_amount > old_amount {
304-
self.set_purse_balance(*delegator.bonding_purse(), *delegator.staked_amount());
317+
self.set_purse_balance(*delegator.bonding_purse(), new_amount);
305318
} else if new_amount < old_amount {
306-
let already_unbonded = self.already_unbonding_amount(&account_hash, &public_key);
307319
self.create_unbonding_purse(
308320
*delegator.bonding_purse(),
309321
&public_key,
310322
delegator_public_key,
311-
old_amount - new_amount - already_unbonded,
323+
old_amount - new_amount,
312324
);
313325
}
314326
}
315327
}
316328

329+
fn get_withdraws(&mut self) -> WithdrawPurses {
330+
let mut result = self.reader.get_withdraws();
331+
for (acc, purses) in &self.withdraws_cache {
332+
result.insert(*acc, purses.clone());
333+
}
334+
result
335+
}
336+
337+
fn get_unbonds(&mut self) -> UnbondingPurses {
338+
let mut result = self.reader.get_unbonds();
339+
for (acc, purses) in &self.unbonds_cache {
340+
result.insert(*acc, purses.clone());
341+
}
342+
result
343+
}
344+
345+
fn write_withdraw(&mut self, account_hash: AccountHash, withdraws: Vec<WithdrawPurse>) {
346+
self.withdraws_cache.insert(account_hash, withdraws.clone());
347+
self.write_entry(
348+
Key::Withdraw(account_hash),
349+
StoredValue::Withdraw(withdraws),
350+
);
351+
}
352+
353+
fn write_unbond(&mut self, account_hash: AccountHash, unbonds: Vec<UnbondingPurse>) {
354+
self.unbonds_cache.insert(account_hash, unbonds.clone());
355+
self.write_entry(Key::Unbond(account_hash), StoredValue::Unbonding(unbonds));
356+
}
357+
317358
/// Returns the sum of already unbonding purses for the given validator account & unbonder.
318359
fn already_unbonding_amount(&mut self, account: &AccountHash, unbonder: &PublicKey) -> U512 {
319-
let existing_purses = self
320-
.reader
321-
.get_unbonds()
322-
.get(account)
323-
.cloned()
324-
.unwrap_or_default();
360+
let current_era = *self.read_snapshot().1.keys().next().unwrap();
361+
let unbonding_delay = self.reader.get_unbonding_delay();
362+
let limit_era = current_era.saturating_sub(unbonding_delay);
363+
364+
let existing_purses = self.get_unbonds().get(account).cloned().unwrap_or_default();
325365
let existing_purses_legacy = self
326-
.reader
327366
.get_withdraws()
328367
.get(account)
329368
.cloned()
330369
.unwrap_or_default()
331370
.into_iter()
332-
.map(UnbondingPurse::from);
371+
.map(UnbondingPurse::from)
372+
// There may be some leftover old legacy purses that haven't been purged - this is to
373+
// make sure that we don't accidentally take them into account.
374+
.filter(|purse| purse.era_of_creation() >= limit_era);
333375

334376
existing_purses_legacy
335377
.chain(existing_purses)
@@ -338,26 +380,23 @@ impl<T: StateReader> StateTracker<T> {
338380
.sum()
339381
}
340382

341-
/// Generates the writes to the global state that will remove the pending withdraws and unbonds
342-
/// of all the old validators that will cease to be validators, and slashes their unbonding
343-
/// purses.
344-
pub fn remove_withdraws_and_unbonds(&mut self, removed: &BTreeSet<PublicKey>) {
345-
let withdraws = self.reader.get_withdraws();
346-
let unbonds = self.reader.get_unbonds();
347-
for removed_validator in removed {
348-
let acc = removed_validator.to_account_hash();
349-
if let Some(withdraw_set) = withdraws.get(&acc) {
350-
for withdraw in withdraw_set {
351-
self.set_purse_balance(*withdraw.bonding_purse(), U512::zero());
352-
}
353-
self.write_entry(Key::Withdraw(acc), StoredValue::Withdraw(vec![]));
383+
pub fn remove_withdraws_and_unbonds_with_bonding_purse(&mut self, affected_purse: &URef) {
384+
let withdraws = self.get_withdraws();
385+
let unbonds = self.get_unbonds();
386+
387+
for (acc, mut purses) in withdraws {
388+
let old_len = purses.len();
389+
purses.retain(|purse| purse.bonding_purse().addr() != affected_purse.addr());
390+
if purses.len() != old_len {
391+
self.write_withdraw(acc, purses);
354392
}
393+
}
355394

356-
if let Some(unbond_set) = unbonds.get(&acc) {
357-
for unbond in unbond_set {
358-
self.set_purse_balance(*unbond.bonding_purse(), U512::zero());
359-
}
360-
self.write_entry(Key::Unbond(acc), StoredValue::Unbonding(vec![]));
395+
for (acc, mut purses) in unbonds {
396+
let old_len = purses.len();
397+
purses.retain(|purse| purse.bonding_purse().addr() != affected_purse.addr());
398+
if purses.len() != old_len {
399+
self.write_unbond(acc, purses);
361400
}
362401
}
363402
}

0 commit comments

Comments
 (0)