mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
MEDIUM (epoch binding): stake-weighted finality must be measured at the
cert-position epoch, not at verification time. Bind the active weighted
validator-set commitment into VotePosition.ValidatorSetRoot and fold it into
CanonicalVoteMessage + the wire codec, so a cert is cryptographically pinned to
the exact set it was certified under: re-presenting an epoch-E cert as
certifying under a different epoch's set fails clause-6 signature verification,
and a later stake change cannot retroactively flip an already-correct cert.
Mirrors quasar.ConsensusCert.ValidatorSetRoot. New optional ValidatorSetRootSource
(WithValidatorSetRoot / NetworkConfig.ValidatorSetRoot), stamped in
blockPositionLocked; nil source => ids.Empty => behavior identical to before
(backward-safe for fixed-set chains). QuorumCertVersion bumped 1->2 so a
mixed-version cert fails loudly (version + signature) rather than mis-parsing —
a deliberate forward-only break (the set upgrades together in one cascade).
TDD: TestEpochBinding_{CrossEpochCertRejected,StakeReadAtCertHeight,RoundTripPreservesRoot}
(proven to fail without the canonical-message binding).
INFO-2 (SyncState monotonic guard): ChainConsensus.SyncState bypasses
markFinalizedLocked by design (import is an out-of-band reconcile), so the
monotonic invariant is re-asserted explicitly — a backward import (height below
the already-finalized height) is refused (ErrSyncStateRegression) and leaves
finalized state untouched; an equal-height re-import must agree on the block;
forward + empty-reset still succeed. SyncState now returns error; the sole caller
(Transitive.SyncState) propagates it and does NOT partially mutate on refusal.
TDD: TestSyncState_{RefusesBackwardRegression,EqualHeightDifferentBlockRefused,
ForwardImportAdvances,EmptyResetAllowed} (proven to fail without the guard).
INFO-3 (BFT message): ErrKTooLowForMainnet/Testnet now state the real bound
(2*AlphaPreference-K >= floor((K-1)/3)+1 with f>=3 / f>=1) instead of a bare
"K>=11" magic number. Sentinel-based assertions unaffected.
INFO-1 (dead code): delete the VerkleWitness apparatus (witness.go + its tests +
the VerkleWitness funcs in comprehensive_test.go + the verifyIPAOpening test) —
zero non-test callers; verifyBLSAggregate was a deserialize-only (no-pairing)
forgery sink if ever revived. The live deserialization path that matters is
crypto/bls.SignatureFromBytes (fixed + tested in the crypto module);
TestVerifyBLSAggregate_C1_ActualDeserialization was proven green in CGO_ENABLED=0
against the crypto fix BEFORE this deletion removed it.
65 lines
2.6 KiB
Go
65 lines
2.6 KiB
Go
package quasar
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// NOTE: TestF002_ConstantTimeProofComparison was removed together with the dead
|
|
// VerkleWitness apparatus (witness.go) it exercised — verifyIPAOpening was a
|
|
// helper of that forgery-sink-adjacent, zero-non-test-caller code. The
|
|
// constant-time-compare property it asserted lives at the (still-tested) callers
|
|
// of crypto/subtle elsewhere; nothing in production depended on verifyIPAOpening.
|
|
|
|
// TestDualSignRound1_ErrorPropagation verifies that DualSignRound1
|
|
// propagates errors from BOTH the BLS and Corona signing paths.
|
|
// Previously, the Corona error was silently discarded (line 329).
|
|
//
|
|
// Regression test for the silent error drop found during review analysis.
|
|
func TestDualSignRound1_ErrorPropagation(t *testing.T) {
|
|
// Attempting dual sign with unconfigured validator should error
|
|
s, err := newSigner(2) // threshold of 2
|
|
require.NoError(t, err)
|
|
|
|
_, _, err = s.DualSignRound1(nil, "nonexistent-validator", []byte("msg"), 1, []byte("prf"))
|
|
require.Error(t, err, "DualSignRound1 must error for unconfigured validator")
|
|
require.Contains(t, err.Error(), "not configured for dual signing")
|
|
}
|
|
|
|
// TestCertBundle_VerifyPanics verifies the deprecated Verify method panics
|
|
// to prevent accidental use of the non-cryptographic path.
|
|
func TestCertBundle_VerifyPanics(t *testing.T) {
|
|
cert := &CertBundle{
|
|
BLSAgg: []byte{0x01, 0x02, 0x03},
|
|
PQCert: []byte{0x04, 0x05, 0x06},
|
|
}
|
|
require.Panics(t, func() { cert.Verify(nil) }, "Verify must panic to enforce VerifyWithKeys usage")
|
|
}
|
|
|
|
// TestCertBundle_VerifyWithKeys_Regression tests cryptographic KMAC256 verification.
|
|
func TestCertBundle_VerifyWithKeys_Regression(t *testing.T) {
|
|
blsKey := []byte("regression-bls-key-32bytes!!")
|
|
pqKey := []byte("regression-pq-key-32bytes!!!")
|
|
message := []byte("regression test")
|
|
|
|
cert := &CertBundle{
|
|
BLSAgg: kmac256(blsKey, message, kmacMACOutLen, customQuasarEventHorizonBLSMAC),
|
|
PQCert: kmac256(pqKey, message, kmacMACOutLen, customQuasarEventHorizonPQMAC),
|
|
Message: message,
|
|
}
|
|
require.True(t, cert.VerifyWithKeys(blsKey, pqKey), "valid cert passes KMAC256 check")
|
|
|
|
// Nil cert fails
|
|
var nilCert *CertBundle
|
|
require.False(t, nilCert.VerifyWithKeys(blsKey, pqKey), "nil cert must fail")
|
|
|
|
// Empty BLS fails
|
|
emptyBLS := &CertBundle{BLSAgg: []byte{}, PQCert: cert.PQCert, Message: message}
|
|
require.False(t, emptyBLS.VerifyWithKeys(blsKey, pqKey), "empty BLS must fail")
|
|
|
|
// Empty PQ fails
|
|
emptyPQ := &CertBundle{BLSAgg: cert.BLSAgg, PQCert: []byte{}, Message: message}
|
|
require.False(t, emptyPQ.VerifyWithKeys(blsKey, pqKey), "empty PQ must fail")
|
|
}
|