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]>
188 lines
5.8 KiB
Go
188 lines
5.8 KiB
Go
// Copyright (C) 2019-2025, Ava Labs, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package common_test
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/luxfi/bft/common"
|
|
. "github.com/luxfi/bft/testutil"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var errorSigAggregation = errors.New("signature error")
|
|
|
|
func TestNewNotarization(t *testing.T) {
|
|
l := MakeLogger(t, 1)
|
|
testBlock := &TestBlock{}
|
|
tests := []struct {
|
|
name string
|
|
votesForCurrentRound map[string]*common.Vote
|
|
block common.VerifiedBlock
|
|
expectError error
|
|
signatureAggregator common.SignatureAggregator
|
|
expectedSigners []common.NodeID
|
|
}{
|
|
{
|
|
name: "valid notarization",
|
|
votesForCurrentRound: func() map[string]*common.Vote {
|
|
votes := make(map[string]*common.Vote)
|
|
nodeIds := [][]byte{{1}, {2}, {3}, {4}, {5}}
|
|
for _, nodeId := range nodeIds {
|
|
vote, err := NewTestVote(testBlock, nodeId)
|
|
require.NoError(t, err)
|
|
votes[string(nodeId)] = vote
|
|
}
|
|
return votes
|
|
}(),
|
|
block: testBlock,
|
|
signatureAggregator: &TestSignatureAggregator{N: 5},
|
|
expectError: nil,
|
|
expectedSigners: []common.NodeID{
|
|
{1}, {2}, {3}, {4}, {5},
|
|
},
|
|
},
|
|
{
|
|
name: "diverging votes",
|
|
votesForCurrentRound: func() map[string]*common.Vote {
|
|
votes := make(map[string]*common.Vote)
|
|
nodeIds := [][]byte{{1}, {2}, {3}, {4}, {5}}
|
|
for _, nodeId := range nodeIds {
|
|
vote, err := NewTestVote(testBlock, nodeId)
|
|
require.NoError(t, err)
|
|
votes[string(nodeId)] = vote
|
|
}
|
|
votes[string(common.NodeID{2})].Vote.Digest[0]++ // Diverging vote
|
|
return votes
|
|
}(),
|
|
block: testBlock,
|
|
signatureAggregator: &TestSignatureAggregator{N: 5},
|
|
expectError: nil,
|
|
expectedSigners: []common.NodeID{
|
|
{1}, {3}, {4}, {5},
|
|
},
|
|
},
|
|
{
|
|
name: "no votes",
|
|
votesForCurrentRound: map[string]*common.Vote{},
|
|
block: testBlock,
|
|
signatureAggregator: &TestSignatureAggregator{N: 5},
|
|
expectError: common.ErrorNoVotes,
|
|
},
|
|
{
|
|
name: "error aggregating",
|
|
votesForCurrentRound: func() map[string]*common.Vote {
|
|
votes := make(map[string]*common.Vote)
|
|
nodeIds := [][]byte{{1}, {2}, {3}, {4}, {5}}
|
|
for _, nodeId := range nodeIds {
|
|
vote, err := NewTestVote(testBlock, nodeId)
|
|
require.NoError(t, err)
|
|
votes[string(nodeId)] = vote
|
|
}
|
|
return votes
|
|
}(),
|
|
block: testBlock,
|
|
signatureAggregator: &TestSignatureAggregator{Err: errorSigAggregation, N: 5},
|
|
expectError: errorSigAggregation,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
notarization, err := common.NewNotarization(l, tt.signatureAggregator, tt.votesForCurrentRound, tt.block.BlockHeader())
|
|
require.ErrorIs(t, err, tt.expectError, "expected error, got nil")
|
|
|
|
if tt.expectError == nil {
|
|
signers := notarization.QC.Signers()
|
|
require.Equal(t, tt.expectedSigners, signers)
|
|
|
|
for i, signer := range signers[1:] {
|
|
require.Negative(t, bytes.Compare(signers[i], signer), "signers not in order")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestNewFinalization(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
finalizeVotes []*common.FinalizeVote
|
|
signatureAggregator common.SignatureAggregator
|
|
expectedFinalization *common.ToBeSignedFinalization
|
|
expectedQC *common.QuorumCertificate
|
|
expectError error
|
|
}{
|
|
{
|
|
name: "valid finalizations in order",
|
|
finalizeVotes: []*common.FinalizeVote{
|
|
NewTestFinalizeVote(t, &TestBlock{}, []byte{1}),
|
|
NewTestFinalizeVote(t, &TestBlock{}, []byte{2}),
|
|
NewTestFinalizeVote(t, &TestBlock{}, []byte{3}),
|
|
},
|
|
signatureAggregator: &TestSignatureAggregator{N: 4},
|
|
expectedFinalization: &NewTestFinalizeVote(t, &TestBlock{}, []byte{1}).Finalization,
|
|
expectError: nil,
|
|
},
|
|
{
|
|
name: "unsorted finalizations",
|
|
finalizeVotes: []*common.FinalizeVote{
|
|
NewTestFinalizeVote(t, &TestBlock{}, []byte{3}),
|
|
NewTestFinalizeVote(t, &TestBlock{}, []byte{1}),
|
|
NewTestFinalizeVote(t, &TestBlock{}, []byte{2}),
|
|
},
|
|
signatureAggregator: &TestSignatureAggregator{N: 4},
|
|
expectedFinalization: &NewTestFinalizeVote(t, &TestBlock{}, []byte{1}).Finalization,
|
|
expectError: nil,
|
|
},
|
|
{
|
|
name: "finalizations with different digests",
|
|
finalizeVotes: []*common.FinalizeVote{
|
|
NewTestFinalizeVote(t, &TestBlock{Digest: [32]byte{1}}, []byte{1}),
|
|
NewTestFinalizeVote(t, &TestBlock{Digest: [32]byte{2}}, []byte{2}),
|
|
NewTestFinalizeVote(t, &TestBlock{Digest: [32]byte{3}}, []byte{3}),
|
|
},
|
|
signatureAggregator: &TestSignatureAggregator{N: 4},
|
|
expectError: common.ErrorInvalidFinalizationDigest,
|
|
},
|
|
{
|
|
name: "signature aggregator errors",
|
|
finalizeVotes: []*common.FinalizeVote{
|
|
NewTestFinalizeVote(t, &TestBlock{}, []byte{1}),
|
|
},
|
|
signatureAggregator: &TestSignatureAggregator{Err: errorSigAggregation, N: 4},
|
|
expectError: errorSigAggregation,
|
|
},
|
|
{
|
|
name: "no votes",
|
|
finalizeVotes: []*common.FinalizeVote{},
|
|
signatureAggregator: &TestSignatureAggregator{N: 4},
|
|
expectError: common.ErrorNoVotes,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
finalization, err := common.NewFinalization(tt.signatureAggregator, tt.finalizeVotes)
|
|
require.ErrorIs(t, err, tt.expectError, "expected error, got nil")
|
|
|
|
if tt.expectError == nil {
|
|
require.Equal(t, finalization.Finalization.Digest, tt.expectedFinalization.Digest, "digests not correct")
|
|
|
|
signers := finalization.QC.Signers()
|
|
require.Equal(t, len(signers), len(tt.finalizeVotes), "unexpected amount of signers")
|
|
|
|
// ensure the qc signers are in order
|
|
for i, signer := range signers[1:] {
|
|
require.Negative(t, bytes.Compare(signers[i], signer), "signers not in order")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|