Skip to content

Commit

Permalink
go/tendermint/staking/state.SlashEscrow: return slashed amount
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrus committed Jan 21, 2021
1 parent b462af8 commit f5e5ace
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 22 deletions.
6 changes: 3 additions & 3 deletions go/consensus/tendermint/apps/staking/slashing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ func TestOnEvidenceConsensusEquivocation(t *testing.T) {
})
require.NoError(err, "SetConsensusParameters")

// Should fail as the validator has no stake (which is an invariant violation as a validator
// needs to have some stake).
// Should not fail if the validator has no stake (which is in any case an
// invariant violation as a validator needs to have some stake).
err = onEvidenceConsensusEquivocation(ctx, validatorAddress, 1, now, 1)
require.Error(err, "should fail when validator has no stake")
require.NoError(err, "should fail when validator has no stake")

// Computes entity's staking address.
addr := staking.NewAddress(ent.ID)
Expand Down
38 changes: 21 additions & 17 deletions go/consensus/tendermint/apps/staking/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,10 @@ func (s *MutableState) SetGovernanceDeposits(ctx context.Context, q *quantity.Qu
}

func slashPool(dst *quantity.Quantity, p *staking.SharePool, amount, total *quantity.Quantity) error {
if total.IsZero() {
// Nothing to slash.
return nil
}
// slashAmount = amount * p.Balance / total
slashAmount := p.Balance.Clone()
if err := slashAmount.Mul(amount); err != nil {
Expand All @@ -624,55 +628,55 @@ func slashPool(dst *quantity.Quantity, p *staking.SharePool, amount, total *quan

// SlashEscrow slashes the escrow balance and the escrow-but-undergoing-debonding
// balance of the account, transferring it to the global common pool, returning
// true iff the amount actually slashed is > 0.
// the amount actually slashed.
//
// WARNING: This is an internal routine to be used to implement staking policy,
// and MUST NOT be exposed outside of backend implementations.
func (s *MutableState) SlashEscrow(
ctx *abciAPI.Context,
fromAddr staking.Address,
amount *quantity.Quantity,
) (bool, error) {
) (*quantity.Quantity, error) {
var slashed quantity.Quantity

commonPool, err := s.CommonPool(ctx)
if err != nil {
return false, fmt.Errorf("tendermint/staking: failed to query common pool for slash: %w", err)
return nil, fmt.Errorf("tendermint/staking: failed to query common pool for slash: %w", err)
}

from, err := s.Account(ctx, fromAddr)
if err != nil {
return false, fmt.Errorf("tendermint/staking: failed to query account %s: %w", fromAddr, err)
return nil, fmt.Errorf("tendermint/staking: failed to query account %s: %w", fromAddr, err)
}

// Compute the amount we need to slash each pool. The amount is split
// between the pools based on relative total balance.
total := from.Escrow.Active.Balance.Clone()
if err = total.Add(&from.Escrow.Debonding.Balance); err != nil {
return false, fmt.Errorf("tendermint/staking: compute total balance: %w", err)
return nil, fmt.Errorf("tendermint/staking: account total balance: %w", err)
}
// Nothing to slash.
if total.IsZero() {
return &slashed, nil
}

var slashed quantity.Quantity
if err = slashPool(&slashed, &from.Escrow.Active, amount, total); err != nil {
return false, fmt.Errorf("tendermint/staking: failed slashing active escrow: %w", err)
return nil, fmt.Errorf("tendermint/staking: failed slashing active escrow: %w", err)
}
if err = slashPool(&slashed, &from.Escrow.Debonding, amount, total); err != nil {
return false, fmt.Errorf("tendermint/staking: failed slashing debonding escrow: %w", err)
return nil, fmt.Errorf("tendermint/staking: failed slashing debonding escrow: %w", err)
}

if slashed.IsZero() {
return false, nil
}

totalSlashed := slashed.Clone()

if err = quantity.Move(commonPool, &slashed, totalSlashed); err != nil {
return false, fmt.Errorf("tendermint/staking: failed moving stake to common pool: %w", err)
return nil, fmt.Errorf("tendermint/staking: failed moving stake to common pool: %w", err)
}

if err = s.SetCommonPool(ctx, commonPool); err != nil {
return false, fmt.Errorf("tendermint/staking: failed to set common pool: %w", err)
return nil, fmt.Errorf("tendermint/staking: failed to set common pool: %w", err)
}
if err = s.SetAccount(ctx, fromAddr, from); err != nil {
return false, fmt.Errorf("tendermint/staking: failed to set account. %w", err)
return nil, fmt.Errorf("tendermint/staking: failed to set account: %w", err)
}

if !ctx.IsCheckOnly() {
Expand All @@ -683,7 +687,7 @@ func (s *MutableState) SlashEscrow(
ctx.EmitEvent(api.NewEventBuilder(AppName).Attribute(KeyTakeEscrow, ev))
}

return true, nil
return totalSlashed, nil
}

// TransferFromCommon transfers up to the amount from the global common pool
Expand Down
4 changes: 2 additions & 2 deletions go/consensus/tendermint/apps/staking/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,9 @@ func TestRewardAndSlash(t *testing.T) {
require.NoError(err, "Account")
require.Equal(mustInitQuantity(t, 300), escrowAccount.Escrow.Active.Balance, "reward late epoch - escrow active escrow")

slashedNonzero, err := s.SlashEscrow(ctx, escrowAddr, mustInitQuantityP(t, 40))
slashed, err := s.SlashEscrow(ctx, escrowAddr, mustInitQuantityP(t, 40))
require.NoError(err, "slash escrow")
require.True(slashedNonzero, "slashed nonzero")
require.False(slashed.IsZero(), "slashed nonzero")

// Loss of 40 base units.
delegatorAccount, err = s.Account(ctx, delegatorAddr)
Expand Down

0 comments on commit f5e5ace

Please sign in to comment.