[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. 90
      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"
"github.com/harmony-one/harmony/shard/committee" "github.com/harmony-one/harmony/shard/committee"
"github.com/harmony-one/harmony/staking/apr" "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" "github.com/harmony-one/harmony/staking/slash"
staking "github.com/harmony-one/harmony/staking/types" staking "github.com/harmony-one/harmony/staking/types"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
@ -2289,7 +2291,29 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
return shard.ErrSuperCommitteeNil 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 { for i := range newEpochSuperCommittee.Shards {
subCommittee := &newEpochSuperCommittee.Shards[i] subCommittee := &newEpochSuperCommittee.Shards[i]
@ -2307,7 +2331,9 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
} }
rosters[i] = roster rosters[i] = roster
} }
networkWide := votepower.AggregateRosters(rosters) networkWide := votepower.AggregateRosters(rosters)
for key, value := range networkWide { for key, value := range networkWide {
stats, err := rawdb.ReadValidatorStats(bc.db, key) stats, err := rawdb.ReadValidatorStats(bc.db, key)
if err != nil { if err != nil {
@ -2347,6 +2373,28 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
utils.Logger().Debug().Err(err).Msg("issue with compute of apr") utils.Logger().Debug().Err(err).Msg("issue with compute of apr")
} }
snapshot, err := bc.ReadValidatorSnapshotAtEpoch(
currentEpochSuperCommittee.Epoch, wrapper.Address,
)
if err != nil {
return err
}
computed := availability.ComputeCurrentSigning(snapshot, wrapper)
if _, wasBooted := bootedFromSuperCommittee[wrapper.Address]; wasBooted {
stats.BootedStatus = effective.LostEPoSAuction
}
if computed.IsBelowThreshold {
stats.BootedStatus = effective.InsufficientUptimeDuringEpoch
}
if slash.IsBanned(wrapper) {
stats.BootedStatus = effective.BannedForDoubleSigning
}
if err := rawdb.WriteValidatorStats( if err := rawdb.WriteValidatorStats(
batch, key, stats, batch, key, stats,
); err != nil { ); err != nil {
@ -2354,37 +2402,25 @@ func (bc *BlockChain) UpdateValidatorVotingPower(
} }
} }
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)
}
}
return nil return nil
} }
// deleteValidatorSnapshots deletes the snapshot staking information of given validator address // deleteValidatorSnapshots deletes the snapshot staking information of given validator address
// TODO: delete validator snapshots from X epochs ago // TODO: delete validator snapshots from X epochs ago
func (bc *BlockChain) deleteValidatorSnapshots(addrs []common.Address) error { // NOTE Use when needed but don't compile at all until then
batch := bc.db.NewBatch() // func (bc *BlockChain) deleteValidatorSnapshots(addrs []common.Address) error {
for i := range addrs { // batch := bc.db.NewBatch()
rawdb.DeleteValidatorSnapshot(batch, addrs[i], bc.CurrentBlock().Epoch()) // for i := range addrs {
} // rawdb.DeleteValidatorSnapshot(batch, addrs[i], bc.CurrentBlock().Epoch())
if err := batch.Write(); err != nil { // }
return err // if err := batch.Write(); err != nil {
} // return err
for i := range addrs { // }
bc.validatorCache.Remove("validator-snapshot-" + string(addrs[i].Bytes())) // for i := range addrs {
} // bc.validatorCache.Remove("validator-snapshot-" + string(addrs[i].Bytes()))
return nil // }
} // return nil
// }
// UpdateValidatorSnapshots updates the content snapshot of all validators // UpdateValidatorSnapshots updates the content snapshot of all validators
// Note: this should only be called within the blockchain insert process. // Note: this should only be called within the blockchain insert process.

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

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

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

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

@ -39,10 +39,14 @@ const (
Elected Elected
) )
const (
doubleSigningBanned = "banned forever from network because was caught double-signing"
)
func (c Candidacy) String() string { func (c Candidacy) String() string {
switch c { switch c {
case ForeverBanned: case ForeverBanned:
return "banned forever from network because was caught double-signing" return doubleSigningBanned
case Candidate: case Candidate:
return "eligible to be elected next epoch" return "eligible to be elected next epoch"
case NotCandidate: case NotCandidate:
@ -69,3 +73,30 @@ func ValidatorStatus(currentlyInCommittee bool, status Eligibility) Candidacy {
return Unknown 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 return slashDiff, nil
} }
// IsBanned ..
func IsBanned(wrapper *staking.ValidatorWrapper) bool {
return wrapper.Status == effective.Banned
}
// Rate is the slashing % rate // Rate is the slashing % rate
func Rate(votingPower *votepower.Roster, records Records) numeric.Dec { func Rate(votingPower *votepower.Roster, records Records) numeric.Dec {
rate := numeric.ZeroDec() rate := numeric.ZeroDec()

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

Loading…
Cancel
Save