Skip to content
This repository has been archived by the owner on Jul 27, 2022. It is now read-only.

Commit

Permalink
Merge #1516
Browse files Browse the repository at this point in the history
1516: Problem (Fix #1515): unbond tx don't subtract fee from bonded r=tomtau a=yihuang

Solution:
- Fix the bug and add unit test


Co-authored-by: yihuang <[email protected]>
  • Loading branch information
bors[bot] and yihuang authored May 1, 2020
2 parents e55aa53 + f4300b6 commit 56b5cd8
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 29 deletions.
3 changes: 2 additions & 1 deletion chain-abci/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,9 @@ fn generate_tx_staking_change_event(tx_action: TxAction) -> Option<abci::Event>
TxPublicAction::Unbond {
unbond,
unbonded_from,
fee,
..
} => Some(StakingEvent::Unbond(&unbond.0, unbond.1, unbonded_from).into()),
} => Some(StakingEvent::Unbond(&unbond.0, unbond.1, unbonded_from, fee).into()),
TxPublicAction::NodeJoin(staking_address, council_node) => {
Some(StakingEvent::NodeJoin(&staking_address, council_node).into())
}
Expand Down
24 changes: 17 additions & 7 deletions chain-abci/src/app/staking_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ use abci::*;
use chain_core::common::{TendermintEventKey, TendermintEventType, Timespec};
use chain_core::init::coin::Coin;
use chain_core::state::account::{CouncilNode, PunishmentKind, StakedStateAddress};
use chain_core::tx::fee::Fee;

pub(crate) enum StakingEvent<'a> {
Deposit(&'a StakedStateAddress, Coin),
Unbond(&'a StakedStateAddress, Coin, Timespec),
Unbond(&'a StakedStateAddress, Coin, Timespec, Fee),
Withdraw(&'a StakedStateAddress, Coin),
NodeJoin(&'a StakedStateAddress, CouncilNode),
Reward(&'a StakedStateAddress, Coin),
Expand All @@ -27,8 +28,8 @@ impl<'a> From<StakingEvent<'a>> for Event {
StakingEvent::Deposit(staking_address, deposit_amount) => {
builder.deposit(staking_address, deposit_amount)
}
StakingEvent::Unbond(staking_address, unbond_amount, unbonded_from) => {
builder.unbond(staking_address, unbond_amount, unbonded_from)
StakingEvent::Unbond(staking_address, unbond_amount, unbonded_from, fee) => {
builder.unbond(staking_address, unbond_amount, unbonded_from, fee)
}
StakingEvent::Withdraw(staking_address, withdraw_amount) => {
builder.withdraw(staking_address, withdraw_amount)
Expand Down Expand Up @@ -85,14 +86,18 @@ impl StakingEventBuilder {
staking_address: &StakedStateAddress,
unbond_amount: Coin,
unbonded_from: Timespec,
fee: Fee,
) {
self.attributes
.push(staking_address_attribute(staking_address));
self.attributes.push(StakingEventOpType::Unbond.into());

self.attributes.push(
StakingDiffField(vec![
StakingDiff::Bonded(StakingCoinChange::Decrease, unbond_amount),
StakingDiff::Bonded(
StakingCoinChange::Decrease,
(unbond_amount + fee.to_coin()).unwrap(),
),
StakingDiff::Unbonded(StakingCoinChange::Increase, unbond_amount),
StakingDiff::UnbondedFrom(unbonded_from),
])
Expand Down Expand Up @@ -354,6 +359,7 @@ mod tests {
use super::*;
use chain_core::state::account::ConfidentialInit;
use chain_core::state::tendermint::TendermintValidatorPubKey;
use chain_core::tx::fee::Fee;
use std::str::FromStr;

mod staking_diff_field {
Expand Down Expand Up @@ -489,9 +495,13 @@ mod tests {
let any_amount = Coin::unit();
let any_unbonded_from: Timespec = 1587071014;

let event: Event =
StakingEvent::Unbond(&any_staking_address, any_amount, any_unbonded_from)
.into();
let event: Event = StakingEvent::Unbond(
&any_staking_address,
any_amount,
any_unbonded_from,
Fee::new(Coin::zero()),
)
.into();

assert_unbonded_event(event, &any_staking_address, any_amount, any_unbonded_from);
}
Expand Down
35 changes: 29 additions & 6 deletions chain-abci/src/staking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod tests {
};
use chain_core::state::tendermint::{BlockHeight, TendermintValidatorPubKey};
use chain_core::state::validator::NodeJoinRequestTx;
use chain_core::tx::fee::Fee;
use chain_storage::buffer::{Get, GetStaking, MemStore, StoreStaking};
use test_common::chain_env::get_init_network_params;

Expand Down Expand Up @@ -153,7 +154,14 @@ mod tests {
attributes: Default::default(),
};
table
.unbond(&mut store, 10, 0, BlockHeight::genesis(), &unbond)
.unbond(
&mut store,
10,
0,
BlockHeight::genesis(),
&unbond,
Fee::new(Coin::zero()),
)
.unwrap();
assert_eq!(
table.end_block(&store, 3),
Expand Down Expand Up @@ -233,7 +241,7 @@ mod tests {
attributes: Default::default(),
};
assert!(matches!(
table.unbond(&mut store, 10, 2, 3.into(), &unbond),
table.unbond(&mut store, 10, 2, 3.into(), &unbond, Fee::new(Coin::zero())),
Err(PublicTxError::Unbond(UnbondError::IsJailed))
));
assert!(matches!(
Expand Down Expand Up @@ -301,7 +309,9 @@ mod tests {
value: amount,
attributes: Default::default(),
};
table.unbond(store, 10, 0, 1.into(), &unbond).unwrap();
table
.unbond(store, 10, 0, 1.into(), &unbond, Fee::new(Coin::zero()))
.unwrap();
assert_eq!(
table.end_block(&*store, 3),
vec![(val_pk.clone(), Coin::zero().into())]
Expand Down Expand Up @@ -557,7 +567,16 @@ mod tests {
value: Coin::new(11_0000_0000).unwrap(),
attributes: Default::default(),
};
table.unbond(&mut store, 10, 10, 9.into(), &unbond).unwrap();
table
.unbond(
&mut store,
10,
10,
9.into(),
&unbond,
Fee::new(Coin::zero()),
)
.unwrap();

assert_eq!(
table.end_block(&mut store, 3),
Expand Down Expand Up @@ -642,7 +661,9 @@ mod tests {
value: unbond_amount,
attributes: Default::default(),
};
table.unbond(&mut store, 10, 1, 1.into(), &unbond).unwrap();
table
.unbond(&mut store, 10, 1, 1.into(), &unbond, Fee::new(Coin::zero()))
.unwrap();
assert_eq!(store.get(&addr1).unwrap().unbonded, unbond_amount);
let bonded = (bonded - unbond_amount).unwrap();

Expand Down Expand Up @@ -690,7 +711,9 @@ mod tests {
value: Coin::new(12_0000_0000).unwrap(),
attributes: Default::default(),
};
table.unbond(&mut store, 10, 2, 2.into(), &unbond).unwrap();
table
.unbond(&mut store, 10, 2, 2.into(), &unbond, Fee::new(Coin::zero()))
.unwrap();
assert_eq!(
table.end_block(&mut store, 3),
vec![(val_pk2.clone(), Coin::zero().into())]
Expand Down
12 changes: 10 additions & 2 deletions chain-abci/src/staking/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use chain_core::init::coin::Coin;
use chain_core::state::account::{StakedStateAddress, UnbondTx, UnjailTx, Validator};
use chain_core::state::tendermint::{BlockHeight, TendermintValidatorAddress};
use chain_core::state::validator::NodeJoinRequestTx;
use chain_core::tx::fee::Fee;
use chain_storage::buffer::StoreStaking;

use super::table::{set_staking, StakingTable};
Expand Down Expand Up @@ -145,6 +146,7 @@ impl StakingTable {
block_time: Timespec,
block_height: BlockHeight,
tx: &UnbondTx,
fee: Fee,
) -> Result<Timespec, PublicTxError> {
let mut staking = self.get_or_default(heap, &tx.from_staked_account);
if tx.nonce != staking.nonce {
Expand All @@ -153,12 +155,18 @@ impl StakingTable {
if staking.is_jailed() {
return Err(UnbondError::IsJailed.into());
}
let fee_amount = fee.to_coin();
if tx.value == Coin::zero() {
return Err(UnbondError::ZeroValue.into());
}
let unbonded = (staking.unbonded + tx.value).map_err(UnbondError::CoinError)?;
self.sub_bonded(block_time, block_height, tx.value, &mut staking)
.map_err(UnbondError::CoinError)?;
self.sub_bonded(
block_time,
block_height,
(tx.value + fee_amount).map_err(UnbondError::CoinError)?,
&mut staking,
)
.map_err(UnbondError::CoinError)?;
staking.unbonded = unbonded;

let unbonded_from = block_time.saturating_add(unbonding_period);
Expand Down
1 change: 1 addition & 0 deletions chain-abci/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ pub fn process_public_tx(
chain_info.block_time,
chain_info.block_height,
&maintx,
chain_info.min_fee_computed,
)?;

Ok(TxPublicAction::unbond(
Expand Down
10 changes: 8 additions & 2 deletions chain-abci/tests/abci_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,16 +1029,22 @@ fn all_valid_tx_types_should_commit() {
);
let witness4 = StakedStateOpWitness::new(get_ecdsa_witness(&secp, &tx4.id(), &secret_key));
let unbondtx = TxAux::PublicTx(TxPublicAux::UnbondStakeTx(tx4, witness4));
{
let bonded = {
let account = get_account(&addr, &app).expect("account not exist");
assert_eq!(account.unbonded, Coin::zero());
assert_eq!(account.nonce, 2);
}
account.bonded
};
block_commit(&mut app, unbondtx, 6);
{
let account = get_account(&addr, &app).expect("account not exist");
assert_eq!(account.unbonded, Coin::unit());
assert_eq!(account.nonce, 3);
// fee is non zero
assert!(
account.bonded < (bonded - Coin::unit()).unwrap(),
"bonded should subtract fee amount"
);
}
}

Expand Down
31 changes: 20 additions & 11 deletions client-network/src/network_ops/default_network_ops_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,6 @@ where
)
})?;

if staked_state.bonded < value {
return Err(Error::new(
ErrorKind::InvalidInput,
"Staking account does not have enough coins to unbond (synchronizing your wallet may help)",
));
}

let nonce = staked_state.nonce;

let transaction = UnbondTx::new(address, nonce, value, attributes);
Expand All @@ -257,10 +250,26 @@ where

let signature = sign_key.sign(&tx).map(StakedStateOpWitness::new)?;

Ok(TxAux::PublicTx(TxPublicAux::UnbondStakeTx(
transaction,
signature,
)))
let txaux = TxAux::PublicTx(TxPublicAux::UnbondStakeTx(transaction, signature));

let fee = self
.fee_algorithm
.calculate_for_txaux(&txaux)
.chain(|| {
(
ErrorKind::IllegalInput,
"Calculated fee is more than the maximum allowed value",
)
})?
.to_coin();
if staked_state.bonded < (value + fee).unwrap() {
return Err(Error::new(
ErrorKind::InvalidInput,
"Staking account does not have enough coins to unbond (synchronizing your wallet may help)",
));
}

Ok(txaux)
}

fn create_withdraw_unbonded_stake_transaction(
Expand Down

0 comments on commit 56b5cd8

Please sign in to comment.