gecko/snow/engine/avalanche/bootstrapper.go

285 lines
7.2 KiB
Go
Raw Normal View History

2020-03-10 12:20:34 -07:00
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package avalanche
import (
2020-05-29 12:03:00 -07:00
"fmt"
2020-03-10 12:20:34 -07:00
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/choices"
"github.com/ava-labs/gecko/snow/consensus/avalanche"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/snow/engine/common/queue"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/prometheus/client_golang/prometheus"
)
// BootstrapConfig ...
type BootstrapConfig struct {
common.Config
// VtxBlocked tracks operations that are blocked on vertices
// TxBlocked tracks operations that are blocked on transactions
VtxBlocked, TxBlocked *queue.Jobs
State State
VM DAGVM
}
type bootstrapper struct {
BootstrapConfig
metrics
common.Bootstrapper
// IDs of vertices that we're already in the process of getting
// TODO: Find a better way to track; this keeps every single vertex's ID in memory when bootstrapping from nothing
seen ids.Set
numFetched uint64 // number of vertices that have been fetched from validators
// vtxReqs prevents asking validators for the same vertex
vtxReqs common.Requests
// IDs of vertices that we have requested from other validators but haven't received
2020-03-10 12:20:34 -07:00
pending ids.Set
finished bool
2020-05-28 20:48:08 -07:00
onFinished func() error
2020-03-10 12:20:34 -07:00
}
// Initialize this engine.
2020-05-28 20:48:08 -07:00
func (b *bootstrapper) Initialize(config BootstrapConfig) error {
2020-03-10 12:20:34 -07:00
b.BootstrapConfig = config
b.VtxBlocked.SetParser(&vtxParser{
2020-05-20 08:37:01 -07:00
log: config.Context.Log,
numAccepted: b.numBSVtx,
numDropped: b.numBSDroppedVtx,
2020-03-10 12:20:34 -07:00
state: b.State,
})
b.TxBlocked.SetParser(&txParser{
2020-05-20 08:37:01 -07:00
log: config.Context.Log,
numAccepted: b.numBSTx,
numDropped: b.numBSDroppedTx,
2020-03-10 12:20:34 -07:00
vm: b.VM,
})
config.Bootstrapable = b
b.Bootstrapper.Initialize(config.Config)
2020-05-28 20:48:08 -07:00
return nil
2020-03-10 12:20:34 -07:00
}
// CurrentAcceptedFrontier ...
func (b *bootstrapper) CurrentAcceptedFrontier() ids.Set {
acceptedFrontier := ids.Set{}
acceptedFrontier.Add(b.State.Edge()...)
return acceptedFrontier
}
// FilterAccepted ...
func (b *bootstrapper) FilterAccepted(containerIDs ids.Set) ids.Set {
acceptedVtxIDs := ids.Set{}
for _, vtxID := range containerIDs.List() {
if vtx, err := b.State.GetVertex(vtxID); err == nil && vtx.Status() == choices.Accepted {
acceptedVtxIDs.Add(vtxID)
}
}
return acceptedVtxIDs
}
// ForceAccepted ...
2020-05-28 20:48:08 -07:00
func (b *bootstrapper) ForceAccepted(acceptedContainerIDs ids.Set) error {
2020-03-10 12:20:34 -07:00
for _, vtxID := range acceptedContainerIDs.List() {
2020-05-29 12:03:00 -07:00
if err := b.fetch(vtxID); err != nil {
return err
}
2020-03-10 12:20:34 -07:00
}
if numPending := b.pending.Len(); numPending == 0 {
// TODO: This typically indicates bootstrapping has failed, so this
// should be handled appropriately
2020-05-28 20:48:08 -07:00
return b.finish()
2020-03-10 12:20:34 -07:00
}
2020-05-28 20:48:08 -07:00
return nil
2020-03-10 12:20:34 -07:00
}
// Put ...
2020-05-28 20:48:08 -07:00
func (b *bootstrapper) Put(vdr ids.ShortID, requestID uint32, vtxID ids.ID, vtxBytes []byte) error {
2020-03-10 12:20:34 -07:00
vtx, err := b.State.ParseVertex(vtxBytes)
if err != nil {
b.BootstrapConfig.Context.Log.Debug("ParseVertex failed due to %s for block:\n%s",
2020-03-10 12:20:34 -07:00
err,
formatting.DumpBytes{Bytes: vtxBytes})
2020-05-28 20:48:08 -07:00
return b.GetFailed(vdr, requestID)
}
if !b.pending.Contains(vtx.ID()) {
2020-05-12 12:18:02 -07:00
b.BootstrapConfig.Context.Log.Debug("Validator %s sent an unrequested vertex:\n%s",
vdr,
formatting.DumpBytes{Bytes: vtxBytes})
2020-05-28 20:48:08 -07:00
return b.GetFailed(vdr, requestID)
2020-03-10 12:20:34 -07:00
}
2020-05-28 20:48:08 -07:00
return b.addVertex(vtx)
2020-03-10 12:20:34 -07:00
}
// GetFailed ...
2020-05-28 20:48:08 -07:00
func (b *bootstrapper) GetFailed(vdr ids.ShortID, requestID uint32) error {
vtxID, ok := b.vtxReqs.Remove(vdr, requestID)
if !ok {
2020-05-12 12:18:02 -07:00
b.BootstrapConfig.Context.Log.Debug("GetFailed called without sending the corresponding Get message from %s",
vdr)
2020-05-28 20:48:08 -07:00
return nil
}
b.sendRequest(vtxID)
2020-05-28 20:48:08 -07:00
return nil
}
2020-03-10 12:20:34 -07:00
2020-05-29 12:03:00 -07:00
func (b *bootstrapper) fetch(vtxID ids.ID) error {
2020-03-10 12:20:34 -07:00
if b.pending.Contains(vtxID) {
2020-05-29 12:03:00 -07:00
return nil
2020-03-10 12:20:34 -07:00
}
vtx, err := b.State.GetVertex(vtxID)
if err != nil {
b.sendRequest(vtxID)
2020-05-29 12:03:00 -07:00
return nil
2020-03-10 12:20:34 -07:00
}
2020-05-29 12:03:00 -07:00
return b.storeVertex(vtx)
2020-03-10 12:20:34 -07:00
}
func (b *bootstrapper) sendRequest(vtxID ids.ID) {
validators := b.BootstrapConfig.Validators.Sample(1)
if len(validators) == 0 {
b.BootstrapConfig.Context.Log.Error("Dropping request for %s as there are no validators", vtxID)
return
}
validatorID := validators[0].ID()
b.RequestID++
b.vtxReqs.RemoveAny(vtxID)
b.vtxReqs.Add(validatorID, b.RequestID, vtxID)
2020-03-10 12:20:34 -07:00
b.pending.Add(vtxID)
b.BootstrapConfig.Sender.Get(validatorID, b.RequestID, vtxID)
2020-05-20 08:37:01 -07:00
b.numBSPendingRequests.Set(float64(b.pending.Len()))
2020-03-10 12:20:34 -07:00
}
2020-05-28 20:48:08 -07:00
func (b *bootstrapper) addVertex(vtx avalanche.Vertex) error {
2020-05-29 12:03:00 -07:00
if err := b.storeVertex(vtx); err != nil {
return err
}
if numPending := b.pending.Len(); numPending == 0 {
2020-05-28 20:48:08 -07:00
return b.finish()
}
2020-05-28 20:48:08 -07:00
return nil
}
2020-05-29 12:03:00 -07:00
func (b *bootstrapper) storeVertex(vtx avalanche.Vertex) error {
2020-03-10 12:20:34 -07:00
vts := []avalanche.Vertex{vtx}
b.numFetched++
if b.numFetched%2500 == 0 { // perioidcally inform user of progress
b.BootstrapConfig.Context.Log.Info("bootstrapping has fetched %d vertices", b.numFetched)
}
2020-03-10 12:20:34 -07:00
for len(vts) > 0 {
newLen := len(vts) - 1
vtx := vts[newLen]
vts = vts[:newLen]
vtxID := vtx.ID()
switch status := vtx.Status(); status {
case choices.Unknown:
b.sendRequest(vtxID)
case choices.Processing:
b.pending.Remove(vtxID)
if err := b.VtxBlocked.Push(&vertexJob{
2020-05-20 08:37:01 -07:00
log: b.BootstrapConfig.Context.Log,
numAccepted: b.numBSVtx,
numDropped: b.numBSDroppedVtx,
2020-03-10 12:20:34 -07:00
vtx: vtx,
}); err == nil {
2020-05-20 08:37:01 -07:00
b.numBSBlockedVtx.Inc()
} else {
b.BootstrapConfig.Context.Log.Verbo("couldn't push to vtxBlocked")
2020-03-10 12:20:34 -07:00
}
2020-05-29 12:03:00 -07:00
if err := b.VtxBlocked.Commit(); err != nil {
return err
}
2020-03-10 12:20:34 -07:00
for _, tx := range vtx.Txs() {
if err := b.TxBlocked.Push(&txJob{
2020-05-20 08:37:01 -07:00
log: b.BootstrapConfig.Context.Log,
numAccepted: b.numBSTx,
numDropped: b.numBSDroppedTx,
2020-03-10 12:20:34 -07:00
tx: tx,
}); err == nil {
2020-05-20 08:37:01 -07:00
b.numBSBlockedTx.Inc()
} else {
b.BootstrapConfig.Context.Log.Verbo("couldn't push to txBlocked")
2020-03-10 12:20:34 -07:00
}
}
2020-05-29 12:03:00 -07:00
if err := b.TxBlocked.Commit(); err != nil {
return err
}
for _, parent := range vtx.Parents() {
2020-05-25 19:18:43 -07:00
if parentID := parent.ID(); !b.seen.Contains(parentID) {
b.seen.Add(parentID)
vts = append(vts, parent)
}
}
2020-03-10 12:20:34 -07:00
case choices.Accepted:
2020-05-29 12:03:00 -07:00
b.BootstrapConfig.Context.Log.Verbo("bootstrapping confirmed %s", vtxID)
2020-03-10 12:20:34 -07:00
case choices.Rejected:
2020-05-29 12:03:00 -07:00
return fmt.Errorf("bootstrapping wants to accept %s, however it was previously rejected", vtxID)
2020-03-10 12:20:34 -07:00
}
}
numPending := b.pending.Len()
2020-05-20 08:37:01 -07:00
b.numBSPendingRequests.Set(float64(numPending))
2020-05-29 12:03:00 -07:00
return nil
2020-03-10 12:20:34 -07:00
}
2020-05-28 20:48:08 -07:00
func (b *bootstrapper) finish() error {
2020-03-10 12:20:34 -07:00
if b.finished {
2020-05-28 20:48:08 -07:00
return nil
2020-03-10 12:20:34 -07:00
}
b.BootstrapConfig.Context.Log.Info("bootstrapping finished fetching vertices. executing state transitions...")
2020-03-10 12:20:34 -07:00
2020-05-28 20:48:08 -07:00
if err := b.executeAll(b.TxBlocked, b.numBSBlockedTx); err != nil {
return err
}
if err := b.executeAll(b.VtxBlocked, b.numBSBlockedVtx); err != nil {
return err
}
2020-03-10 12:20:34 -07:00
// Start consensus
2020-05-28 20:48:08 -07:00
if err := b.onFinished(); err != nil {
return err
}
b.seen = ids.Set{}
2020-03-10 12:20:34 -07:00
b.finished = true
2020-05-28 20:48:08 -07:00
return nil
2020-03-10 12:20:34 -07:00
}
2020-05-28 20:48:08 -07:00
func (b *bootstrapper) executeAll(jobs *queue.Jobs, numBlocked prometheus.Gauge) error {
2020-03-10 12:20:34 -07:00
for job, err := jobs.Pop(); err == nil; job, err = jobs.Pop() {
numBlocked.Dec()
2020-04-20 17:00:36 -07:00
b.BootstrapConfig.Context.Log.Debug("Executing: %s", job.ID())
2020-03-10 12:20:34 -07:00
if err := jobs.Execute(job); err != nil {
2020-05-28 20:48:08 -07:00
b.BootstrapConfig.Context.Log.Error("Error executing: %s", err)
return err
2020-03-10 12:20:34 -07:00
}
2020-05-29 12:03:00 -07:00
if err := jobs.Commit(); err != nil {
return err
}
2020-03-10 12:20:34 -07:00
}
2020-05-28 20:48:08 -07:00
return nil
2020-03-10 12:20:34 -07:00
}