ceremonyclient/consensus/helper/state.go
2025-12-15 16:45:31 -06:00

468 lines
11 KiB
Go

package helper
import (
crand "crypto/rand"
"fmt"
"math/rand"
"slices"
"strings"
"time"
"source.quilibrium.com/quilibrium/monorepo/consensus"
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
)
type TestWeightedIdentity struct {
ID string
}
// Identity implements models.WeightedIdentity.
func (t *TestWeightedIdentity) Identity() models.Identity {
return t.ID
}
// PublicKey implements models.WeightedIdentity.
func (t *TestWeightedIdentity) PublicKey() []byte {
return make([]byte, 585)
}
// Weight implements models.WeightedIdentity.
func (t *TestWeightedIdentity) Weight() uint64 {
return 1000
}
var _ models.WeightedIdentity = (*TestWeightedIdentity)(nil)
type TestState struct {
Rank uint64
Signature []byte
Timestamp uint64
ID models.Identity
Prover models.Identity
}
// Clone implements models.Unique.
func (t *TestState) Clone() models.Unique {
return &TestState{
Rank: t.Rank,
Signature: slices.Clone(t.Signature),
Timestamp: t.Timestamp,
ID: t.ID,
Prover: t.Prover,
}
}
// GetRank implements models.Unique.
func (t *TestState) GetRank() uint64 {
return t.Rank
}
// GetSignature implements models.Unique.
func (t *TestState) GetSignature() []byte {
return t.Signature
}
// GetTimestamp implements models.Unique.
func (t *TestState) GetTimestamp() uint64 {
return t.Timestamp
}
// Identity implements models.Unique.
func (t *TestState) Identity() models.Identity {
return t.ID
}
// Source implements models.Unique.
func (t *TestState) Source() models.Identity {
return t.Prover
}
type TestVote struct {
Rank uint64
Signature []byte
Timestamp uint64
ID models.Identity
StateID models.Identity
}
// Clone implements models.Unique.
func (t *TestVote) Clone() models.Unique {
return &TestVote{
Rank: t.Rank,
Signature: slices.Clone(t.Signature),
Timestamp: t.Timestamp,
ID: t.ID,
StateID: t.StateID,
}
}
// GetRank implements models.Unique.
func (t *TestVote) GetRank() uint64 {
return t.Rank
}
// GetSignature implements models.Unique.
func (t *TestVote) GetSignature() []byte {
return t.Signature
}
// GetTimestamp implements models.Unique.
func (t *TestVote) GetTimestamp() uint64 {
return t.Timestamp
}
// Identity implements models.Unique.
func (t *TestVote) Identity() models.Identity {
return t.ID
}
// Source implements models.Unique.
func (t *TestVote) Source() models.Identity {
return t.StateID
}
type TestPeer struct {
PeerID string
}
// Clone implements models.Unique.
func (t *TestPeer) Clone() models.Unique {
return &TestPeer{
PeerID: t.PeerID,
}
}
// GetRank implements models.Unique.
func (t *TestPeer) GetRank() uint64 {
return 0
}
// GetSignature implements models.Unique.
func (t *TestPeer) GetSignature() []byte {
return []byte{}
}
// GetTimestamp implements models.Unique.
func (t *TestPeer) GetTimestamp() uint64 {
return 0
}
// Identity implements models.Unique.
func (t *TestPeer) Identity() models.Identity {
return t.PeerID
}
// Source implements models.Unique.
func (t *TestPeer) Source() models.Identity {
return t.PeerID
}
type TestCollected struct {
Rank uint64
TXs [][]byte
}
// Clone implements models.Unique.
func (t *TestCollected) Clone() models.Unique {
return &TestCollected{
Rank: t.Rank,
TXs: slices.Clone(t.TXs),
}
}
// GetRank implements models.Unique.
func (t *TestCollected) GetRank() uint64 {
return t.Rank
}
// GetSignature implements models.Unique.
func (t *TestCollected) GetSignature() []byte {
return []byte{}
}
// GetTimestamp implements models.Unique.
func (t *TestCollected) GetTimestamp() uint64 {
return 0
}
// Identity implements models.Unique.
func (t *TestCollected) Identity() models.Identity {
return fmt.Sprintf("%d", t.Rank)
}
// Source implements models.Unique.
func (t *TestCollected) Source() models.Identity {
return ""
}
var _ models.Unique = (*TestState)(nil)
var _ models.Unique = (*TestVote)(nil)
var _ models.Unique = (*TestPeer)(nil)
var _ models.Unique = (*TestCollected)(nil)
func MakeIdentity() models.Identity {
s := make([]byte, 32)
crand.Read(s)
return models.Identity(s)
}
func MakeState[StateT models.Unique](options ...func(*models.State[StateT])) *models.State[StateT] {
rank := rand.Uint64()
state := models.State[StateT]{
Rank: rank,
Identifier: MakeIdentity(),
ProposerID: MakeIdentity(),
Timestamp: uint64(time.Now().UnixMilli()),
ParentQuorumCertificate: MakeQC(WithQCRank(rank - 1)),
}
for _, option := range options {
option(&state)
}
return &state
}
func WithStateRank[StateT models.Unique](rank uint64) func(*models.State[StateT]) {
return func(state *models.State[StateT]) {
state.Rank = rank
}
}
func WithStateProposer[StateT models.Unique](proposerID models.Identity) func(*models.State[StateT]) {
return func(state *models.State[StateT]) {
state.ProposerID = proposerID
}
}
func WithParentState[StateT models.Unique](parent *models.State[StateT]) func(*models.State[StateT]) {
return func(state *models.State[StateT]) {
state.ParentQuorumCertificate.(*TestQuorumCertificate).Selector = parent.Identifier
state.ParentQuorumCertificate.(*TestQuorumCertificate).Rank = parent.Rank
}
}
func WithParentSigners[StateT models.Unique](signerIndices []byte) func(*models.State[StateT]) {
return func(state *models.State[StateT]) {
state.ParentQuorumCertificate.(*TestQuorumCertificate).AggregatedSignature.(*TestAggregatedSignature).Bitmask = signerIndices // buildutils:allow-slice-alias
}
}
func WithStateQC[StateT models.Unique](qc models.QuorumCertificate) func(*models.State[StateT]) {
return func(state *models.State[StateT]) {
state.ParentQuorumCertificate = qc
}
}
func MakeVote[VoteT models.Unique]() *VoteT {
return new(VoteT)
}
func MakeSignedProposal[StateT models.Unique, VoteT models.Unique](options ...func(*models.SignedProposal[StateT, VoteT])) *models.SignedProposal[StateT, VoteT] {
proposal := &models.SignedProposal[StateT, VoteT]{
Proposal: *MakeProposal[StateT](),
Vote: MakeVote[VoteT](),
}
for _, option := range options {
option(proposal)
}
return proposal
}
func MakeProposal[StateT models.Unique](options ...func(*models.Proposal[StateT])) *models.Proposal[StateT] {
proposal := &models.Proposal[StateT]{
State: MakeState[StateT](),
PreviousRankTimeoutCertificate: nil,
}
for _, option := range options {
option(proposal)
}
return proposal
}
func WithProposal[StateT models.Unique, VoteT models.Unique](proposal *models.Proposal[StateT]) func(*models.SignedProposal[StateT, VoteT]) {
return func(signedProposal *models.SignedProposal[StateT, VoteT]) {
signedProposal.Proposal = *proposal
}
}
func WithState[StateT models.Unique](state *models.State[StateT]) func(*models.Proposal[StateT]) {
return func(proposal *models.Proposal[StateT]) {
proposal.State = state
}
}
func WithVote[StateT models.Unique, VoteT models.Unique](vote *VoteT) func(*models.SignedProposal[StateT, VoteT]) {
return func(proposal *models.SignedProposal[StateT, VoteT]) {
proposal.Vote = vote
}
}
func WithPreviousRankTimeoutCertificate[StateT models.Unique](previousRankTimeoutCert models.TimeoutCertificate) func(*models.Proposal[StateT]) {
return func(proposal *models.Proposal[StateT]) {
proposal.PreviousRankTimeoutCertificate = previousRankTimeoutCert
}
}
func WithWeightedIdentityList(count int) []models.WeightedIdentity {
wi := []models.WeightedIdentity{}
for range count {
wi = append(wi, &TestWeightedIdentity{
ID: MakeIdentity(),
})
}
return wi
}
func VoteForStateFixture(state *models.State[*TestState], ops ...func(vote **TestVote)) *TestVote {
v := &TestVote{
Rank: state.Rank,
ID: MakeIdentity(),
StateID: state.Identifier,
Signature: make([]byte, 74),
}
for _, op := range ops {
op(&v)
}
return v
}
func VoteFixture(op func(vote **TestVote)) *TestVote {
v := &TestVote{
Rank: rand.Uint64(),
ID: MakeIdentity(),
StateID: MakeIdentity(),
Signature: make([]byte, 74),
}
op(&v)
return v
}
type FmtLog struct {
params []consensus.LogParam
}
// Error implements consensus.TraceLogger.
func (n *FmtLog) Error(message string, err error, params ...consensus.LogParam) {
b := strings.Builder{}
b.WriteString(fmt.Sprintf("ERROR: %s: %v\n", message, err))
for _, param := range n.params {
b.WriteString(fmt.Sprintf(
"\t%s: %s\n",
param.GetKey(),
stringFromValue(param),
))
}
for _, param := range params {
b.WriteString(fmt.Sprintf(
"\t%s: %s\n",
param.GetKey(),
stringFromValue(param),
))
}
fmt.Println(b.String())
}
// Trace implements consensus.TraceLogger.
func (n *FmtLog) Trace(message string, params ...consensus.LogParam) {
b := strings.Builder{}
b.WriteString(fmt.Sprintf("TRACE: %s\n", message))
b.WriteString(fmt.Sprintf("\t[%s]\n", time.Now().String()))
for _, param := range n.params {
b.WriteString(fmt.Sprintf(
"\t%s: %s\n",
param.GetKey(),
stringFromValue(param),
))
}
for _, param := range params {
b.WriteString(fmt.Sprintf(
"\t%s: %s\n",
param.GetKey(),
stringFromValue(param),
))
}
fmt.Println(b.String())
}
func (n *FmtLog) With(params ...consensus.LogParam) consensus.TraceLogger {
return &FmtLog{
params: slices.Concat(n.params, params),
}
}
func stringFromValue(param consensus.LogParam) string {
switch param.GetKind() {
case "string":
return param.GetValue().(string)
case "time":
return param.GetValue().(time.Time).String()
default:
return fmt.Sprintf("%v", param.GetValue())
}
}
func Logger() *FmtLog {
return &FmtLog{}
}
type BufferLog struct {
params []consensus.LogParam
b *strings.Builder
}
// Error implements consensus.TraceLogger.
func (n *BufferLog) Error(message string, err error, params ...consensus.LogParam) {
n.b.WriteString(fmt.Sprintf("ERROR: %s: %v\n", message, err))
for _, param := range n.params {
n.b.WriteString(fmt.Sprintf(
"\t%s: %s\n",
param.GetKey(),
stringFromValue(param),
))
}
for _, param := range params {
n.b.WriteString(fmt.Sprintf(
"\t%s: %s\n",
param.GetKey(),
stringFromValue(param),
))
}
}
// Trace implements consensus.TraceLogger.
func (n *BufferLog) Trace(message string, params ...consensus.LogParam) {
n.b.WriteString(fmt.Sprintf("TRACE: %s\n", message))
n.b.WriteString(fmt.Sprintf("\t[%s]\n", time.Now().String()))
for _, param := range n.params {
n.b.WriteString(fmt.Sprintf(
"\t%s: %s\n",
param.GetKey(),
stringFromValue(param),
))
}
for _, param := range params {
n.b.WriteString(fmt.Sprintf(
"\t%s: %s\n",
param.GetKey(),
stringFromValue(param),
))
}
}
func (n *BufferLog) Flush() {
fmt.Println(n.b.String())
}
func (n *BufferLog) With(params ...consensus.LogParam) consensus.TraceLogger {
return &BufferLog{
params: slices.Concat(n.params, params),
b: n.b,
}
}
func BufferLogger() *BufferLog {
return &BufferLog{
b: &strings.Builder{},
}
}