ceremonyclient/consensus/integration/integration_test.go
Cassandra Heart c797d482f9
v2.1.0.5 (#457)
* wip: conversion of hotstuff from flow into Q-oriented model

* bulk of tests

* remaining non-integration tests

* add integration test, adjust log interface, small tweaks

* further adjustments, restore full pacemaker shape

* add component lifecycle management+supervisor

* further refinements

* resolve timeout hanging

* mostly finalized state for consensus

* bulk of engine swap out

* lifecycle-ify most types

* wiring nearly complete, missing needed hooks for proposals

* plugged in, vetting message validation paths

* global consensus, plugged in and verified

* app shard now wired in too

* do not decode empty keys.yml (#456)

* remove obsolete engine.maxFrames config parameter (#454)

* default to Info log level unless debug is enabled (#453)

* respect config's  "logging" section params, remove obsolete single-file logging (#452)

* Trivial code cleanup aiming to reduce Go compiler warnings (#451)

* simplify range traversal

* simplify channel read for single select case

* delete rand.Seed() deprecated in Go 1.20 and no-op as of Go 1.24

* simplify range traversal

* simplify channel read for single select case

* remove redundant type from array

* simplify range traversal

* simplify channel read for single select case

* RC slate

* finalize 2.1.0.5

* Update comments in StrictMonotonicCounter

Fix comment formatting and clarify description.

---------

Co-authored-by: Black Swan <3999712+blacks1ne@users.noreply.github.com>
2025-11-11 05:00:17 -06:00

154 lines
4.9 KiB
Go

package integration
import (
"errors"
"fmt"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"source.quilibrium.com/quilibrium/monorepo/consensus/helper"
)
// a pacemaker timeout to wait for proposals. Usually 10 ms is enough,
// but for slow environment like CI, a longer one is needed.
const safeTimeout = 2 * time.Second
// number of failed rounds before first timeout increase
const happyPathMaxRoundFailures = 6
func TestSingleInstance(t *testing.T) {
fmt.Println("starting single instance test")
// set up a single instance to run
finalRank := uint64(10)
in := NewInstance(t,
WithStopCondition(RankFinalized(finalRank)),
)
// run the event handler until we reach a stop condition
err := in.Run(t)
require.ErrorIs(t, err, errStopCondition, "should run until stop condition")
// check if forks and pacemaker are in expected rank state
assert.Equal(t, finalRank, in.forks.FinalizedRank(), "finalized rank should be three lower than current rank")
fmt.Println("ending single instance test")
}
func TestThreeInstances(t *testing.T) {
fmt.Println("starting three instance test")
// test parameters
num := 3
finalRank := uint64(100)
// generate three hotstuff participants
participants := helper.WithWeightedIdentityList(num)
root := DefaultRoot()
// set up three instances that are exactly the same
// since we don't drop any messages we should have enough data to advance in happy path
// for that reason we will drop all TO related communication.
instances := make([]*Instance, 0, num)
for n := 0; n < num; n++ {
in := NewInstance(t,
WithRoot(root),
WithParticipants(participants),
WithLocalID(participants[n].Identity()),
WithStopCondition(RankFinalized(finalRank)),
WithIncomingTimeoutStates(DropAllTimeoutStates),
)
instances = append(instances, in)
}
// connect the communicators of the instances together
Connect(t, instances)
// start the instances and wait for them to finish
var wg sync.WaitGroup
for _, in := range instances {
wg.Add(1)
go func(in *Instance) {
err := in.Run(t)
require.True(t, errors.Is(err, errStopCondition), "should run until stop condition")
wg.Done()
}(in)
}
wg.Wait()
// check that all instances have the same finalized state
in1 := instances[0]
in2 := instances[1]
in3 := instances[2]
// verify progress has been made
assert.GreaterOrEqual(t, in1.forks.FinalizedState().Rank, finalRank, "the first instance 's finalized rank should be four lower than current rank")
// verify same progresses have been made
assert.Equal(t, in1.forks.FinalizedState(), in2.forks.FinalizedState(), "second instance should have same finalized state as first instance")
assert.Equal(t, in1.forks.FinalizedState(), in3.forks.FinalizedState(), "third instance should have same finalized state as first instance")
assert.Equal(t, FinalizedRanks(in1), FinalizedRanks(in2))
assert.Equal(t, FinalizedRanks(in1), FinalizedRanks(in3))
fmt.Println("ending three instance test")
}
func TestSevenInstances(t *testing.T) {
fmt.Println("starting seven instance test")
// test parameters
numPass := 5
numFail := 2
finalRank := uint64(30)
// generate the seven hotstuff participants
participants := helper.WithWeightedIdentityList(numPass + numFail)
instances := make([]*Instance, 0, numPass+numFail)
root := DefaultRoot()
// set up five instances that work fully
for n := 0; n < numPass; n++ {
in := NewInstance(t,
WithRoot(root),
WithParticipants(participants),
WithLocalID(participants[n].Identity()),
WithStopCondition(RankFinalized(finalRank)),
)
instances = append(instances, in)
}
// set up two instances which can't vote
for n := numPass; n < numPass+numFail; n++ {
in := NewInstance(t,
WithRoot(root),
WithParticipants(participants),
WithLocalID(participants[n].Identity()),
WithStopCondition(RankFinalized(finalRank)),
WithOutgoingVotes(DropAllVotes),
)
instances = append(instances, in)
}
// connect the communicators of the instances together
Connect(t, instances)
// start all seven instances and wait for them to wrap up
var wg sync.WaitGroup
for _, in := range instances {
wg.Add(1)
go func(in *Instance) {
err := in.Run(t)
require.True(t, errors.Is(err, errStopCondition), "should run until stop condition")
wg.Done()
}(in)
}
wg.Wait()
// check that all instances have the same finalized state
ref := instances[0]
assert.Less(t, finalRank-uint64(2*numPass+numFail), ref.forks.FinalizedState().Rank, "expect instance 0 should made enough progress, but didn't")
finalizedRanks := FinalizedRanks(ref)
for i := 1; i < numPass; i++ {
assert.Equal(t, ref.forks.FinalizedState(), instances[i].forks.FinalizedState(), "instance %d should have same finalized state as first instance")
assert.Equal(t, finalizedRanks, FinalizedRanks(instances[i]), "instance %d should have same finalized rank as first instance")
}
fmt.Println("ending seven instance test")
}