[staking] Richer errors (#2012)

* [state] Add more information to insufficient balance for stake errors

* [staking] Wrap Sanity check errors with more information

* [errors] Add gas information to insufficient balance in gas to pay fee error message

* [errors] Fix mistaken use of Wrapf
pull/2022/head
Edgar Aroutiounian 5 years ago committed by GitHub
parent c730ab585b
commit df1ec49859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 34
      core/state_transition.go
  2. 80
      staking/types/validator.go

@ -18,7 +18,6 @@ package core
import (
"bytes"
"errors"
"math"
"math/big"
@ -26,9 +25,11 @@ import (
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/core/vm"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/internal/utils"
staking "github.com/harmony-one/harmony/staking/types"
"github.com/pkg/errors"
)
var (
@ -173,8 +174,11 @@ func (st *StateTransition) useGas(amount uint64) error {
func (st *StateTransition) buyGas() error {
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice)
if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 {
return errInsufficientBalanceForGas
if have := st.state.GetBalance(st.msg.From()); have.Cmp(mgval) < 0 {
return errors.Wrapf(
errInsufficientBalanceForGas,
"had: %s but need: %s", have.String(), mgval.String(),
)
}
if err := st.gp.SubGas(st.msg.Gas()); err != nil {
return err
@ -372,8 +376,8 @@ func (st *StateTransition) applyCreateValidatorTx(createValidator *staking.Creat
return errNegativeAmount
}
if st.state.IsValidator(createValidator.ValidatorAddress) {
return errValidatorExist
if val := createValidator.ValidatorAddress; st.state.IsValidator(val) {
return errors.Wrapf(errValidatorExist, common2.MustAddressToBech32(val))
}
v, err := staking.CreateValidatorFromNewMsg(createValidator, blockNum)
@ -381,14 +385,15 @@ func (st *StateTransition) applyCreateValidatorTx(createValidator *staking.Creat
return err
}
delegations := []staking.Delegation{}
delegations = append(delegations, staking.NewDelegation(v.Address, createValidator.Amount))
delegations := []staking.Delegation{
staking.NewDelegation(v.Address, createValidator.Amount),
}
wrapper := staking.ValidatorWrapper{*v, delegations}
if err := st.state.UpdateStakingInfo(v.Address, &wrapper); err != nil {
return err
}
st.state.SetValidatorFlag(v.Address)
return nil
}
@ -453,8 +458,9 @@ func (st *StateTransition) applyDelegateTx(delegate *staking.Delegate) error {
if bytes.Equal(delegation.DelegatorAddress.Bytes(), delegate.DelegatorAddress.Bytes()) {
delegatorExist = true
totalInUndelegation := delegation.TotalInUndelegation()
balance := stateDB.GetBalance(delegate.DelegatorAddress)
// If the sum of normal balance and the total amount of tokens in undelegation is greater than the amount to delegate
if big.NewInt(0).Add(totalInUndelegation, stateDB.GetBalance(delegate.DelegatorAddress)).Cmp(delegate.Amount) >= 0 {
if big.NewInt(0).Add(totalInUndelegation, balance).Cmp(delegate.Amount) >= 0 {
// Firstly use the tokens in undelegation to delegate (redelegate)
delegateBalance := big.NewInt(0).Set(delegate.Amount)
// Use the latest undelegated token first as it has the longest remaining locking time.
@ -468,8 +474,8 @@ func (st *StateTransition) applyDelegateTx(delegate *staking.Delegate) error {
break
}
}
delegation.Undelegations = delegation.Undelegations[:i+1]
delegation.Undelegations = delegation.Undelegations[:i+1]
delegation.Amount.Add(delegation.Amount, delegate.Amount)
err := stateDB.UpdateStakingInfo(wrapper.Validator.Address, wrapper)
@ -479,7 +485,13 @@ func (st *StateTransition) applyDelegateTx(delegate *staking.Delegate) error {
}
return err
}
return errInsufficientBalanceForStake
return errors.Wrapf(
errInsufficientBalanceForStake,
"total-delegated %s own-current-balance %s amount-to-delegate %s",
totalInUndelegation.String(),
balance.String(),
delegate.Amount.String(),
)
}
}

@ -1,20 +1,19 @@
package types
import (
"errors"
"fmt"
"math/big"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/common/denominations"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/common/denominations"
"github.com/harmony-one/harmony/crypto/hash"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/ctxerror"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
"github.com/pkg/errors"
)
// Define validator staking related const
@ -37,6 +36,7 @@ var (
errInvalidComissionRate = errors.New("commission rate, change rate and max rate should be within 0-100 percent")
errNeedAtLeastOneSlotKey = errors.New("need at least one slot key")
errBLSKeysNotMatchSigs = errors.New("bls keys and corresponding signatures could not be verified")
errNilMinSelfDelegation = errors.New("nil min self delegation")
)
// ValidatorWrapper contains validator and its delegation information
@ -113,55 +113,96 @@ func (w *ValidatorWrapper) TotalDelegation() *big.Int {
return total
}
var (
hundredPercent = numeric.NewDec(1)
zeroPercent = numeric.NewDec(0)
)
// SanityCheck checks the basic requirements
func (w *ValidatorWrapper) SanityCheck() error {
if len(w.SlotPubKeys) == 0 {
return errNeedAtLeastOneSlotKey
}
if w.Validator.MinSelfDelegation == nil {
return errNilMinSelfDelegation
}
// MinSelfDelegation must be >= 1 ONE
if w.Validator.MinSelfDelegation == nil || w.Validator.MinSelfDelegation.Cmp(big.NewInt(denominations.One)) < 0 {
return errMinSelfDelegationTooSmall
if w.Validator.MinSelfDelegation.Cmp(big.NewInt(denominations.One)) < 0 {
return errors.Wrapf(
errMinSelfDelegationTooSmall,
"delegation-given %s", w.Validator.MinSelfDelegation.String(),
)
}
// Self delegation must be >= MinSelfDelegation
if len(w.Delegations) == 0 || w.Delegations[0].Amount.Cmp(w.Validator.MinSelfDelegation) < 0 {
return errInvalidSelfDelegation
switch len(w.Delegations) {
case 0:
return errors.Wrapf(
errInvalidSelfDelegation, "no self delegation given at all",
)
default:
if w.Delegations[0].Amount.Cmp(w.Validator.MinSelfDelegation) < 0 {
return errors.Wrapf(
errInvalidSelfDelegation,
"have %s want %s", w.Delegations[0].Amount.String(), w.Validator.MinSelfDelegation,
)
}
}
// Only enforce rules on MaxTotalDelegation is it's > 0; 0 means no limit for max total delegation
if w.Validator.MaxTotalDelegation != nil && w.Validator.MaxTotalDelegation.Cmp(big.NewInt(0)) > 0 {
// MaxTotalDelegation must not be less than MinSelfDelegation
if w.Validator.MaxTotalDelegation.Cmp(w.Validator.MinSelfDelegation) < 0 {
return errInvalidMaxTotalDelegation
return errors.Wrapf(
errInvalidMaxTotalDelegation,
"max-total-delegation %s min-self-delegation %s",
w.Validator.MaxTotalDelegation.String(),
w.Validator.MinSelfDelegation.String(),
)
}
totalDelegation := w.TotalDelegation()
// Total delegation must be <= MaxTotalDelegation
if totalDelegation.Cmp(w.Validator.MaxTotalDelegation) > 0 {
return errInvalidTotalDelegation
return errors.Wrapf(
errInvalidTotalDelegation,
"total %s max-total %s",
totalDelegation.String(),
w.Validator.MaxTotalDelegation.String(),
)
}
}
hundredPercent := numeric.NewDec(1)
zeroPercent := numeric.NewDec(0)
if w.Validator.Rate.LT(zeroPercent) || w.Validator.Rate.GT(hundredPercent) {
return errInvalidComissionRate
return errors.Wrapf(
errInvalidComissionRate, "rate:%s", w.Validator.Rate.String(),
)
}
if w.Validator.MaxRate.LT(zeroPercent) || w.Validator.MaxRate.GT(hundredPercent) {
return errInvalidComissionRate
return errors.Wrapf(
errInvalidComissionRate, "rate:%s", w.Validator.MaxRate.String(),
)
}
if w.Validator.MaxChangeRate.LT(zeroPercent) || w.Validator.MaxChangeRate.GT(hundredPercent) {
return errInvalidComissionRate
return errors.Wrapf(
errInvalidComissionRate, "rate:%s", w.Validator.MaxChangeRate.String(),
)
}
if w.Validator.Rate.GT(w.Validator.MaxRate) {
return errCommissionRateTooLarge
return errors.Wrapf(
errCommissionRateTooLarge, "rate:%s", w.Validator.MaxRate.String(),
)
}
if w.Validator.MaxChangeRate.GT(w.Validator.MaxRate) {
return errCommissionRateTooLarge
return errors.Wrapf(
errCommissionRateTooLarge, "rate:%s", w.Validator.MaxChangeRate.String(),
)
}
return nil
@ -289,8 +330,7 @@ func CreateValidatorFromNewMsg(val *CreateValidator, blockNum *big.Int) (*Valida
return nil, err
}
commission := Commission{val.CommissionRates, blockNum}
pubKeys := []shard.BlsPublicKey{}
pubKeys = append(pubKeys, val.SlotPubKeys...)
pubKeys := append(val.SlotPubKeys[0:0], val.SlotPubKeys...)
if err = verifyBLSKeys(pubKeys, val.SlotKeySigs); err != nil {
return nil, err

Loading…
Cancel
Save