Files
consensus/engine/chain/derived_authority_test.go
zeekayandHanzo Dev bdb331cfac chain: accept a block only on the two-thirds certificate
Local, unreleased. Acceptance and the ledger's decided height move only on
the Quasar (two-thirds) certificate; a certificate's tier label is read at
that rung. Winner in two tiers; votes parked only from their authenticated
signer, bounded and expiring; relay once per proposer a height; stateless
blocks charged to no sender.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-24 17:29:55 -07:00

530 lines
23 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// derived_authority_test.go — a certificate states its quorum; it does not choose it.
//
// The floor a certificate is counted against is a function of the SET it is
// weighed against and the RUNG it attests. The Threshold field carries that
// number so a reader knows what was required, and VerifyWeighted checks the
// field against what the set actually derives — so the field is a claim, never a
// value the verifier adopts.
//
// Without the clause the field is load-bearing on the count-only road, because
// Verify's last clause counts distinct valid accepts against the certificate's
// OWN Threshold. A certificate declaring 1 clears it on one signature.
package chain
import (
"context"
"errors"
"fmt"
"testing"
"github.com/luxfi/consensus/config"
"github.com/luxfi/ids"
"github.com/luxfi/log"
)
// certDeclaring assembles a certificate over votes and stamps the threshold a set
// of n signers DERIVES for the rung — the only declaration VerifyWeighted admits.
//
// Assembly runs at the vote count rather than at the floor on purpose: an
// under-quorum certificate is one no honest assembler can build and one every
// verifier must still refuse, so a test that wants to state one has to build it
// the way an adversary would. The ordering and dedup clauses still run.
func certDeclaring(t *testing.T, pos VotePosition, tier Finality, n int, votes []SignedVote) *QuorumCert {
t.Helper()
c, err := AssembleQuorumCert(pos, tier, uint32(len(votes)), votes)
if err != nil {
t.Fatalf("assemble: %v", err)
}
c.Threshold = uint32(SignerFloor(tier, n))
return c
}
// TestSignerFloorIsTheOneDefinition holds the two rungs' floors to the arithmetic
// the rest of the standard states them in, and holds a non-accept rung to zero —
// which every caller reads as a refusal.
func TestSignerFloorIsTheOneDefinition(t *testing.T) {
for n := 1; n <= 64; n++ {
if got, want := SignerFloor(Nova, n), NovaSignerFloor(n); got != want {
t.Fatalf("n=%d: nova floor %d, want %d", n, got, want)
}
if got, want := SignerFloor(Quasar, n), config.TwoThirdsCount(n); got != want {
t.Fatalf("n=%d: quasar floor %d, want %d", n, got, want)
}
}
for _, rung := range []Finality{Photon, Wave, Horizon} {
if got := SignerFloor(rung, 41); got != 0 {
t.Fatalf("%s is not an accept rung and must derive no floor, got %d", rung, got)
}
}
// A set size below one must not produce a floor no quorum could ever reach.
// Both rungs are total there and answer one — the predicate refuses an
// unresolved set before consulting this.
for _, n := range []int{0, -1, -41} {
for _, rung := range []Finality{Nova, Quasar} {
if got := SignerFloor(rung, n); got != 1 {
t.Fatalf("%s over n=%d derives %d, want 1 — an unresolved set has no bar to raise",
rung, n, got)
}
if got := Quorum(rung, n); got != 1 {
t.Fatalf("%s quorum over n=%d is %d, want 1 — the count-only road has the same "+
"answer for a set it cannot see", rung, n, got)
}
}
}
}
// TestDerivedThresholdRefusesAnUnderClaim is finding-3, stated as the attack it
// is. A certificate carrying a real export quorum but DECLARING a quorum of one
// is refused — and refused on the derived clause, before the rung's own floors,
// because the number it declared was never the set's to give.
//
// The under-claim is what makes the count-only road exploitable: Verify counts
// against the declaration, so declaring 1 turns "α distinct validators agreed"
// into "one did".
func TestDerivedThresholdRefusesAnUnderClaim(t *testing.T) {
vs := newTestValidatorSet(4)
pos := VotePosition{ChainID: ids.GenerateTestID(), Height: 1, BlockID: ids.GenerateTestID()}
set := &stakeMap{w: map[ids.NodeID]uint64{
vs.nodeID(0): 1, vs.nodeID(1): 1, vs.nodeID(2): 1, vs.nodeID(3): 1}, total: 4}
votes := make([]SignedVote, 0, 4)
for i := 0; i < 4; i++ {
votes = append(votes, SignedVote{NodeID: vs.nodeID(i), Accept: true, Signature: vs.sign(i, pos)})
}
// The honest certificate: four of four, declaring the floor the set derives.
honest := certDeclaring(t, pos, Quasar, 4, votes)
if honest.Threshold != uint32(config.TwoThirdsCount(4)) {
t.Fatalf("the honest cert declares %d, the set derives %d", honest.Threshold, config.TwoThirdsCount(4))
}
if err := honest.VerifyWeighted(vs, set, 1); err != nil {
t.Fatalf("a unanimous export cert declaring the derived floor must verify: %v", err)
}
// The same votes, under-claiming. Every signature still verifies and the stake
// is still unanimous — the ONLY thing wrong is the number the cert names.
under := certDeclaring(t, pos, Quasar, 4, votes)
under.Threshold = 1
err := under.VerifyWeighted(vs, set, 1)
if !errors.Is(err, ErrQCThresholdNotDerived) {
t.Fatalf("a cert declaring a quorum of one must be refused on the derived clause, got %v", err)
}
// And an OVER-claim, for the same reason in the other direction: a certificate
// asserting a quorum this set does not require is still naming a number that is
// not the set's. Tolerating it would let a cert redefine the rung upward exactly
// as tolerating the under-claim lets it redefine the rung down.
over := certDeclaring(t, pos, Quasar, 4, votes)
over.Threshold = 4
if err := over.VerifyWeighted(vs, set, 1); !errors.Is(err, ErrQCThresholdNotDerived) {
t.Fatalf("a cert declaring a quorum the set does not require must be refused, got %v", err)
}
}
// TestMintingNeedsTheSet is MED-2 and the finding after it, in the form the fix
// takes: the minting door names no quorum of its own and mints nothing without a
// set to derive one from.
//
// BuildVerifiedQuorumCert is the exported producer of the finality authority token,
// and it USED to count the caller's votes against the caller's own alpha whenever no
// stake source was wired. One vote and an alpha of one satisfied every clause on that
// branch with the same self-declared 1 — for the export rung until that was refused
// outright, and for the accept rung after, on a chain of five, while a certificate
// carrying exactly those bytes was refused on arrival. AcceptWithCert trusts the
// token, which is the whole point of the token.
//
// A floor is a property of the SET. There is no set here, so there is no floor and
// nothing to mint — at BOTH rungs, which is the rule stated once instead of a list
// of tier names kept in step with another list.
func TestMintingNeedsTheSet(t *testing.T) {
vs := newTestValidatorSet(4)
pos := VotePosition{ChainID: ids.GenerateTestID(), Height: 1, BlockID: ids.GenerateTestID()}
votes := make([]SignedVote, 0, 4)
for i := 0; i < 4; i++ {
votes = append(votes, SignedVote{NodeID: vs.nodeID(i), Accept: true, Signature: vs.sign(i, pos)})
}
one := votes[:1]
for _, rung := range []Finality{Nova, Quasar, Horizon} {
v, err := BuildVerifiedQuorumCert(vs, nil, rung, 1, pos, one)
if !errors.Is(err, ErrCertNeedsStake) {
t.Fatalf("%s minted over one signer with no set, err=%v", rung, err)
}
if !errors.Is(err, ErrNoVerifiedQC) {
t.Fatalf("%s: the refusal must still present as the liveness answer, got %v", rung, err)
}
if !v.IsZero() {
t.Fatalf("%s: a refused build must return the zero token", rung)
}
}
// With a set in hand the floor is the set's, and the caller has no say in it:
// there is no alpha to pass. Four of four at unit stake carries both rungs.
set := &stakeMap{w: map[ids.NodeID]uint64{
vs.nodeID(0): 1, vs.nodeID(1): 1, vs.nodeID(2): 1, vs.nodeID(3): 1}, total: 4}
for _, rung := range []Finality{Nova, Quasar} {
tok, err := BuildVerifiedQuorumCert(vs, set, rung, 1, pos, votes)
if err != nil {
t.Fatalf("%s over a real set must mint: %v", rung, err)
}
if tok.IsZero() || tok.Cert() == nil {
t.Fatalf("%s: a build that returned no error must carry a witness", rung)
}
if got, want := tok.Cert().Threshold, uint32(SignerFloor(rung, 4)); got != want {
t.Fatalf("%s: the minted cert declares %d, the set derives %d", rung, got, want)
}
}
// A rung that is not an accept tier derives no floor at all, so there is no
// quorum for a certificate to state and none is stated.
if _, err := BuildVerifiedQuorumCert(vs, set, Photon, 1, pos, votes); !errors.Is(err, ErrCertFloorUnstatable) {
t.Fatalf("a non-accept rung derives no floor and must mint nothing, got %v", err)
}
}
// TestGossipRoadDerivesItsFloorToo is the same PRINCIPLE at the other door, in
// the form that door can carry.
//
// A certificate that ARRIVES is promoted to the authority token by verifyCert,
// and on a chain with no stake source that gate fell to the count-only Verify —
// which counts votes against the number the CERTIFICATE declares. So a gossiped
// Quasar cert declaring a quorum of one, carrying one signature, was promoted and
// finalized.
//
// The two doors answer differently and the difference is the threat, not an
// oversight:
//
// - MINTING a token from raw votes derives the floor from the set, so with no set
// there is no floor and nothing to mint. Refused outright, at both rungs.
// - ADMITTING one that arrived checks N real signatures from N distinct in-set
// validators. What was missing was only that its declared quorum was its own.
// So the floor is derived here too — from the node's live committee, which is
// this road's authoritative view of its set — and the equal-stake deployment
// keeps the export rung it has always had. Not "such a chain cannot export",
// but "it may not export on a quorum the certificate wrote for itself".
func TestGossipRoadDerivesItsFloorToo(t *testing.T) {
// A real stake-less engine. Its committee is K, and the
// export quorum over five seats is four — so a certificate declaring one is a
// certificate naming its own quorum, and a certificate declaring four is the
// one this node would have built.
vs := newTestValidatorSet(5)
chainID := ids.GenerateTestID()
e := NewWithConfig(Config{Params: params5()},
WithQuorumCert(chainID, vs.nodeID(0), vs, &recordingGossiper{}, vs.signerFor(0)))
if err := e.Start(context.Background(), true); err != nil {
t.Fatalf("Start: %v", err)
}
t.Cleanup(func() { _ = e.Stop(context.Background()) })
if e.stakeSource != nil {
t.Fatal("this engine is supposed to have no stake model; the case tests the other road")
}
pos := VotePosition{ChainID: chainID, Height: 1, Round: 0, BlockID: ids.GenerateTestID()}
votes := make([]SignedVote, 0, 4)
for i := 0; i < 4; i++ {
votes = append(votes, SignedVote{NodeID: vs.nodeID(i), Accept: true, Signature: vs.sign(i, pos)})
}
honest, err := AssembleQuorumCert(pos, Quasar, uint32(Quorum(Quasar, 5)), votes)
if err != nil {
t.Fatalf("assemble: %v", err)
}
if err := e.verifyCert(honest, 1); err != nil {
t.Fatalf("a certificate declaring the quorum this committee derives must pass: %v", err)
}
forged := *honest
forged.Threshold = 1
if err := e.verifyCert(&forged, 1); !errors.Is(err, ErrQCThresholdNotDerived) {
t.Fatalf("a gossiped export cert naming its own quorum of one was admitted: %v", err)
}
// One signature under that declaration is the whole attack: before the clause,
// Verify counted one accept against a declared one and answered nil.
lone, err := AssembleQuorumCert(pos, Quasar, 1, votes[:1])
if err != nil {
t.Fatalf("assemble lone: %v", err)
}
if lone.Verify(vs, 1) != nil {
t.Fatal("the lone certificate must clear the structural predicate, or the case proves nothing")
}
if err := e.verifyCert(lone, 1); !errors.Is(err, ErrQCThresholdNotDerived) {
t.Fatalf("one signature under a self-named quorum of one was admitted: %v", err)
}
}
// TestTheTwoRoadsDeriveTheNumberTheyDeclare is the regression the K<6 fixtures
// could not catch.
//
// There are TWO derived counts, because there are two predicates. SignerFloor is
// the distinct-signer GUARD the weighted road carries beside a stake clause;
// Quorum is the count that IS the predicate where no stake clause stands beside
// it. They agree at Quasar and diverge at Nova past five signers, because
// NovaSignerFloor SATURATES at three — correct as a guard on a road whose majority
// is carried in stake, and wrong as a majority on a road that has only the count.
//
// Reading the wrong one on the count-only road makes a node refuse its OWN
// certificates: the assembler declares NovaQuorum(n) there, which is 4 at six
// signers where NovaSignerFloor is 3. Every committee the fixtures use is 4 or 5,
// which is exactly where the two agree, so nothing in the suite would have said so.
func TestTheTwoRoadsDeriveTheNumberTheyDeclare(t *testing.T) {
diverged := false
for n := 1; n <= 64; n++ {
// What the count-only assembler declares (engine.go, the nil-stake arm).
if got, want := Quorum(Nova, n), NovaQuorum(n); got != want {
t.Fatalf("n=%d: the count-only road derives %d for nova, its assembler declares %d",
n, got, want)
}
// What the weighted assembler declares (engine.go, the stake arm).
if got, want := SignerFloor(Nova, n), NovaSignerFloor(n); got != want {
t.Fatalf("n=%d: the weighted road derives %d for nova, its assembler declares %d",
n, got, want)
}
// The export rung is one number on both roads, and attestation.go declares it.
if Quorum(Quasar, n) != SignerFloor(Quasar, n) {
t.Fatalf("n=%d: the export supermajority reads as two different counts (%d, %d)",
n, Quorum(Quasar, n), SignerFloor(Quasar, n))
}
if Quorum(Nova, n) != SignerFloor(Nova, n) {
diverged = true
}
}
if !diverged {
t.Fatal("the two nova counts never diverged over 64 sizes — if that is now true they " +
"are one quantity and should be one function; if it is not, this test stopped looking")
}
// State the divergence outright at the size the fixtures never reach.
if Quorum(Nova, 6) != 4 || SignerFloor(Nova, 6) != 3 {
t.Fatalf("at six signers the count-only majority is %d and the weighted guard is %d, "+
"want 4 and 3", Quorum(Nova, 6), SignerFloor(Nova, 6))
}
for _, rung := range []Finality{Photon, Wave, Horizon} {
if Quorum(rung, 41) != 0 {
t.Fatalf("%s is not an accept rung and must derive no quorum", rung)
}
}
}
// TestDerivedThresholdSurvivesAnUnresolvedSet holds the one place the clause steps
// aside. A source reporting no signers derives no floor, so there is nothing to
// compare a declaration against — and the rung's own clause refuses the cert under
// the name it has always been refused by. Stepping aside is not a pass.
func TestDerivedThresholdSurvivesAnUnresolvedSet(t *testing.T) {
vs := newTestValidatorSet(4)
pos := VotePosition{ChainID: ids.GenerateTestID(), Height: 1, BlockID: ids.GenerateTestID()}
votes := []SignedVote{{NodeID: vs.nodeID(0), Accept: true, Signature: vs.sign(0, pos)}}
cert := certDeclaring(t, pos, Quasar, 0, votes)
cert.Threshold = 1 // SignerFloor over an unresolved set is 1; say so outright.
err := cert.VerifyWeighted(vs, &stakeMap{w: map[ids.NodeID]uint64{}, total: 0}, 1)
if err == nil {
t.Fatal("an unresolved set must refuse, not pass")
}
if errors.Is(err, ErrQCThresholdNotDerived) {
t.Fatalf("an unresolved set derives no floor and must not be named by the derived clause: %v", err)
}
}
// arrivalFleet is one node holding a set of `signers` weighted validators, under a
// committee of K seats. The two numbers are deliberately allowed to differ: K sizes
// the sample a ROUND asks, and the set is what a certificate's floor is a property
// of. Every place that has confused the two has produced a second floor.
type arrivalFleet struct {
vs *testValidatorSet
stake *stakeMap
chainID ids.ID
rt *Runtime
vm *catchupVM
}
func newArrivalFleet(t *testing.T, k, signers int) *arrivalFleet {
t.Helper()
vs := newTestValidatorSet(k)
weights := make([]uint64, signers)
for i := range weights {
weights[i] = 1
}
f := &arrivalFleet{
vs: vs,
stake: newStakeMap(vs, weights...),
chainID: ids.GenerateTestID(),
vm: newCatchupVM(),
}
f.rt = NewRuntime(NetworkConfig{
ChainID: f.chainID,
NetworkID: ids.GenerateTestID(),
NodeID: vs.nodeID(0),
Logger: log.Noop(),
Params: ptrParams(matrixParams(k, quorumq(k))),
VoteVerifier: vs,
VoteSigner: vs.signerFor(0),
StakeSource: f.stake,
Gossiper: &recordingGossiper{},
VM: f.vm,
})
if err := f.rt.Start(context.Background(), true); err != nil {
t.Fatalf("Start: %v", err)
}
t.Cleanup(func() { _ = f.rt.Stop(context.Background()) })
return f
}
// cert is the certificate an honest assembler on this chain produces at the given tier:
// `voters` signatures under the floor the SET derives for that tier, which is the number
// VerifyWeighted demands and the only one it admits. With fewer voters than that floor it
// declares what it carries.
func (f *arrivalFleet) cert(t *testing.T, tier Finality, blk *verifyOnceBlock, voters int) (*QuorumCert, []byte) {
t.Helper()
pos := VotePosition{ChainID: f.chainID, Height: blk.height, Round: 0, BlockID: blk.id, ParentID: blk.parentID}
votes := make([]SignedVote, 0, voters)
for i := 0; i < voters; i++ {
votes = append(votes, SignedVote{NodeID: f.vs.nodeID(i), Accept: true, Signature: f.vs.sign(i, pos)})
}
floor := SignerFloor(tier, f.stake.SignerCount(0))
if voters < floor {
floor = voters // a short certificate states what it carries
}
cert, err := AssembleQuorumCert(pos, tier, uint32(floor), votes)
if err != nil {
t.Fatalf("assemble: %v", err)
}
b, err := cert.MarshalBinary()
if err != nil {
t.Fatalf("marshal: %v", err)
}
return cert, b
}
// TestTheDoorAdmitsWhatTheGateAdmits is the invariant a pre-filter has to keep: a
// certificate the finality predicate ACCEPTS must reach it, on both arrival roads.
//
// The floor has one definition, read off the SIGNING SET, and it is a different
// quantity from the sample committee's: where the set is SMALLER than the committee, the
// committee's ⅔ asks for more signatures than the set's own floor. The rows are sizes
// where the two numbers part, and the arithmetic assertion is what makes each row
// non-vacuous. A certificate's tier and declared quorum are labels no signature covers,
// so the door reads every certificate at the ⅔ floor: the same signatures labeled Nova
// are admitted, and one signer fewer under either label is refused.
func TestTheDoorAdmitsWhatTheGateAdmits(t *testing.T) {
for _, row := range []struct{ k, signers, voters int }{
{k: 5, signers: 4, voters: 3},
{k: 7, signers: 5, voters: 4},
{k: 11, signers: 9, voters: 7},
} {
t.Run(fmt.Sprintf("k=%d/signers=%d", row.k, row.signers), func(t *testing.T) {
f := newArrivalFleet(t, row.k, row.signers)
derived := SignerFloor(Quasar, row.signers)
if Quorum(Quasar, row.k) <= derived {
t.Fatalf("vacuous: the committee's ⅔ %d must exceed the set's floor %d, "+
"or this row proves nothing about a door reading the committee",
Quorum(Quasar, row.k), derived)
}
// THE GATE, on its own: the predicate accepts this certificate.
blk := newTestBlock(1, ids.Empty, "arrival")
trackVerifiedBlock(f.rt, blk, 0)
cert, wire := f.cert(t, Quasar, blk, row.voters)
if err := f.rt.Transitive.verifyCert(cert, 0); err != nil {
t.Fatalf("the finality predicate refused an honest certificate: %v", err)
}
if int(cert.Threshold) != derived {
t.Fatalf("the cert declares %d, the set derives %d", cert.Threshold, derived)
}
// The label is not what is signed: the same signatures labeled Nova clear the
// ⅔ floor, and one signer fewer does not.
nova, _ := f.cert(t, Nova, blk, row.voters)
if err := f.rt.Transitive.verifyCert(nova, 0); err != nil {
t.Fatalf("the door refused ⅔ of the set under a Nova label: %v", err)
}
short, _ := f.cert(t, Nova, blk, row.voters-1)
if err := f.rt.Transitive.verifyCert(short, 0); err == nil {
t.Fatalf("the door admitted %d of %d signers, below the ⅔ floor %d", row.voters-1, row.signers, derived)
}
// THE GOSSIP ROAD: it reaches the gate, and finalizes.
if !f.rt.HandleIncomingCert(wire) {
t.Fatalf("the gossip road dropped a certificate the predicate accepts — "+
"a door reading the committee's ⅔ %d refuses the set's floor %d",
Quorum(Quasar, row.k), derived)
}
if got := blk.AcceptCalled(); got != 1 {
t.Fatalf("VM.Accept=%d want 1", got)
}
// THE CATCH-UP ROAD: the mirror of it, and the same answer. A separate
// runtime so the height gate does not see the block already finalized.
g := newArrivalFleet(t, row.k, row.signers)
far := newTestBlock(41, ids.GenerateTestID(), "arrival-frontier")
g.vm.register(far)
_, farWire := g.cert(t, Quasar, far, row.voters)
if err := g.rt.VerifyCatchupCertificate(context.Background(), far.bytes, farWire); err != nil {
t.Fatalf("the catch-up road refused a certificate the predicate accepts: %v", err)
}
_, farShort := g.cert(t, Nova, far, row.voters-1)
if err := g.rt.VerifyCatchupCertificate(context.Background(), far.bytes, farShort); err == nil {
t.Fatalf("the catch-up road admitted %d of %d signers, below the ⅔ floor", row.voters-1, row.signers)
}
})
}
}
// TestTheArrivalRoadsRefuseAQuorumTheSetDoesNotDerive is the other half: the gate
// demands EQUALITY with the derived floor, so a ⅔-labeled certificate naming its own
// quorum — smaller or larger — is refused, and a Nova-labeled one is read at the ⅔
// floor, which one signature does not reach.
func TestTheArrivalRoadsRefuseAQuorumTheSetDoesNotDerive(t *testing.T) {
f := newArrivalFleet(t, 9, 9)
blk := newTestBlock(1, ids.Empty, "self-named")
trackVerifiedBlock(f.rt, blk, 0)
// One signature under a self-named quorum of one, stated the way an attacker builds it.
pos := VotePosition{ChainID: f.chainID, Height: 1, Round: 0, BlockID: blk.id, ParentID: ids.Empty}
one := []SignedVote{{NodeID: f.vs.nodeID(0), Accept: true, Signature: f.vs.sign(0, pos)}}
lone, err := AssembleQuorumCert(pos, Quasar, 1, one)
if err != nil {
t.Fatalf("assemble: %v", err)
}
if lone.Verify(f.vs, 0) != nil {
t.Fatal("the lone certificate must clear the structural predicate, or the case proves nothing")
}
if err := f.rt.Transitive.verifyCert(lone, 0); !errors.Is(err, ErrQCThresholdNotDerived) {
t.Fatalf("a self-named quorum of one must be refused on the derived clause, got %v", err)
}
wire, err := lone.MarshalBinary()
if err != nil {
t.Fatalf("marshal: %v", err)
}
if f.rt.HandleIncomingCert(wire) || blk.AcceptCalled() != 0 {
t.Fatal("SAFETY BREAK: one signature under a self-named quorum finalized a block")
}
loneNova, err := AssembleQuorumCert(pos, Nova, 1, one)
if err != nil {
t.Fatalf("assemble: %v", err)
}
if err := f.rt.Transitive.verifyCert(loneNova, 0); err == nil {
t.Fatal("one signature under a Nova label must be refused at the ⅔ floor")
}
// An OVER-claim: a certificate declaring more than its set requires is naming a
// number that is not the set's, in the other direction.
derived := SignerFloor(Quasar, 9)
honest, honestWire := f.cert(t, Quasar, blk, derived+1)
over := *honest
over.Threshold = uint32(derived + 1)
if int(over.Threshold) == derived {
t.Fatal("vacuous: the over-claim must differ from the derived floor")
}
if err := f.rt.Transitive.verifyCert(&over, 0); !errors.Is(err, ErrQCThresholdNotDerived) {
t.Fatalf("an over-claim must be refused on the derived clause, got %v", err)
}
// The honest certificate, last, so the refusals above are attributable to the number
// each carries and not to the fixture.
if !f.rt.HandleIncomingCert(honestWire) || blk.AcceptCalled() != 1 {
t.Fatal("control broke: the honest certificate must finalize")
}
}