[offchain][effective][validator][rpc] Give reason why booted from committeee, move effective stake in rpc outside metrics (#2699)

pull/2698/head
Edgar Aroutiounian 5 years ago committed by GitHub
parent d21f68d34d
commit 56d19d2a84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 88
      core/blockchain.go
  2. 9
      core/offchain.go
  3. 12
      hmy/api_backend.go
  4. 35
      shard/shard_state.go
  5. 1
      staking/effective/calculate_test.go
  6. 33
      staking/effective/eligible.go
  7. 5
      staking/slash/double-sign.go
  8. 7
      staking/types/validator.go

@ -51,6 +51,8 @@ import (
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/harmony/shard/committee"
"github.com/harmony-one/harmony/staking/apr"
"github.com/harmony-one/harmony/staking/availability"
"github.com/harmony-one/harmony/staking/effective"
"github.com/harmony-one/harmony/staking/slash"
staking "github.com/harmony-one/harmony/staking/types"
lru "github.com/hashicorp/golang-lru"
@ -2289,7 +2291,29 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
return shard.ErrSuperCommitteeNil
}
rosters := make([]*votepower.Roster, len(newEpochSuperCommittee.Shards))
rosters, bootedFromSuperCommittee :=
make([]*votepower.Roster, len(newEpochSuperCommittee.Shards)),
map[common.Address]struct{}{}
existing, replacing :=
currentEpochSuperCommittee.StakedValidators(),
newEpochSuperCommittee.StakedValidators()
// TODO could also keep track of the BLS keys which
// lost a slot because just losing slots doesn't mean that the
// validator was booted, just that some of their keys lost slots
for currentValidator := range existing.LookupSet {
if _, keptSlot := replacing.LookupSet[currentValidator]; !keptSlot {
bootedFromSuperCommittee[currentValidator] = struct{}{}
// NOTE Think carefully about when time comes to delete offchain things
// TODO Someone: collect and then delete every 30 epochs
// rawdb.DeleteValidatorSnapshot(
// bc.db, currentValidator, currentEpochSuperCommittee.Epoch,
// )
// rawdb.DeleteValidatorStats(bc.db, currentValidator)
}
}
for i := range newEpochSuperCommittee.Shards {
subCommittee := &newEpochSuperCommittee.Shards[i]
@ -2307,7 +2331,9 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
}
rosters[i] = roster
}
networkWide := votepower.AggregateRosters(rosters)
for key, value := range networkWide {
stats, err := rawdb.ReadValidatorStats(bc.db, key)
if err != nil {
@ -2347,45 +2373,55 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
utils.Logger().Debug().Err(err).Msg("issue with compute of apr")
}
if err := rawdb.WriteValidatorStats(
batch, key, stats,
); err != nil {
snapshot, err := bc.ReadValidatorSnapshotAtEpoch(
currentEpochSuperCommittee.Epoch, wrapper.Address,
)
if err != nil {
return err
}
}
existing, replacing :=
currentEpochSuperCommittee.StakedValidators(),
newEpochSuperCommittee.StakedValidators()
for currentValidator := range existing.LookupSet {
if _, keptSlot := replacing.LookupSet[currentValidator]; !keptSlot {
// TODO Someone: collect and then delete every 30 epochs
// rawdb.DeleteValidatorSnapshot(
// bc.db, currentValidator, currentEpochSuperCommittee.Epoch,
// )
rawdb.DeleteValidatorStats(bc.db, currentValidator)
}
computed := availability.ComputeCurrentSigning(snapshot, wrapper)
if _, wasBooted := bootedFromSuperCommittee[wrapper.Address]; wasBooted {
stats.BootedStatus = effective.LostEPoSAuction
}
return nil
if computed.IsBelowThreshold {
stats.BootedStatus = effective.InsufficientUptimeDuringEpoch
}
// deleteValidatorSnapshots deletes the snapshot staking information of given validator address
// TODO: delete validator snapshots from X epochs ago
func (bc *BlockChain) deleteValidatorSnapshots(addrs []common.Address) error {
batch := bc.db.NewBatch()
for i := range addrs {
rawdb.DeleteValidatorSnapshot(batch, addrs[i], bc.CurrentBlock().Epoch())
if slash.IsBanned(wrapper) {
stats.BootedStatus = effective.BannedForDoubleSigning
}
if err := batch.Write(); err != nil {
if err := rawdb.WriteValidatorStats(
batch, key, stats,
); err != nil {
return err
}
for i := range addrs {
bc.validatorCache.Remove("validator-snapshot-" + string(addrs[i].Bytes()))
}
return nil
}
// deleteValidatorSnapshots deletes the snapshot staking information of given validator address
// TODO: delete validator snapshots from X epochs ago
// NOTE Use when needed but don't compile at all until then
// func (bc *BlockChain) deleteValidatorSnapshots(addrs []common.Address) error {
// batch := bc.db.NewBatch()
// for i := range addrs {
// rawdb.DeleteValidatorSnapshot(batch, addrs[i], bc.CurrentBlock().Epoch())
// }
// if err := batch.Write(); err != nil {
// return err
// }
// for i := range addrs {
// bc.validatorCache.Remove("validator-snapshot-" + string(addrs[i].Bytes()))
// }
// return nil
// }
// UpdateValidatorSnapshots updates the content snapshot of all validators
// Note: this should only be called within the blockchain insert process.
func (bc *BlockChain) UpdateValidatorSnapshots(

@ -248,10 +248,13 @@ func (bc *BlockChain) CommitOffChainData(
addr common.Address
stats *staking.ValidatorStats
}
sortedStats := []t{}
for key, value := range tempValidatorStats {
sortedStats = append(sortedStats, t{key, value})
sortedStats, i := make([]t, len(tempValidatorStats)), 0
for key := range tempValidatorStats {
sortedStats[i] = t{key, tempValidatorStats[key]}
i++
}
sort.SliceStable(
sortedStats,
func(i, j int) bool {

@ -366,6 +366,8 @@ func (b *APIBackend) GetValidatorInformation(
EPoSStatus: effective.ValidatorStatus(
inCommittee, wrapper.Status,
).String(),
EPoSWinningStake: nil,
BootedStatus: nil,
Lifetime: &staking.AccumulatedOverLifetime{
wrapper.BlockReward,
wrapper.Counters,
@ -384,7 +386,9 @@ func (b *APIBackend) GetValidatorInformation(
computed := availability.ComputeCurrentSigning(
snapshot, wrapper,
)
beaconChainBlocks := uint64(b.hmy.BeaconChain().CurrentBlock().Header().Number().Int64()) % shard.Schedule.BlocksPerEpoch()
beaconChainBlocks := uint64(
b.hmy.BeaconChain().CurrentBlock().Header().Number().Int64(),
) % shard.Schedule.BlocksPerEpoch()
computed.BlocksLeftInEpoch = shard.Schedule.BlocksPerEpoch() - beaconChainBlocks
stats, err := bc.ReadValidatorStats(addr)
@ -399,6 +403,12 @@ func (b *APIBackend) GetValidatorInformation(
CurrentSigningPercentage: *computed,
}
defaultReply.ComputedMetrics = stats
defaultReply.EPoSWinningStake = &stats.TotalEffectiveStake
}
if !defaultReply.CurrentlyInCommittee {
reason := stats.BootedStatus.String()
defaultReply.BootedStatus = &reason
}
return defaultReply, nil

@ -150,29 +150,28 @@ func EncodeWrapper(shardState State, isStaking bool) ([]byte, error) {
return data, err
}
// StakedSlots gives overview of subset of shard state that is
// coming via an stake, that is, view epos
// StakedSlots gives overview of members
// in a subcommittee (aka a shard)
type StakedSlots struct {
CountStakedValidator int
CountStakedBLSKey int
Addrs []common.Address
LookupSet map[common.Address]struct{}
TotalEffectiveStaked numeric.Dec
}
// StakedValidators filters for non-harmony operated nodes,
// returns (
// totalStakedValidatorsCount, totalStakedBLSKeys,
// addrsOnNetworkSlice, addrsOnNetworkSet,
// )
// StakedValidators ..
func (c Committee) StakedValidators() *StakedSlots {
countStakedValidator, countStakedBLSKey := 0, 0
networkWideSlice, networkWideSet :=
[]common.Address{}, map[common.Address]struct{}{}
for _, slot := range c.Slots {
totalEffectiveStake := numeric.ZeroDec()
for _, slot := range c.Slots {
// an external validator,
// non-nil EffectiveStake is how we known
if addr := slot.EcdsaAddress; slot.EffectiveStake != nil {
totalEffectiveStake = totalEffectiveStake.Add(*slot.EffectiveStake)
countStakedBLSKey++
if _, seen := networkWideSet[addr]; !seen {
countStakedValidator++
@ -187,20 +186,28 @@ func (c Committee) StakedValidators() *StakedSlots {
CountStakedBLSKey: countStakedBLSKey,
Addrs: networkWideSlice,
LookupSet: networkWideSet,
TotalEffectiveStaked: totalEffectiveStake,
}
}
// StakedValidators filters for non-harmony operated nodes,
// returns (
// totalStakedValidatorsCount, totalStakedBLSKeys,
// addrsOnNetworkSlice, addrsOnNetworkSet,
// )
// TODO refactor with and update corresponding places
// func (ss *State) StakedValidators() []*StakedSlots {
// networkWide := make([]*StakedSlots, len(ss.Shards))
// for i := range ss.Shards {
// networkWide[i] = ss.Shards[i].StakedValidators()
// }
// return networkWide
// }
// StakedValidators here is supercommittee wide
func (ss *State) StakedValidators() *StakedSlots {
countStakedValidator, countStakedBLSKey := 0, 0
networkWideSlice, networkWideSet :=
[]common.Address{},
map[common.Address]struct{}{}
totalEffectiveStake := numeric.ZeroDec()
for i := range ss.Shards {
shard := ss.Shards[i]
for j := range shard.Slots {
@ -209,6 +216,7 @@ func (ss *State) StakedValidators() *StakedSlots {
// an external validator,
// non-nil EffectiveStake is how we known
if addr := slot.EcdsaAddress; slot.EffectiveStake != nil {
totalEffectiveStake = totalEffectiveStake.Add(*slot.EffectiveStake)
countStakedBLSKey++
if _, seen := networkWideSet[addr]; !seen {
countStakedValidator++
@ -224,6 +232,7 @@ func (ss *State) StakedValidators() *StakedSlots {
CountStakedBLSKey: countStakedBLSKey,
Addrs: networkWideSlice,
LookupSet: networkWideSet,
TotalEffectiveStaked: totalEffectiveStake,
}
}

@ -19,7 +19,6 @@ const eposTestingFile = "epos.json"
var (
testingNumber = 20
testingSlots slotsData
testingPurchases []SlotPurchase
expectedMedian numeric.Dec
maxAccountGen = int64(98765654323123134)

@ -39,10 +39,14 @@ const (
Elected
)
const (
doubleSigningBanned = "banned forever from network because was caught double-signing"
)
func (c Candidacy) String() string {
switch c {
case ForeverBanned:
return "banned forever from network because was caught double-signing"
return doubleSigningBanned
case Candidate:
return "eligible to be elected next epoch"
case NotCandidate:
@ -69,3 +73,30 @@ func ValidatorStatus(currentlyInCommittee bool, status Eligibility) Candidacy {
return Unknown
}
}
// BootedStatus ..
type BootedStatus byte
const (
// NotBooted ..
NotBooted BootedStatus = iota
// LostEPoSAuction ..
LostEPoSAuction
// InsufficientUptimeDuringEpoch ..
InsufficientUptimeDuringEpoch
// BannedForDoubleSigning ..
BannedForDoubleSigning
)
func (r BootedStatus) String() string {
switch r {
case LostEPoSAuction:
return "lost epos auction"
case InsufficientUptimeDuringEpoch:
return "bad uptime"
case BannedForDoubleSigning:
return doubleSigningBanned
default:
return "not booted"
}
}

@ -482,6 +482,11 @@ func Apply(
return slashDiff, nil
}
// IsBanned ..
func IsBanned(wrapper *staking.ValidatorWrapper) bool {
return wrapper.Status == effective.Banned
}
// Rate is the slashing % rate
func Rate(votingPower *votepower.Roster, records Records) numeric.Dec {
rate := numeric.ZeroDec()

@ -122,6 +122,7 @@ func NewEmptyStats() *ValidatorStats {
numeric.ZeroDec(),
numeric.ZeroDec(),
[]VoteWithCurrentEpochEarning{},
effective.NotBooted,
}
}
@ -139,6 +140,8 @@ type ValidatorRPCEnchanced struct {
TotalDelegated *big.Int `json:"total-delegation"`
CurrentlyInCommittee bool `json:"currently-in-committee"`
EPoSStatus string `json:"epos-status"`
EPoSWinningStake *numeric.Dec `json:"epos-winning-stake"`
BootedStatus *string `json:"booted-status"`
Lifetime *AccumulatedOverLifetime `json:"lifetime"`
}
@ -178,9 +181,11 @@ type ValidatorStats struct {
// APR ..
APR numeric.Dec `json:"-"`
// TotalEffectiveStake is the total effective stake this validator has
TotalEffectiveStake numeric.Dec `json:"total-effective-stake"`
TotalEffectiveStake numeric.Dec `json:"-"`
// MetricsPerShard ..
MetricsPerShard []VoteWithCurrentEpochEarning `json:"by-bls-key"`
// BootedStatus
BootedStatus effective.BootedStatus `json:"boot-from-committee-status"`
}
func (s ValidatorStats) String() string {

Loading…
Cancel
Save