mirror of https://github.com/poanetwork/gecko.git
171 lines
5.6 KiB
Go
171 lines
5.6 KiB
Go
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package common
|
|
|
|
import (
|
|
stdmath "math"
|
|
"time"
|
|
|
|
"github.com/ava-labs/gecko/ids"
|
|
"github.com/ava-labs/gecko/utils/math"
|
|
)
|
|
|
|
const (
|
|
// MaxContainersPerMultiPut is the maximum number of containers that can be sent in a MultiPut
|
|
MaxContainersPerMultiPut = 2000
|
|
|
|
// StatusUpdateFrequency ... bootstrapper logs "processed X blocks/vertices" every [statusUpdateFrequency] blocks/vertices
|
|
StatusUpdateFrequency = 2500
|
|
|
|
// MaxOutstandingRequests is the maximum number of GetAncestors sent but not responsded to/failed
|
|
MaxOutstandingRequests = 8
|
|
)
|
|
|
|
var (
|
|
// MaxTimeFetchingAncestors is the maximum amount of time to spend fetching vertices during a call to GetAncestors
|
|
MaxTimeFetchingAncestors = 50 * time.Millisecond
|
|
)
|
|
|
|
// Bootstrapper implements the Engine interface.
|
|
type Bootstrapper struct {
|
|
Config
|
|
|
|
// IDs of validators we have requested the accepted frontier from but haven't
|
|
// received a reply from
|
|
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{})
|
|
}
|
|
|
|
// Ask each of the bootstrap validators to send their accepted frontier
|
|
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 {
|
|
// If we can't get a response from [validatorID], act as though they said their accepted frontier is empty
|
|
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
|
|
}
|
|
// Mark that we received a response from [validatorID]
|
|
b.pendingAcceptedFrontier.Remove(validatorID)
|
|
|
|
// Union the reported accepted frontier from [validatorID] with the accepted frontier we got from others
|
|
b.acceptedFrontier.Union(containerIDs)
|
|
|
|
// We've received the accepted frontier from every bootstrap validator
|
|
// Ask each bootstrap validator to filter the list of containers that we were
|
|
// told are on the accepted frontier such that the list only contains containers
|
|
// they think are accepted
|
|
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 {
|
|
// If we can't get a response from [validatorID], act as though they said
|
|
// that they think none of the containers we sent them in GetAccepted are accepted
|
|
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
|
|
}
|
|
// Mark that we received a response from [validatorID]
|
|
b.pendingAccepted.Remove(validatorID)
|
|
|
|
weight := uint64(0)
|
|
if vdr, ok := b.Beacons.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
|
|
}
|
|
|
|
// We've received the filtered accepted frontier from every bootstrap validator
|
|
// Accept all containers that have a sufficient weight behind them
|
|
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)
|
|
}
|