mirror of
https://github.com/luxfi/bft.git
synced 2026-10-30 17:01:54 +00:00
Reapplies the fork's whole delta on top of the merge, as one reviewable diff rather than 43 conflict resolutions. Nothing here changes consensus behaviour. - module github.com/luxfi/bft, Go 1.26.4; root package simplex -> bft, which would otherwise collide with the simplex/ subpackage under this module name. - go.uber.org/zap -> github.com/luxfi/log, so common.Logger takes ...log.Field. zap.Error maps to log.Err: log.Error is luxfi/log's package-level logging function, not a field constructor, and the compiler is the only thing that catches that. Needs luxfi/log v1.6.1 for the Stringers constructor. - avalanchego/ -> host/. The package holds NodeID, VMBlock, Bitmask and size limits — the host node's types, vendored to avoid importing a whole node — so it is named for what it holds. Renaming it luxd/ would have swapped one brand for another and said just as little. - testutil/logger.go reimplemented without zapcore. Upstream layers a keyword-filtering Core, a debug-swallowing Encoder and a Tee because that is zap's extension model; luxfi/log has no such seam, so every policy is applied in one emit. That also fixes two defects the layering caused: SilenceExceptKeywords rebuilt the logger and discarded an installed Intercept hook, and the two sinks read different env vars (LOG_LEVEL vs log_level) so they disagreed about suppressing debug. Hooks run outside the lock, so a hook that logs fails the test instead of deadlocking it. Fatal panics rather than exiting, because exiting takes the suite down with no attribution. - simplex/monitor_test.go keeps a local logger: testutil imports simplex, so an internal test cannot import testutil back without closing a cycle. - README keeps the name Simplex throughout — it is the protocol's own name and the subject of reference [1]. Only the deployment target changed. The HyperSDK sentence still says Avalanche because that sentence is true. - Drops the fork's Makefile: it duplicated unit-tests.sh and fuzz.sh, and ran the suite with -timeout 30s when simplex alone needs over 100s. Co-authored-by: Hanzo Dev <[email protected]>
114 lines
3.7 KiB
Go
114 lines
3.7 KiB
Go
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package common
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/luxfi/log"
|
|
)
|
|
|
|
var (
|
|
ErrorNoVotes = errors.New("no votes to notarize")
|
|
ErrorInvalidFinalizationDigest = errors.New("finalization digests do not match")
|
|
)
|
|
|
|
func NewEmptyNotarizationRecord(emptyNotarization *EmptyNotarization) []byte {
|
|
return NewQuorumRecord(emptyNotarization.QC.Bytes(), emptyNotarization.Vote.Bytes(), EmptyNotarizationRecordType)
|
|
}
|
|
|
|
func EmptyNotarizationFromRecord(record []byte, qd QCDeserializer) (EmptyNotarization, error) {
|
|
qcBytes, emptyVote, err := ParseEmptyNotarizationRecord(record)
|
|
if err != nil {
|
|
return EmptyNotarization{}, err
|
|
}
|
|
|
|
qc, err := qd.DeserializeQuorumCertificate(qcBytes)
|
|
if err != nil {
|
|
return EmptyNotarization{}, err
|
|
}
|
|
|
|
return EmptyNotarization{
|
|
Vote: emptyVote,
|
|
QC: qc,
|
|
}, nil
|
|
}
|
|
|
|
// NewNotarization builds a Notarization for a block described by [blockHeader] from [votesForCurrentRound].
|
|
func NewNotarization(logger Logger, signatureAggregator SignatureAggregator, votesForCurrentRound map[string]*Vote, blockHeader BlockHeader) (Notarization, error) {
|
|
voteCount := len(votesForCurrentRound)
|
|
signatures := make([]Signature, 0, voteCount)
|
|
logger.Info("Collected Quorum of votes", log.Uint64("round", blockHeader.Round), log.Int("votes", voteCount))
|
|
|
|
var toBeSignedVote *ToBeSignedVote
|
|
for _, vote := range votesForCurrentRound {
|
|
if !vote.Vote.BlockHeader.Equals(&blockHeader) {
|
|
logger.Debug("Skipping vote for block header different than the proposal block header",
|
|
log.Stringer("voteDigest", &vote.Vote.BlockHeader), log.Stringer("NodeID", vote.Signature.Signer))
|
|
continue
|
|
}
|
|
logger.Debug("Collected vote from node", log.Stringer("NodeID", vote.Signature.Signer),
|
|
log.String("Digest", vote.Vote.Digest.String()), log.Uint64("round", vote.Vote.Round),
|
|
log.Uint64("seq", vote.Vote.Seq))
|
|
signatures = append(signatures, vote.Signature)
|
|
if toBeSignedVote == nil {
|
|
toBeSignedVote = &vote.Vote
|
|
}
|
|
}
|
|
|
|
if toBeSignedVote == nil {
|
|
return Notarization{}, ErrorNoVotes
|
|
}
|
|
// sort the signatures by Signer to ensure consistent ordering
|
|
slices.SortFunc(signatures, compareSignatures)
|
|
|
|
var notarization Notarization
|
|
var err error
|
|
notarization.Vote = *toBeSignedVote
|
|
notarization.QC, err = signatureAggregator.Aggregate(signatures)
|
|
if err != nil {
|
|
return Notarization{}, fmt.Errorf("could not aggregate signatures for notarization: %w", err)
|
|
}
|
|
|
|
return notarization, nil
|
|
}
|
|
|
|
// NewFinalization builds a Finalization from [finalizeVotes].
|
|
func NewFinalization(signatureAggregator SignatureAggregator, finalizeVotes []*FinalizeVote) (Finalization, error) {
|
|
voteCount := len(finalizeVotes)
|
|
if voteCount == 0 {
|
|
return Finalization{}, ErrorNoVotes
|
|
}
|
|
|
|
signatures := make([]Signature, 0, voteCount)
|
|
expectedDigest := finalizeVotes[0].Finalization.Digest
|
|
for _, vote := range finalizeVotes {
|
|
if vote.Finalization.Digest != expectedDigest {
|
|
return Finalization{}, ErrorInvalidFinalizationDigest
|
|
}
|
|
signatures = append(signatures, vote.Signature)
|
|
}
|
|
|
|
// sort the signatures, as they are not guaranteed to be in the same order
|
|
slices.SortFunc(signatures, compareSignatures)
|
|
|
|
var finalization Finalization
|
|
var err error
|
|
finalization.Finalization = finalizeVotes[0].Finalization
|
|
finalization.QC, err = signatureAggregator.Aggregate(signatures)
|
|
if err != nil {
|
|
return Finalization{}, fmt.Errorf("could not aggregate signatures for finalization: %w", err)
|
|
}
|
|
|
|
return finalization, nil
|
|
}
|
|
|
|
// compareSignatures compares two signatures by their Signer field returning -1, 0, 1 if i is less than, equal to, or greater than j.
|
|
func compareSignatures(i, j Signature) int {
|
|
return bytes.Compare(i.Signer, j.Signer)
|
|
}
|