|
|
|
package votepower
|
|
|
|
|
|
|
|
import (
|
[slash][consensus] Notice double sign & broadcast, factor out tech debt of consensus (#2152)
* [slash] Remove dead interface, associated piping
* [slash] Expand out structs
* [consensus] Write to a chan when find a case of double-signing, remove dead code
* [slash] Broadcast the noticing of a double signing
* [rawdb] CRUD for slashing candidates
* [slashing][node][proto] Broadcast the slash record after receive from consensus, handle received proto message, persist in off-chain db while pending
* [slash][node][propose-block] Add verified slashes proposed into the header in block proposal
* [slash][shard] Factor out external validator as method on shard state, add double-signature field
* [slash][engine] Apply slash, name boolean expression for sorts, use stable sort
* [slash] Abstract Ballot results so keep track of both pre and post double sign event
* [slash] Fix type errors on test code
* [slash] Read from correct rawdb
* [slash] Add epoch based guards in CRUD of slashing
* [slash] Write to correct cache for slashing candidates
* [shard] Use explicit named type of BLS Signature, use convention
* [slash] Fix mistake done in refactor, improper header used. Factor out fromSlice to set
* [slash][node] Restore newblock to master, try again minimial change
* [cx-receipts] Break up one-liner, use SliceStable, not Slice
* [network] Finish refactor that makes network message headers once
* [network] Simplify creation further of headers write
* [slash] Adjust data structure of slash after offline discussion with RJ, Chao
* [slash] Still did need signature of the double signature
* [consensus] Prepare message does not have block header
* [consensus] Soft reset three files to 968517d~1
* [consensus] Begin factor consensus network intended message out with prepare first
* [consensus] Factor out Prepared message
* [consensus] Factor out announce message creation
* [consensus] Committed Message, branch on verify sender key for clearer log
* [consensus] Committed Message Factor out
* [consensus] Do jenkins MVP of signatures adjustment
* [main][slash] Provide YAML config as webhook config for double sign event
* [consensus] Adjust signatures, whitespace, lessen GC pressure
* [consensus] Remove dead code
* [consensus] Factor out commit overloaded message, give commit payload override in construct
* [consensus] Fix travis tests
* [consensus] Provide block bytes in SubmitVote(quorum.Commit)
* [consensus] Factor out noisy sanity checks in BFT, move existing commit check earlier as was before
* [quorum] Adjust signatures in quorum
* [staking] Adjust after merge from master
* [consensus] Finish refactor of consensus
* [node] Fix import
* [consensus] Fix travis
* [consensus] Use origin/master copy of block, fix mistake of pointer to empty byte
* [consensus] Less verbose bools
* [consensus] Remove unused trailing mutation hook in message construct
* [consensus] Address some TODOs on err, comment out double sign
5 years ago
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/harmony-one/bls/ffi/go/bls"
|
|
|
|
"github.com/harmony-one/harmony/internal/utils"
|
|
|
|
"github.com/harmony-one/harmony/numeric"
|
|
|
|
"github.com/harmony-one/harmony/shard"
|
|
|
|
staking "github.com/harmony-one/harmony/staking/types"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// HarmonysShare ..
|
|
|
|
HarmonysShare = numeric.MustNewDecFromStr("0.68")
|
|
|
|
// StakersShare ..
|
|
|
|
StakersShare = numeric.MustNewDecFromStr("0.32")
|
|
|
|
// ErrVotingPowerNotEqualOne ..
|
|
|
|
ErrVotingPowerNotEqualOne = errors.New("voting power not equal to one")
|
|
|
|
)
|
|
|
|
|
|
|
|
// Ballot is a vote cast by a validator
|
|
|
|
type Ballot struct {
|
[slash][consensus] Notice double sign & broadcast, factor out tech debt of consensus (#2152)
* [slash] Remove dead interface, associated piping
* [slash] Expand out structs
* [consensus] Write to a chan when find a case of double-signing, remove dead code
* [slash] Broadcast the noticing of a double signing
* [rawdb] CRUD for slashing candidates
* [slashing][node][proto] Broadcast the slash record after receive from consensus, handle received proto message, persist in off-chain db while pending
* [slash][node][propose-block] Add verified slashes proposed into the header in block proposal
* [slash][shard] Factor out external validator as method on shard state, add double-signature field
* [slash][engine] Apply slash, name boolean expression for sorts, use stable sort
* [slash] Abstract Ballot results so keep track of both pre and post double sign event
* [slash] Fix type errors on test code
* [slash] Read from correct rawdb
* [slash] Add epoch based guards in CRUD of slashing
* [slash] Write to correct cache for slashing candidates
* [shard] Use explicit named type of BLS Signature, use convention
* [slash] Fix mistake done in refactor, improper header used. Factor out fromSlice to set
* [slash][node] Restore newblock to master, try again minimial change
* [cx-receipts] Break up one-liner, use SliceStable, not Slice
* [network] Finish refactor that makes network message headers once
* [network] Simplify creation further of headers write
* [slash] Adjust data structure of slash after offline discussion with RJ, Chao
* [slash] Still did need signature of the double signature
* [consensus] Prepare message does not have block header
* [consensus] Soft reset three files to 968517d~1
* [consensus] Begin factor consensus network intended message out with prepare first
* [consensus] Factor out Prepared message
* [consensus] Factor out announce message creation
* [consensus] Committed Message, branch on verify sender key for clearer log
* [consensus] Committed Message Factor out
* [consensus] Do jenkins MVP of signatures adjustment
* [main][slash] Provide YAML config as webhook config for double sign event
* [consensus] Adjust signatures, whitespace, lessen GC pressure
* [consensus] Remove dead code
* [consensus] Factor out commit overloaded message, give commit payload override in construct
* [consensus] Fix travis tests
* [consensus] Provide block bytes in SubmitVote(quorum.Commit)
* [consensus] Factor out noisy sanity checks in BFT, move existing commit check earlier as was before
* [quorum] Adjust signatures in quorum
* [staking] Adjust after merge from master
* [consensus] Finish refactor of consensus
* [node] Fix import
* [consensus] Fix travis
* [consensus] Use origin/master copy of block, fix mistake of pointer to empty byte
* [consensus] Less verbose bools
* [consensus] Remove unused trailing mutation hook in message construct
* [consensus] Address some TODOs on err, comment out double sign
5 years ago
|
|
|
SignerPubKey shard.BlsPublicKey `json:"bls-public-key"`
|
|
|
|
Signature *bls.Sign `json:"signature"`
|
|
|
|
OptSerializedBlock []byte `json:"opt-rlp-encoded-block"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BallotResults are a completed round of votes
|
|
|
|
type BallotResults struct {
|
|
|
|
Signature shard.BLSSignature // (aggregated) signature
|
|
|
|
Bitmap []byte // corresponding bitmap mask for agg signature
|
|
|
|
}
|
|
|
|
|
|
|
|
// EncodePair returns hex encoded tuple (signature, bitmap)
|
|
|
|
func (b BallotResults) EncodePair() (string, string) {
|
|
|
|
return hex.EncodeToString(b.Signature[:]), hex.EncodeToString(b.Bitmap[:])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Round is a round of voting in any FBFT phase
|
|
|
|
type Round struct {
|
|
|
|
AggregatedVote *bls.Sign
|
|
|
|
BallotBox map[string]*Ballot
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRound ..
|
|
|
|
func NewRound() *Round {
|
|
|
|
return &Round{AggregatedVote: nil, BallotBox: map[string]*Ballot{}}
|
|
|
|
}
|
|
|
|
|
|
|
|
type stakedVoter struct {
|
|
|
|
IsActive bool `json:"is-active"`
|
|
|
|
IsHarmonyNode bool `json:"is-harmony"`
|
|
|
|
EarningAccount common.Address `json:"earning-account"`
|
|
|
|
Identity shard.BlsPublicKey `json:"bls-public-key"`
|
|
|
|
EffectivePercent numeric.Dec `json:"voting"`
|
|
|
|
EffectiveStake numeric.Dec `json:"effective-stake"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Roster ..
|
|
|
|
type Roster struct {
|
|
|
|
Voters map[shard.BlsPublicKey]stakedVoter
|
|
|
|
OurVotingPowerTotalPercentage numeric.Dec
|
|
|
|
TheirVotingPowerTotalPercentage numeric.Dec
|
|
|
|
RawStakedTotal numeric.Dec
|
|
|
|
HmySlotCount int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// Staker ..
|
|
|
|
type Staker struct {
|
|
|
|
TotalEffectiveStake numeric.Dec
|
|
|
|
VotingPower []staking.VotePerShard
|
|
|
|
BLSPublicKeysOwned []staking.KeysPerShard
|
|
|
|
}
|
|
|
|
|
|
|
|
// RosterPerShard ..
|
|
|
|
type RosterPerShard struct {
|
|
|
|
ShardID uint32
|
|
|
|
Record *Roster
|
|
|
|
}
|
|
|
|
|
|
|
|
// AggregateRosters ..
|
|
|
|
func AggregateRosters(rosters []RosterPerShard) map[common.Address]Staker {
|
|
|
|
result := map[common.Address]Staker{}
|
|
|
|
sort.SliceStable(rosters,
|
|
|
|
func(i, j int) bool { return rosters[i].ShardID < rosters[j].ShardID },
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, roster := range rosters {
|
|
|
|
for key, value := range roster.Record.Voters {
|
|
|
|
if !value.IsHarmonyNode {
|
|
|
|
payload, alreadyExists := result[value.EarningAccount]
|
|
|
|
if alreadyExists {
|
|
|
|
payload.TotalEffectiveStake = payload.TotalEffectiveStake.Add(
|
|
|
|
value.EffectiveStake,
|
|
|
|
)
|
|
|
|
payload.VotingPower = append(payload.VotingPower,
|
|
|
|
staking.VotePerShard{roster.ShardID, value.EffectivePercent},
|
|
|
|
)
|
|
|
|
for i := range payload.BLSPublicKeysOwned {
|
|
|
|
if payload.BLSPublicKeysOwned[i].ShardID == roster.ShardID {
|
|
|
|
payload.BLSPublicKeysOwned[i].Keys = append(
|
|
|
|
payload.BLSPublicKeysOwned[i].Keys, key,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result[value.EarningAccount] = Staker{
|
|
|
|
TotalEffectiveStake: value.EffectiveStake,
|
|
|
|
VotingPower: []staking.VotePerShard{
|
|
|
|
{roster.ShardID, value.EffectivePercent},
|
|
|
|
},
|
|
|
|
BLSPublicKeysOwned: []staking.KeysPerShard{
|
|
|
|
{roster.ShardID, []shard.BlsPublicKey{key}}},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
// JSON dump
|
|
|
|
func (r *Roster) JSON() string {
|
|
|
|
v := map[string]stakedVoter{}
|
|
|
|
for k, value := range r.Voters {
|
|
|
|
v[k.Hex()] = value
|
|
|
|
}
|
|
|
|
c := struct {
|
|
|
|
Voters map[string]stakedVoter `json:"voters"`
|
|
|
|
Our string `json:"ours"`
|
|
|
|
Their string `json:"theirs"`
|
|
|
|
Raw string `json:"raw-total"`
|
|
|
|
}{
|
|
|
|
v,
|
|
|
|
r.OurVotingPowerTotalPercentage.String(),
|
|
|
|
r.TheirVotingPowerTotalPercentage.String(),
|
|
|
|
r.RawStakedTotal.String(),
|
|
|
|
}
|
|
|
|
b, _ := json.Marshal(&c)
|
|
|
|
return string(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute creates a new roster based off the shard.SlotList
|
|
|
|
func Compute(staked shard.SlotList) (*Roster, error) {
|
|
|
|
roster := NewRoster()
|
|
|
|
for i := range staked {
|
|
|
|
if staked[i].EffectiveStake == nil {
|
|
|
|
roster.HmySlotCount++
|
|
|
|
} else {
|
|
|
|
roster.RawStakedTotal = roster.RawStakedTotal.Add(
|
|
|
|
*staked[i].EffectiveStake,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO Check for duplicate BLS Keys
|
|
|
|
ourCount := numeric.NewDec(roster.HmySlotCount)
|
|
|
|
ourPercentage := numeric.ZeroDec()
|
|
|
|
theirPercentage := numeric.ZeroDec()
|
|
|
|
var lastStakedVoter *stakedVoter
|
|
|
|
|
|
|
|
for i := range staked {
|
|
|
|
member := stakedVoter{
|
|
|
|
IsActive: true,
|
|
|
|
IsHarmonyNode: true,
|
|
|
|
EarningAccount: staked[i].EcdsaAddress,
|
|
|
|
Identity: staked[i].BlsPublicKey,
|
|
|
|
EffectivePercent: numeric.ZeroDec(),
|
|
|
|
EffectiveStake: numeric.ZeroDec(),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Real Staker
|
|
|
|
if staked[i].EffectiveStake != nil {
|
|
|
|
member.IsHarmonyNode = false
|
|
|
|
member.EffectiveStake = member.EffectiveStake.Add(*staked[i].EffectiveStake)
|
|
|
|
member.EffectivePercent = staked[i].EffectiveStake.
|
|
|
|
Quo(roster.RawStakedTotal).
|
|
|
|
Mul(StakersShare)
|
|
|
|
theirPercentage = theirPercentage.Add(member.EffectivePercent)
|
|
|
|
lastStakedVoter = &member
|
|
|
|
} else { // Our node
|
|
|
|
member.EffectivePercent = HarmonysShare.Quo(ourCount)
|
|
|
|
ourPercentage = ourPercentage.Add(member.EffectivePercent)
|
|
|
|
}
|
|
|
|
|
|
|
|
roster.Voters[staked[i].BlsPublicKey] = member
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE Enforce voting power sums to one, give diff (expect tiny amt) to last staked voter
|
|
|
|
if diff := numeric.OneDec().Sub(
|
|
|
|
ourPercentage.Add(theirPercentage),
|
|
|
|
); !diff.IsZero() && lastStakedVoter != nil {
|
|
|
|
utils.Logger().Info().
|
|
|
|
Str("theirs", theirPercentage.String()).
|
|
|
|
Str("ours", ourPercentage.String()).
|
|
|
|
Str("diff", diff.String()).
|
|
|
|
Str("combined", theirPercentage.Add(diff).Add(ourPercentage).String()).
|
|
|
|
Str("bls-public-key-of-receipent", lastStakedVoter.Identity.Hex()).
|
|
|
|
Msg("voting power of hmy & staked slots not sum to 1, giving diff to staked slot")
|
|
|
|
lastStakedVoter.EffectivePercent = lastStakedVoter.EffectivePercent.Add(diff)
|
|
|
|
theirPercentage = theirPercentage.Add(diff)
|
|
|
|
}
|
|
|
|
|
|
|
|
if lastStakedVoter != nil &&
|
|
|
|
!ourPercentage.Add(theirPercentage).Equal(numeric.OneDec()) {
|
|
|
|
utils.Logger().Error().
|
|
|
|
Str("theirs", theirPercentage.String()).
|
|
|
|
Str("ours", ourPercentage.String()).
|
|
|
|
Msg("Total voting power not equal 100 percent")
|
|
|
|
return nil, ErrVotingPowerNotEqualOne
|
|
|
|
}
|
|
|
|
|
|
|
|
roster.OurVotingPowerTotalPercentage = ourPercentage
|
|
|
|
roster.TheirVotingPowerTotalPercentage = theirPercentage
|
|
|
|
return roster, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRoster ..
|
|
|
|
func NewRoster() *Roster {
|
|
|
|
return &Roster{
|
|
|
|
map[shard.BlsPublicKey]stakedVoter{},
|
|
|
|
numeric.ZeroDec(),
|
|
|
|
numeric.ZeroDec(),
|
|
|
|
numeric.ZeroDec(),
|
|
|
|
0,
|
|
|
|
}
|
|
|
|
}
|