Optimize delegation reward distribution with caching (#2839)

* Optimize delegation reward distribution with caching

* fix build

* fix lint
pull/2841/head
Rongjian Lan 5 years ago committed by GitHub
parent cc37995a31
commit db8f1a05b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      core/blockchain.go
  2. 4
      core/chain_makers.go
  3. 2
      core/evm.go
  4. 5
      core/rawdb/accessors_offchain.go
  5. 18
      core/state/statedb.go
  6. 4
      core/vm/interface.go
  7. 4
      hmy/api_backend.go
  8. 2
      internal/chain/engine.go
  9. 81
      internal/chain/reward.go
  10. 2
      node/node_handler.go
  11. 10
      shard/committee/assignment.go
  12. 4
      staking/apr/compute.go
  13. 4
      staking/availability/measure.go
  14. 4
      staking/slash/double-sign.go
  15. 6
      staking/slash/double-sign_test.go
  16. 8
      staking/types/validator.go
  17. 49
      test/chain/reward/main.go

@ -2187,14 +2187,14 @@ func (bc *BlockChain) ReadValidatorInformation(
func (bc *BlockChain) ReadValidatorSnapshotAtEpoch( func (bc *BlockChain) ReadValidatorSnapshotAtEpoch(
epoch *big.Int, epoch *big.Int,
addr common.Address, addr common.Address,
) (*staking.ValidatorWrapper, error) { ) (*staking.ValidatorSnapshot, error) {
return rawdb.ReadValidatorSnapshot(bc.db, addr, epoch) return rawdb.ReadValidatorSnapshot(bc.db, addr, epoch)
} }
// ReadValidatorSnapshot reads the snapshot staking information of given validator address // ReadValidatorSnapshot reads the snapshot staking information of given validator address
func (bc *BlockChain) ReadValidatorSnapshot( func (bc *BlockChain) ReadValidatorSnapshot(
addr common.Address, addr common.Address,
) (*staking.ValidatorWrapper, error) { ) (*staking.ValidatorSnapshot, error) {
epoch := bc.CurrentBlock().Epoch() epoch := bc.CurrentBlock().Epoch()
if cached, ok := bc.validatorCache.Get("validator-snapshot-" + string(addr.Bytes()) + epoch.String()); ok { if cached, ok := bc.validatorCache.Get("validator-snapshot-" + string(addr.Bytes()) + epoch.String()); ok {
by := cached.([]byte) by := cached.([]byte)
@ -2202,7 +2202,8 @@ func (bc *BlockChain) ReadValidatorSnapshot(
if err := rlp.DecodeBytes(by, &v); err != nil { if err := rlp.DecodeBytes(by, &v); err != nil {
return nil, err return nil, err
} }
return &v, nil s := staking.ValidatorSnapshot{&v, epoch}
return &s, nil
} }
return rawdb.ReadValidatorSnapshot(bc.db, addr, epoch) return rawdb.ReadValidatorSnapshot(bc.db, addr, epoch)
} }

@ -275,12 +275,12 @@ func (cr *fakeChainReader) ReadValidatorInformation(
} }
func (cr *fakeChainReader) ReadValidatorSnapshot( func (cr *fakeChainReader) ReadValidatorSnapshot(
addr common.Address, addr common.Address,
) (*staking.ValidatorWrapper, error) { ) (*staking.ValidatorSnapshot, error) {
return nil, nil return nil, nil
} }
func (cr *fakeChainReader) ReadValidatorSnapshotAtEpoch( func (cr *fakeChainReader) ReadValidatorSnapshotAtEpoch(
epoch *big.Int, addr common.Address, epoch *big.Int, addr common.Address,
) (*staking.ValidatorWrapper, error) { ) (*staking.ValidatorSnapshot, error) {
return nil, nil return nil, nil
} }

@ -40,7 +40,7 @@ type ChainContext interface {
ReadDelegationsByDelegator(common.Address) (staking.DelegationIndexes, error) ReadDelegationsByDelegator(common.Address) (staking.DelegationIndexes, error)
// ReadValidatorSnapshot returns the snapshot of validator at the beginning of current epoch. // ReadValidatorSnapshot returns the snapshot of validator at the beginning of current epoch.
ReadValidatorSnapshot(common.Address) (*staking.ValidatorWrapper, error) ReadValidatorSnapshot(common.Address) (*staking.ValidatorSnapshot, error)
} }
// NewEVMContext creates a new context for use in the EVM. // NewEVMContext creates a new context for use in the EVM.

@ -171,7 +171,7 @@ func DeleteCXReceiptsProofSpent(db DatabaseDeleter, shardID uint32, number uint6
// ReadValidatorSnapshot retrieves validator's snapshot by its address // ReadValidatorSnapshot retrieves validator's snapshot by its address
func ReadValidatorSnapshot( func ReadValidatorSnapshot(
db DatabaseReader, addr common.Address, epoch *big.Int, db DatabaseReader, addr common.Address, epoch *big.Int,
) (*staking.ValidatorWrapper, error) { ) (*staking.ValidatorSnapshot, error) {
data, err := db.Get(validatorSnapshotKey(addr, epoch)) data, err := db.Get(validatorSnapshotKey(addr, epoch))
if err != nil || len(data) == 0 { if err != nil || len(data) == 0 {
utils.Logger().Info().Err(err).Msg("ReadValidatorSnapshot") utils.Logger().Info().Err(err).Msg("ReadValidatorSnapshot")
@ -184,7 +184,8 @@ func ReadValidatorSnapshot(
Msg("Unable to decode validator snapshot from database") Msg("Unable to decode validator snapshot from database")
return nil, err return nil, err
} }
return &v, nil s := staking.ValidatorSnapshot{&v, epoch}
return &s, nil
} }
// WriteValidatorSnapshot stores validator's snapshot by its address // WriteValidatorSnapshot stores validator's snapshot by its address

@ -753,7 +753,7 @@ var (
) )
// AddReward distributes the reward to all the delegators based on stake percentage. // AddReward distributes the reward to all the delegators based on stake percentage.
func (db *DB) AddReward(snapshot *stk.ValidatorWrapper, reward *big.Int) error { func (db *DB) AddReward(snapshot *stk.ValidatorWrapper, reward *big.Int, shareLookup map[common.Address]numeric.Dec) error {
if reward.Cmp(common.Big0) == 0 { if reward.Cmp(common.Big0) == 0 {
utils.Logger().Info().RawJSON("validator", []byte(snapshot.String())). utils.Logger().Info().RawJSON("validator", []byte(snapshot.String())).
Msg("0 given as reward") Msg("0 given as reward")
@ -783,19 +783,17 @@ func (db *DB) AddReward(snapshot *stk.ValidatorWrapper, reward *big.Int) error {
) )
rewardPool.Sub(rewardPool, commissionInt) rewardPool.Sub(rewardPool, commissionInt)
} }
totalRewardForDelegators := big.NewInt(0).Set(rewardPool)
// Payout each delegator's reward pro-rata // Payout each delegator's reward pro-rata
totalDelegationDec := numeric.NewDecFromBigInt(snapshot.TotalDelegation()) totalRewardForDelegators := big.NewInt(0).Set(rewardPool)
for i := range snapshot.Delegations { for i := range snapshot.Delegations {
delegation := snapshot.Delegations[i] delegation := snapshot.Delegations[i]
// NOTE percentage = <this_delegator_amount>/<total_delegation> percentage, ok := shareLookup[delegation.DelegatorAddress]
if totalDelegationDec.Equal(zero) {
utils.Logger().Info(). if !ok {
RawJSON("validator-snapshot", []byte(snapshot.String())). continue
Msg("zero total delegation during AddReward delegation payout")
return nil
} }
percentage := numeric.NewDecFromBigInt(delegation.Amount).Quo(totalDelegationDec)
rewardInt := percentage.MulInt(totalRewardForDelegators).RoundInt() rewardInt := percentage.MulInt(totalRewardForDelegators).RoundInt()
curDelegation := curValidator.Delegations[i] curDelegation := curValidator.Delegations[i]
curDelegation.Reward.Add(curDelegation.Reward, rewardInt) curDelegation.Reward.Add(curDelegation.Reward, rewardInt)

@ -19,6 +19,8 @@ package vm
import ( import (
"math/big" "math/big"
"github.com/harmony-one/harmony/numeric"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
staking "github.com/harmony-one/harmony/staking/types" staking "github.com/harmony-one/harmony/staking/types"
@ -45,7 +47,7 @@ type StateDB interface {
SetValidatorFlag(common.Address) SetValidatorFlag(common.Address)
UnsetValidatorFlag(common.Address) UnsetValidatorFlag(common.Address)
IsValidator(common.Address) bool IsValidator(common.Address) bool
AddReward(*staking.ValidatorWrapper, *big.Int) error AddReward(*staking.ValidatorWrapper, *big.Int, map[common.Address]numeric.Dec) error
AddRefund(uint64) AddRefund(uint64)
SubRefund(uint64) SubRefund(uint64)

@ -406,7 +406,7 @@ func (b *APIBackend) GetValidatorInformation(
} }
computed := availability.ComputeCurrentSigning( computed := availability.ComputeCurrentSigning(
snapshot, wrapper, snapshot.Validator, wrapper,
) )
beaconChainBlocks := uint64( beaconChainBlocks := uint64(
b.hmy.BeaconChain().CurrentBlock().Header().Number().Int64(), b.hmy.BeaconChain().CurrentBlock().Header().Number().Int64(),
@ -470,7 +470,7 @@ func (b *APIBackend) GetTotalStakingSnapshot() *big.Int {
snapshot, _ := b.hmy.BlockChain().ReadValidatorSnapshot(candidates[i]) snapshot, _ := b.hmy.BlockChain().ReadValidatorSnapshot(candidates[i])
validator, _ := b.hmy.BlockChain().ReadValidatorInformation(candidates[i]) validator, _ := b.hmy.BlockChain().ReadValidatorInformation(candidates[i])
if !committee.IsEligibleForEPoSAuction( if !committee.IsEligibleForEPoSAuction(
snapshot, validator, b.hmy.BlockChain().CurrentBlock().Epoch(), snapshot, validator,
) { ) {
continue continue
} }

@ -444,7 +444,7 @@ func applySlashes(
// Apply the slashes, invariant: assume been verified as legit slash by this point // Apply the slashes, invariant: assume been verified as legit slash by this point
var slashApplied *slash.Application var slashApplied *slash.Application
votingPower, err := lookupVotingPower( votingPower, err := lookupVotingPower(
header.Epoch(), new(big.Int).SetUint64(key.epoch), subComm, big.NewInt(int64(key.epoch)), subComm,
) )
if err != nil { if err != nil {
return errors.Wrapf(err, "could not lookup cached voting power in slash application") return errors.Wrapf(err, "could not lookup cached voting power in slash application")

@ -5,6 +5,9 @@ import (
"math/big" "math/big"
"sort" "sort"
"github.com/harmony-one/harmony/numeric"
types2 "github.com/harmony-one/harmony/staking/types"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/block" "github.com/harmony-one/harmony/block"
@ -42,11 +45,12 @@ func ballotResultBeaconchain(
} }
var ( var (
votingPowerCache singleflight.Group votingPowerCache singleflight.Group
delegateShareCache singleflight.Group
) )
func lookupVotingPower( func lookupVotingPower(
epoch, beaconCurrentEpoch *big.Int, subComm *shard.Committee, epoch *big.Int, subComm *shard.Committee,
) (*votepower.Roster, error) { ) (*votepower.Roster, error) {
key := fmt.Sprintf("%s-%d", epoch.String(), subComm.ShardID) key := fmt.Sprintf("%s-%d", epoch.String(), subComm.ShardID)
results, err, _ := votingPowerCache.Do( results, err, _ := votingPowerCache.Do(
@ -55,6 +59,12 @@ func lookupVotingPower(
if err != nil { if err != nil {
return nil, err return nil, err
} }
// For new calc, remove old data from 3 epochs ago
deleteEpoch := big.NewInt(0).Sub(epoch, big.NewInt(3))
deleteKey := fmt.Sprintf("%s-%d", deleteEpoch.String(), subComm.ShardID)
votingPowerCache.Forget(deleteKey)
return votingPower, nil return votingPower, nil
}, },
) )
@ -62,14 +72,49 @@ func lookupVotingPower(
return nil, err return nil, err
} }
// TODO consider if this is the best way to clear the cache return results.(*votepower.Roster), nil
if new(big.Int).Sub(beaconCurrentEpoch, epoch).Cmp(common.Big3) == 1 { }
go func() {
votingPowerCache.Forget(key) // Lookup or compute the shares of stake for all delegators in a validator
}() func lookupDelegatorShares(
snapshot *types2.ValidatorSnapshot,
) (map[common.Address]numeric.Dec, error) {
epoch := snapshot.Epoch
validatorSnapshot := snapshot.Validator
key := fmt.Sprintf("%s-%s", epoch.String(), validatorSnapshot.Address.Hex())
shares, err, _ := delegateShareCache.Do(
key, func() (interface{}, error) {
result := map[common.Address]numeric.Dec{}
totalDelegationDec := numeric.NewDecFromBigInt(validatorSnapshot.TotalDelegation())
if totalDelegationDec.IsZero() {
utils.Logger().Info().
RawJSON("validator-snapshot", []byte(validatorSnapshot.String())).
Msg("zero total delegation during AddReward delegation payout")
return result, nil
}
for i := range validatorSnapshot.Delegations {
delegation := validatorSnapshot.Delegations[i]
// NOTE percentage = <this_delegator_amount>/<total_delegation>
percentage := numeric.NewDecFromBigInt(delegation.Amount).Quo(totalDelegationDec)
result[delegation.DelegatorAddress] = percentage
}
// For new calc, remove old data from 3 epochs ago
deleteEpoch := big.NewInt(0).Sub(epoch, big.NewInt(3))
deleteKey := fmt.Sprintf("%s-%s", deleteEpoch.String(), validatorSnapshot.Address.Hex())
votingPowerCache.Forget(deleteKey)
return result, nil
},
)
if err != nil {
return nil, err
} }
return results.(*votepower.Roster), nil return shares.(map[common.Address]numeric.Dec), nil
} }
// AccumulateRewardsAndCountSigs credits the coinbase of the given block with the mining // AccumulateRewardsAndCountSigs credits the coinbase of the given block with the mining
@ -91,6 +136,7 @@ func AccumulateRewardsAndCountSigs(
if bc.Config().IsStaking(header.Epoch()) && if bc.Config().IsStaking(header.Epoch()) &&
bc.CurrentHeader().ShardID() != shard.BeaconChainShardID { bc.CurrentHeader().ShardID() != shard.BeaconChainShardID {
return network.EmptyPayout, nil return network.EmptyPayout, nil
} }
// After staking // After staking
@ -142,9 +188,8 @@ func AccumulateRewardsAndCountSigs(
); err != nil { ); err != nil {
return network.EmptyPayout, err return network.EmptyPayout, err
} }
beaconCurrentEpoch := beaconChain.CurrentHeader().Epoch()
votingPower, err := lookupVotingPower( votingPower, err := lookupVotingPower(
headerE, beaconCurrentEpoch, &subComm, headerE, &subComm,
) )
if err != nil { if err != nil {
return network.EmptyPayout, err return network.EmptyPayout, err
@ -167,7 +212,12 @@ func AccumulateRewardsAndCountSigs(
voter.OverallPercent.Quo(beaconExternalShare), voter.OverallPercent.Quo(beaconExternalShare),
).RoundInt() ).RoundInt()
newRewards.Add(newRewards, due) newRewards.Add(newRewards, due)
if err := state.AddReward(snapshot, due); err != nil {
shares, err := lookupDelegatorShares(snapshot)
if err != nil {
return network.EmptyPayout, err
}
if err := state.AddReward(snapshot.Validator, due, shares); err != nil {
return network.EmptyPayout, err return network.EmptyPayout, err
} }
beaconP = append(beaconP, reward.Payout{ beaconP = append(beaconP, reward.Payout{
@ -237,7 +287,7 @@ func AccumulateRewardsAndCountSigs(
} }
votingPower, err := lookupVotingPower( votingPower, err := lookupVotingPower(
epoch, beaconCurrentEpoch, subComm, epoch, subComm,
) )
if err != nil { if err != nil {
@ -303,7 +353,12 @@ func AccumulateRewardsAndCountSigs(
} }
due := resultsHandle[bucket][payThem].payout due := resultsHandle[bucket][payThem].payout
newRewards.Add(newRewards, due) newRewards.Add(newRewards, due)
if err := state.AddReward(snapshot, due); err != nil {
shares, err := lookupDelegatorShares(snapshot)
if err != nil {
return network.EmptyPayout, err
}
if err := state.AddReward(snapshot.Validator, due, shares); err != nil {
return network.EmptyPayout, err return network.EmptyPayout, err
} }
shardP = append(shardP, reward.Payout{ shardP = append(shardP, reward.Payout{

@ -464,7 +464,7 @@ func (node *Node) PostConsensusProcessing(
return return
} }
computed := availability.ComputeCurrentSigning( computed := availability.ComputeCurrentSigning(
snapshot, wrapper, snapshot.Validator, wrapper,
) )
beaconChainBlocks := uint64(node.Beaconchain().CurrentBlock().Header().Number().Int64()) % beaconChainBlocks := uint64(node.Beaconchain().CurrentBlock().Header().Number().Int64()) %
shard.Schedule.BlocksPerEpoch() shard.Schedule.BlocksPerEpoch()

@ -37,7 +37,7 @@ type Reader interface {
type StakingCandidatesReader interface { type StakingCandidatesReader interface {
CurrentBlock() *types.Block CurrentBlock() *types.Block
ReadValidatorInformation(addr common.Address) (*staking.ValidatorWrapper, error) ReadValidatorInformation(addr common.Address) (*staking.ValidatorWrapper, error)
ReadValidatorSnapshot(addr common.Address) (*staking.ValidatorWrapper, error) ReadValidatorSnapshot(addr common.Address) (*staking.ValidatorSnapshot, error)
ValidatorCandidates() []common.Address ValidatorCandidates() []common.Address
} }
@ -142,7 +142,7 @@ func prepareOrders(
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !IsEligibleForEPoSAuction(snapshot, validator, stakedReader.CurrentBlock().Epoch()) { if !IsEligibleForEPoSAuction(snapshot, validator) {
continue continue
} }
@ -184,18 +184,18 @@ func prepareOrders(
} }
// IsEligibleForEPoSAuction .. // IsEligibleForEPoSAuction ..
func IsEligibleForEPoSAuction(snapshot, validator *staking.ValidatorWrapper, curEpoch *big.Int) bool { func IsEligibleForEPoSAuction(snapshot *staking.ValidatorSnapshot, validator *staking.ValidatorWrapper) bool {
// This original condition to check whether a validator is in last committee is not stable // This original condition to check whether a validator is in last committee is not stable
// because cross-links may arrive after the epoch ends and it still got counted into the // because cross-links may arrive after the epoch ends and it still got counted into the
// NumBlocksToSign, making this condition to be true when the validator is actually not in committee // NumBlocksToSign, making this condition to be true when the validator is actually not in committee
//if snapshot.Counters.NumBlocksToSign.Cmp(validator.Counters.NumBlocksToSign) != 0 { //if snapshot.Counters.NumBlocksToSign.Cmp(validator.Counters.NumBlocksToSign) != 0 {
// Check whether the validator is in current committee // Check whether the validator is in current committee
if validator.LastEpochInCommittee.Cmp(curEpoch) == 0 { if validator.LastEpochInCommittee.Cmp(snapshot.Epoch) == 0 {
// validator was in last epoch's committee // validator was in last epoch's committee
// validator with below-threshold signing activity won't be considered for next epoch // validator with below-threshold signing activity won't be considered for next epoch
// and their status will be turned to inactive in FinalizeNewBlock // and their status will be turned to inactive in FinalizeNewBlock
computed := availability.ComputeCurrentSigning(snapshot, validator) computed := availability.ComputeCurrentSigning(snapshot.Validator, validator)
if computed.IsBelowThreshold { if computed.IsBelowThreshold {
return false return false
} }

@ -33,7 +33,7 @@ type Reader interface {
ReadValidatorSnapshotAtEpoch( ReadValidatorSnapshotAtEpoch(
epoch *big.Int, epoch *big.Int,
addr common.Address, addr common.Address,
) (*staking.ValidatorWrapper, error) ) (*staking.ValidatorSnapshot, error)
} }
const ( const (
@ -121,7 +121,7 @@ func ComputeForValidator(
estimatedRewardPerYear, err := expectedRewardPerYear( estimatedRewardPerYear, err := expectedRewardPerYear(
block.Header(), headerOneEpochAgo, block.Header(), headerOneEpochAgo,
validatorNow, oneSnapshotAgo, validatorNow, oneSnapshotAgo.Validator,
) )
if err != nil { if err != nil {

@ -137,7 +137,7 @@ func IncrementValidatorSigningCounts(
type Reader interface { type Reader interface {
ReadValidatorSnapshot( ReadValidatorSnapshot(
addr common.Address, addr common.Address,
) (*staking.ValidatorWrapper, error) ) (*staking.ValidatorSnapshot, error)
} }
// ComputeCurrentSigning returns (signed, toSign, quotient, error) // ComputeCurrentSigning returns (signed, toSign, quotient, error)
@ -210,7 +210,7 @@ func ComputeAndMutateEPOSStatus(
return err return err
} }
computed := ComputeCurrentSigning(snapshot, wrapper) computed := ComputeCurrentSigning(snapshot.Validator, wrapper)
utils.Logger(). utils.Logger().
Info().Msg("check if signing percent is meeting required threshold") Info().Msg("check if signing percent is meeting required threshold")

@ -460,7 +460,7 @@ func Apply(
// stake, rest are external delegations. // stake, rest are external delegations.
// Bottom line: everyone will be slashed under the same rule. // Bottom line: everyone will be slashed under the same rule.
if err := delegatorSlashApply( if err := delegatorSlashApply(
snapshot, current, rate, state, snapshot.Validator, current, rate, state,
slash.Reporter, slash.Evidence.Epoch, slashDiff, slash.Reporter, slash.Evidence.Epoch, slashDiff,
); err != nil { ); err != nil {
return nil, err return nil, err
@ -474,7 +474,7 @@ func Apply(
Msg("about to update staking info for a validator after a slash") Msg("about to update staking info for a validator after a slash")
if err := state.UpdateValidatorWrapper( if err := state.UpdateValidatorWrapper(
snapshot.Address, current, snapshot.Validator.Address, current,
); err != nil { ); err != nil {
return nil, err return nil, err
} }

@ -377,13 +377,13 @@ func exampleSlashRecords() Records {
} }
type mockOutSnapshotReader struct { type mockOutSnapshotReader struct {
snapshot staking.ValidatorWrapper snapshot staking.ValidatorSnapshot
} }
func (m mockOutSnapshotReader) ReadValidatorSnapshotAtEpoch( func (m mockOutSnapshotReader) ReadValidatorSnapshotAtEpoch(
epoch *big.Int, epoch *big.Int,
addr common.Address, addr common.Address,
) (*staking.ValidatorWrapper, error) { ) (*staking.ValidatorSnapshot, error) {
return &m.snapshot, nil return &m.snapshot, nil
} }
@ -448,7 +448,7 @@ func testScenario(
// state looks like as of this point // state looks like as of this point
slashResult, err := Apply( slashResult, err := Apply(
mockOutSnapshotReader{*s.snapshot}, mockOutSnapshotReader{staking.ValidatorSnapshot{s.snapshot, big.NewInt(0)}},
stateHandle, stateHandle,
slashes, slashes,
numeric.MustNewDecFromStr( numeric.MustNewDecFromStr(

@ -69,7 +69,7 @@ type ValidatorSnapshotReader interface {
ReadValidatorSnapshotAtEpoch( ReadValidatorSnapshotAtEpoch(
epoch *big.Int, epoch *big.Int,
addr common.Address, addr common.Address,
) (*ValidatorWrapper, error) ) (*ValidatorSnapshot, error)
} }
type counters struct { type counters struct {
@ -91,6 +91,12 @@ type ValidatorWrapper struct {
BlockReward *big.Int `json:"-"` BlockReward *big.Int `json:"-"`
} }
// ValidatorSnapshot contains validator snapshot and the corresponding epoch
type ValidatorSnapshot struct {
Validator *ValidatorWrapper
Epoch *big.Int
}
// Computed represents current epoch // Computed represents current epoch
// availability measures, mostly for RPC // availability measures, mostly for RPC
type Computed struct { type Computed struct {

@ -6,6 +6,8 @@ import (
"math/rand" "math/rand"
"time" "time"
"github.com/harmony-one/harmony/internal/utils"
common2 "github.com/ethereum/go-ethereum/common" common2 "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/harmony-one/bls/ffi/go/bls" "github.com/harmony-one/bls/ffi/go/bls"
@ -27,7 +29,6 @@ var (
) )
func init() { func init() {
bls.Init(bls.BLS12_381) bls.Init(bls.BLS12_381)
} }
@ -62,12 +63,12 @@ func createValidator() *staking.CreateValidator {
MaxRate: maxRate, MaxRate: maxRate,
MaxChangeRate: maxChangeRate, MaxChangeRate: maxChangeRate,
} }
minSelfDel := big.NewInt(1e18) minSelfDel := new(big.Int).Mul(big.NewInt(5e18), big.NewInt(2000))
maxTotalDel := big.NewInt(9e18) maxTotalDel := new(big.Int).Mul(big.NewInt(5e18), big.NewInt(100000))
pubKey, pubSig := generateBLSKeySigPair() pubKey, pubSig := generateBLSKeySigPair()
slotPubKeys := []shard.BLSPublicKey{pubKey} slotPubKeys := []shard.BLSPublicKey{pubKey}
slotKeySigs := []shard.BLSSignature{pubSig} slotKeySigs := []shard.BLSSignature{pubSig}
amount := big.NewInt(5e18) amount := new(big.Int).Mul(big.NewInt(5e18), big.NewInt(2000))
v := staking.CreateValidator{ v := staking.CreateValidator{
ValidatorAddress: validatorAddress, ValidatorAddress: validatorAddress,
Description: desc, Description: desc,
@ -84,10 +85,13 @@ func createValidator() *staking.CreateValidator {
func main() { func main() {
statedb, _ := state.New(common2.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ := state.New(common2.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
msg := createValidator() msg := createValidator()
statedb.AddBalance(msg.ValidatorAddress, big.NewInt(5e18)) statedb.AddBalance(msg.ValidatorAddress, new(big.Int).Mul(big.NewInt(5e18), big.NewInt(2000)))
validator, _ := core.VerifyAndCreateValidatorFromMsg( validator, err := core.VerifyAndCreateValidatorFromMsg(
statedb, postStakingEpoch, big.NewInt(0), msg, statedb, postStakingEpoch, big.NewInt(0), msg,
) )
if err != nil {
fmt.Print(err)
}
for i := 0; i < 100000; i++ { for i := 0; i < 100000; i++ {
validator.Delegations = append(validator.Delegations, staking.Delegation{ validator.Delegations = append(validator.Delegations, staking.Delegation{
common2.Address{}, common2.Address{},
@ -101,9 +105,36 @@ func main() {
startTime := time.Now() startTime := time.Now()
validator, _ = statedb.ValidatorWrapper(msg.ValidatorAddress) validator, _ = statedb.ValidatorWrapper(msg.ValidatorAddress)
fmt.Printf("Total num delegations: %d\n", len(validator.Delegations))
statedb.AddReward(validator, big.NewInt(1000))
endTime := time.Now() endTime := time.Now()
fmt.Printf("Time required to read validator: %f seconds\n", endTime.Sub(startTime).Seconds())
startTime = time.Now()
shares, _ := lookupDelegatorShares(validator)
endTime = time.Now()
fmt.Printf("Time required to calc percentage %d delegations: %f seconds\n", len(validator.Delegations), endTime.Sub(startTime).Seconds())
startTime = time.Now()
statedb.AddReward(validator, big.NewInt(1000), shares)
endTime = time.Now()
fmt.Printf("Time required to reward a validator with %d delegations: %f seconds\n", len(validator.Delegations), endTime.Sub(startTime).Seconds()) fmt.Printf("Time required to reward a validator with %d delegations: %f seconds\n", len(validator.Delegations), endTime.Sub(startTime).Seconds())
} }
func lookupDelegatorShares(
snapshot *staking.ValidatorWrapper,
) (result map[common2.Address]numeric.Dec, err error) {
result = map[common2.Address]numeric.Dec{}
totalDelegationDec := numeric.NewDecFromBigInt(snapshot.TotalDelegation())
for i := range snapshot.Delegations {
delegation := snapshot.Delegations[i]
// NOTE percentage = <this_delegator_amount>/<total_delegation>
if totalDelegationDec.IsZero() {
utils.Logger().Info().
RawJSON("validator-snapshot", []byte(snapshot.String())).
Msg("zero total delegation during AddReward delegation payout")
return nil, nil
}
percentage := numeric.NewDecFromBigInt(delegation.Amount).Quo(totalDelegationDec)
result[delegation.DelegatorAddress] = percentage
}
return result, nil
}

Loading…
Cancel
Save