Files
consensus/engine/bft/block_builder.go
T
zeekay 4aac78700d Refactor: remove dead code, add thread safety, improve test coverage
- Remove unused packages: engine/dag/{getter,bootstrap,vertex/vertexmock},
  engine/pq/{bootstrap,block}, core/{tracker,verify}
- Add sync.RWMutex to protocol/wave for thread-safe map access
- Increase engine/dag test coverage from 11.7% to 98.3%
- Add comprehensive tests for DAG consensus, vertex, and engine
- All 27 packages pass tests, 16 at 100% coverage
2025-12-13 07:42:01 +00:00

111 lines
3.2 KiB
Go

//go:build ignore
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package bft
import (
"context"
"fmt"
"time"
simplex "github.com/luxfi/bft"
"go.uber.org/zap"
"github.com/luxfi/consensus/engine/chain/block"
"github.com/luxfi/consensus/core"
"github.com/luxfi/log"
)
var _ simplex.BlockBuilder = (*BlockBuilder)(nil)
type BlockBuilder struct {
log log.Logger
vm block.ChainVM
blockTracker *blockTracker
}
const (
maxBackoff = 5 * time.Second
initBackoff = 10 * time.Millisecond
)
// BuildBlock continuously tries to build a block until the context is cancelled. If there are no blocks to be built, it will wait for an event from the VM.
// It returns false if the context was cancelled, otherwise it returns the built block and true.
func (b *BlockBuilder) BuildBlock(ctx context.Context, metadata simplex.ProtocolMetadata) (simplex.VerifiedBlock, bool) {
for curWait := initBackoff; ; curWait = backoff(ctx, curWait) {
if ctx.Err() != nil {
b.log.Debug("Context cancelled, stopping block building", zap.Error(ctx.Err()))
return nil, false
}
err := b.waitForPendingBlock(ctx)
if err != nil {
b.log.Debug("Error waiting for incoming block", zap.Error(err))
continue
}
vmBlock, err := b.vm.BuildBlock(ctx)
if err != nil {
b.log.Info("Error building block", zap.Error(err))
continue
}
simplexBlock, err := newBlock(metadata, vmBlock, b.blockTracker)
if err != nil {
b.log.Error("Error creating simplex block from built block", zap.Error(err))
return nil, false
}
curWait = initBackoff // Reset backoff after a successful block build
verifiedBlock, err := simplexBlock.Verify(ctx)
if err != nil {
b.log.Warn("Error verifying block we built ourselves", zap.Error(err))
continue
}
return verifiedBlock, true
}
}
// IncomingBlock blocks until a new block is ready to be built from the VM, or until the
// context is cancelled. This method implements the bft.BlockBuilder interface.
func (b *BlockBuilder) IncomingBlock(ctx context.Context) {
err := b.waitForPendingBlock(ctx)
if err != nil {
b.log.Debug("Error waiting for incoming block", zap.Error(err))
}
}
// WaitForPendingBlock blocks until a new block is ready to be built from the VM, or until the
// context is cancelled.
func (b *BlockBuilder) WaitForPendingBlock(ctx context.Context) {
err := b.waitForPendingBlock(ctx)
if err != nil {
b.log.Debug("Error waiting for incoming block", zap.Error(err))
}
}
func (b *BlockBuilder) waitForPendingBlock(ctx context.Context) error {
for curBackoff := initBackoff; ; curBackoff = backoff(ctx, curBackoff) {
msg, err := b.vm.WaitForEvent(ctx)
if err != nil {
return err
}
if msg == core.PendingTxs {
return nil
}
b.log.Warn("Received unexpected message", log.String("message", fmt.Sprintf("%v", msg)))
}
}
// backoff waits for `backoff` duration before returning the next backoff duration.
// It doubles the backoff duration each time it is called, up to a maximum of `maxBackoff`.
func backoff(ctx context.Context, backoff time.Duration) time.Duration {
select {
case <-ctx.Done():
return 0
case <-time.After(backoff):
}
return min(maxBackoff, 2*backoff) // exponential backoff
}