gecko/snow/engine/common/bootstrapper.go

139 lines
4.0 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package common
import (
stdmath "math"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/utils/math"
)
// Bootstrapper implements the Engine interface.
type Bootstrapper struct {
Config
pendingAcceptedFrontier ids.ShortSet
acceptedFrontier ids.Set
pendingAccepted ids.ShortSet
acceptedVotes map[[32]byte]uint64
RequestID uint32
}
// Initialize implements the Engine interface.
func (b *Bootstrapper) Initialize(config Config) {
b.Config = config
for _, vdr := range b.Beacons.List() {
vdrID := vdr.ID()
b.pendingAcceptedFrontier.Add(vdrID)
b.pendingAccepted.Add(vdrID)
}
b.acceptedVotes = make(map[[32]byte]uint64)
}
// Startup implements the Engine interface.
func (b *Bootstrapper) Startup() error {
if b.pendingAcceptedFrontier.Len() == 0 {
b.Context.Log.Info("Bootstrapping skipped due to no provided bootstraps")
return b.Bootstrapable.ForceAccepted(ids.Set{})
}
vdrs := ids.ShortSet{}
vdrs.Union(b.pendingAcceptedFrontier)
b.RequestID++
b.Sender.GetAcceptedFrontier(vdrs, b.RequestID)
return nil
}
// GetAcceptedFrontier implements the Engine interface.
func (b *Bootstrapper) GetAcceptedFrontier(validatorID ids.ShortID, requestID uint32) error {
b.Sender.AcceptedFrontier(validatorID, requestID, b.Bootstrapable.CurrentAcceptedFrontier())
return nil
}
// GetAcceptedFrontierFailed implements the Engine interface.
func (b *Bootstrapper) GetAcceptedFrontierFailed(validatorID ids.ShortID, requestID uint32) error {
b.AcceptedFrontier(validatorID, requestID, ids.Set{})
return nil
}
// AcceptedFrontier implements the Engine interface.
func (b *Bootstrapper) AcceptedFrontier(validatorID ids.ShortID, requestID uint32, containerIDs ids.Set) error {
if !b.pendingAcceptedFrontier.Contains(validatorID) {
b.Context.Log.Debug("Received an AcceptedFrontier message from %s unexpectedly", validatorID)
return nil
}
b.pendingAcceptedFrontier.Remove(validatorID)
b.acceptedFrontier.Union(containerIDs)
if b.pendingAcceptedFrontier.Len() == 0 {
vdrs := ids.ShortSet{}
vdrs.Union(b.pendingAccepted)
b.RequestID++
b.Sender.GetAccepted(vdrs, b.RequestID, b.acceptedFrontier)
}
return nil
}
// GetAccepted implements the Engine interface.
func (b *Bootstrapper) GetAccepted(validatorID ids.ShortID, requestID uint32, containerIDs ids.Set) error {
b.Sender.Accepted(validatorID, requestID, b.Bootstrapable.FilterAccepted(containerIDs))
return nil
}
// GetAcceptedFailed implements the Engine interface.
func (b *Bootstrapper) GetAcceptedFailed(validatorID ids.ShortID, requestID uint32) error {
return b.Accepted(validatorID, requestID, ids.Set{})
}
// Accepted implements the Engine interface.
func (b *Bootstrapper) Accepted(validatorID ids.ShortID, requestID uint32, containerIDs ids.Set) error {
if !b.pendingAccepted.Contains(validatorID) {
b.Context.Log.Debug("Received an Accepted message from %s unexpectedly", validatorID)
return nil
}
b.pendingAccepted.Remove(validatorID)
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 {
return nil
}
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.Info("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 chain yet")
} else {
b.Context.Log.Info("Bootstrapping started syncing with %d vertices in the accepted frontier", size)
}
return b.Bootstrapable.ForceAccepted(accepted)
}