mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 18:37:26 +08:00
361 lines
10 KiB
Go
361 lines
10 KiB
Go
package consensus
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// StateMachineViz provides visualization utilities for the generic state machine
|
|
type StateMachineViz[
|
|
StateT Unique,
|
|
VoteT Unique,
|
|
PeerIDT Unique,
|
|
CollectedT Unique,
|
|
] struct {
|
|
sm *StateMachine[StateT, VoteT, PeerIDT, CollectedT]
|
|
}
|
|
|
|
// NewStateMachineViz creates a new visualizer for the generic state machine
|
|
func NewStateMachineViz[
|
|
StateT Unique,
|
|
VoteT Unique,
|
|
PeerIDT Unique,
|
|
CollectedT Unique,
|
|
](
|
|
sm *StateMachine[StateT, VoteT, PeerIDT, CollectedT],
|
|
) *StateMachineViz[StateT, VoteT, PeerIDT, CollectedT] {
|
|
return &StateMachineViz[StateT, VoteT, PeerIDT, CollectedT]{sm: sm}
|
|
}
|
|
|
|
// GenerateMermaidDiagram generates a Mermaid diagram of the state machine
|
|
func (
|
|
v *StateMachineViz[StateT, VoteT, PeerIDT, CollectedT],
|
|
) GenerateMermaidDiagram() string {
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("```mermaid\n")
|
|
sb.WriteString("stateDiagram-v2\n")
|
|
sb.WriteString(" [*] --> Stopped\n")
|
|
|
|
// Define states with descriptions
|
|
// Use CamelCase for state IDs to avoid underscore issues
|
|
stateMap := map[State]string{
|
|
StateStopped: "Stopped",
|
|
StateStarting: "Starting",
|
|
StateLoading: "Loading",
|
|
StateCollecting: "Collecting",
|
|
StateLivenessCheck: "LivenessCheck",
|
|
StateProving: "Proving",
|
|
StatePublishing: "Publishing",
|
|
StateVoting: "Voting",
|
|
StateFinalizing: "Finalizing",
|
|
StateVerifying: "Verifying",
|
|
StateStopping: "Stopping",
|
|
}
|
|
|
|
stateDescriptions := map[State]string{
|
|
StateStopped: "Engine not running",
|
|
StateStarting: "Initializing components",
|
|
StateLoading: "Syncing with network",
|
|
StateCollecting: "Gathering consensus data",
|
|
StateLivenessCheck: "Checking prover availability",
|
|
StateProving: "Generating cryptographic proof",
|
|
StatePublishing: "Broadcasting proposal",
|
|
StateVoting: "Participating in consensus",
|
|
StateFinalizing: "Aggregating votes",
|
|
StateVerifying: "Publishing confirmation",
|
|
StateStopping: "Cleaning up resources",
|
|
}
|
|
|
|
// Add state descriptions
|
|
for state, id := range stateMap {
|
|
desc := stateDescriptions[state]
|
|
sb.WriteString(fmt.Sprintf(" %s : %s\n", id, desc))
|
|
}
|
|
|
|
sb.WriteString("\n")
|
|
|
|
// Add transitions using mapped state names
|
|
transitions := v.getTransitionList()
|
|
for _, t := range transitions {
|
|
fromID := stateMap[t.From]
|
|
toID := stateMap[t.To]
|
|
if t.Guard != nil {
|
|
sb.WriteString(fmt.Sprintf(
|
|
" %s --> %s : %s [guarded]\n",
|
|
fromID, toID, t.Event))
|
|
} else {
|
|
sb.WriteString(fmt.Sprintf(
|
|
" %s --> %s : %s\n",
|
|
fromID, toID, t.Event))
|
|
}
|
|
}
|
|
|
|
// Add special annotations using mapped names
|
|
sb.WriteString("\n")
|
|
sb.WriteString(" note right of Proving : Leader only\n")
|
|
sb.WriteString(
|
|
" note right of LivenessCheck : Divergence point\\nfor leader/non-leader\n",
|
|
)
|
|
sb.WriteString(" note right of Voting : Convergence point\n")
|
|
|
|
sb.WriteString("```\n")
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// GenerateDotDiagram generates a Graphviz DOT diagram
|
|
func (
|
|
v *StateMachineViz[StateT, VoteT, PeerIDT, CollectedT],
|
|
) GenerateDotDiagram() string {
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("digraph ConsensusStateMachine {\n")
|
|
sb.WriteString(" rankdir=TB;\n")
|
|
sb.WriteString(" node [shape=box, style=rounded];\n")
|
|
sb.WriteString(" edge [fontsize=10];\n\n")
|
|
|
|
// Define node styles
|
|
sb.WriteString(" // State styles\n")
|
|
sb.WriteString(
|
|
" Stopped [style=\"rounded,filled\", fillcolor=lightgray];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Starting [style=\"rounded,filled\", fillcolor=lightyellow];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Loading [style=\"rounded,filled\", fillcolor=lightyellow];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Collecting [style=\"rounded,filled\", fillcolor=lightblue];\n",
|
|
)
|
|
sb.WriteString(
|
|
" LivenessCheck [style=\"rounded,filled\", fillcolor=orange];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Proving [style=\"rounded,filled\", fillcolor=lightgreen];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Publishing [style=\"rounded,filled\", fillcolor=lightgreen];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Voting [style=\"rounded,filled\", fillcolor=lightblue];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Finalizing [style=\"rounded,filled\", fillcolor=lightblue];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Verifying [style=\"rounded,filled\", fillcolor=lightblue];\n",
|
|
)
|
|
sb.WriteString(
|
|
" Stopping [style=\"rounded,filled\", fillcolor=lightcoral];\n\n",
|
|
)
|
|
|
|
// Add transitions
|
|
sb.WriteString(" // Transitions\n")
|
|
transitions := v.getTransitionList()
|
|
for _, t := range transitions {
|
|
label := string(t.Event)
|
|
if t.Guard != nil {
|
|
label += " [G]"
|
|
}
|
|
sb.WriteString(fmt.Sprintf(
|
|
" %s -> %s [label=\"%s\"];\n",
|
|
t.From, t.To, label))
|
|
}
|
|
|
|
// Add legend
|
|
sb.WriteString("\n // Legend\n")
|
|
sb.WriteString(" subgraph cluster_legend {\n")
|
|
sb.WriteString(" label=\"Legend\";\n")
|
|
sb.WriteString(" style=dotted;\n")
|
|
sb.WriteString(" \"[G] = Guarded transition\" [shape=none];\n")
|
|
sb.WriteString(" \"Yellow = Initialization\" [shape=none];\n")
|
|
sb.WriteString(" \"Blue = Consensus flow\" [shape=none];\n")
|
|
sb.WriteString(" \"Green = Leader specific\" [shape=none];\n")
|
|
sb.WriteString(" \"Orange = Decision point\" [shape=none];\n")
|
|
sb.WriteString(" }\n")
|
|
|
|
sb.WriteString("}\n")
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// GenerateTransitionTable generates a markdown table of all transitions
|
|
func (
|
|
v *StateMachineViz[StateT, VoteT, PeerIDT, CollectedT],
|
|
) GenerateTransitionTable() string {
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("| From State | Event | To State | Condition |\n")
|
|
sb.WriteString("|------------|-------|----------|----------|\n")
|
|
|
|
transitions := v.getTransitionList()
|
|
for _, t := range transitions {
|
|
condition := "None"
|
|
if t.Guard != nil {
|
|
condition = "Has guard"
|
|
}
|
|
sb.WriteString(fmt.Sprintf(
|
|
"| %s | %s | %s | %s |\n",
|
|
t.From, t.Event, t.To, condition))
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// getTransitionList extracts all transitions from the state machine
|
|
func (
|
|
v *StateMachineViz[StateT, VoteT, PeerIDT, CollectedT],
|
|
) getTransitionList() []*Transition[StateT, VoteT, PeerIDT, CollectedT] {
|
|
var transitions []*Transition[StateT, VoteT, PeerIDT, CollectedT]
|
|
|
|
v.sm.mu.RLock()
|
|
defer v.sm.mu.RUnlock()
|
|
|
|
for _, eventMap := range v.sm.transitions {
|
|
for _, transition := range eventMap {
|
|
transitions = append(transitions, transition)
|
|
}
|
|
}
|
|
|
|
return transitions
|
|
}
|
|
|
|
// GetStateStats returns statistics about the state machine
|
|
func (
|
|
v *StateMachineViz[StateT, VoteT, PeerIDT, CollectedT],
|
|
) GetStateStats() string {
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("State Machine Statistics:\n")
|
|
sb.WriteString("========================\n\n")
|
|
|
|
v.sm.mu.RLock()
|
|
defer v.sm.mu.RUnlock()
|
|
|
|
// Count states and transitions
|
|
stateCount := 0
|
|
transitionCount := 0
|
|
eventCount := make(map[Event]int)
|
|
|
|
for _, eventMap := range v.sm.transitions {
|
|
// Only count if we have transitions for this state
|
|
if len(eventMap) > 0 {
|
|
stateCount++
|
|
}
|
|
for event := range eventMap {
|
|
transitionCount++
|
|
eventCount[event]++
|
|
}
|
|
}
|
|
|
|
sb.WriteString(fmt.Sprintf("Total States: %d\n", stateCount))
|
|
sb.WriteString(fmt.Sprintf("Total Transitions: %d\n", transitionCount))
|
|
sb.WriteString(fmt.Sprintf("Current State: %s\n", v.sm.machineState))
|
|
sb.WriteString(fmt.Sprintf("Transitions Made: %d\n", v.sm.transitionCount))
|
|
sb.WriteString(
|
|
fmt.Sprintf("Time in Current State: %v\n", v.sm.GetStateTime()),
|
|
)
|
|
|
|
// Display current leader info if available
|
|
if len(v.sm.nextProvers) > 0 {
|
|
sb.WriteString("\nNext Leaders:\n")
|
|
for i, leader := range v.sm.nextProvers {
|
|
sb.WriteString(fmt.Sprintf(" %d. %v\n", i+1, leader))
|
|
}
|
|
}
|
|
|
|
// Display active state info
|
|
if v.sm.activeState != nil {
|
|
sb.WriteString(fmt.Sprintf("\nActive State: %+v\n", v.sm.activeState))
|
|
}
|
|
|
|
// Display liveness info
|
|
sb.WriteString(fmt.Sprintf("\nLiveness Checks: %d\n", len(v.sm.liveness)))
|
|
|
|
// Display voting info
|
|
sb.WriteString(fmt.Sprintf("Proposals: %d\n", len(v.sm.proposals)))
|
|
sb.WriteString(fmt.Sprintf("Votes: %d\n", len(v.sm.votes)))
|
|
sb.WriteString(fmt.Sprintf("Confirmations: %d\n", len(v.sm.confirmations)))
|
|
|
|
sb.WriteString("\nEvent Usage:\n")
|
|
for event, count := range eventCount {
|
|
sb.WriteString(fmt.Sprintf(" %s: %d transitions\n", event, count))
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// GetCurrentStateInfo returns detailed information about the current state
|
|
func (
|
|
v *StateMachineViz[StateT, VoteT, PeerIDT, CollectedT]) GetCurrentStateInfo() string {
|
|
v.sm.mu.RLock()
|
|
defer v.sm.mu.RUnlock()
|
|
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("Current State Information:\n")
|
|
sb.WriteString("=========================\n\n")
|
|
sb.WriteString(fmt.Sprintf("State: %s\n", v.sm.machineState))
|
|
sb.WriteString(
|
|
fmt.Sprintf("Time in State: %v\n", time.Since(v.sm.stateStartTime)),
|
|
)
|
|
sb.WriteString(fmt.Sprintf("Total Transitions: %d\n", v.sm.transitionCount))
|
|
|
|
// State configuration info
|
|
if config, exists := v.sm.stateConfigs[v.sm.machineState]; exists {
|
|
sb.WriteString("\nState Configuration:\n")
|
|
if config.Timeout > 0 {
|
|
sb.WriteString(fmt.Sprintf(" Timeout: %v\n", config.Timeout))
|
|
sb.WriteString(fmt.Sprintf(" Timeout Event: %s\n", config.OnTimeout))
|
|
}
|
|
if config.Behavior != nil {
|
|
sb.WriteString(" Has Behavior: Yes\n")
|
|
}
|
|
if config.OnEnter != nil {
|
|
sb.WriteString(" Has OnEnter Callback: Yes\n")
|
|
}
|
|
if config.OnExit != nil {
|
|
sb.WriteString(" Has OnExit Callback: Yes\n")
|
|
}
|
|
}
|
|
|
|
// Available transitions from current state
|
|
sb.WriteString("\nAvailable Transitions:\n")
|
|
if transitions, exists := v.sm.transitions[v.sm.machineState]; exists {
|
|
for event, transition := range transitions {
|
|
guardStr := ""
|
|
if transition.Guard != nil {
|
|
guardStr = " [guarded]"
|
|
}
|
|
sb.WriteString(
|
|
fmt.Sprintf(" %s -> %s%s\n", event, transition.To, guardStr),
|
|
)
|
|
}
|
|
}
|
|
|
|
return sb.String()
|
|
}
|
|
|
|
// GenerateEventFlow generates a flow of events that occurred
|
|
func (
|
|
v *StateMachineViz[StateT, VoteT, PeerIDT, CollectedT],
|
|
) GenerateEventFlow() string {
|
|
var sb strings.Builder
|
|
|
|
sb.WriteString("Event Flow:\n")
|
|
sb.WriteString("===========\n\n")
|
|
|
|
transitions := v.getTransitionList()
|
|
for i, tr := range transitions {
|
|
sb.WriteString(fmt.Sprintf(
|
|
"%d. %s -> %s [%s]\n",
|
|
i+1, tr.From, tr.To, tr.Event,
|
|
))
|
|
}
|
|
|
|
return sb.String()
|
|
}
|