From 740aeac3e7db5910386355ec15c8b3f1660bbe18 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Thu, 14 Nov 2019 16:01:37 -0800 Subject: [PATCH] Add low-level validator/delegator block reward logic --- core/state/statedb.go | 38 ++++++++++++++++++++++++++++++++++--- core/vm/interface.go | 2 +- numeric/decimal.go | 12 ++++++------ staking/types/delegation.go | 1 + staking/types/validator.go | 10 ++++++++++ 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/core/state/statedb.go b/core/state/statedb.go index 94c0cf1d2..2397f197b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -23,6 +23,8 @@ import ( "math/big" "sort" + "github.com/harmony-one/harmony/numeric" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" @@ -727,9 +729,39 @@ func (db *DB) IsValidator(addr common.Address) bool { return so.IsValidator(db.db) } -func (db *DB) AddReward(validator common.Address, reward *big.Int) { - //TODO: implement reward logic - // the reward will be distributed to all the delegators based on stake percentage. +// AddReward distributes the reward to all the delegators based on stake percentage. +func (db *DB) AddReward(validator common.Address, reward *big.Int) error { + rewardPool := big.NewInt(0).Set(reward) + + wrapper := db.GetStakingInfo(validator) + if wrapper == nil { + return errors.New("failed to distribute rewards: validator does not exist") + } + + // Payout commission + commissionInt := wrapper.Validator.CommissionRates.Rate.MulInt(reward).RoundInt() + wrapper.Delegations[0].Reward.Add(wrapper.Delegations[0].Reward, commissionInt) + rewardPool.Sub(rewardPool, commissionInt) + + totalRewardForDelegators := big.NewInt(0).Set(rewardPool) + + // Payout each delegator's reward pro-rata + totalDelegationDec := numeric.NewDecFromBigInt(wrapper.TotalDelegation()) + for i := range wrapper.Delegations { + delegation := wrapper.Delegations[i] + percentage := numeric.NewDecFromBigInt(delegation.Amount).Quo(totalDelegationDec) // percentage = / + rewardInt := percentage.MulInt(totalRewardForDelegators).RoundInt() + + delegation.Reward.Add(delegation.Reward, rewardInt) + rewardPool.Sub(rewardPool, rewardInt) + } + + // The last remaining bit belongs to the validator (remember the validator's self delegation is always at index 0) + if rewardPool.Cmp(big.NewInt(0)) > 0 { + wrapper.Delegations[0].Reward.Add(wrapper.Delegations[0].Reward, rewardPool) + } + + return db.UpdateStakingInfo(validator, wrapper) } func (db *DB) CollectReward(delegator common.Address) { diff --git a/core/vm/interface.go b/core/vm/interface.go index e486cd0f9..052e297ed 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -45,7 +45,7 @@ type StateDB interface { SetValidatorFlag(common.Address) UnsetValidatorFlag(common.Address) IsValidator(common.Address) bool - AddReward(common.Address, *big.Int) + AddReward(common.Address, *big.Int) error CollectReward(common.Address) AddRefund(uint64) diff --git a/numeric/decimal.go b/numeric/decimal.go index 263651633..05afa710c 100644 --- a/numeric/decimal.go +++ b/numeric/decimal.go @@ -483,9 +483,9 @@ func (d Dec) RoundInt64() int64 { } // RoundInt round the decimal using bankers rounding -//func (d Dec) RoundInt() big.Int { -// return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.Int)) -//} +func (d Dec) RoundInt() *big.Int { + return chopPrecisionAndRoundNonMutative(d.Int) +} //___________________________________________________________________________________ @@ -509,9 +509,9 @@ func (d Dec) TruncateInt64() int64 { } // TruncateInt truncates the decimals from the number and returns an Int -//func (d Dec) TruncateInt() big.Int { -// return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) -//} +func (d Dec) TruncateInt() *big.Int { + return chopPrecisionAndTruncateNonMutative(d.Int) +} // TruncateDec truncates the decimals from the number and returns a Dec func (d Dec) TruncateDec() Dec { diff --git a/staking/types/delegation.go b/staking/types/delegation.go index a6ff4ac3a..f9fa710f4 100644 --- a/staking/types/delegation.go +++ b/staking/types/delegation.go @@ -18,6 +18,7 @@ var ( type Delegation struct { DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"` Amount *big.Int `json:"amount" yaml:"amount"` + Reward *big.Int `json:"reward" yaml:"reward"` Entries []*UndelegationEntry `json:"entries" yaml:"entries"` } diff --git a/staking/types/validator.go b/staking/types/validator.go index 8623a7c67..18c4a2fbb 100644 --- a/staking/types/validator.go +++ b/staking/types/validator.go @@ -68,6 +68,15 @@ func printSlotPubKeys(pubKeys []shard.BlsPublicKey) string { return str } +// TotalDelegation - return the total amount of token in delegation +func (w *ValidatorWrapper) TotalDelegation() *big.Int { + total := big.NewInt(0) + for _, entry := range w.Delegations { + total.Add(total, entry.Amount) + } + return total +} + // Description - some possible IRL connections type Description struct { Name string `json:"name" yaml:"name"` // name @@ -157,6 +166,7 @@ func CreateValidatorFromNewMsg(val *CreateValidator) (*Validator, error) { commission := Commission{val.CommissionRates, new(big.Int)} pubKeys := []shard.BlsPublicKey{} pubKeys = append(pubKeys, val.SlotPubKeys...) + // TODO: a new validator should have a minimum of 1 token as self delegation, and that should be added as a delegation entry here. v := Validator{val.ValidatorAddress, pubKeys, val.Amount, new(big.Int), val.MinSelfDelegation, val.MaxTotalDelegation, false, commission, desc, big.NewInt(0)}