properly weight bootstrapping validators by stake

This commit is contained in:
StephenButtolph 2020-04-01 21:20:31 -04:00
parent 90f7ba2d6a
commit 11ebe7c6b1
8 changed files with 97 additions and 29 deletions

View File

@ -27,6 +27,7 @@ import (
"github.com/ava-labs/gecko/snow/triggers"
"github.com/ava-labs/gecko/snow/validators"
"github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/utils/math"
"github.com/ava-labs/gecko/vms"
avacon "github.com/ava-labs/gecko/snow/consensus/avalanche"
@ -390,13 +391,22 @@ func (m *manager) createAvalancheChain(
},
}
bootstrapWeight := uint64(0)
for _, beacon := range beacons.List() {
newWeight, err := math.Add64(bootstrapWeight, beacon.Weight())
if err != nil {
return err
}
bootstrapWeight = newWeight
}
engine.Initialize(avaeng.Config{
BootstrapConfig: avaeng.BootstrapConfig{
Config: common.Config{
Context: ctx,
Validators: validators,
Beacons: beacons,
Alpha: beacons.Len()/2 + 1, // must be > 50%
Alpha: bootstrapWeight/2 + 1, // must be > 50%
Sender: &sender,
},
VtxBlocked: vtxBlocker,
@ -417,6 +427,8 @@ func (m *manager) createAvalancheChain(
go ctx.Log.RecoverAndPanic(handler.Dispatch)
awaiting := &networking.AwaitingConnections{
Requested: beacons,
WeightRequired: (3*bootstrapWeight + 3) / 4, // 75% must be connected to
Finish: func() {
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
@ -424,10 +436,6 @@ func (m *manager) createAvalancheChain(
engine.Startup()
},
}
for _, vdr := range beacons.List() {
awaiting.Requested.Add(vdr.ID())
}
awaiting.NumRequired = (3*awaiting.Requested.Len() + 3) / 4 // 75% must be connected to
m.awaiter.AwaitConnections(awaiting)
return nil
@ -468,6 +476,15 @@ func (m *manager) createSnowmanChain(
sender := sender.Sender{}
sender.Initialize(ctx, m.sender, m.chainRouter, m.timeoutManager)
bootstrapWeight := uint64(0)
for _, beacon := range beacons.List() {
newWeight, err := math.Add64(bootstrapWeight, beacon.Weight())
if err != nil {
return err
}
bootstrapWeight = newWeight
}
// The engine handles consensus
engine := smeng.Transitive{}
engine.Initialize(smeng.Config{
@ -476,7 +493,7 @@ func (m *manager) createSnowmanChain(
Context: ctx,
Validators: validators,
Beacons: beacons,
Alpha: beacons.Len()/2 + 1, // must be > 50%
Alpha: bootstrapWeight/2 + 1, // must be > 50%
Sender: &sender,
},
Blocked: blocked,
@ -496,6 +513,8 @@ func (m *manager) createSnowmanChain(
go ctx.Log.RecoverAndPanic(handler.Dispatch)
awaiting := &networking.AwaitingConnections{
Requested: beacons,
WeightRequired: (3*bootstrapWeight + 3) / 4, // 75% must be connected to
Finish: func() {
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
@ -503,10 +522,6 @@ func (m *manager) createSnowmanChain(
engine.Startup()
},
}
for _, vdr := range beacons.List() {
awaiting.Requested.Add(vdr.ID())
}
awaiting.NumRequired = (3*awaiting.Requested.Len() + 3) / 4 // 75% must be connected to
m.awaiter.AwaitConnections(awaiting)
return nil
}

View File

@ -69,7 +69,7 @@ func newConfig(t *testing.T) (BootstrapConfig, ids.ShortID, *common.SenderTest,
Context: ctx,
Validators: peers,
Beacons: peers,
Alpha: peers.Len()/2 + 1,
Alpha: uint64(peers.Len()/2 + 1),
Sender: sender,
}
return BootstrapConfig{

View File

@ -4,7 +4,10 @@
package common
import (
stdmath "math"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/math"
)
// Bootstrapper implements the Engine interface.
@ -15,7 +18,7 @@ type Bootstrapper struct {
acceptedFrontier ids.Set
pendingAccepted ids.ShortSet
accepted ids.Bag
acceptedVotes map[[32]byte]uint64
RequestID uint32
}
@ -30,7 +33,7 @@ func (b *Bootstrapper) Initialize(config Config) {
b.pendingAccepted.Add(vdrID)
}
b.accepted.SetThreshold(config.Alpha)
b.acceptedVotes = make(map[[32]byte]uint64)
}
// Startup implements the Engine interface.
@ -95,10 +98,29 @@ func (b *Bootstrapper) Accepted(validatorID ids.ShortID, requestID uint32, conta
}
b.pendingAccepted.Remove(validatorID)
b.accepted.Add(containerIDs.List()...)
weight := uint64(0)
if vdr, ok := b.Validators.Get(validatorID); ok {
weight = vdr.Weight()
}
for _, containerID := range containerIDs.List() {
key := containerID.Key()
previousWeight := b.acceptedVotes[key]
newWeight, err := math.Add64(weight, previousWeight)
if err != nil {
newWeight = stdmath.MaxUint64
}
b.acceptedVotes[key] = newWeight
}
if b.pendingAccepted.Len() == 0 {
accepted := b.accepted.Threshold()
accepted := ids.Set{}
for key, weight := range b.acceptedVotes {
if weight >= b.Config.Alpha {
accepted.Add(ids.NewID(key))
}
}
if size := accepted.Len(); size == 0 && b.Config.Beacons.Len() > 0 {
b.Context.Log.Warn("Bootstrapping finished with no accepted frontier. This is likely a result of failing to be able to connect to the specified bootstraps, or no transactions have been issued on this network yet")
} else {

View File

@ -15,7 +15,7 @@ type Config struct {
Validators validators.Set
Beacons validators.Set
Alpha int
Alpha uint64
Sender Sender
Bootstrapable Bootstrapable
}

View File

@ -62,7 +62,7 @@ func newConfig(t *testing.T) (BootstrapConfig, ids.ShortID, *common.SenderTest,
Context: ctx,
Validators: peers,
Beacons: peers,
Alpha: peers.Len()/2 + 1,
Alpha: uint64(peers.Len()/2 + 1),
Sender: sender,
}
return BootstrapConfig{

View File

@ -4,31 +4,43 @@
package networking
import (
stdmath "math"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/validators"
"github.com/ava-labs/gecko/utils/math"
)
// AwaitingConnections ...
type AwaitingConnections struct {
Requested ids.ShortSet
NumRequired int
Finish func()
Requested validators.Set
WeightRequired uint64
Finish func()
connected ids.ShortSet
weight uint64
}
// Add ...
func (aw *AwaitingConnections) Add(conn ids.ShortID) {
if aw.Requested.Contains(conn) {
aw.connected.Add(conn)
vdr, ok := aw.Requested.Get(conn)
if !ok {
return
}
weight, err := math.Add64(vdr.Weight(), aw.weight)
if err != nil {
weight = stdmath.MaxUint64
}
aw.weight = weight
}
// Remove ...
func (aw *AwaitingConnections) Remove(conn ids.ShortID) {
aw.connected.Remove(conn)
vdr, ok := aw.Requested.Get(conn)
if !ok {
return
}
aw.weight -= vdr.Weight()
}
// Ready ...
func (aw *AwaitingConnections) Ready() bool {
return aw.connected.Len() >= aw.NumRequired
}
func (aw *AwaitingConnections) Ready() bool { return aw.weight >= aw.WeightRequired }

View File

@ -24,6 +24,9 @@ type Set interface {
// Add the provided validator to the set.
Add(Validator)
// Get the validator from the set.
Get(ids.ShortID) (Validator, bool)
// Remove the validator with the specified ID.
Remove(ids.ShortID)
@ -102,6 +105,22 @@ func (s *set) add(vdr Validator) {
s.sampler.Weights = append(s.sampler.Weights, w)
}
// Get implements the Set interface.
func (s *set) Get(vdrID ids.ShortID) (Validator, bool) {
s.lock.Lock()
defer s.lock.Unlock()
return s.get(vdrID)
}
func (s *set) get(vdrID ids.ShortID) (Validator, bool) {
index, ok := s.vdrMap[vdrID.Key()]
if !ok {
return nil, false
}
return s.vdrSlice[index], true
}
// Remove implements the Set interface.
func (s *set) Remove(vdrID ids.ShortID) {
s.lock.Lock()

View File

@ -86,7 +86,7 @@ func ConsensusLeader(numBlocks, numTxsPerBlock int, b *testing.B) {
Context: ctx,
Validators: vdrs,
Beacons: beacons,
Alpha: (beacons.Len() + 1) / 2,
Alpha: uint64(beacons.Len()/2 + 1),
Sender: &sender,
},
Blocked: blocked,
@ -217,7 +217,7 @@ func ConsensusFollower(numBlocks, numTxsPerBlock int, b *testing.B) {
Context: ctx,
Validators: vdrs,
Beacons: beacons,
Alpha: (beacons.Len() + 1) / 2,
Alpha: uint64(beacons.Len()/2 + 1),
Sender: &sender,
},
Blocked: blocked,