mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
- 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
111 lines
3.2 KiB
Go
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
|
|
}
|