mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 10:27:26 +08:00
* 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>
166 lines
5.7 KiB
Go
166 lines
5.7 KiB
Go
package forks
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"source.quilibrium.com/quilibrium/monorepo/consensus/helper"
|
|
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
|
|
)
|
|
|
|
// StateRank specifies the data to create a state
|
|
type StateRank struct {
|
|
// Rank is the rank of the state to be created
|
|
Rank uint64
|
|
// StateVersion is the version of the state for that rank.
|
|
// Useful for creating conflicting states at the same rank.
|
|
StateVersion int
|
|
// QCRank is the rank of the QC embedded in this state (also: the rank of the state's parent)
|
|
QCRank uint64
|
|
// QCVersion is the version of the QC for that rank.
|
|
QCVersion int
|
|
}
|
|
|
|
// QCIndex returns a unique identifier for the state's QC.
|
|
func (bv *StateRank) QCIndex() string {
|
|
return fmt.Sprintf("%v-%v", bv.QCRank, bv.QCVersion)
|
|
}
|
|
|
|
// StateIndex returns a unique identifier for the state.
|
|
func (bv *StateRank) StateIndex() string {
|
|
return fmt.Sprintf("%v-%v", bv.Rank, bv.StateVersion)
|
|
}
|
|
|
|
// StateBuilder is a test utility for creating state structure fixtures.
|
|
type StateBuilder struct {
|
|
stateRanks []*StateRank
|
|
}
|
|
|
|
func NewStateBuilder() *StateBuilder {
|
|
return &StateBuilder{
|
|
stateRanks: make([]*StateRank, 0),
|
|
}
|
|
}
|
|
|
|
// Add adds a state with the given qcRank and stateRank. Returns self-reference for chaining.
|
|
func (bb *StateBuilder) Add(qcRank uint64, stateRank uint64) *StateBuilder {
|
|
bb.stateRanks = append(bb.stateRanks, &StateRank{
|
|
Rank: stateRank,
|
|
QCRank: qcRank,
|
|
})
|
|
return bb
|
|
}
|
|
|
|
// GenesisState returns the genesis state, which is always finalized.
|
|
func (bb *StateBuilder) GenesisState() *models.CertifiedState[*helper.TestState] {
|
|
return makeGenesis()
|
|
}
|
|
|
|
// AddVersioned adds a state with the given qcRank and stateRank.
|
|
// In addition, the version identifier of the QC embedded within the state
|
|
// is specified by `qcVersion`. The version identifier for the state itself
|
|
// (primarily for emulating different state ID) is specified by `stateVersion`.
|
|
// [(◄3) 4] denotes a state of rank 4, with a qc for rank 3
|
|
// [(◄3) 4'] denotes a state of rank 4 that is different than [(◄3) 4], with a qc for rank 3
|
|
// [(◄3) 4'] can be created by AddVersioned(3, 4, 0, 1)
|
|
// [(◄3') 4] can be created by AddVersioned(3, 4, 1, 0)
|
|
// Returns self-reference for chaining.
|
|
func (bb *StateBuilder) AddVersioned(qcRank uint64, stateRank uint64, qcVersion int, stateVersion int) *StateBuilder {
|
|
bb.stateRanks = append(bb.stateRanks, &StateRank{
|
|
Rank: stateRank,
|
|
QCRank: qcRank,
|
|
StateVersion: stateVersion,
|
|
QCVersion: qcVersion,
|
|
})
|
|
return bb
|
|
}
|
|
|
|
// Proposals returns a list of all proposals added to the StateBuilder.
|
|
// Returns an error if the states do not form a connected tree rooted at genesis.
|
|
func (bb *StateBuilder) Proposals() ([]*models.Proposal[*helper.TestState], error) {
|
|
states := make([]*models.Proposal[*helper.TestState], 0, len(bb.stateRanks))
|
|
|
|
genesisState := makeGenesis()
|
|
genesisBV := &StateRank{
|
|
Rank: genesisState.State.Rank,
|
|
QCRank: genesisState.CertifyingQuorumCertificate.GetRank(),
|
|
}
|
|
|
|
qcs := make(map[string]models.QuorumCertificate)
|
|
qcs[genesisBV.QCIndex()] = genesisState.CertifyingQuorumCertificate
|
|
|
|
for _, bv := range bb.stateRanks {
|
|
qc, ok := qcs[bv.QCIndex()]
|
|
if !ok {
|
|
return nil, fmt.Errorf("test fail: no qc found for qc index: %v", bv.QCIndex())
|
|
}
|
|
var previousRankTimeoutCert models.TimeoutCertificate
|
|
if qc.GetRank()+1 != bv.Rank {
|
|
previousRankTimeoutCert = helper.MakeTC(helper.WithTCRank(bv.Rank - 1))
|
|
}
|
|
proposal := &models.Proposal[*helper.TestState]{
|
|
State: &models.State[*helper.TestState]{
|
|
Rank: bv.Rank,
|
|
ParentQuorumCertificate: qc,
|
|
},
|
|
PreviousRankTimeoutCertificate: previousRankTimeoutCert,
|
|
}
|
|
proposal.State.Identifier = makeIdentifier(proposal.State, bv.StateVersion)
|
|
|
|
states = append(states, proposal)
|
|
|
|
// generate QC for the new proposal
|
|
qcs[bv.StateIndex()] = &helper.TestQuorumCertificate{
|
|
Rank: proposal.State.Rank,
|
|
Selector: proposal.State.Identifier,
|
|
AggregatedSignature: nil,
|
|
}
|
|
}
|
|
|
|
return states, nil
|
|
}
|
|
|
|
// States returns a list of all states added to the StateBuilder.
|
|
// Returns an error if the states do not form a connected tree rooted at genesis.
|
|
func (bb *StateBuilder) States() ([]*models.State[*helper.TestState], error) {
|
|
proposals, err := bb.Proposals()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("StateBuilder failed to generate proposals: %w", err)
|
|
}
|
|
return toStates(proposals), nil
|
|
}
|
|
|
|
// makeIdentifier creates a state identifier based on the state's rank, QC, and state version.
|
|
// This is used to identify states uniquely, in this specific test setup.
|
|
// ATTENTION: this should not be confused with the state ID used in production code which is a collision-resistant hash
|
|
// of the full state content.
|
|
func makeIdentifier(state *models.State[*helper.TestState], stateVersion int) models.Identity {
|
|
return fmt.Sprintf("%d-%s-%d", state.Rank, state.Identifier, stateVersion)
|
|
}
|
|
|
|
// constructs the genesis state (identical for all calls)
|
|
func makeGenesis() *models.CertifiedState[*helper.TestState] {
|
|
genesis := &models.State[*helper.TestState]{
|
|
Rank: 1,
|
|
}
|
|
genesis.Identifier = makeIdentifier(genesis, 0)
|
|
|
|
genesisQC := &helper.TestQuorumCertificate{
|
|
Rank: 1,
|
|
Selector: genesis.Identifier,
|
|
}
|
|
certifiedGenesisState, err := models.NewCertifiedState(genesis, genesisQC)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("combining genesis state and genensis QC to certified state failed: %s", err.Error()))
|
|
}
|
|
return certifiedGenesisState
|
|
}
|
|
|
|
// toStates converts the given proposals to slice of states
|
|
func toStates(proposals []*models.Proposal[*helper.TestState]) []*models.State[*helper.TestState] {
|
|
states := make([]*models.State[*helper.TestState], 0, len(proposals))
|
|
for _, b := range proposals {
|
|
states = append(states, b.State)
|
|
}
|
|
return states
|
|
}
|