Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify avalanche bootstrapping #2286

Merged
merged 16 commits into from
Nov 15, 2023
35 changes: 13 additions & 22 deletions chains/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -929,29 +929,20 @@ func (m *manager) createAvalancheChain(
}

// create bootstrap gear
_, specifiedLinearizationTime := version.CortinaTimes[ctx.NetworkID]
specifiedLinearizationTime = specifiedLinearizationTime && ctx.ChainID == m.XChainID
avalancheBootstrapperConfig := avbootstrap.Config{
Config: common.Config{
Ctx: ctx,
Beacons: vdrs,
SampleK: sampleK,
StartupTracker: startupTracker,
Alpha: bootstrapWeight/2 + 1, // must be > 50%
Sender: avalancheMessageSender,
BootstrapTracker: sb,
Timer: h,
RetryBootstrap: m.RetryBootstrap,
RetryBootstrapWarnFrequency: m.RetryBootstrapWarnFrequency,
AncestorsMaxContainersReceived: m.BootstrapAncestorsMaxContainersReceived,
SharedCfg: &common.SharedConfig{},
},
AllGetsServer: avaGetHandler,
VtxBlocked: vtxBlocker,
TxBlocked: txBlocker,
Manager: vtxManager,
VM: linearizableVM,
LinearizeOnStartup: !specifiedLinearizationTime,
AllGetsServer: avaGetHandler,
Ctx: ctx,
Beacons: vdrs,
StartupTracker: startupTracker,
Sender: avalancheMessageSender,
AncestorsMaxContainersReceived: m.BootstrapAncestorsMaxContainersReceived,
VtxBlocked: vtxBlocker,
TxBlocked: txBlocker,
Manager: vtxManager,
VM: linearizableVM,
}
if ctx.ChainID == m.XChainID {
avalancheBootstrapperConfig.StopVertexID = version.CortinaXChainStopVertexID[ctx.NetworkID]
}

avalancheBootstrapper, err := avbootstrap.New(
Expand Down
2 changes: 1 addition & 1 deletion node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,7 @@ func (n *Node) initChainManager(avaxAssetID ids.ID) error {
BootstrapAncestorsMaxContainersSent: n.Config.BootstrapAncestorsMaxContainersSent,
BootstrapAncestorsMaxContainersReceived: n.Config.BootstrapAncestorsMaxContainersReceived,
ApricotPhase4Time: version.GetApricotPhase4Time(n.Config.NetworkID),
ApricotPhase4MinPChainHeight: version.GetApricotPhase4MinPChainHeight(n.Config.NetworkID),
ApricotPhase4MinPChainHeight: version.ApricotPhase4MinPChainHeight[n.Config.NetworkID],
ResourceTracker: n.resourceTracker,
StateSyncBeacons: n.Config.StateSyncIDs,
TracingEnabled: n.Config.TraceConfig.Enabled,
Expand Down
141 changes: 53 additions & 88 deletions snow/engine/avalanche/bootstrap/bootstrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func New(

StateSummaryFrontierHandler: common.NewNoOpStateSummaryFrontierHandler(config.Ctx.Log),
AcceptedStateSummaryHandler: common.NewNoOpAcceptedStateSummaryHandler(config.Ctx.Log),
AcceptedFrontierHandler: common.NewNoOpAcceptedFrontierHandler(config.Ctx.Log),
AcceptedHandler: common.NewNoOpAcceptedHandler(config.Ctx.Log),
PutHandler: common.NewNoOpPutHandler(config.Ctx.Log),
QueryHandler: common.NewNoOpQueryHandler(config.Ctx.Log),
ChitsHandler: common.NewNoOpChitsHandler(config.Ctx.Log),
Expand All @@ -52,41 +54,41 @@ func New(
OnFinished: onFinished,
},
}

if err := b.metrics.Initialize("bs", config.Ctx.AvalancheRegisterer); err != nil {
return nil, err
}

config.Config.Bootstrapable = b
b.Bootstrapper = common.NewCommonBootstrapper(config.Config)
return b, nil
return b, b.metrics.Initialize("bs", config.Ctx.AvalancheRegisterer)
}

// Note: To align with the Snowman invariant, it should be guaranteed the VM is
// not used until after the bootstrapper has been Started.
type bootstrapper struct {
Config
common.Halter

// list of NoOpsHandler for messages dropped by bootstrapper
common.StateSummaryFrontierHandler
common.AcceptedStateSummaryHandler
common.AcceptedFrontierHandler
abi87 marked this conversation as resolved.
Show resolved Hide resolved
common.AcceptedHandler
common.PutHandler
common.QueryHandler
common.ChitsHandler
common.AppHandler

common.Bootstrapper
common.Fetcher
metrics

started bool

// IDs of vertices that we will send a GetAncestors request for once we are
// not at the max number of outstanding requests
needToFetch set.Set[ids.ID]

// Contains IDs of vertices that have recently been processed
processedCache *cache.LRU[ids.ID, struct{}]

// Tracks the last requestID that was used in a request
requestID uint32
}

func (b *bootstrapper) Context() *snow.ConsensusContext {
return b.Ctx
}

func (b *bootstrapper) Clear(context.Context) error {
Expand Down Expand Up @@ -256,16 +258,7 @@ func (b *bootstrapper) Connected(
return err
}

if err := b.StartupTracker.Connected(ctx, nodeID, nodeVersion); err != nil {
return err
}

if b.started || !b.StartupTracker.ShouldStart() {
return nil
}

b.started = true
return b.Startup(ctx)
return b.StartupTracker.Connected(ctx, nodeID, nodeVersion)
abi87 marked this conversation as resolved.
Show resolved Hide resolved
}

func (b *bootstrapper) Disconnected(ctx context.Context, nodeID ids.NodeID) error {
Expand Down Expand Up @@ -327,7 +320,7 @@ func (b *bootstrapper) Start(ctx context.Context, startReqID uint32) error {
return err
}

b.Config.SharedCfg.RequestID = startReqID
b.requestID = startReqID
StephenButtolph marked this conversation as resolved.
Show resolved Hide resolved

// If the network was already linearized, don't attempt to linearize it
// again.
Expand All @@ -336,38 +329,38 @@ func (b *bootstrapper) Start(ctx context.Context, startReqID uint32) error {
return fmt.Errorf("failed to get linearization status: %w", err)
}
if linearized {
edge := b.Manager.Edge(ctx)
return b.ForceAccepted(ctx, edge)
return b.ForceAccepted(ctx, nil)
}

// If requested, assume the currently accepted state is what was linearized.
//
// Note: This is used to linearize networks that were created after the
// linearization occurred.
if b.Config.LinearizeOnStartup {
edge := b.Manager.Edge(ctx)
stopVertex, err := b.Manager.BuildStopVtx(ctx, edge)
if err != nil {
return fmt.Errorf("failed to create stop vertex: %w", err)
}
if err := stopVertex.Accept(ctx); err != nil {
return fmt.Errorf("failed to accept stop vertex: %w", err)
}

stopVertexID := stopVertex.ID()
b.Ctx.Log.Info("accepted stop vertex",
zap.Stringer("vtxID", stopVertexID),
// If a stop vertex is well known, accept that.
if b.Config.StopVertexID != ids.Empty {
b.Ctx.Log.Info("using well known stop vertex",
zap.Stringer("vtxID", b.Config.StopVertexID),
)

return b.ForceAccepted(ctx, []ids.ID{stopVertexID})
return b.ForceAccepted(ctx, []ids.ID{b.Config.StopVertexID})
}

if !b.StartupTracker.ShouldStart() {
return nil
// If a stop vertex isn't well known, treat the current state as the final
// DAG state.
Comment on lines +344 to +345
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: wouldn't this make stop vertex different for different nodes? Isn't this a problem that we linearize VMs in different nodes starting from different stop vertexes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avalanchego doesn't support DAGs other than the Fuji X-chain and the Mainnet X-chain. This code is here so that the local network X-chain will be immediately linearized (it is assumed that the DAG is empty in this case).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add this comment to the codebase

//
// Note: This is used to linearize networks that were created after the
// linearization occurred.
edge := b.Manager.Edge(ctx)
stopVertex, err := b.Manager.BuildStopVtx(ctx, edge)
if err != nil {
return fmt.Errorf("failed to create stop vertex: %w", err)
}
if err := stopVertex.Accept(ctx); err != nil {
return fmt.Errorf("failed to accept stop vertex: %w", err)
}

b.started = true
return b.Startup(ctx)
stopVertexID := stopVertex.ID()
b.Ctx.Log.Info("generated stop vertex",
zap.Stringer("vtxID", stopVertexID),
)

return b.ForceAccepted(ctx, nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: why is this not b.ForceAccepted(ctx, []ids.ID{stopVertexID})?
IIUC with b.ForceAccepted(ctx, nil) will start downloading missing vertexes, while stopVertexID is guaranteed to be already available locally

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

b.ForceAccepted(ctx, nil)

will not download anything - as nothing is provided.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that in ForceAccepted we append the vertexs found there with those missing already stored locally. So I understand that ForceAccepted may download something even if we pass ni here.
Also my question is: do we want to guarantee that a node download all accepted vertexes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do (and must) guarantee that a node downloads all accepted vertices

}

func (b *bootstrapper) HealthCheck(ctx context.Context) (interface{}, error) {
Expand Down Expand Up @@ -410,10 +403,10 @@ func (b *bootstrapper) fetch(ctx context.Context, vtxIDs ...ids.ID) error {
return fmt.Errorf("dropping request for %s as there are no validators", vtxID)
}
validatorID := validatorIDs[0]
b.Config.SharedCfg.RequestID++
b.requestID++

b.OutstandingRequests.Add(validatorID, b.Config.SharedCfg.RequestID, vtxID)
b.Config.Sender.SendGetAncestors(ctx, validatorID, b.Config.SharedCfg.RequestID, vtxID) // request vertex and ancestors
b.OutstandingRequests.Add(validatorID, b.requestID, vtxID)
b.Config.Sender.SendGetAncestors(ctx, validatorID, b.requestID, vtxID) // request vertex and ancestors
}
return b.checkFinish(ctx)
}
Expand Down Expand Up @@ -498,15 +491,9 @@ func (b *bootstrapper) process(ctx context.Context, vtxs ...avalanche.Vertex) er

verticesFetchedSoFar := b.VtxBlocked.Jobs.PendingJobs()
if verticesFetchedSoFar%common.StatusUpdateFrequency == 0 { // Periodically print progress
if !b.Config.SharedCfg.Restarted {
b.Ctx.Log.Info("fetched vertices",
zap.Uint64("numVerticesFetched", verticesFetchedSoFar),
)
} else {
b.Ctx.Log.Debug("fetched vertices",
zap.Uint64("numVerticesFetched", verticesFetchedSoFar),
)
}
abi87 marked this conversation as resolved.
Show resolved Hide resolved
b.Ctx.Log.Info("fetched vertices",
zap.Uint64("numVerticesFetched", verticesFetchedSoFar),
)
}

parents, err := vtx.Parents()
Expand Down Expand Up @@ -578,66 +565,44 @@ func (b *bootstrapper) ForceAccepted(ctx context.Context, acceptedContainerIDs [
// checkFinish repeatedly executes pending transactions and requests new frontier blocks until there aren't any new ones
// after which it finishes the bootstrap process
func (b *bootstrapper) checkFinish(ctx context.Context) error {
// If there are outstanding requests for vertices or we still need to fetch vertices, we can't finish
pendingJobs := b.VtxBlocked.MissingIDs()
if b.IsBootstrapped() || len(pendingJobs) > 0 {
// If we still need to fetch vertices, we can't finish
if len(b.VtxBlocked.MissingIDs()) > 0 {
return nil
}

if !b.Config.SharedCfg.Restarted {
b.Ctx.Log.Info("executing transactions")
} else {
b.Ctx.Log.Debug("executing transactions")
}

b.Ctx.Log.Info("executing transactions")
_, err := b.TxBlocked.ExecuteAll(
ctx,
b.Config.Ctx,
b,
b.Config.SharedCfg.Restarted,
false,
b.Ctx.TxAcceptor,
)
if err != nil || b.Halted() {
return err
}

if !b.Config.SharedCfg.Restarted {
b.Ctx.Log.Info("executing vertices")
} else {
b.Ctx.Log.Debug("executing vertices")
}

b.Ctx.Log.Info("executing vertices")
_, err = b.VtxBlocked.ExecuteAll(
ctx,
b.Config.Ctx,
b,
b.Config.SharedCfg.Restarted,
false,
b.Ctx.VertexAcceptor,
)
if err != nil || b.Halted() {
return err
}

// If the chain is linearized, we should immediately move on to start
// bootstrapping snowman.
linearized, err := b.Manager.StopVertexAccepted(ctx)
if err != nil {
return err
}
if !linearized {
b.Ctx.Log.Debug("checking for stop vertex before finishing bootstrapping")
return b.Restart(ctx, true)
}

// Invariant: edge will only be the stop vertex after its acceptance.
// Invariant: edge will only be the stop vertex
edge := b.Manager.Edge(ctx)
stopVertexID := edge[0]
if err := b.VM.Linearize(ctx, stopVertexID); err != nil {
return err
}

b.processedCache.Flush()
return b.OnFinished(ctx, b.Config.SharedCfg.RequestID)
return b.OnFinished(ctx, b.requestID)
}

// A vertex is less than another vertex if it is unknown. Ties are broken by
Expand Down
Loading
Loading