The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
woop/consensus/quorum/quorum.go

340 lines
7.8 KiB

package quorum
import (
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/votepower"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
)
// Phase is a phase that needs quorum to proceed
type Phase byte
const (
// Prepare ..
Prepare Phase = iota
// Commit ..
Commit
// ViewChange ..
ViewChange
)
var phaseNames = map[Phase]string{
Prepare: "Prepare",
Commit: "Prepare",
ViewChange: "Commit",
}
func (p Phase) String() string {
if name, ok := phaseNames[p]; ok {
return name
}
return fmt.Sprintf("Unknown Quorum Phase %+v", byte(p))
}
// Policy is the rule we used to decide is quorum achieved
type Policy byte
const (
// SuperMajorityVote is a 2/3s voting mechanism, pre-PoS
SuperMajorityVote Policy = iota
// SuperMajorityStake is 2/3s of total staked amount for epoch
SuperMajorityStake
)
var policyNames = map[Policy]string{
SuperMajorityStake: "SuperMajorityStake",
SuperMajorityVote: "SuperMajorityVote",
}
func (p Policy) String() string {
if name, ok := policyNames[p]; ok {
return name
}
return fmt.Sprintf("Unknown Quorum Policy %+v", byte(p))
}
// ParticipantTracker ..
type ParticipantTracker interface {
Participants() []*bls.PublicKey
IndexOf(*bls.PublicKey) int
ParticipantsCount() int64
NextAfter(*bls.PublicKey) (bool, *bls.PublicKey)
UpdateParticipants(pubKeys []*bls.PublicKey)
DumpParticipants() []string
}
// SignatoryTracker ..
type SignatoryTracker interface {
ParticipantTracker
AddSignature(
p Phase, PubKey *bls.PublicKey,
sig *bls.Sign, roundLeader *bls.PublicKey, roundNumber uint64,
)
// Caller assumes concurrency protection
SignersCount(Phase) int64
reset([]Phase)
}
// SignatureReader ..
type SignatureReader interface {
SignatoryTracker
ReadAllSignatures(Phase) []*bls.Sign
ReadSignature(p Phase, PubKey *bls.PublicKey) *bls.Sign
TwoThirdsSignersCount() int64
// 96 bytes aggregated signature
AggregateVotes(p Phase) *bls.Sign
}
// DependencyInjectionWriter ..
type DependencyInjectionWriter interface {
SetShardIDProvider(func() (uint32, error))
SetMyPublicKeyProvider(func() (*bls.PublicKey, error))
}
// DependencyInjectionReader ..
type DependencyInjectionReader interface {
ShardIDProvider() func() (uint32, error)
MyPublicKey() func() (*bls.PublicKey, error)
}
//WithJSONDump representation dump
type WithJSONDump interface {
JSON() string
}
// Decider ..
type Decider interface {
SignatureReader
DependencyInjectionWriter
WithJSONDump
ToggleActive(*bls.PublicKey) bool
SetVoters(shard.SlotList, bool) (*TallyResult, error)
Policy() Policy
IsQuorumAchieved(Phase) bool
IsQuorumAchievedByMask(mask *bls_cosi.Mask, debug bool) bool
QuorumThreshold() numeric.Dec
AmIMemberOfCommitee() bool
IsRewardThresholdAchieved() bool
ResetPrepareAndCommitVotes()
ResetViewChangeVotes()
}
// These maps represent the signatories (validators), keys are BLS public keys
// and values are BLS private key signed signatures
type cIdentities struct {
// Public keys of the committee including leader and validators
publicKeys []*bls.PublicKey
announcement *votepower.Round
commit *votepower.Round
// viewIDSigs: every validator
// sign on |viewID|blockHash| in view changing message
viewID *votepower.Round
seenCounter map[[shard.PublicKeySizeInBytes]byte]int
}
type depInject struct {
shardIDProvider func() (uint32, error)
publicKeyProvider func() (*bls.PublicKey, error)
}
func (s *cIdentities) AggregateVotes(p Phase) *bls.Sign {
return bls_cosi.AggregateSig(s.ReadAllSignatures(p))
}
func (s *cIdentities) IndexOf(pubKey *bls.PublicKey) int {
idx := -1
for k, v := range s.publicKeys {
if v.IsEqual(pubKey) {
idx = k
}
}
return idx
}
func (s *cIdentities) NextAfter(pubKey *bls.PublicKey) (bool, *bls.PublicKey) {
found := false
idx := s.IndexOf(pubKey)
if idx != -1 {
found = true
}
idx = (idx + 1) % int(s.ParticipantsCount())
return found, s.publicKeys[idx]
}
func (s *cIdentities) Participants() []*bls.PublicKey {
return s.publicKeys
}
func (s *cIdentities) UpdateParticipants(pubKeys []*bls.PublicKey) {
// TODO - might need to put reset of seen counter in separate method
s.seenCounter = make(map[[shard.PublicKeySizeInBytes]byte]int, len(pubKeys))
for i := range pubKeys {
k := shard.BlsPublicKey{}
k.FromLibBLSPublicKey(pubKeys[i])
s.seenCounter[k] = 0
}
s.publicKeys = append(pubKeys[:0:0], pubKeys...)
}
func (s *cIdentities) DumpParticipants() []string {
keys := make([]string, len(s.publicKeys))
for i := range s.publicKeys {
keys[i] = s.publicKeys[i].SerializeToHexStr()
}
return keys
}
func (s *cIdentities) ParticipantsCount() int64 {
return int64(len(s.publicKeys))
}
func (s *cIdentities) SignersCount(p Phase) int64 {
switch p {
case Prepare:
return int64(len(s.announcement.BallotBox))
case Commit:
return int64(len(s.commit.BallotBox))
case ViewChange:
return int64(len(s.viewID.BallotBox))
default:
return 0
}
}
func (s *cIdentities) AddSignature(
p Phase, PubKey *bls.PublicKey,
sig *bls.Sign, roundLeader *bls.PublicKey, roundNumber uint64,
) {
hex := PubKey.SerializeToHexStr()
ballot := &votepower.Ballot{
*shard.FromLibBLSPublicKeyUnsafe(PubKey),
*shard.FromLibBLSPublicKeyUnsafe(roundLeader),
roundNumber,
sig,
}
switch p {
case Prepare:
s.announcement.BallotBox[hex] = ballot
case Commit:
s.commit.BallotBox[hex] = ballot
case ViewChange:
s.viewID.BallotBox[hex] = ballot
}
}
func (s *cIdentities) reset(ps []Phase) {
for i := range ps {
switch m := votepower.NewRound(); ps[i] {
case Prepare:
s.announcement = m
case Commit:
s.commit = m
case ViewChange:
s.viewID = m
}
}
}
func (s *cIdentities) TwoThirdsSignersCount() int64 {
return s.ParticipantsCount()*2/3 + 1
}
func (s *cIdentities) ReadSignature(p Phase, PubKey *bls.PublicKey) *bls.Sign {
var ballotBox map[string]*votepower.Ballot
hex := PubKey.SerializeToHexStr()
switch p {
case Prepare:
ballotBox = s.announcement.BallotBox
case Commit:
ballotBox = s.commit.BallotBox
case ViewChange:
ballotBox = s.viewID.BallotBox
}
payload, ok := ballotBox[hex]
if !ok {
return nil
}
return payload.Signature
}
func (s *cIdentities) ReadAllSignatures(p Phase) []*bls.Sign {
var m map[string]*votepower.Ballot
switch p {
case Prepare:
m = s.announcement.BallotBox
case Commit:
m = s.commit.BallotBox
case ViewChange:
m = s.viewID.BallotBox
}
sigs := make([]*bls.Sign, 0, len(m))
for _, value := range m {
sigs = append(sigs, value.Signature)
}
return sigs
}
func newMapBackedSignatureReader() *cIdentities {
return &cIdentities{
publicKeys: []*bls.PublicKey{},
announcement: votepower.NewRound(),
commit: votepower.NewRound(),
viewID: votepower.NewRound(),
seenCounter: map[[shard.PublicKeySizeInBytes]byte]int{},
}
}
type composite struct {
DependencyInjectionWriter
DependencyInjectionReader
SignatureReader
}
func (d *depInject) SetShardIDProvider(p func() (uint32, error)) {
d.shardIDProvider = p
}
func (d *depInject) ShardIDProvider() func() (uint32, error) {
return d.shardIDProvider
}
func (d *depInject) SetMyPublicKeyProvider(p func() (*bls.PublicKey, error)) {
d.publicKeyProvider = p
}
func (d *depInject) MyPublicKey() func() (*bls.PublicKey, error) {
return d.publicKeyProvider
}
// NewDecider ..
func NewDecider(p Policy) Decider {
signatureStore := newMapBackedSignatureReader()
deps := &depInject{}
c := &composite{deps, deps, signatureStore}
switch p {
case SuperMajorityVote:
return &uniformVoteWeight{
c.DependencyInjectionWriter, c.DependencyInjectionReader, c,
}
case SuperMajorityStake:
return &stakedVoteWeight{
c.SignatureReader,
c.DependencyInjectionWriter,
c.DependencyInjectionWriter.(DependencyInjectionReader),
*votepower.NewRoster(),
newBallotBox(),
}
default:
// Should not be possible
return nil
}
}