fuzz

package
v1.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

OpenAudio Fuzz Harness

pkg/fuzz is an opt-in harness for running many OpenAudio nodes through odd validator lifecycle states and asserting that the network keeps making progress.

This package is intentionally separate from the core runtime. It observes nodes through public HTTP surfaces and can manage local processes when a scenario provides commands. That keeps the first version usable from Go on macOS without requiring Docker, while leaving room to move the package into its own repository later.

What exists now

  • A scenario runner with deterministic seeds and event capture.
  • Static network support for asserting against already-running endpoints.
  • Local process support for nodes whose NodeSpec includes a command.
  • HTTP observation through /health-check, /core.v1.CoreService/GetStatus, and /core/crpc/status.
  • Reusable actions for wait/start/stop/restart/hook/parallel/sequence.
  • Interface-backed register, deregister, and advertised-endpoint mutation actions.
  • Reusable assertions for reachability, quorum readiness, height advancement, height regression, and validator voting power.
  • Basic and live liveness scenarios that assert height progress, no height regression, live-validator height convergence, and no same-height live-validator fork.
  • A restart scenario that restarts a target node, then asserts quorum readiness, height progress, live-validator agreement, and restoration of the initial validator power plus endpoint health.
  • A pure validator-lifecycle model that can stress up to 300 nodes and catches invalid Comet validator-update emissions before running real processes.
  • A seedable validator-chaos scenario generator that composes stop/restart, register/deregister, jail/unjail, endpoint mutation, and periodic liveness assertions.
  • Generated chaos defaults to outcome assertions after every generated step unless a caller explicitly sets a coarser LivenessEvery cadence, so transient halts cannot hide behind a later repair.
  • An in-memory SimulatedNetwork that runs the same chaos scenarios through the real runner/controller interfaces at up to 300 nodes without Docker.
  • Outcome edge-case scenarios that leave faults in place and assert the net result: the chain advances when quorum should survive, stalls when quorum is intentionally lost, recovers after repair, and finishes with validator power plus endpoint health restored.
  • Process-outage assertions that require stopped nodes to become unavailable while the chain outcome is checked, so no-op stop actions cannot masquerade as successful resilience coverage.
  • Generated transient bounce and minority-outage actions assert initially healthy targets become unavailable before restart, then available again after restart.
  • Generated validator-set round trips assert initially active targets lose validator power during deregister/jail-style removal, the chain follows that intermediate validator set, validator power returns after restore, and targets that were healthy before removal become available again.
  • Generated endpoint lie/repair actions assert initially healthy targets become unavailable while validator power and consensus outcome stay stable, then become available again after repair, register, or unjail.
  • Compound outcome scenarios that mix endpoint lies, stopped validators, deregistered cohorts, jailed cohorts, duplicate removals, and quorum-boundary transitions while asserting the expected chain outcome throughout and final validator power plus endpoint health restoration.
  • Power-skew outcome scenarios where node-count quorum and voting-power quorum disagree, such as one high-power validator being enough to halt progress even when most nodes are still live, then assert final validator power and endpoint health restoration.
  • Power-boundary outcome scenarios that inspect observed validator power at runtime, stop the largest partition that should still allow progress, stop one more validator and assert the chain stalls, then restart both sides and assert height recovery plus final validator power and endpoint health restoration.
  • A validator-quorum outcome oracle that inspects the resulting validator power after a chaos action and asserts height advances or stalls accordingly, with fork and height-regression checks bundled into generated chaos outcomes.
  • Planned quorum-loss assertions that require the observed validator set to be below quorum before accepting a stall, then still check for no live-validator fork and no height regression.
  • Live-validator height convergence assertions, so one progressing validator cannot mask other reachable live validators being stuck behind.
  • Live-validator block-hash agreement checks, so same-height forks cannot hide behind successful height progression.
  • Persistent-fault simulated chaos that can accumulate stops, deregistrations, jails, and endpoint lies across many generated steps instead of always repairing inside the same action.
  • Opt-in recovery sweeps for generated simulated chaos that repair all controllable faults, then assert the chain advances again and validator power plus endpoint reachability return to their pre-chaos baselines.
  • A jailed-then-deregistered compatibility scenario that captures post-jail validator power, then asserts the formal deregistration does not create a new halt, validator-set change, or live-validator fork.
  • A duplicate-deregister idempotency scenario that captures post-deregister validator power, then asserts repeating the deregistration does not create a new halt, validator-set change, or live-validator fork.
  • A duplicate-jail idempotency scenario that captures post-jail validator power, then asserts repeating the jail action does not create a new halt, validator-set change, or live-validator fork.
  • An endpoint-lie consensus-isolation scenario that captures validator power, then asserts a bad advertised endpoint does not change consensus power, halt progress, or fork live validators.
  • An endpoint-repair idempotency scenario that asserts repairing an already-honest endpoint, repairing a bad endpoint, and duplicate repair do not alter validator power, halt progress, fork live validators, or leave repaired endpoints unhealthy.
  • An endpoint-register round-trip scenario that advertises a bad endpoint, deregisters the validator, then asserts registration restores both validator power and externally observable endpoint health.
  • A cohort-endpoint consensus-isolation scenario that advertises and repairs bad endpoints for a quorum-impacting validator cohort, then asserts validator power and consensus outcome remain unchanged and repaired endpoints become healthy again.
  • An inactive-endpoint isolation scenario that asserts endpoint updates and repairs for jailed or absent validators do not resurrect validator power, halt progress, or fork live validators.
  • A jailed-endpoint repair round-trip scenario that repairs a bad endpoint while the validator is jailed, then asserts unjailing restores both validator power and externally observable endpoint health.
  • A stop/start round-trip scenario that captures initial live validator power and endpoint reachability, stops a validator, then asserts starting it restores the original live validator and endpoint outcome.
  • An inactive-start isolation scenario that jails and deregisters a validator, then asserts starting that inactive process does not resurrect validator power, halt progress, or fork live validators.
  • A non-jailed unjail isolation scenario that asserts unjailing an already-active or absent validator does not alter validator power, halt progress, or fork live validators.
  • A register round-trip scenario that captures initial validator power and endpoint reachability, deregisters a validator, then asserts registering it restores the original validator set, endpoint health, and consensus outcome.
  • A jailed-register round-trip scenario that captures initial validator power and endpoint reachability, jails a validator, then asserts registration restores the original validator set, endpoint health, and consensus outcome.
  • A register idempotency scenario that asserts registering an already-active validator, and registering again after restore, does not alter validator power, endpoint health, halt progress, or fork live validators.
  • An unjail round-trip scenario that captures initial validator power and endpoint reachability, jails a validator, then asserts unjailing it restores the original validator set, endpoint health, and consensus outcome.
  • A cohort lifecycle round-trip scenario that deregisters/registers and jails/unjails a quorum-impacting validator cohort, then asserts the original validator set, endpoint health, and consensus outcome are restored each time.
  • A mixed lifecycle quorum-recovery scenario that combines validator-set removal with process outages, asserts the chain stalls below quorum, then asserts register/unjail restores progress before the stopped processes return and the final restart restores validator power plus endpoint health.
  • Seeded skewed validator-power profiles for simulated fuzzing so generated programs exercise power quorum, not only node-count quorum.
  • A quorum-loss recovery scenario that intentionally drops equal-power validator sets below quorum, asserts height stalls, restarts the cohort, and asserts height resumes with validator power and endpoint health restored.
  • An opt-in live liveness test guarded by OPENAUDIO_FUZZ_RUN=1.

Running against an existing devnet

OPENAUDIO_FUZZ_RUN=1 \
OPENAUDIO_FUZZ_ENDPOINTS=https://node1.oap.devnet,https://node2.oap.devnet,https://node3.oap.devnet,https://node4.oap.devnet \
OPENAUDIO_FUZZ_INSECURE_TLS=1 \
go test ./pkg/fuzz -run TestLiveLivenessScenario -count=1 -v

OPENAUDIO_FUZZ_ENDPOINTS may point at any reachable validator-like nodes. Use OPENAUDIO_FUZZ_SCHEME=http when endpoints are provided without a scheme and should default to HTTP.

You can also discover the current validator endpoints from a console node:

OPENAUDIO_FUZZ_RUN=1 \
OPENAUDIO_FUZZ_DISCOVERY_ENDPOINT=https://creatornode.audius.co \
go test ./pkg/fuzz -run TestLiveLivenessScenario -count=1 -v

Use OPENAUDIO_FUZZ_MAX_ENDPOINTS=300 to cap very large discovered sets. Set OPENAUDIO_FUZZ_MIN_REACHABLE when a live run should allow some public endpoints to be slow or unreachable.

Local process shape

Build a native binary:

make bin/openaudio-native

Then construct a ProcessNetwork with one NodeSpec per node:

spec := fuzz.NetworkSpec{
    Name: "local-process-devnet",
    Nodes: []fuzz.NodeSpec{
        {
            ID:       "node1",
            Endpoint: "https://node1.oap.devnet",
            Command:  []string{"./bin/openaudio-native"},
            Env: map[string]string{
                "NETWORK":                   "dev",
                "OPENAUDIO_NODE_ENDPOINT":   "https://node1.oap.devnet",
                "OPENAUDIO_CORE_ROOT_DIR":   "/tmp/openaudio-fuzz/node1/core",
                "OPENAUDIO_TLS_SELF_SIGNED": "true",
            },
            LogPath: "/tmp/openaudio-fuzz/node1.log",
        },
    },
}

Set ValidatorChaosOptions.StartNodes=true when the scenario should start those commands itself.

There is one important topology constraint: current node endpoint validation expects FQDN-style URLs without explicit ports. A fully local multi-process validator set still needs stable hostnames and port routing, or a future core change outside this package. This package does not change that behavior.

Next tactical additions

  • Process profiles that generate node env, data dirs, P2P peers, and HTTP routing consistently.
  • Fault actions for endpoint lies, partitions, delayed restarts, stale state, and signer/key mismatches.
  • Internal/protocol-level hooks once they can be introduced without coupling the harness to production runtime code.

Devnet Registry Controller

EthRegistryController implements register, deregister, and endpoint-update actions against the ServiceProviderFactory contract. It is opt-in and requires explicit RPC, registry, and private-key configuration:

controller, err := fuzz.NewEthRegistryController(ctx, fuzz.EthRegistryControllerOptions{
    RPCURL:          "ws://localhost:8545",
    RegistryAddress: "0x...",
    PrivateKey:      "...",
})

Pass it into ValidatorChaosController as the Registrar and EndpointMutator to exercise real L1-backed lifecycle changes on a disposable devnet.

The fuzzrun command can wire this controller for a disposable devnet. It refuses to send transactions unless -allow-mutations is present:

go run ./pkg/fuzz/cmd/fuzzrun \
  -mode chaos \
  -allow-mutations \
  -endpoints https://node4.oap.devnet \
  -action-node-ids node1 \
  -registry-rpc-url ws://localhost:8545 \
  -registry-address 0x... \
  -registry-private-key ... \
  -chaos-steps 20 \
  -iterations 10

Use -action-node-ids to restrict registry mutations to nodes controlled by the supplied private key.

Fast model fuzzing

The model fuzz target exercises validator lifecycle ordering without starting real nodes:

go test ./pkg/fuzz -run TestValidatorLifecycleModelStress300Nodes -count=1 -v
go test ./pkg/fuzz -run '^$' -fuzz FuzzValidatorLifecycleModel -fuzztime=30s

The seed corpus includes the incident class where a validator is first jailed, then formally deregistered. The current behavior must delete app state without emitting a second zero-power Comet update.

The simulated chaos fuzz target drives the real runner/controller path against the in-memory network:

go test ./pkg/fuzz -run '^$' -fuzz FuzzSimulatedChaosProgram -fuzztime=30s

For a replayable long-running loop:

go run ./pkg/fuzz/cmd/fuzzrun -mode model -nodes 300 -steps 20000 -iterations 1000 -seed 1

To exercise the full runner/controller chaos path without Docker or contract credentials:

go run ./pkg/fuzz/cmd/fuzzrun -mode sim -nodes 300 -steps 1000 -iterations 100 -seed 1

sim mode first runs outcome edge-case scenarios, compound outcome edge cases, power-skew outcome edge cases, dynamic power-boundary edge cases, quorum-loss recovery, and targeted lifecycle compatibility/round-trip scenarios, including endpoint-register recovery, jailed-endpoint repair recovery, jailed-register recovery, cohort endpoint-isolation, lifecycle round trips, and mixed lifecycle quorum recovery, once. It then runs the seeded chaos loop with persistent faults plus validator-quorum, live-validator convergence, and same-height block-hash agreement assertions after every generated step. The generated simulated chaos loop also repairs all controllable faults at the end and asserts the chain advances, validator power returns, and endpoint reachability returns. Because this mode is fully in-memory, long live-style assertion windows are capped to short simulated windows.

For repeated read-only live liveness checks:

go run ./pkg/fuzz/cmd/fuzzrun \
  -mode live \
  -discovery-endpoint https://creatornode.audius.co \
  -max-endpoints 50 \
  -min-reachable 34 \
  -iterations 5

Documentation

Overview

Package fuzz provides a lightweight harness for exercising OpenAudio node networks with disruptive actions and liveness assertions.

The package intentionally depends only on externally observable node behavior and local process management. That keeps it usable against a local devnet, staging-like environments, or an already-running validator set without importing the core runtime packages.

Index

Constants

View Source
const (
	DefaultModelNodeLimit = 300
)

Variables

View Source
var (
	ErrNodeNotFound    = errors.New("node not found")
	ErrNodeNotManaged  = errors.New("node is not managed by this network")
	ErrNoStatusReader  = errors.New("network has no status reader")
	ErrInvalidScenario = errors.New("invalid scenario")
)

Functions

func IsModelInvariantError

func IsModelInvariantError(err error) bool

func SeededValidatorPowers

func SeededValidatorPowers(nodeCount int, seed int64) map[NodeID]int64

SeededValidatorPowers returns deterministic, uneven voting power for simulated networks. The skew keeps generated chaos from quietly assuming node-count quorum is equivalent to validator-power quorum.

Types

type Action

type Action interface {
	Name() string
	Run(ctx context.Context, run *RunContext) error
}

func AdvertiseEndpointWith

func AdvertiseEndpointWith(mutator EndpointMutator, id NodeID, endpoint string) Action

func CaptureReachabilityBaseline

func CaptureReachabilityBaseline(baseline *ReachabilityBaseline) Action

func CaptureValidatorPowerBaseline

func CaptureValidatorPowerBaseline(baseline *ValidatorPowerBaseline) Action

func DeregisterNodeWith

func DeregisterNodeWith(registrar Registrar, id NodeID) Action

func HookAction

func HookAction(name string, fn func(context.Context, *RunContext) error) Action

func JailNodeWith

func JailNodeWith(jailer Jailer, id NodeID) Action

func Parallel

func Parallel(name string, actions ...Action) Action

func RegisterNodeWith

func RegisterNodeWith(registrar Registrar, id NodeID) Action

func RestartNode

func RestartNode(id NodeID) Action

func Sequence

func Sequence(name string, actions ...Action) Action

func StartNode

func StartNode(id NodeID) Action

func StopNode

func StopNode(id NodeID) Action

func UnjailNodeWith

func UnjailNodeWith(jailer Jailer, id NodeID) Action

func Wait

func Wait(duration time.Duration) Action

type ActionFunc

type ActionFunc struct {
	Label string
	Fn    func(context.Context, *RunContext) error
}

func (ActionFunc) Name

func (a ActionFunc) Name() string

func (ActionFunc) Run

func (a ActionFunc) Run(ctx context.Context, run *RunContext) error

type Assertion

type Assertion interface {
	Name() string
	Check(ctx context.Context, run *RunContext) error
}

func AllReachable

func AllReachable() Assertion

func HeightAdvances

func HeightAdvances(minDelta int64, within, pollInterval time.Duration) Assertion

func HeightFollowsValidatorQuorum

func HeightFollowsValidatorQuorum(within, pollInterval time.Duration) Assertion

func HeightStalls

func HeightStalls(observeFor, pollInterval time.Duration) Assertion

func HeightStallsWithoutValidatorQuorum

func HeightStallsWithoutValidatorQuorum(within, pollInterval time.Duration) Assertion

func LiveValidatorHeightsConverge

func LiveValidatorHeightsConverge(maxSpread int64, within, pollInterval time.Duration) Assertion

func NoHeightRegression

func NoHeightRegression(observeFor, pollInterval time.Duration) Assertion

func NoLiveValidatorFork

func NoLiveValidatorFork() Assertion

func NodesAvailable

func NodesAvailable(ids []NodeID, within, pollInterval time.Duration) Assertion

func NodesUnavailable

func NodesUnavailable(ids []NodeID, within, pollInterval time.Duration) Assertion

func NodesWithoutValidatorPower

func NodesWithoutValidatorPower(ids []NodeID, within, pollInterval time.Duration) Assertion

func QuorumReady

func QuorumReady(required int, within, pollInterval time.Duration) Assertion

func ReachabilityRestored

func ReachabilityRestored(baseline *ReachabilityBaseline, within, pollInterval time.Duration) Assertion

func ReachableAtLeast

func ReachableAtLeast(required int, within, pollInterval time.Duration) Assertion

func ValidatorOutcomeAssertions

func ValidatorOutcomeAssertions(within, pollInterval time.Duration, requireConvergence bool) []Assertion

func ValidatorPowerEquals

func ValidatorPowerEquals(id NodeID, power int64, within, pollInterval time.Duration) Assertion

func ValidatorPowerRestored

func ValidatorPowerRestored(baseline *ValidatorPowerBaseline, within, pollInterval time.Duration) Assertion

func ValidatorStallOutcomeAssertions

func ValidatorStallOutcomeAssertions(within, pollInterval time.Duration) []Assertion

type AssertionFunc

type AssertionFunc struct {
	Label string
	Fn    func(context.Context, *RunContext) error
}

func (AssertionFunc) Check

func (a AssertionFunc) Check(ctx context.Context, run *RunContext) error

func (AssertionFunc) Name

func (a AssertionFunc) Name() string

type Client

type Client struct {
	HTTPClient     *http.Client
	DefaultScheme  string
	RequestTimeout time.Duration
}

Client reads node status through public HTTP surfaces.

func NewClient

func NewClient(opts ...ClientOption) *Client

func (*Client) DiscoverValidatorEndpoints

func (c *Client) DiscoverValidatorEndpoints(ctx context.Context, endpoint string) ([]string, error)

func (*Client) GetNodeStatus

func (c *Client) GetNodeStatus(ctx context.Context, node NodeSpec) (NodeStatus, error)

type ClientOption

type ClientOption func(*Client)

func WithDefaultScheme

func WithDefaultScheme(scheme string) ClientOption

func WithHTTPClient

func WithHTTPClient(client *http.Client) ClientOption

func WithInsecureTLS

func WithInsecureTLS() ClientOption

func WithRequestTimeout

func WithRequestTimeout(timeout time.Duration) ClientOption

type EndpointMutator

type EndpointMutator interface {
	SetNodeEndpoint(ctx context.Context, node NodeSpec, endpoint string) error
}

EndpointMutator is implemented by test helpers that can make a node advertise a different endpoint. This is the harness hook for "lying" behaviors.

type EthRegistryController

type EthRegistryController struct {
	// contains filtered or unexported fields
}

EthRegistryController drives the L1 ServiceProviderFactory contract for devnet-style lifecycle fuzzing. It is deliberately opt-in: callers must provide the RPC URL, registry address, and private key.

func (*EthRegistryController) Close

func (c *EthRegistryController) Close()

func (*EthRegistryController) DeregisterNode

func (c *EthRegistryController) DeregisterNode(ctx context.Context, node NodeSpec) error

func (*EthRegistryController) RegisterNode

func (c *EthRegistryController) RegisterNode(ctx context.Context, node NodeSpec) error

func (*EthRegistryController) SetNodeEndpoint

func (c *EthRegistryController) SetNodeEndpoint(ctx context.Context, node NodeSpec, endpoint string) error

type EthRegistryControllerOptions

type EthRegistryControllerOptions struct {
	RPCURL              string
	RegistryAddress     string
	PrivateKey          string
	ServiceType         [32]byte
	StakeAmount         *big.Int
	DelegateOwnerWallet string
	ReceiptTimeout      time.Duration
}

type Event

type Event struct {
	Time    time.Time
	Kind    string
	Message string
	Detail  string
}

type Jailer

type Jailer interface {
	JailNode(ctx context.Context, node NodeSpec) error
	UnjailNode(ctx context.Context, node NodeSpec) error
}

Jailer is implemented by test helpers that can remove and restore a node from the active validator set without deleting the app-level node row.

type ModelAction

type ModelAction struct {
	Kind ModelActionKind
	Node NodeID
}

type ModelActionKind

type ModelActionKind uint8
const (
	ModelRegister ModelActionKind = iota
	ModelJail
	ModelUnjail
	ModelDeregister
	ModelDeregisterTwice
	ModelStop
	ModelStart
	ModelLieEndpoint
	ModelRepairEndpoint
	ModelTick
)

func (ModelActionKind) String

func (a ModelActionKind) String() string

type ModelEvent

type ModelEvent struct {
	Step          int
	Action        ModelActionKind
	Node          NodeID
	StateBefore   ModelValidatorState
	StateAfter    ModelValidatorState
	InCometBefore bool
	InCometAfter  bool
	OnlineBefore  bool
	OnlineAfter   bool
	EmittedUpdate bool
	QuorumBefore  bool
	QuorumAfter   bool
	TotalPower    int64
	OnlinePower   int64
	Height        int64
	Error         string
}

type ModelInvariantError

type ModelInvariantError struct {
	Message string
	Event   ModelEvent
	Trace   []ModelEvent
}

func (*ModelInvariantError) Error

func (e *ModelInvariantError) Error() string

type ModelNode

type ModelNode struct {
	ID             NodeID
	State          ModelValidatorState
	InCometSet     bool
	Online         bool
	EndpointHonest bool
	Power          int64
}

type ModelResult

type ModelResult struct {
	Seed      int64
	NodeCount int
	Steps     int
	Height    int64
	Events    []ModelEvent
}

func RunValidatorLifecycleModel

func RunValidatorLifecycleModel(seed int64, nodeCount, steps int, behavior ValidatorSetBehavior) (ModelResult, error)

func RunValidatorLifecycleProgram

func RunValidatorLifecycleProgram(program []byte, nodeCount int, behavior ValidatorSetBehavior) (ModelResult, error)

type ModelValidatorState

type ModelValidatorState int
const (
	ModelValidatorAbsent ModelValidatorState = iota
	ModelValidatorActive
	ModelValidatorJailed
)

func (ModelValidatorState) String

func (s ModelValidatorState) String() string

type Network

type Network interface {
	Spec() NetworkSpec
	StartNode(ctx context.Context, id NodeID) error
	StopNode(ctx context.Context, id NodeID) error
	RestartNode(ctx context.Context, id NodeID) error
	Snapshot(ctx context.Context) (Snapshot, error)
	Close(ctx context.Context) error
}

Network is the control and observation surface used by scenarios.

type NetworkSpec

type NetworkSpec struct {
	Name  string
	Nodes []NodeSpec
}

NetworkSpec describes a network under test.

func (NetworkSpec) Node

func (s NetworkSpec) Node(id NodeID) (NodeSpec, bool)

func (NetworkSpec) NodeIDs

func (s NetworkSpec) NodeIDs() []NodeID

func (NetworkSpec) Validate

func (s NetworkSpec) Validate() error

type NodeID

type NodeID string

NodeID is the stable name used by scenarios to refer to a node.

type NodeSpec

type NodeSpec struct {
	ID              NodeID
	Endpoint        string
	Command         []string
	Env             map[string]string
	Dir             string
	LogPath         string
	StartupTimeout  time.Duration
	ShutdownTimeout time.Duration
}

NodeSpec describes one node from the harness point of view.

Endpoint is the externally reachable HTTP(S) endpoint used for observations. Command is optional; when it is set, ProcessNetwork can start and stop the node as a local process.

type NodeStatus

type NodeStatus struct {
	ID               NodeID
	Endpoint         string
	Reachable        bool
	Ready            bool
	Live             bool
	Synced           bool
	Height           int64
	BlockHash        string
	Version          string
	Git              string
	ValidatorPower   int64
	ProcessState     string
	ProcessError     string
	ObservationError string
	ObservedAt       time.Time
}

NodeStatus is a single observation of one node.

type ProcessNetwork

type ProcessNetwork struct {
	// contains filtered or unexported fields
}

ProcessNetwork observes a network and can manage nodes whose NodeSpec includes a Command. Nodes without a command are still observable but are not startable or stoppable through this type.

func NewProcessNetwork

func NewProcessNetwork(spec NetworkSpec, reader StatusReader) (*ProcessNetwork, error)

func (*ProcessNetwork) Close

func (n *ProcessNetwork) Close(ctx context.Context) error

func (*ProcessNetwork) RestartNode

func (n *ProcessNetwork) RestartNode(ctx context.Context, id NodeID) error

func (*ProcessNetwork) Snapshot

func (n *ProcessNetwork) Snapshot(ctx context.Context) (Snapshot, error)

func (*ProcessNetwork) Spec

func (n *ProcessNetwork) Spec() NetworkSpec

func (*ProcessNetwork) StartNode

func (n *ProcessNetwork) StartNode(ctx context.Context, id NodeID) error

func (*ProcessNetwork) StopNode

func (n *ProcessNetwork) StopNode(ctx context.Context, id NodeID) error

type ReachabilityBaseline

type ReachabilityBaseline struct {
	// contains filtered or unexported fields
}

type Registrar

type Registrar interface {
	RegisterNode(ctx context.Context, node NodeSpec) error
	DeregisterNode(ctx context.Context, node NodeSpec) error
}

Registrar is implemented by test helpers that can register or deregister a node through whatever backing system the scenario is using.

type Result

type Result struct {
	ScenarioName string
	Seed         int64
	StartedAt    time.Time
	FinishedAt   time.Time
	Passed       bool
	Error        string
	Events       []Event
}

type RunContext

type RunContext struct {
	Network Network
	Rand    *rand.Rand
	// contains filtered or unexported fields
}

func (*RunContext) Events

func (r *RunContext) Events() []Event

type Runner

type Runner struct {
	Network     Network
	Seed        int64
	StepTimeout time.Duration
	EventSink   func(Event)
}

func (Runner) Run

func (r Runner) Run(ctx context.Context, scenario Scenario) (Result, error)

type Scenario

type Scenario struct {
	Name  string
	Seed  int64
	Steps []Step
}

func BasicLivenessScenario

func BasicLivenessScenario(minHeightDelta int64, within, pollInterval time.Duration) Scenario

func CohortEndpointConsensusIsolationScenario

func CohortEndpointConsensusIsolationScenario(spec NetworkSpec, controller ValidatorChaosController, within, pollInterval time.Duration) Scenario

func CohortLifecycleRoundTripScenario

func CohortLifecycleRoundTripScenario(spec NetworkSpec, controller ValidatorChaosController, within, pollInterval time.Duration) Scenario

func CompoundOutcomeEdgeCaseScenario

func CompoundOutcomeEdgeCaseScenario(spec NetworkSpec, controller ValidatorChaosController, within, pollInterval time.Duration) Scenario

func DuplicateDeregisterIdempotencyScenario

func DuplicateDeregisterIdempotencyScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func DuplicateJailIdempotencyScenario

func DuplicateJailIdempotencyScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func EndpointLieConsensusIsolationScenario

func EndpointLieConsensusIsolationScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func EndpointRegisterRoundTripScenario

func EndpointRegisterRoundTripScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func EndpointRepairIdempotencyScenario

func EndpointRepairIdempotencyScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func InactiveEndpointIsolationScenario

func InactiveEndpointIsolationScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func InactiveStartIsolationScenario

func InactiveStartIsolationScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func JailedDeregisterCompatibilityScenario

func JailedDeregisterCompatibilityScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func JailedEndpointRepairRoundTripScenario

func JailedEndpointRepairRoundTripScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func JailedRegisterRoundTripScenario

func JailedRegisterRoundTripScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func LiveLivenessScenario

func LiveLivenessScenario(requiredReachable int, minHeightDelta int64, within, pollInterval time.Duration) Scenario

func MixedLifecycleQuorumRecoveryScenario

func MixedLifecycleQuorumRecoveryScenario(spec NetworkSpec, controller ValidatorChaosController, within, pollInterval time.Duration) Scenario

func NonJailedUnjailIsolationScenario

func NonJailedUnjailIsolationScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func OutcomeEdgeCaseScenario

func OutcomeEdgeCaseScenario(spec NetworkSpec, controller ValidatorChaosController, within, pollInterval time.Duration) Scenario

func PowerBoundaryOutcomeScenario

func PowerBoundaryOutcomeScenario(within, pollInterval time.Duration) Scenario

func PowerSkewOutcomeScenario

func PowerSkewOutcomeScenario(spec NetworkSpec, controller ValidatorChaosController, highPowerID NodeID, lowPowerIDs []NodeID, within, pollInterval time.Duration) Scenario

func QuorumLossRecoveryScenario

func QuorumLossRecoveryScenario(spec NetworkSpec, within, pollInterval time.Duration) Scenario

func RegisterIdempotencyScenario

func RegisterIdempotencyScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func RegisterRoundTripScenario

func RegisterRoundTripScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func RestartNodeScenario

func RestartNodeScenario(id NodeID, readyCount int, within, pollInterval time.Duration) Scenario

func SimulatedChaosScenarioFromProgram

func SimulatedChaosScenarioFromProgram(spec NetworkSpec, controller ValidatorChaosController, program []byte, opts SimulatedProgramOptions) Scenario

func StopStartRoundTripScenario

func StopStartRoundTripScenario(spec NetworkSpec, target NodeID, within, pollInterval time.Duration) Scenario

func UnjailRoundTripScenario

func UnjailRoundTripScenario(spec NetworkSpec, controller ValidatorChaosController, target NodeID, within, pollInterval time.Duration) Scenario

func ValidatorChaosScenario

func ValidatorChaosScenario(spec NetworkSpec, controller ValidatorChaosController, opts ValidatorChaosOptions) Scenario

type SimulatedNetwork

type SimulatedNetwork struct {
	// contains filtered or unexported fields
}

SimulatedNetwork is an in-memory Network plus lifecycle controller. It runs the same chaos scenarios as a real network but keeps everything inside Go, which makes high-volume 300-node loops practical on a laptop.

func NewSimulatedNetwork

func NewSimulatedNetwork(opts SimulatedNetworkOptions) (*SimulatedNetwork, error)

func (*SimulatedNetwork) Close

func (*SimulatedNetwork) DeregisterNode

func (n *SimulatedNetwork) DeregisterNode(ctx context.Context, node NodeSpec) error

func (*SimulatedNetwork) JailNode

func (n *SimulatedNetwork) JailNode(ctx context.Context, node NodeSpec) error

func (*SimulatedNetwork) RegisterNode

func (n *SimulatedNetwork) RegisterNode(ctx context.Context, node NodeSpec) error

func (*SimulatedNetwork) RestartNode

func (n *SimulatedNetwork) RestartNode(ctx context.Context, id NodeID) error

func (*SimulatedNetwork) SetNodeEndpoint

func (n *SimulatedNetwork) SetNodeEndpoint(ctx context.Context, node NodeSpec, endpoint string) error

func (*SimulatedNetwork) Snapshot

func (n *SimulatedNetwork) Snapshot(ctx context.Context) (Snapshot, error)

func (*SimulatedNetwork) Spec

func (n *SimulatedNetwork) Spec() NetworkSpec

func (*SimulatedNetwork) StartNode

func (n *SimulatedNetwork) StartNode(ctx context.Context, id NodeID) error

func (*SimulatedNetwork) StopNode

func (n *SimulatedNetwork) StopNode(ctx context.Context, id NodeID) error

func (*SimulatedNetwork) UnjailNode

func (n *SimulatedNetwork) UnjailNode(ctx context.Context, node NodeSpec) error

type SimulatedNetworkOptions

type SimulatedNetworkOptions struct {
	Name           string
	NodeCount      int
	InitialActive  int
	Behavior       ValidatorSetBehavior
	NodePowers     map[NodeID]int64
	TickOnSnapshot bool
}

type SimulatedProgramOptions

type SimulatedProgramOptions struct {
	MaxSteps                int
	LivenessEvery           int
	LivenessWithin          time.Duration
	PollInterval            time.Duration
	AssertAfterEachStep     bool
	AssertConvergence       bool
	IncludePersistentFaults bool
	RecoverAtEnd            bool
}

type Snapshot

type Snapshot struct {
	ObservedAt time.Time
	Nodes      []NodeStatus
}

Snapshot is a point-in-time observation of the whole network.

func (Snapshot) ByNode

func (s Snapshot) ByNode(id NodeID) (NodeStatus, bool)

func (Snapshot) HasValidatorQuorum

func (s Snapshot) HasValidatorQuorum() bool

func (Snapshot) MaxHeight

func (s Snapshot) MaxHeight() int64

func (Snapshot) MaxObservedHeight

func (s Snapshot) MaxObservedHeight() int64

func (Snapshot) ReachableCount

func (s Snapshot) ReachableCount() int

func (Snapshot) ReadyCount

func (s Snapshot) ReadyCount() int

func (Snapshot) Summary

func (s Snapshot) Summary() string

func (Snapshot) ValidatorPower

func (s Snapshot) ValidatorPower() (totalPower, livePower int64)

type StaticNetwork

type StaticNetwork struct {
	// contains filtered or unexported fields
}

StaticNetwork observes an already-running network. It cannot start or stop nodes, but it is useful for running assertions against devnet, staging, or production-like endpoints.

func NewStaticNetwork

func NewStaticNetwork(spec NetworkSpec, reader StatusReader) (*StaticNetwork, error)

func (*StaticNetwork) Close

func (n *StaticNetwork) Close(context.Context) error

func (*StaticNetwork) RestartNode

func (n *StaticNetwork) RestartNode(context.Context, NodeID) error

func (*StaticNetwork) Snapshot

func (n *StaticNetwork) Snapshot(ctx context.Context) (Snapshot, error)

func (*StaticNetwork) Spec

func (n *StaticNetwork) Spec() NetworkSpec

func (*StaticNetwork) StartNode

func (n *StaticNetwork) StartNode(context.Context, NodeID) error

func (*StaticNetwork) StopNode

func (n *StaticNetwork) StopNode(context.Context, NodeID) error

type StatusReader

type StatusReader interface {
	GetNodeStatus(ctx context.Context, node NodeSpec) (NodeStatus, error)
}

StatusReader reads externally visible node status.

type Step

type Step struct {
	Name       string
	Actions    []Action
	Assertions []Assertion
	Timeout    time.Duration
}

func ActionStep

func ActionStep(name string, actions ...Action) Step

func AssertionStep

func AssertionStep(name string, assertions ...Assertion) Step

type ValidatorChaosController

type ValidatorChaosController struct {
	Registrar       Registrar
	EndpointMutator EndpointMutator
	Jailer          Jailer
}

type ValidatorChaosOptions

type ValidatorChaosOptions struct {
	Seed                    int64
	Steps                   int
	StepTimeout             time.Duration
	LivenessEvery           int
	LivenessWithin          time.Duration
	PollInterval            time.Duration
	ActionNodeIDs           []NodeID
	StartNodes              bool
	IncludeProcessFaults    bool
	NoProcessFaultDelay     bool
	AssertAfterEachStep     bool
	AssertConvergence       bool
	IncludePersistentFaults bool
	RecoverAtEnd            bool
}

type ValidatorLifecycleModel

type ValidatorLifecycleModel struct {
	Behavior ValidatorSetBehavior
	Nodes    map[NodeID]*ModelNode
	Order    []NodeID
	Height   int64
	Events   []ModelEvent
}

func NewValidatorLifecycleModel

func NewValidatorLifecycleModel(opts ValidatorModelOptions) *ValidatorLifecycleModel

func (*ValidatorLifecycleModel) Apply

func (m *ValidatorLifecycleModel) Apply(step int, action ModelAction) error

func (*ValidatorLifecycleModel) HasOnlineQuorum

func (m *ValidatorLifecycleModel) HasOnlineQuorum() bool

func (*ValidatorLifecycleModel) Power

func (m *ValidatorLifecycleModel) Power() (totalPower, onlinePower int64)

func (*ValidatorLifecycleModel) RecentEvents

func (m *ValidatorLifecycleModel) RecentEvents(limit int) []ModelEvent

type ValidatorModelOptions

type ValidatorModelOptions struct {
	NodeCount     int
	InitialActive int
	Behavior      ValidatorSetBehavior
	NodePowers    map[NodeID]int64
}

type ValidatorPowerBaseline

type ValidatorPowerBaseline struct {
	// contains filtered or unexported fields
}

type ValidatorSetBehavior

type ValidatorSetBehavior int
const (
	// ValidatorSetBehaviorCurrent models the intended behavior: deregistration
	// emits a Comet zero-power update only when the validator is active.
	ValidatorSetBehaviorCurrent ValidatorSetBehavior = iota
	// ValidatorSetBehaviorBuggyJailedDeregistration models the pre-fix class of
	// bug where deregistration emits a zero-power update even for a jailed row.
	ValidatorSetBehaviorBuggyJailedDeregistration
	// ValidatorSetBehaviorBuggyAnyDeregistrationUpdate models duplicate or
	// absent-validator removals emitting repeated zero-power updates.
	ValidatorSetBehaviorBuggyAnyDeregistrationUpdate
	// ValidatorSetBehaviorBuggyRegisterWithoutCometUpdate models app state
	// becoming active while the Comet validator set is not updated.
	ValidatorSetBehaviorBuggyRegisterWithoutCometUpdate
	// ValidatorSetBehaviorBuggyRegisterNoop models recovery code that accepts a
	// register action but leaves a removed validator out of the validator set.
	ValidatorSetBehaviorBuggyRegisterNoop
	// ValidatorSetBehaviorBuggyStartAbsentOnline models lifecycle code marking
	// an absent or jailed validator online.
	ValidatorSetBehaviorBuggyStartAbsentOnline
	// ValidatorSetBehaviorBuggyTickWithoutQuorum models height advancement when
	// the active online validator power does not have consensus quorum.
	ValidatorSetBehaviorBuggyTickWithoutQuorum
	// ValidatorSetBehaviorBuggyStallWithQuorum models a halt despite enough
	// online active validator power to make progress.
	ValidatorSetBehaviorBuggyStallWithQuorum
)

type VersionSkewMode

type VersionSkewMode int
const (
	VersionSkewModeHaltLegacyOnJailedDeregister VersionSkewMode = iota
	VersionSkewModeForkLegacyOnJailedDeregister
	VersionSkewModeHaltLegacyOnDuplicateDeregister
	VersionSkewModeForkLegacyOnDuplicateDeregister
	VersionSkewModeHaltLegacyOnDuplicateJail
	VersionSkewModeForkLegacyOnDuplicateJail
	VersionSkewModeHaltLegacyOnEndpointLie
	VersionSkewModeForkLegacyOnEndpointLie
	VersionSkewModeHaltLegacyOnEndpointRepair
	VersionSkewModeForkLegacyOnEndpointRepair
	VersionSkewModeKeepBadEndpointOnRepair
	VersionSkewModeHaltLegacyOnEndpointNoopRepair
	VersionSkewModeForkLegacyOnEndpointNoopRepair
	VersionSkewModeReactivateJailedOnInactiveEndpoint
	VersionSkewModeReactivateAbsentOnInactiveEndpoint
	VersionSkewModeHaltLegacyOnInactiveEndpoint
	VersionSkewModeForkLegacyOnInactiveEndpoint
	VersionSkewModeKeepBadEndpointOnJailedRepair
	VersionSkewModeHaltLegacyOnRegister
	VersionSkewModeForkLegacyOnRegister
	VersionSkewModeKeepBadEndpointOnRegister
	VersionSkewModeBadEndpointOnRegister
	VersionSkewModeHaltLegacyOnActiveRegister
	VersionSkewModeForkLegacyOnActiveRegister
	VersionSkewModeNoopOnJailedRegister
	VersionSkewModeHaltLegacyOnJailedRegister
	VersionSkewModeForkLegacyOnJailedRegister
	VersionSkewModeNoopOnUnjail
	VersionSkewModeBadEndpointOnUnjail
	VersionSkewModeHaltLegacyOnUnjail
	VersionSkewModeForkLegacyOnUnjail
	VersionSkewModeHaltLegacyOnActiveUnjail
	VersionSkewModeForkLegacyOnActiveUnjail
	VersionSkewModeReactivateAbsentOnUnjail
	VersionSkewModeHaltLegacyOnAbsentUnjail
	VersionSkewModeForkLegacyOnAbsentUnjail
	VersionSkewModeNoopOnStart
	VersionSkewModeBadEndpointOnStart
	VersionSkewModeHaltLegacyOnStart
	VersionSkewModeForkLegacyOnStart
	VersionSkewModeReactivateJailedOnStart
	VersionSkewModeReactivateAbsentOnStart
	VersionSkewModeHaltLegacyOnInactiveStart
	VersionSkewModeForkLegacyOnInactiveStart
)

type VersionSkewNetwork

type VersionSkewNetwork struct {
	// contains filtered or unexported fields
}

func NewVersionSkewNetwork

func NewVersionSkewNetwork(opts VersionSkewNetworkOptions) (*VersionSkewNetwork, error)

func (*VersionSkewNetwork) Close

func (*VersionSkewNetwork) DeregisterNode

func (n *VersionSkewNetwork) DeregisterNode(ctx context.Context, node NodeSpec) error

func (*VersionSkewNetwork) JailNode

func (n *VersionSkewNetwork) JailNode(ctx context.Context, node NodeSpec) error

func (*VersionSkewNetwork) RegisterNode

func (n *VersionSkewNetwork) RegisterNode(ctx context.Context, node NodeSpec) error

func (*VersionSkewNetwork) RestartNode

func (n *VersionSkewNetwork) RestartNode(ctx context.Context, id NodeID) error

func (*VersionSkewNetwork) SetNodeEndpoint

func (n *VersionSkewNetwork) SetNodeEndpoint(ctx context.Context, node NodeSpec, endpoint string) error

func (*VersionSkewNetwork) Snapshot

func (n *VersionSkewNetwork) Snapshot(ctx context.Context) (Snapshot, error)

func (*VersionSkewNetwork) Spec

func (n *VersionSkewNetwork) Spec() NetworkSpec

func (*VersionSkewNetwork) StartNode

func (n *VersionSkewNetwork) StartNode(ctx context.Context, id NodeID) error

func (*VersionSkewNetwork) StopNode

func (n *VersionSkewNetwork) StopNode(ctx context.Context, id NodeID) error

func (*VersionSkewNetwork) UnjailNode

func (n *VersionSkewNetwork) UnjailNode(ctx context.Context, node NodeSpec) error

type VersionSkewNetworkOptions

type VersionSkewNetworkOptions struct {
	Name          string
	NodeCount     int
	LegacyNodeIDs []NodeID
	Mode          VersionSkewMode
	NodePowers    map[NodeID]int64
}

Directories

Path Synopsis
cmd
fuzzrun command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL