wbft

module
v0.0.0-...-3d5562a Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: GPL-3.0, LGPL-3.0

README

wbft

wbft is a consensus-only implementation of the WBFT protocol in Go, written from the WBFT specification. It plays the role that CometBFT (Tendermint) plays for Cosmos applications: it runs the consensus state machine, verifies headers and seals, keeps the validator set and epochs, signs safely across restarts, and reports what happened, while the application behind a narrow interface executes blocks and owns chain state.

The goals are:

  • Conformance. Every normative requirement of Part A of the specification is implemented by identifiable code (// Spec: WBFT-... comments) and checked by unit tests, conformance vectors or simulations.
  • Interoperability. In its default profile a wbft node keeps the wire format (istanbul/100, message RLP) and block format (Ethereum header plus WBFT extra data) of go-stablenet and can run in the same network as go-stablenet nodes.
  • Replayability. The consensus core is a pure state machine: no clocks, goroutines, randomness or I/O. Recorded inputs replay to the same outputs.
  • Observability. Consensus events, participation records and raw message journals are exposed for analysis instead of being discarded.

Block storage, synchronisation, devp2p, the EVM, governance and transaction rules are not part of wbft; applications provide them through the interfaces of package app, chain/validator/source, mempool and p2p/transport.

Nodes derive an independent effective proposer configuration at startup: an omitted policy selects RoundRobin (0), and explicit base or transition policies must select RoundRobin (0) or Sticky (1). The original application JSON and rule declarations remain unchanged. Production quorum and early round-change thresholds use integer arithmetic, including at large committee sizes; an empty committee retains a quorum of one. These defaults are reported as checked_proposer_policy and integer_quorum with source profile. types.ParseChainConfig, Config.CheckProposerPolicy, validator.QuorumSize and validator.InFPlusOneWindow retain their explicit reference semantics.

Status

Milestone W1 (pure modules) is complete: crypto/keccak, crypto/ecdsa, crypto/bls, types (heights and rounds as arbitrary-precision integers, chain configuration and config_at), codec/rlp and codec (extra data, headers, blocks, the four consensus messages, signing payloads and hashes), internal/refsort (the reference implementation's sort), chain/validator (quorum, proposer selection, epoch schedule, validator-set lookup), chain/epoch (next-epoch computation, candidate order, shuffle), chain/validator/source (authority snapshots and their cache) and chain/header (proposal construction, seal writing and merging, proposal, header, batch and light verification) pass the conformance vectors of their handlers.

Milestone W2 (consensus core, write-ahead log, private validator, in simulation) covers:

  • consensus: the pure state machine with the reference behaviour. One Step takes one input (start, stop, new head, request, message, replay, timer expiry) and returns the outputs to execute (messages to sign and send, relays, scheduled inputs, timer requests, build requests, commits, message outcomes, events). It reads the outside only through Env; Vars exposes the state variables and Snapshot the extra seals for proposal building.
  • consensus/inputlog: the encoding of core inputs, Env answers and step outputs shared by the write-ahead log and the message journal.
  • consensus/wal: a segmented append-only log with CRC-framed records, synced appends and repair of a damaged tail.
  • consensus/privval: the signer on a go-stablenet node key file (the BLS key is derived from it), per-kind sign rules that refuse a second value for a view across restarts, and the sign floor for a node that takes over a key without its sign record.
  • consensus/runner: the consensus goroutine, bounded per-peer receive queues with receive-check workers and a slot table, the timer scheduler with generations, signing and logging of own messages, the commit goroutine, and the replay of the write-ahead log at start.
  • p2p/transport: the transport interface, the deduplication caches with the gossip target choice, and the frame verdicts DecodeFrame, CheckOutbound and StoppedEngineAction for the application adapters.
  • observe/event and observe/journal: the event vocabulary with a JSON Lines writer, the schema of the event records (observe/event/schema/events-v1.json, JSON Schema 2020-12, which the simulator and the node tests check every record they write against), and the message journal (a store of its own with pruning by height and size). cmd/wbft-journal reports on a journal directory (stat), checks it (verify), prunes it (prune) and exports it, also while a node writes it: as a bundle (export, a tar file of the segments that cover a range of heights with a manifest, which the analyzer and cmd/wbft-replay read) or as the R-01 frame dump that wbft-inspector reads (export --format r01).
  • observe/metrics: the metric registry (counters, gauges, histograms with labels) and its Prometheus text export, on the standard library. A node derives the metrics its event records carry (height and round, round changes, timer expiries, messages by code and outcome, dropped inbound messages, finalize durations, evidence), reads the pool sizes, the receive-queue bytes and the core's backlog (wbft_backlog_messages, from the snapshot of the last step) when gathered (GaugeFunc), counts sends left out by the recent cache (wbft_send_suppressed_total) and times the write-ahead log's fsyncs by method (wbft_wal_fsync_seconds, through wal.Options.Now and Synced), privval refusals by message code (runner.Deps.Refused) and parent snapshots missing from the authority cache by context (preprepare, header_only, build). A head announced before its snapshot and a PRE-PREPARE whose parent snapshot is missing are also HEALTH records (snapshot_missing_at_head, snapshot_missing_at_preprepare). Each new head's header gives the per-validator series wbft_validator_seals_total{validator, type} and wbft_validator_missed_proposals_total{validator}, always under validator="total" and per address for a set of at most 64; the event records give wbft_validator_message_delay_seconds{validator, code}, each source's first message of a code in a view less the round's start (off above 64 validators); a validator absent from the sets for more than two epochs loses its series (DeleteWhere); Node.Metrics() returns the registry for the application to serve or read.
  • observe/logcat: log levels by module (observe.md 7.1). A node logs each part under a fixed module name (node, consensus.round, mempool, ...) at the level of Config.Log (a base level and per-module exceptions, off to trace); an unknown module refuses the start. A disabled call makes no record. The runner writes one line of the fixed message catalog (logcat.EventEntry) per event record, under consensus.round (round progress, timers, decisions; info for engine start/stop and finalized blocks) or consensus.msg (messages, outcomes, evidence); log settings do not change a run (TestLogLevelsDoNotChangeRun). The settings in force go to a LOG_CONFIG event and a log line at start and on Node.SetLogLevels, with the application's settings that name no module (Config.LogUnmapped, field unmapped); wbft_logLevels reads them, and admin_wbftSetLogLevels (Node.AdminAPIs, for IPC or an authenticated endpoint only) changes them. logcat.BuildProfile (and cmd/wbft-logprofile) gives the inspector's log profile: which line (message, level, module) is which event kind, for logs received without the event stream; its ID, wbft@<digest>, is wbft_nodeInfo.logProfile and NODE_START.log_profile.
  • conformance/stepdriver: the driver of the steps vectors, which a transport adapter can reuse with its own frame stage, and RunTrace, which replays a message journal through the core (cmd/wbft-replay).
  • conformance/sim: a deterministic multi-node simulator with a fake application and a fake transport, crash and disk faults, and a scenario bundle.

Nodes run the reference behaviour with two restart-safety rules: the write-ahead log replay and the sign rules of the private validator (with the core's guard against a second round-0 proposal, and the core's mark on a ROUND-CHANGE whose prepared pair the bad-block rule released, which lets the private validator sign it). Conformance vectors run with every optional behaviour off.

Milestone W3a (node and application interface) is in progress:

  • app: the application boundary: the Application interface with its optional extensions, the Consensus service interface, the request and response types and the error values.
  • node: the node assembly. Start reads the application's Info, parses and checks the chain configuration, opens the write-ahead log and the sign state, sets the sign floor of a taken-over key (TakeoverGuard; without it a floor no signature followed is cleared, and NODE_START.sign_floor reports set, kept or cleared), runs the start-up handshake with the application head (finalizing a decided block again when needed), loads the authority snapshot of the head and starts the core at head + 1 with the restart-safety rules. A validator keeps a message journal in <data dir>/journal unless Config.Journal.Disabled is set; cmd/wbft-replay replays it. The node implements the Consensus service (proposal fields, epoch information, header verification, head and synchronisation notifications) on top of the header and epoch rules.
  • mempool: the transaction pool. Validity is decided by the application's admission hook; the pool keeps per-sender nonce lists (executable and waiting), de-duplicates, bounds its size, rechecks after every new head before later admissions, and hands out proposals through an iterator that the builder reports to (skip a transaction or drop a sender). Ordering is a plugin; the built-in fifo orders senders by arrival and each sender by nonce. The node starts the pool when the application gives an admission hook and refuses an unknown ordering name.
  • rpc: the read-only wbft namespace (wbft_nodeInfo, wbft_consensusState, wbft_peers) over a backend the node implements, and a JSON-RPC over HTTP handler for applications without an RPC server.
  • examples/kvstore: an example application, a replicated key-value store. It implements the application interface (building proposals from the pool, executing them in proposal verification and at finalization, storing blocks, the authority source), announces blocks and lets a node that fell behind fetch them from its peers. wbft-kvstore init writes a local network and wbft-kvstore start runs one node with an HTTP API; make devnet (scripts/kvstore-devnet.sh) runs four processes, submits transactions, stops and restarts a node and checks that it catches up. Transactions are not signed: it is a development tool.
  • p2p/devnet: a TCP transport for development networks and tests, with a consensus channel (through the frame stage) and an application channel. Peers are identified by the address they claim; it is not for production networks.

Layout

Directories group packages by layer. Lower layers never import higher ones, and the lint rules below enforce the direction.

Layer Directory Packages Responsibility
Primitives types, crypto, codec types, crypto/keccak, crypto/ecdsa, crypto/bls, codec, codec/rlp Basic types and chain configuration, hashing, secp256k1 and BLS12-381, encoding, signing payloads and hashes
Chain rules chain chain/validator, chain/validator/source, chain/epoch, chain/header Quorums, validator sets and proposers, authority source interface, epoch computation, header and proposal rules
Consensus consensus consensus, consensus/inputlog, consensus/wal, consensus/privval, consensus/runner The pure state machine, input encoding for WAL and journal, write-ahead log, private validator, the runtime around the core
Network p2p p2p/transport, p2p/devnet Transport interface, deduplication and frame verdicts for the application adapters; a development transport
Observation observe observe, observe/event, observe/journal, observe/metrics, observe/logcat, observe/participation Event vocabulary, message journal, metrics, logging, participation records
Node node, app, mempool, rpc, storage node, app, mempool, rpc, storage/kv Node assembly, application boundary, transaction pool, RPC, key-value store
Examples examples examples/kvstore, examples/kvstore/cmd/wbft-kvstore An example application and its command for local networks
Conformance conformance conformance/stepdriver, conformance/sim The step driver for vectors and traces, the deterministic simulator
Commands cmd cmd/wbft-vector-adapter, cmd/wbft-replay, cmd/wbft-journal, cmd/wbft-logprofile Conformance vector adapter, journal replay helper, journal inspection and pruning, inspector log profile

internal holds helpers that are not part of the API: internal/refsort (the reference-compatible sort), internal/fsys (the file system interface with an in-memory implementation), internal/faultpoint (fault injection under the wbft_faults build tag), internal/snetpartb (the StableNet execution-side header rules used by the vector adapter and headerscan) and internal/trace (traceability data).

Developer tools live in the separate module tools/ so that their dependencies never enter the wbft module graph:

Path Purpose
tools/lint/coredet go/analysis analyzer for the determinism rules that linters cannot express
tools/lint/heightlow Analyzer for the 64-bit truncation sites of heights and rounds: every use of RefLow64, RefLowInt64 or RefLow32 carries //wbft:low64 HH-nn
tools/tracegen Builds the requirement traceability matrix
tools/headerscan Verifies the headers of a running network over JSON-RPC
tools/chainfetch Downloads raw blocks and receipts of block ranges over JSON-RPC, for replay and development
internal/trace/owners.yaml Owner table read by tracegen: requirement ID to package and symbols
internal/trace/wbft-spec.ref Commit of wbft-spec that CI checks out
internal/trace/baseline.tsv Committed baseline of the matrix (requirement IDs and vector handlers) that CI compares against

Dependencies

wbft may depend only on the Go standard library and these modules; CI rejects anything else (.golangci.yml, scripts/check-deps.sh):

Module Used by
github.com/ethereum/go-ethereum v1.17.x (rlp, crypto, common only) types, codec/rlp, codec, crypto/keccak, crypto/ecdsa
github.com/supranational/blst crypto/bls
github.com/holiman/uint256 chain/validator/source, chain/header, mempool, the simulator's fake application
github.com/cockroachdb/pebble storage/kv
github.com/prometheus/client_golang observe/metrics/prom (reserved; observe/metrics writes the text format itself)
github.com/BurntSushi/toml node

golang.org/x/sys is linked as a dependency of the go-ethereum crypto package only; no wbft package may import it. The go-ethereum packages core/*, consensus/*, eth/* and p2p are forbidden. wbft is built and tested against upstream go-ethereum v1.17.x only (.github/workflows/ci.yml).

Build, test and lint

Requirements: Go 1.26.8 or newer (both modules), a C toolchain (cgo is required: BLS12-381 through blst and the secp256k1 of go-ethereum; a build without cgo stops with a compile error), and golangci-lint v2.

make build           # go build in both modules
make test            # go vet and go test in both modules
make lint            # golangci-lint, coredet, heightlow and the dependency allow-list
make lint-negative   # proves the lint rules reject violating code
make sim             # simulation tests (WBFT_SIM_SEEDS seeds per scenario)
make sim-full        # the scenario bundle with 10000 seeds per scenario
make faults          # crashes at every fault point (build tag wbft_faults)

The same steps without make:

go build ./... && go vet ./... && go test ./...
go -C tools test ./...
golangci-lint run ./...
go -C tools build -o ../bin/coredet ./lint/coredet/cmd/coredet && bin/coredet ./...
go -C tools build -o ../bin/heightlow ./lint/heightlow/cmd/heightlow && \
  bin/heightlow -require "$(tr -d ' ' < scripts/heightlow-rows.txt | paste -sd, -)" ./...
scripts/check-deps.sh
Lint rules
  • Boundaries (depguard): the module allow-list above; chain/header does not import consensus or node; the pure modules do not import consensus, node, app, p2p, storage or the observers other than observe/event; consensus does not import app, p2p/transport, consensus/privval or consensus/wal; only types and chain/epoch use internal/refsort.
  • Determinism of the core (consensus, consensus/inputlog) and the pure modules (crypto/*, internal/refsort, types, codec, codec/rlp, chain/validator, chain/epoch, chain/header), test files excepted:
    • depguard forbids math/rand, crypto/rand, os, net, syscall and runtime (and sync in the core);
    • forbidigo forbids clock calls (time.Now, timers, time.Sleep, ...), unstable sorts (sort.Slice, sort.Sort, slices.SortFunc) and math.FMA;
    • coredet forbids go, select and channel operations (in pure modules except header.VerifyHeaders) and requires //wbft:unordered <reason> on every direct range over a map.
  • Heights and rounds: heights and rounds are arbitrary-precision integers; the reference implementation reads only their low 64 (or 32) bits at some places, and wbft does the same at those places. heightlow requires //wbft:low64 HH-nn on every use of a truncating accessor outside types, where HH-nn is a site label that groups the uses by the reference code they reproduce, and requires at least one use for every label listed in scripts/heightlow-rows.txt.
Reference sort

internal/refsort holds a copy of the reference implementation's sort so that candidate and transition orderings match it; see internal/refsort/testdata/gen to regenerate the fixture.

Vector adapter

cmd/wbft-vector-adapter runs the implementation under a conformance vector runner using the adapter protocol wbft-vector/1 of the specification (A-11 §3.4): one JSON object per line on standard input and output, protocol messages only on standard output, diagnostics on standard error.

make adapter
bin/wbft-vector-adapter -version
# Run the vectors of the specification with its reference runner:
python3 <wbft-spec>/spec/tools/vectorgen/check_adapter.py <wbft-spec>/spec/vectors -- bin/wbft-vector-adapter

In a conformance run all optional behaviour switches are off and hello.improvements is empty. The adapter announces in its hello the handlers it implements: crypto/*, encoding/*, validators/*, timers/*, state_machine/*, network/receive_outcome, header/* and chain/config_at. The steps handlers run the core through conformance/stepdriver without a frame stage, so network cases whose verdict belongs to the transport adapter (framing, the legacy code, size limits, empty payloads) are answered unsupported here and by the application's adapter there. Handlers of the execution layer are answered by the adapters of the application repositories. The header handlers decide the execution-side steps of header verification (uncle hash, gas limit, fork times, base fee) with a stand-in of the application hook for the StableNet presets (internal/snetpartb). The adapter exits with status 0 after bye, 1 when its input ends without bye, and 2 on a protocol error.

conformance/stepdriver can also run the steps cases the way a node runs them: the core with consensus.RestartSafety and own messages signed by a private validator (Options.Improvements, Options.PrivVal, Options.SignFloor). TestVectorsWithRestartSafety checks that every steps case then sends and records exactly the reference output, and TestVectorsWithSignFloor that a node which took over its key signs nothing at the first height.

scripts/cross-arch-vectors.sh runs the vectors whose results rest on binary64 arithmetic (validators/quorum, timers/round_timeout) and writes one line per case with the adapter's result. CI runs it on amd64 and arm64 (determinism-cross-arch) and fails when a case fails on either architecture or when the two result files differ.

scripts/cross-arch-vectors.sh <wbft-spec>/spec bin/wbft-vector-adapter vectors.txt

Configuration time units

The chain configuration's requestTimeoutSeconds, blockPeriodSeconds, maxRequestTimeoutSeconds and allowedFutureBlockTime are in whole seconds. For example, requestTimeoutSeconds: 2 sets a two-second base timeout. types.NewConfig and height transitions convert that base to the internal types.Params.RequestTimeoutMs value of 2000; they do not interpret the JSON value as milliseconds. Use 2, rather than 2000, for this example.

An absent or zero base request timeout does not set a value. A zero request timeout in a transition inherits the earlier value. A missing maximum inherits the earlier cap, while an explicit maxRequestTimeoutSeconds: 0 removes it. The kvstore example configures a two-second base timeout. Simulation timing overrides below also take seconds and retain their own positive-value checks. These unit descriptions do not add conversion range checks or change existing timeout arithmetic, genesis files or stored chain configurations.

Simulation and journal replay

conformance/sim runs N nodes on a manual clock with the real runner, private validator, write-ahead log and journal on an in-memory file system that keeps only synced data across a crash. The seed determines the run: two runs of one scenario write identical event files and journals. Every run checks agreement, that no honest node signs two values in one view, progress, the header rules of every stored block, and that a write-ahead log replay never differs from the original run. Fault points (build tag wbft_faults) crash a node between two durable steps.

Scheduled engine_stop and engine_start actions pause consensus on the same runner while the node's application, block synchronisation and transport remain live. The signer, WAL, journal, event writer and node clock survive the pause; engine start uses the application's current head and configured replay policy. Control actions wait in source order behind a blocked manual finalization, and a full node stop or crash discards pending engine controls. These actions model an observed engine lifecycle; they do not infer synchronisation decisions.

NodeSpec.Timing optionally overrides RequestTimeoutSeconds, BlockPeriodSeconds and MaxRequestTimeoutSeconds for one node, including its later restarts. Nil fields inherit normalized Scenario.Params; an explicit zero maximum removes the timeout cap. Explicit timeout and block period values must be positive. Supplied seconds must fit a time.Duration when added to the simulation horizon. The existing round timeout arithmetic is unchanged. Each node's runner, fake application, header verification and journal use the same resolved configuration. Header verification results are shared only among nodes with identical configurations. Inputs are copied at startup, so later changes to caller-owned timing pointers do not alter a run. Different block periods can cause nodes to reject each other's headers; these are reported as header violations rather than hidden by a shared cache.

Scenario.Params.RuleActivations preserves chain-wide consensus rule activations in the simulator's generated chain configuration. Values are exact non-negative decimal strings, including heights beyond 64 bits; missing rules are disabled. The official chain configuration parser refuses unsupported rule names. Nil or empty declarations retain the original default configuration and outputs. Node profiles and timing overrides do not change these activations. Every node's runner, fake application and header checks use the configured rules, and actual journal segments retain their activation heights and configuration digest across restarts. Declarations are copied at startup so caller-owned map changes cannot change an executing scenario.

Scenario.Params.AllowedFutureBlockTime optionally preserves the chain's static future-block allowance. Nil keeps the original default representation; a supplied value, including zero, is retained across timing overrides and restarts without narrowing the chain configuration's unsigned integer domain.

NodeSpec.Processing optionally declares service samples by receive-check or core-input phase, input kind and message code. An explicit worker count bounds receive checks independently of the single reserved core input. Samples use the node's seeded generator, retaining their order and multiplicity. Nil and all-zero profiles preserve the original execution and evidence bytes without extra random draws. Unlisted work classes keep zero cost. Lifecycle steps and WAL replay retain their existing synchronous execution.

The optional node.Config.NextHeight (or runner.Config.NextHeight) takes runner.NextHeightLimits with four explicit positive limits: total entries, owned raw bytes, entries per peer, and entries per recovered outer signer. Nil preserves the original receiver. The lane authenticates only the outer signature of exactly the next sequence, then verifies the entire message against the active validator set when that sequence becomes current. Raw-byte reservations survive queued work, authentication, WAL persistence and core consumption; they do not bound process heap, CPU time or WAL disk usage. Enabling the lane requires WAL replay and a compatible binary on later starts.

Journal segments declare this receive profile and its limits separately from consensus improvement flags. A deferred_message input retains inline message bytes and the first writer-run/capture pair, including after process restart and WAL replay. wbft-replay reports deferred_origin separately from functional replay: missing or pruned captures leave the inline input usable, without substituting a matching frame from a later run. Receipt times belong to the original writer's clock. Timed and scheduled replay require a verified source in the same writer run; cross-run clock comparison remains unavailable. sim.NodeSpec.NextHeight declares the same explicit limits and persists the receive profile in every incarnation. Nil preserves existing execution bytes and random draws. Enabled lanes require WAL replay and atomic receive/core processing; positive pre-dispatch service samples remain unsupported. JSON receive limits use decimal strings to retain the full unsigned integer domain.

The pre_dispatch_service/1 policy reserves an input, occupies its worker for the sampled time, then executes the existing operation atomically. Core state, environment lookups, timer arms and outputs are evaluated at dispatch after service; the service interval is not a measured core step. Applying a recorded entry-to-completion sample here is an explicit approximation, not reconstruction of internal CPU, signing, WAL or output milestones. The startup record retains the declaration with decimal-string nanoseconds. It does not certify that a sample, worker count or unlisted zero cost was measured on a physical node.

Application inputs, head notifications and timer callbacks can arrive while core work waits. Already queued expiries survive later cancellation. Ordered engine controls and a clean node stop wait behind reserved core work, including a restart requested before that work finishes. A crash invalidates callbacks of the old incarnation, and old engine receive checks cannot affect a new run. Work extending beyond the simulation horizon remains incomplete. The manual runner requires a caller-serialized completion scheduler; production runners reject these callbacks and retain their existing goroutine implementation.

The per-node application model keeps independent request scheduling by default. Setting NodeSpec.BuildQueuePolicy to "serial-latest" selects one worker and one latest pending request. BuildDoneResult must explicitly be "failed" or "submitted": it defines the fake executor's response to Done at the sampled assembly return. Done signals cancellation without freeing the worker, while Stop lets an active executor return before a clean restart. A crash drops that executor without inventing a completion. Each request retains its original run's event writer and clock.

NODE_START.application_model records the queue, completion and scheduler assumptions. The shared callback scheduler orders deadlines and equal-deadline insertion; worker polling then selects Done before a pending replacement. conformance/buildqueue exposes explicit choices for ambiguous selections. Callers must validate the selected executor and scheduling assumptions against execution evidence before fitting. Legacy miner queues require a separate model.

WBFT_SIM_SEEDS=100 go test -run TestBundle ./conformance/sim/
go test -tags wbft_faults -run TestCrashAtFaultPoints ./conformance/sim/
go test -run TestReplayDeterminism ./conformance/stepdriver/
# Replay one node journal of a simulator export:
bin/wbft-replay -journal <dir>/<node> -chain <dir>/chain.rlp
# Inspect, check and prune a node's journal (JSON output):
bin/wbft-journal stat --dir <data dir>/journal
bin/wbft-journal verify --dir <data dir>/journal
bin/wbft-journal prune --dir <data dir>/journal --keep-heights 100000
# Bundle of heights 100 to 200 (two warm-up heights before), and its replay
# when it starts at the start of a writer run:
bin/wbft-journal export --dir <data dir>/journal --out node.tar --from 100 --to 200
bin/wbft-replay -journal node.tar -chain <chain.rlp>
# Explicit approximation when the bundle omits the recorded engine start:
# bin/wbft-replay -journal node.tar -chain <chain.rlp> -warmup-from 98 -compare-from 100
# R-01 frame dump (frames-<run>.jsonl and payloads/) of heights 100 to 200:
bin/wbft-journal export --dir <data dir>/journal --out <dir> --format r01 --from 100 --to 200

Testnet header scan

tools/headerscan checks the headers of a running StableNet network with the header rules of wbft. It fetches headers over JSON-RPC, one request at a time and at most five per second, and verifies every header of the scanned ranges with header.VerifyHeader (header-only mode with seal checks; the steps that need the parent state are skipped) and with header.VerifyLight. It also checks that the block hash computed by wbft equals the hash the node reports. The ranges are blocks 0 to 100 and, for every fork block F after genesis, blocks F to F + 100 (-span).

make headerscan
bin/headerscan -rpc https://api.test.stablenet.network

The built-in preset is the StableNet testnet (chain ID 8283, Boho at block 14 408 500); -config and -forks select another network. Every rejected header is printed with its number, hash, failing step and error; the exit status is 1 when a header is rejected. CI does not run the scan, since it depends on a public endpoint.

Traceability matrix

make tracegen
bin/tracegen -spec <spec-dir> -vectors <spec-dir>/vectors \
    -owners internal/trace/owners.yaml -code . -prefix WBFT- -format md

tracegen reads the requirement IDs from the specification chapters (or a list file such as speclint output with -requirements), the meta.yaml of every vector case, the owner table and the // Spec: / // Covers: comments in the code, and writes one row per requirement: owner, symbols, code and test references, vector handlers and case count. References to unknown IDs are reported on standard error; -strict turns them into a failure.

Coverage check

-check decides, for the chapters it names, whether every requirement that owners.yaml gives to a wbft package (no class) is implemented and has evidence. A requirement passes when

  • every package in its owner field exists in the module,
  • its symbols field is not empty and every symbol is declared in the module (pkg.Name, pkg.Type.Method or pkg.Type.Field, where pkg is a package name; an unqualified name is looked up in the owner packages),
  • a // Spec: ID comment sits in a non-test file of one of its owner packages, and
  • a vector handler that cites it is implemented by the adapter (its cases then run in the conformance step, which fails on any failed case), or, when no implemented handler cites it, a test file carries a // Covers: ID comment next to the test that checks it.
make adapter tracegen
bin/tracegen -spec <wbft-spec>/spec -vectors <wbft-spec>/spec/vectors \
    -owners internal/trace/owners.yaml -code . -prefix WBFT- -o /dev/null \
    -check A-01,A-02,A-03,A-04,A-08 -adapter bin/wbft-vector-adapter

Every gap is printed with its kind (owner, symbols, code, evidence) and the exit status is 1 when there is one. CI runs the check for chapters A-01, A-02, A-03, A-04 and A-08 after the conformance vectors.

Baseline

internal/trace/baseline.tsv records two columns of the matrix: the requirement IDs of the public specification and, for each, the sorted vector handlers that cite it. The traceability matrix step of CI (enabled by the repository variable WBFT_SPEC_REPOSITORY) runs tracegen with -baseline internal/trace/baseline.tsv and fails when a requirement was added or removed or when the handler column of a requirement changed. The differences are printed one per line (+ new requirement, - removed requirement, ~ changed handlers with the added and removed handlers).

CI checks out the specification at the commit recorded in internal/trace/wbft-spec.ref, so a change merged into wbft-spec does not affect wbft until this file is updated. To move to a newer specification revision, update wbft-spec.ref, regenerate the baseline against that revision and commit both in one pull request:

make tracegen
bin/tracegen -spec <wbft-spec>/spec -vectors <wbft-spec>/spec/vectors \
    -owners internal/trace/owners.yaml -code . -prefix WBFT- -o /dev/null \
    -write-baseline internal/trace/baseline.tsv
git diff internal/trace/baseline.tsv

Review the diff of baseline.tsv in the pull request: every added or removed ID and every added or removed handler should be explained by the specification or vector change it follows. A removed handler or a requirement that lost all its vectors is a coverage loss and needs a reason.

License

wbft is licensed under the GNU Lesser General Public License, version 3 or (at your option) any later version (SPDX: LGPL-3.0-or-later, LICENSE). The LGPL supplements the GNU General Public License v3.0 (COPYING).

Chain rules

Chain-level rules come only from the genesis chain configuration's wbftRules.consensusRules. The supported keys are rejectNumberJump, minimalSealerBitmap, stableCandidateSort, firstProposerDiligenceCap, committedEpochDiligence, replayPerBlockPolicy, epochInfoInProposal and blsRandao. Each key is disabled when missing or null; a non-negative JSON integer sets its activation height. Node profiles and local options do not override these values. Unknown consensus or feature rule keys refuse startup.

types.Config.Rules reads activation using full heights. Header rules select activation from parent.Number + 1, including during proposal and batch verification. NODE_START, configuration logs and wbft_nodeInfo expose the configured activation heights and the rules digest.

minimalSealerBitmap checks the representation of present aggregate seals. From its activation height, prepared and committed seal bitmaps must omit trailing zero bytes. Previous seals use the parent's own activation context, so they are checked starting with the next child of an activation block. These checks also apply when signature verification is omitted for a proposal. Optional absent seals stay absent; ordinary seal-presence and BLS checks remain separate. Wire decoding preserves the supplied representation. For a scheduled rule, deciding a non-minimal previous bitmap requires the grandparent header. Header verification consults the batch and stored history. A light caller supplies LightInputs.Grandparent; missing context remains undecided. Canonical previous bitmaps need no additional context.

committedEpochDiligence scores the fully committed window from the previous actual epoch boundary L through the parent of the current boundary e. Its length is e-L, including after a schedule transition. The current block's own round and carried signer entries enter the next window; prepared re-proposals therefore retain the same epoch information and signing digest.

Each historical block uses its own proposer policy and authority order. Selection starts from the parent's Coinbase, or the zero address for genesis. All attempts through the successor header's hash-bound PrevRound count, including full rotations; the historical header's own Round and current seals are node-local and do not affect accounting. For the final historical parent, the current candidate supplies this previous-round field. It is already part of the prepared proposal hash and stays fixed during re-proposal. The final-round proposer receives credit for carried previous signatures. Capacity uses the parent authority's size, and signing participation uses actual membership in that authority. Genesis and its child provide no previous certificate observations. A candidate without eligible signing observations keeps its prior score; a candidate absent from L starts at the default score. The score combines the observed signing and proposal fractions with its prior, weighted by eligible slots over ten times the window length. Arithmetic retains full precision and rejects invalid ranges rather than clamping.

This rule replaces the legacy score calculation when active. firstProposerDiligenceCap continues to affect the legacy calculation only; stableCandidateSort independently controls ranking in either mode. Header, EpochInfo, wire and signature formats do not change. The activation is bound to the actual parent's full height plus one.

replayPerBlockPolicy changes only the legacy epoch score replay. Each historical block uses the proposer policy configured at its own full height, and the first child of genesis starts from the zero proposer address. The existing bounded search for each recorded Coinbase and score weighting stay in place. Activation uses the actual hash-bound parent plus one before epoch classification; an active call must supply that exact height. committedEpochDiligence retains its separate window and accounting when both rules are enabled. Missing, null and future activations preserve legacy replay.

The digest is Keccak-256 of the canonical JSON object containing anzeon.wbft, transitions and every wbftRules group. Object keys are sorted, array order is preserved, missing selected sections are null, and numbers use an exact decimal coefficient without trailing zeroes plus a base-ten exponent (omitted when zero). No floating-point conversion is used; 100, 100.0 and 1e2 have the same digest representation. Activation values themselves must be integer tokens, not exponent notation.

Replay protocol hello includes the embedded WBFT module version. Simulator journal segments use the same version resolver. A locally replaced dependency without version metadata remains unknown, and a modified main module carries a -modified suffix. These labels do not prove a matching clean core revision.

Trace replay refuses journal gap records instead of claiming matches across lost inputs. Protocol errors include a bounded code: input_gap for missing segment/step continuity or explicit journal loss, and replay_error otherwise. Readable steps emitted before an error are only a prefix. Independent payload completeness checks are still required when step inputs contain inline bytes.

Received-frame replay references

Adapters implementing transport.IOObserver retain the observer clock's monotonic and wall-clock endpoints around transport I/O. Writes end at the underlying write return; reads end after consuming the payload and before validation or queue delivery. A read interval may include waiting for a frame. The existing callback timestamp remains unchanged. Optional journal intervals and R-01 io fields are absent when the adapter did not measure them. Wall-clock regressions are preserved. Interval overlap neither proves a clock fault nor establishes a corrected one-way latency; clock agreement and message pairing still need independent evidence. R-01 outgoing frames retain explicit write results when recorded, including successful writes.

Adapters that support transport.CaptureObserver reserve a run-local capture ID before Receiver.Offer and report it with the original received frame after the offer. transport.Inbound.Capture carries only journal provenance. The runner stores peer journal steps as captured_message references; the WAL continues to store the original inline input for recovery. Legacy adapters keep their existing inline journal steps without claiming a frame association.

The journal reader resolves references with a bounded, position-only index, including frames recorded after their steps. Source runs, peer mappings, frame stage delivery and message codes must agree. Missing, ambiguous or truncated captures are refused in normal closed replay. Older journal layouts still read; new capture metadata does not change wire or block encodings.

wbft-replay -omit-missing-captures (protocol omit_missing_captures: true) explicitly models intentional input loss. The missing received input is not fed to the core; its actual empty output list is compared at the original recorded position. Each omitted step is labelled and the protocol summary marks the run counterfactual. Invalid or ambiguous references are not treated as intentional omissions. App answers are never invented; an unrecorded application call reports env_exhausted.

Replay execution identity

Core-step events include engine_run alongside their step number. The writer run, engine generation and step identify the corresponding journal input, including when a restarted engine reuses step numbers. Older events may omit the engine generation and require an explicitly unambiguous association.

Trace replay loads each new writer's recorded configuration and requires its engine start before feeding its steps. A changed configuration within the same writer is refused. Recorded application answers are matched by call kind and argument key within their source step; repeated answers for the same key are consumed in recorded order and never reused. Missing answers remain failures. Protocol steps and structured step errors include the source writer_run, engine_run and step; callers do not need to parse diagnostic text.

Recorded application answer context

The default -answer-scope step preserves closed replay: application responses are consumed once by call kind and argument key from their original step. Explicit -answer-scope context permits a missing local answer to be found in other recorded steps of the same writer, engine, head number/hash and validator context. Local original responses take precedence. Cross-step lookup refuses conflicting original responses, including a conflict whose other response has already been consumed. Absent answers remain env_exhausted; conflicts are env_ambiguous and source/context changes are env_source_mismatch.

stepdriver.NewAnswerPool reads the retained source separately; RunTrace validates the recorded configuration and step response fingerprint. Each replay gets a new consumption session. Successful calls retain their original writer, engine, step and one-based call index. The pool is bounded to one million source records, 100000 steps, 100000 application answers and 16 MiB encoded responses. Journal sequence gaps, duplicate coordinates and unsupported response kinds are refused. Head lookups verify both the full number and hash, including fallbacks.

This option permits counterfactual application lookup; it does not reconstruct unrecorded application execution or the reactions of other nodes. CLI returns 3 and protocol results mark the mode counterfactual even if all outputs match. Hello advertises context_answers_v1; replay requests use answer_scope, steps carry context_answers and answer_sources, and completion echoes the scope. The validity boundary of an open-loop experiment must still be handled by its caller.

Bounded replay warmup

Closed trace replay still requires a recorded engine start. Callers can explicitly set TraceOptions.Warmup with a positive FromHeight and a later CompareFromHeight (at most 10000 heights apart) to process a retained prefix whose original start is absent. The first available step at FromHeight must have a verified recorded head and a valid validator set. The driver starts a fresh core there; its synthetic initial outputs are neither executed nor reported as an original step. Backlog, prior views and unknown timer generations are not reconstructed. Journal gaps, missing captures, discontinuous steps, missing application answers and unavailable prefix heads still fail closed.

Every subsequent result carries Approximate, and approximate steps before the comparison height also carry Warmup. Matching outputs alone do not establish original reproduction. An actual recorded start restores its original timer generations and clears the approximation marker. Available complete histories use the normal exact path even when the fallback is configured.

wbft-replay accepts -warmup-from N -compare-from M with -journal. It returns exit 3 when approximate results were emitted, separately from exact success (0), different outputs (1) and failed/unavailable replay (2). Zero replayed steps are not success. The wbft-replay/1 hello advertises bounded_warmup_v1; clients must check it before sending warmup_from_height and compare_from_height. Protocol steps expose approximate and warmup, and completion reports separate approximate_steps and warmup_steps totals. Warmup failures use warmup_unavailable with structured source coordinates when available.

External input schedule experiments

TraceOptions.Schedule and wbft-replay -input-schedule FILE apply one explicit drop, delay, or swap to a recorded external input identified by writer, engine and original step. Delay places that input after other_step; swap exchanges two external inputs only when recorded wire stamps prove that both were available before the earlier position. Inline transport timestamps with an unknown journal clock origin cannot prove that boundary. Internal inputs and lifecycle boundaries cannot be moved by these operations.

Source continuity is validated before altered execution. Original step and journal sequence coordinates remain distinct from execution order and slot coordinates. Source bytes and receipt timestamps are not rewritten. A drop compares an actual empty output list at its original position. Reserved self-deliveries, replays, requests and timer inputs must match outputs actually produced by prior replayed handlers; unavailable reservations are retained as empty positions with reservation_unavailable after the change. They are not implicitly executed. Unprocessed newly reserved events remain pending at the end of the finite input tape.

Experiments require explicit context application answers and a complete recorded start; missing application responses remain failures. Moved inputs read the recorded execution slot's head and actual validator context, rather than reusing an old source-step head. No signing, sending, application execution, timer-length change, or automatic reaction of other nodes is performed here. Changed timer policies and open-loop validity boundaries require the caller's recorded evidence and simulator scheduling integration. CLI returns 3 for a completed experiment even when every output matches. Protocol hello advertises input_schedule_v1, requests use input_schedule, steps retain scheduled_input source/slot fields and suppression_reason, and completion echoes the experiment with counterfactual: true. Scheduled source buffering is limited to one million records, 100000 steps and 256 MiB encoded step inputs and responses.

Shared replay clock and timer reservations

conformance/manualclock is the callback scheduler used by the deterministic simulator. It orders equal deadlines by insertion order, preserves cancellation identity, and delivers callbacks at their deadlines when advanced. It provides the runner clock interface; time reversal, deadline overflow and reentrant advancement are refused. runner.TimerQueue exposes the existing runner timer implementation, including full timeout context, generation replacement, cancellation before expiry, and preservation of already-queued expiries.

New live runner journal steps include a measured StepRec.Entry monotonic/wall clock pair. StepRec.Mono remains the completion stamp. Entry time zero is a known measurement; absence in older records and reconstructed WAL steps is unknown. A trailing optional RLP entry field preserves the old encoding when absent. Current readers accept both shapes; readers built before the extension must be upgraded before consuming new steps with entry metadata.

These are the timing and scheduling prerequisites for timer-policy replay. Recorded-input schedule experiments still use their finite recorded timer tape; they do not yet regenerate timer inputs under changed duration parameters.

Measured timer operation journal

New node and simulator journals declare Core.TimerTrace = 1 in a trailing optional segment field and include timer records (kind 8). Readers must be upgraded before reading these journals. The absent capability preserves the legacy segment encoding and does not enable timer recording. Custom writers opt in through TimerTraceEnabled.

The runner measures scheduler mutations under the queue mutex. A sequence orders equal monotonic timestamps. Records include arm/replacement, actual cancellation, the cancellation request even when every timer has expired, callback queue insertion, dequeue and lifecycle clear. Each arm keeps its writer/engine/step/output cause, entry-based deadline and full encoded timeout context. Output indices are one-based; zero identifies lifecycle, dequeue or WAL restoration rather than an invented core output. Queued callbacks retain the arm's original cause and never inherit the currently running step.

Operation timestamps are distinct from step entry and completion. In particular, a callback can queue during processing before a later cancellation, and that expiry remains available for dequeue. Sequence and full-context records allow this ordering to be checked without choosing a guessed cancellation instant. Timer input bytes count toward journal queue limits; loss remains explicit as gap records. Segment rotation and journal bundles preserve the records and capability. These observations are prerequisites for timer-policy execution, not a claim that every journal can already be used for what-if replay.

Timer policy experiments

wbft-replay -journal DIR -chain FILE -answer-scope context -timer-policy POLICY regenerates timeout inputs from actual ArmTimer/CancelTimers outputs through runner.TimerQueue. Policy JSON uses decimal string seconds, for example {"request_timeout_seconds":"2","max_request_timeout_seconds":"30"}. Unknown fields, duplicates, numeric JSON values, zero base timeout and duration overflow are refused. The original configuration and height transitions remain intact apart from these explicit parameters.

A complete source with measured step entry timing is required. The source must declare measured timer operation tracing; legacy operation clocks remain unavailable. The untouched baseline must reproduce outputs and timer contexts before any experiment step is emitted. Internal Schedule inputs are regenerated in runner FIFO order. Recorded signing results can be reused only for identical full Broadcast requests, writer/engine/head context and recorded signing order; nested signatures remain unchanged. Missing or conflicting signing results and new application questions stop execution. This does not execute a new signer or block builder.

The explicit execution model is recorded-external-slots/zero-generated-processing/recorded-signing-results/1. External processing slots and their recorded busy intervals are fixed. Generated handlers have zero modeled processing cost. Recorded wall anchors are used at source slots, with monotonic extrapolation between them. These are conditional open-loop assumptions, not proof of physical execution or another node's response. All experiment results remain counterfactual, including identical policy values. The finite source horizon is preserved and remaining outputs are not run beyond it.

Hello advertises timed_inputs_v1. Use timed_replay with timer_policy and answer_scope:"context"; partial ranges, missing-input omission, warmup, input schedule combinations and vars are refused. Replies use timed_step and timed_done. Execution/source coordinates, monotonic nanoseconds and policy seconds are decimal strings. Generated inputs have no source step; timer arm, causal execution parent and reused signing evidence are distinct fields. Errors retain execution and available source/causal coordinates. CLI exits 3 for a finished experiment and 2 for a refused or incomplete run. These experiments still need analyzer storage, UI and explicit open-loop validity boundaries.

The baseline uses measured arm/cancel operation times and observed callback queue times through the official scheduler. Complete operation sequence, source-output joins, FIFO dequeue and full timeout contexts are validated before emitting results. Missing or conflicting timing evidence is refused. Changed policy execution uses the deterministic clock and ordinary FIFO Pop. Reused application questions require complete output and writer/engine/head/validator context identity; original question source and output index remain explicit. Completion reports each writer horizon and pending internal, queued and armed timers. A recorded Stop applies its measured runtime cleanup; no future tail is manufactured.

Timed replay helpers declare timed_state_v1 and return execution_state from the core snapshot after each execution slot. Height and round are decimal strings while the core runs; a stopped core returns null for both. Suppressed original internal slots report the unchanged execution state, never the source input view. This state is counterfactual metadata, not a canonical chain fact.

Timed helpers declaring timed_actions_v1 return output_actions and recorded_output_actions independently. Every action retains its one-based output index and SHA256 of the complete encoded output. Network descriptors separate unsigned broadcast requests, recorded signing results/failures and actual relay bytes. Signing evidence points to both the original producer output and its later signing input. Application build/commit requests retain exact original-question locations and full decimal-string views; no payload, signature or block hash is exported. Eligible recipients describe validator membership intent, not observed transmission or delivery.

parent_output identifies the precise parent execution output that reserved a regenerated input, including a broadcast awaiting unrecorded signing results. Generated executions have no invented original output slot. A changed timer fingerprint does not imply changed network bytes. No action descriptor proves an arrival time or establishes other nodes' counterfactual reactions.

Application build observations

BUILD_HANDOVER identifies each request actually passed to the application by build_id, writer run, full height and round, parent hash and nanosecond wait. The ID counter is shared across engine restarts of one node. The optional app.BuildRequest.Observation records BUILD_STARTED after the builder's wait and BUILD_FINISHED for submission, replacement, cancellation, unavailable inputs, a changed head, or failure. Request copies share a single observer.

Assembly intervals use actual start/finish boundaries; they do not subtract an assumed wait from a nearby proposal. submitted means the application called SubmitProposal, not that the core accepted the block. Applications without observer support and missing/dropped endpoints remain incomplete evidence. The existing BUILD_REQUEST and PROPOSAL_SUBMITTED records are preserved.

Build callbacks retain the writer and clock of their handover, including when another node run starts before a late completion. Closed original writers leave missing evidence rather than moving records into a later run.

RELAY_TARGETS records the actual local dedup operation in its engine/step and output position: attached-validator candidates, selected send targets and peers excluded by their recent cache. It includes empty selections and explicitly unknown validator context. These are local target-selection observations; selected targets do not prove successful delivery or receipt.

Cache operation sources and queued attempts

New node journals declare Core.CacheTrace = 1 and record initially empty cache instances plus every inbound or gossip mutation under the actual Dedup mutex. Each instance has its own operation sequence. Records retain code, key, payload hash, pre-operation hits, actual candidates, targets and suppressed peers. Cache rules, capacities, payloads, signatures, consensus options and WAL behavior are unchanged. Declaration clocks are unavailable; operation clocks are measured at the actual mutation.

transport.AttemptSender and transport.AttemptObserver are optional queue metadata interfaces. A recording owner and enqueue generation keep a delayed write attached to its original cache decision. The opaque owner is compared only within the process, never serialized as a pointer or nonce. Observer replacement or a legacy adapter leaves identity explicitly unavailable. An attempt sequence identifies a call; it does not replace cache mutex order, transport completion time or peer delivery evidence.

Both development and application adapters must preserve this metadata in their queues. Development streams register their peer identity once per observer lifetime before the first observed frame, including observers installed after a connection already exists. Stream registration is shared by read/write callbacks and retained separately for each actual connection.

R01 exports retain complete cache context outside frame height filters. cache_trace closes available instance/operation counts and marks incomplete snapshots; cache_instance declares the empty state and real capacities; cache_operation preserves the mutation and its original journal coordinates. When scheduler and cache capabilities share a segment record, one scheduler_trace row contains both declarations with the original raw sequence. Outgoing send_attempt and suppression cache_decision metadata point to their exact decision sequence, segment and offset. Missing context stays unavailable; a successful write of a suppressed decision is retained for independent failure checks rather than removed. Export refuses conflicting identities, invalid bindings and source changes between its two passes.

The RLP capability and attempt fields are optional tails. Absent capabilities preserve older layouts and R01 output. Readers must understand the new record kind and closed metadata before consuming new cache-enabled journals. Source coordinate authentication is a separate audit of retained raw bytes; these records alone do not establish remote delivery or complete network coverage.

Application sync control observations

New node journals declare Core.SyncTrace = 1 and record submitted sync states separately from their actual handling by the node. Request identities and processing identities are distinct: the existing latest-state queue can supersede requests or handle one request more than once. The first-success guard and the existing engine stop/start policy are unchanged.

Each processed record retains the selected state, the guard and running state before/after handling, the actual action, and whether its control call failed. Only an actual engine control call gets event writer cursors. These are next sequence numbers sampled immediately before and after that call: the raw event range is [event_before, event_after). An empty range is retained, including failed calls; timestamps do not establish these boundaries. A missing writer, sequence exhaustion, dropped records or missing context remains incomplete evidence. Observation uses the existing bounded asynchronous journal queue.

R01 exports retain sync_control rows outside frame height filters and keep their original journal sequence, segment and offset. sync_trace reports the available request/processing counts and source completeness. Capabilities from one segment share one declaration row with its original sequence. A processed row links to its exact submitted request only when the complete retained source establishes that identity and state; physical enqueue order need not match the logical request order. Export refuses ambiguous identities, unsupported trace versions and source changes between its two passes. Source completeness does not certify the sync policy or an external downloader result.

The sync capability is an optional trailing RLP field; readers of older records preserve their original bytes. Readers must support the additional record kind and export metadata before consuming sync-enabled journals. Consensus inputs, WAL records, application sync state messages and wire payloads are unchanged.

Epoch information before voting

consensusRules.epochInfoInProposal checks epoch information using candidates supplied by the parent authority snapshot. It requires committedEpochDiligence at the same or an earlier height so that a prepared proposal keeps its epoch information and digest across round changes. The execution layer must select parent-state candidates consistently for building, snapshots and execution checks; SDK applications require sourceRules.candidatesFromParentState at or before activation. Missing or null activations preserve the existing behavior.

Snapshots distinguish missing candidate input from a known empty list and bind it to the parent's hash and full height. Proposal verification rejects unavailable input and mismatched epoch information. Header-only synchronization checks known input but can defer absent parent state to the mandatory execution check; this is not complete state validation from headers alone. Light verification reports CANNOT_DECIDE for an activated epoch when only headers are available. Non-epoch headers cannot carry epoch information once the rule is active.

The deterministic simulator uses the same parent-state candidate schedule when this rule is active. Its LightChecks result retains CANNOT_DECIDE observations separately from successful state-bound header checks, including the checked block identity and reason.

Local import monitoring

node.Config.Improvements accepts explicit local behavior names. The default remains the existing restart-safety set. Unknown or repeated names are rejected; these settings cannot disable restart safety and do not change header validity, chain configuration or wire payloads. Enabled names are reported with source node in NODE_START and wbft_nodeInfo; journal metadata retains the exact mask.

commit_watchdog resets the round timer after a block is decided. While import is pending and the block is not marked bad, expiry renews the monitor without changing rounds. Successful import cancels it and waits for the new-head event. A bad block still permits round recovery, and the existing F+1 path remains available. A slow application can therefore keep this node at the decided view longer than the default behavior.

Normal progress is tested with all-default, all-enabled and mixed nodes. A separate deterministic case with every import taking longer than the round timeout records an existing progress boundary: the all-enabled configuration progresses, while the default and half-enabled configurations do not. That boundary is retained explicitly and is not a mixed-deployment liveness claim.

advance_on_commit_failure separately advances to the next round on a failed import of the current decided block. Results must match both its hash and its full height/round; only the first result is consumed. Results from another view, unbound historical results and queued superseded timer expiries cannot change the current view. These options are individually opt-in and do not declare that every behavior of an improved profile is implemented.

The runner appends the decided view to import-result input records only when one of these options is enabled. Older two-field input records retain their original encoding. Readers of new opted-in journals and WALs must understand the bound result format. Ordinary node startup still uses its durable commit handshake before starting or replaying the consensus engine.

Local height and backlog ordering

reset_height_state initializes every new height at round zero, including a catch-up observed while processing a queued round timeout or an F+1 round change. It clears the previous prepared certificate and round-change set, requests a round-zero proposal and suppresses the old height's target-round broadcast. The requested round remains in the round-entry audit record. Same-height round changes and already queued retry timers retain their existing behavior.

scan_ready_backlog scans past messages that are still future for the current phase, releasing ready messages in their existing priority order. Future entries keep their order and duplicate slots; membership checks and queue limits remain in effect. Both options are off by default and use the existing named node configuration, reporting and exact journal mask. They do not change header validity, chain configuration or message encodings.

cancel_stale_retries independently ignores retry timer expiries after cancellation, replacement or a change of the full height/round view. A queued expiry can therefore remain in the runner's recorded timer queue without causing a stale round-change broadcast or rearming a cancelled retry. Current active retries still send and rearm normally. This option is off by default; it uses the existing named settings and exact journal mask. Timer input encodings, durable WAL and header validity remain unchanged.

follow_higher_rounds applies the existing F+1 round-following rule whenever at least its threshold of distinct higher-round senders is available. This allows a later valid round-change message to advance a node when previously retained messages have already moved the count above that threshold. It keeps the existing F+1-before-proposal evaluation order and lowest-higher-round selection. The option is off by default; it can skip rounds sooner than the default behavior and does not change header validity or wire encodings.

bind_prepared_certificate independently keeps the prepared certificate with its locked block and round. It carries that certificate across same-height rounds and clears it with the pair on release or a new height. The original certificate lifetime remains available when the option is disabled.

drop_unbound_extra_seals suppresses relay of late votes when there is no current or prior proposal against which to check their seals. Late votes with a matching target retain their existing storage and relay behavior.

resume_round_proposals relays a round-change quorum even when the proposer is still waiting for its builder. A later proposal request repeats the same quorum and justification checks for that round, with a prepared block taking priority. It does not send twice after a successful proposal broadcast. These three options are independent, disabled by default, and preserve the existing header, message and WAL formats and mandatory restart safeguards.

refresh_round_certificates selects the highest prepared certificate from the currently stored round-change messages whenever a sender replaces its entry. It retains the existing certificate eligibility checks and uses sender address order to resolve equal prepared rounds. The default retains the original increasing-only cache and first-arrival selection for ties.

clear_current_extra_seals clears only the current height's extra seals when the application releases a bad prepared block. Surviving prior-height entries remain available; a prior entry already overwritten by a newer vote cannot be recovered. Ordinary height-transition cleanup keeps its existing behavior. Both options are independent and off by default, with the existing named configuration and journal mask and no header, message or WAL format change.

validate_round_proposals is an independent, default-off local option. Before reproposing a prepared block, it calls proposal validation after the existing quorum and justification checks. A normal future timestamp waits on a distinct local round_proposal timer; failure does not select a builder fallback. Local rejection records use local_reproposal, without claiming an inbound PRE-PREPARE or physical frame.

The option extends local timer/checkpoint records to a fourth generation. Default configurations retain the three reference timer generations and cancellation outputs. New readers can restore three-generation checkpoints; older readers do not support the additional local timer. Header and network message encodings are unchanged. These checks do not establish execution validity, physical provenance or a complete improved profile.

cap_initial_round_timeout is a default-off local option. When the base request timeout exceeds a positive cap and both duration products fit normally, the first round also uses the cap. The public reference timeout function remains unchanged; overflow in the millisecond-base and second-cap duration products keeps its reference path. Earlier configuration-unit conversion is outside this local policy. This changes local timing, not header or network message encoding, and does not establish every schedule or complete-profile acceptance. Existing optional-behavior observations retain their scope rather than becoming reference conformance passes.

Local future proposal timing

use_future_time_allowance and limit_future_proposal_wait are independent, default-off local settings. The first applies inherited nonzero transition allowances at the full height and retries a future proposal at its timestamp minus that allowance. Full and light header checks select this policy explicitly; a missing light-verifier clock remains missing. Ordinary seconds-to-nanoseconds timestamp and allowance products use the selected calculation. Other values retain the reference path for both the header check and retry target.

The second rejects an ordinary positive future wait longer than the current view's positive round timeout. It uses the same initial-cap selection as the round timer and compares timeout lengths, without estimating elapsed time. Equal or shorter waits retain their received or local candidate provenance. Local candidate checks run only when validate_round_proposals is enabled. An excessive wait records future_proposal_wait_exceeded with its closed origin and exact decimal duration metadata; it does not arm a shortened wait.

Disabled reference queries and arithmetic remain available. These settings do not change the header or network message encoding, reconstruct earlier configuration conversion, or establish every clock schedule or complete profile. The event schema adds the explicit wait-limit observation. Existing protected analysis servers are not upgraded by selecting a node setting.

Local round budgets and deadlines

require_initial_round_budget checks each effective configuration at height zero and every distinct transition height before node or simulation startup. For ordinary positive duration products, the selected round-zero timeout must exceed the block period. An enabled initial-round cap participates in this comparison. Equal-height transitions are checked after their final application, with full heights and existing inheritance rules. Other arithmetic retains its prior path. This necessary condition does not reserve application or network processing time. Direct core callers invoke CheckInitialRoundBudget at their configuration boundary.

preserve_round_deadline keeps an already-live round timer when accepting a proposal in that round. It still cancels retry and future-proposal timers; an absent round timer uses the existing reservation path. Entering another round continues to reserve a new timer. A proposal arriving near the original deadline therefore has less time to complete than under the reference reset behavior.

Both options are independent and default off. They change local startup or timing behavior without changing header, message or WAL encodings. Existing reference functions and disabled configurations retain their behavior. These checks do not establish every schedule or a complete improved profile.

Keeping peers during a local engine pause

keep_stopped_peers is a default-off local setting. Consensus frames that arrive while the engine is paused receive DROP_SILENT without a transport disconnect request. They enter neither the receive queue nor the known cache. The existing synchronising drop path is retained. Normal engine restart restores the usual receive checks; dropped frames are not replayed on restart.

The setting is reported through the existing improvement selection and recorded core option mask. It changes no header, message, WAL or journal encoding. Reference stopped-engine observations keep their existing conformance meaning. Normal signature and Stop/Start controls do not establish every remote lifecycle or complete-profile acceptance.

Versioned BLS reveals

wbftRules.consensusRules.blsRandao=A selects BLS reveals starting at the positive activation height A. It requires rejectNumberJump at or before A. The checked parent's full height plus one selects the format in proposal, import and light verification. Missing or null activation keeps ECDSA reveals; there is no signature-length fallback after activation. Genesis is unchanged.

The message is keccak256(RLP(["WBFT_RANDAO_BLS_V1", be_min(chainID), be_min(number)])), signed with the proposer's existing BLS key and ciphersuite. The verifier uses that height's registered validator key, including at epoch boundaries. A builder needs the optional header.BLSRandaoSigner extension; the built-in file signer implements the corresponding typed extension without changing vote-signing state or the legacy signer interface. The builder checks the produced signature against the registered key before returning a proposal. Reveals are compressed G2 signatures (96 bytes). A verifier needs the same activation configuration and validator history as the chain.

The parent-mix XOR hash-of-reveal calculation is unchanged. A unique reveal for the registered key and message does not eliminate block withholding or provide a general unbiased randomness beacon. This is a consensus format change with an additional BLS verification per checked header; it supplies no production activation default and never rewrites stored blocks.

bind_fresh_proposal_author is an independent, default-off local option. After normal proposer and round-change justification checks, a fresh proposal must have the authenticated PRE-PREPARE sender as its Coinbase. A prepared block may retain its original author across later rounds only with a distinct-member PREPARE quorum for the same full sequence, earlier prepared round and block hash. The local send and receive paths apply the same check. The block and its Coinbase are never rewritten.

Select the same option on validators that should enforce this acceptance policy. Existing header imports, standalone header checks and fee distribution remain unchanged; this option does not reconstruct historical proposal authors from headers. The node reports the selected option in its normal runtime and journal metadata, allowing replay to preserve the selected behavior.

Prior-head seal provenance

The optional bind_prior_head local improvement retains the adopted header's historical sealing authority separately from the successor validator set. It requires the matching WAL replay profile and is disabled by default. See prior-head seal provenance for recovery and reader scope.

Validator startup availability

The experimental, default-off defer_validator_start option waits for an available successor validator set before adopting a new height. Its optional durable profile has finite history limits and strict replay requirements. See validator startup availability for configuration, normal lifecycle behavior, budget exhaustion and reader limitations.

Verified previous seals

The default-off check_epoch_info_presence option checks whether epoch information is present exactly at the configured full-height epoch boundaries. It runs after header decoding and before seals, independently of the stronger parent-candidate chain rule. Header-only verification does not require a parent state snapshot for this presence check. Direct light callers select LightInputs.CheckEpochInfoPresence for the same early check; the default light procedure retains its existing order. This local selection changes no block or signature format.

The default-off verify_merged_prev_seals option checks both previous seal aggregates during proposal assembly, falling back only to a verified original parent aggregate. It can be selected with or without bind_prior_head. See verified previous seals for historical validator binding, failure behavior and recorded replay scope.

Runner terminal queue closure

Runner.Close stops admission through Submit, NewHead and PeerConnected, waits for its existing workers, and disposes of remaining application inputs. An application finalization already in progress keeps its ordinary return and notification path. A later Start is refused. A rejected manual Close with pending work leaves admission unchanged; ordinary Stop remains restartable.

Optional application queue sources retain version 1 for append, pop, coalesce and Stop clear. The terminal close operation alone uses version 2. Its independent check binds the last original core Stop and clear, refuses later queue mutation, and does not claim physical or complete schedule termination. Older readers refuse the new operation; absent Stop evidence stays unavailable.

Chain-bound ROUND-CHANGE signing

The common chain rule wbftRules.consensusRules.roundChangeDomain selects a chain-bound signing profile at a positive full activation height. Omission or null keeps the existing behavior. For example:

{"wbftRules":{"consensusRules":{"roundChangeDomain":3}}}

An activated ROUND-CHANGE signs the canonical RLP list ["wbft/round-change-domain", 1, chainID, genesisHash, legacySigningPayload]. The final element is a byte string containing the legacy signing payload. Each ROUND-CHANGE uses its own full sequence, including those nested inside a PRE-PREPARE. Other outer message signatures, BLS seals, RANDAO and wire layouts retain their existing interpretation. The domain does not distinguish forks sharing the same chain ID and genesis.

A Node derives this immutable profile only after checking its actual genesis and chain configuration. Low-level callers must supply an explicitly trusted genesis through types.DomainForConfig, then use codec.SigningPayloadWithDomain and consensus.RecoverWithDomain as appropriate. Recovery alone is not validator membership or independent chain authentication. Existing naked signing/recovery APIs remain legacy-only.

A configured domain requires its profile even before the activation height. It does not enable a local improvement flag or validator-start deferral. Selected live runners require strict functional WAL replay and matching core, signer and transport profiles. Domain-only history has finite default bounds of 64 MiB and 65,536 records; existing explicit validator-history overrides still require validator-start deferral. Budget exhaustion is not an unbounded persistence guarantee.

This profile requires absent or matching domain-bound signing state. A fresh signer persists format 2 before returning its first signature, including before activation. Existing format 1 state is refused even when logically empty; other chain/genesis/activation profiles are also refused. There is no implicit migration, reset or deletion. Use a fresh isolated data directory for an initial deployment; this change supplies no operating-network state migration procedure. Matching profiles support ordinary stop/reopen. Domain WAL history uses a separate format 6; existing format 5 and legacy history are not silently converted. Read-only profile checks precede selected startup writes.

Journal segments and core-step/signer sources retain explicit versioned domain declarations. Readers distinguish unsupported or missing context from malformed supported evidence. R01 cannot retain the domain and refuses selected input before legacy own-message indexing or output, including when height filters exclude it. Keep original journals or supported original-segment bundles; verify and stat check their supported recorded metadata, not external chain authority. Unknown future profiles produce structured unavailable results in the journal CLI. Independent physical/full-runtime source restoration remains unavailable for selected profiles; ordinary same-profile runtime reopen tests do not establish crash or power-loss recovery.

Directories

Path Synopsis
Package app defines the boundary between the consensus layer and the application: the Application interface and its optional extensions, the request and response types, and the Consensus service interface that the consensus layer offers to the application.
Package app defines the boundary between the consensus layer and the application: the Application interface and its optional extensions, the request and response types, and the Consensus service interface that the consensus layer offers to the application.
chain
header
Package header builds the consensus fields of proposal headers, writes and merges seals, and verifies proposals and headers in the fixed order of the specification.
Package header builds the consensus fields of proposal headers, writes and merges seals, and verifies proposals and headers in the fixed order of the specification.
validator
Package validator computes quorum sizes, validator sets, epoch boundaries and proposer selection, and looks up the validator set that governs a given height.
Package validator computes quorum sizes, validator sets, epoch boundaries and proposer selection, and looks up the validator set that governs a given height.
validator/source
Package source defines the authority snapshot type and the AuthoritySource interface through which the consensus layer reads validator candidates and related values.
Package source defines the authority snapshot type and the AuthoritySource interface through which the consensus layer reads validator candidates and related values.
window
Package window verifies and settles participation observations without changing candidate diligence, validator selection, fees or consensus rules.
Package window verifies and settles participation observations without changing candidate diligence, validator selection, fees or consensus rules.
cmd
wbft-journal command
Command wbft-journal inspects and maintains a message journal directory (observe/journal, observe.md 11.7) without stopping the node that writes it: a segment being written is read up to its last complete record.
Command wbft-journal inspects and maintains a message journal directory (observe/journal, observe.md 11.7) without stopping the node that writes it: a segment being written is read up to its last complete record.
wbft-logprofile command
Command wbft-logprofile writes the inspector's log profile of wbft (observe.md 7, 5): which log line is which event kind, for an inspector that receives a node's logs without its event stream.
Command wbft-logprofile writes the inspector's log profile of wbft (observe.md 7, 5): which log line is which event kind, for an inspector that receives a node's logs without its event stream.
wbft-replay command
Command wbft-replay replays a recorded message journal through the consensus core and reports, step by step, whether the replayed outputs match the recorded ones.
Command wbft-replay replays a recorded message journal through the consensus core and reports, step by step, whether the replayed outputs match the recorded ones.
wbft-vector-adapter command
Command wbft-vector-adapter runs the wbft consensus layer under a conformance vector runner.
Command wbft-vector-adapter runs the wbft consensus layer under a conformance vector runner.
Package codec encodes and decodes the WBFT extra data, headers, blocks and the four consensus messages.
Package codec encodes and decodes the WBFT extra data, headers, blocks and the four consensus messages.
rlp
Package rlp is a thin layer over the RLP package of go-ethereum.
Package rlp is a thin layer over the RLP package of go-ethereum.
window
Package window provides an explicit codec for participation observations.
Package window provides an explicit codec for participation observations.
conformance
buildqueue
Package buildqueue models a serial application worker with one latest pending request.
Package buildqueue models a serial application worker with one latest pending request.
eventqueue
Package eventqueue models typed, unbuffered event delivery.
Package eventqueue models typed, unbuffered event delivery.
manualclock
Package manualclock provides the deterministic event queue shared by WBFT simulation and recorded-input experiments.
Package manualclock provides the deterministic event queue shared by WBFT simulation and recorded-input experiments.
sim
Package sim is a deterministic multi-node simulator on a manual clock.
Package sim is a deterministic multi-node simulator on a manual clock.
stepdriver
Package stepdriver drives the pure consensus core through conformance vectors and retained message journals.
Package stepdriver drives the pure consensus core through conformance vectors and retained message journals.
taskqueue
Package taskqueue models an unbuffered sealing-task handoff and a serial caller of an asynchronous sealing engine.
Package taskqueue models an unbuffered sealing-task handoff and a serial caller of an asynchronous sealing engine.
writequeue
Package writequeue models normal single-stream writes on a manual clock.
Package writequeue models normal single-stream writes on a manual clock.
Package consensus is the pure consensus state machine.
Package consensus is the pure consensus state machine.
inputlog
Package inputlog encodes the inputs of the consensus core as they are stored in write-ahead log and message journal records, and decodes them again for recovery and trace replay.
Package inputlog encodes the inputs of the consensus core as they are stored in write-ahead log and message journal records, and decodes them again for recovery and trace replay.
privval
Package privval defines the Signer interface, a file-based signer, and the last-sign-state rules that stop a validator from signing conflicting messages, including after a restart.
Package privval defines the Signer interface, a file-based signer, and the last-sign-state rules that stop a validator from signing conflicting messages, including after a restart.
runner
Package runner drives the consensus core in a node.
Package runner drives the consensus core in a node.
wal
Package wal is an append-only log that knows nothing about consensus types.
Package wal is an append-only log that knows nothing about consensus types.
crypto
bls
Package bls derives BLS12-381 keys from node keys and signs, verifies and aggregates BLS signatures and public keys, including the decoding, subgroup and point-at-infinity checks.
Package bls derives BLS12-381 keys from node keys and signs, verifies and aggregates BLS signatures and public keys, including the decoding, subgroup and point-at-infinity checks.
ecdsa
Package ecdsa signs with secp256k1 keys, recovers signers with the acceptance rules of the cgo build of the reference implementation, and checks that a message was signed by a validator.
Package ecdsa signs with secp256k1 keys, recovers signers with the acceptance rules of the cgo build of the reference implementation, and checks that a message was signed by a validator.
keccak
Package keccak computes the legacy Keccak-256 hash used throughout the protocol.
Package keccak computes the legacy Keccak-256 hash used throughout the protocol.
examples
kvstore
Package kvstore is an example application for the wbft consensus layer: a replicated key-value store.
Package kvstore is an example application for the wbft consensus layer: a replicated key-value store.
kvstore/cmd/wbft-kvstore command
Command wbft-kvstore runs the kvstore example application on the wbft consensus layer, for local development networks.
Command wbft-kvstore runs the kvstore example application on the wbft consensus layer, for local development networks.
internal
buildmeta
Package buildmeta reports the WBFT module identity embedded in a binary.
Package buildmeta reports the WBFT module identity embedded in a binary.
faultpoint
Package faultpoint marks the places between two durable steps where a crash test may stop a node: before and after the sign state is written, before and after an own message reaches the write-ahead log, before and after it is sent, around the commit request and around the end-of-height record.
Package faultpoint marks the places between two durable steps where a crash test may stop a node: before and after the sign state is written, before and after an own message reaches the write-ahead log, before and after it is sent, around the commit request and around the end-of-height record.
fsys
Package fsys is the small file-system interface of the packages that keep durable state (consensus/wal, consensus/privval, observe/journal): open, write, sync, rename and directory sync.
Package fsys is the small file-system interface of the packages that keep durable state (consensus/wal, consensus/privval, observe/journal): open, write, sync, rename and directory sync.
refsort
Package refsort holds a copy of the pattern-defeating quicksort that the reference implementation uses.
Package refsort holds a copy of the pattern-defeating quicksort that the reference implementation uses.
snetpartb
Package snetpartb is a stand-in for the execution-side steps of header verification (B-03 §1 and §2: H3, H5 ..
Package snetpartb is a stand-in for the execution-side steps of header verification (B-03 §1 and §2: H3, H5 ..
Package mempool stores transactions, orders them through pluggable ordering policies (with a built-in first-in-first-out policy), selects transactions for proposals and decides when to gossip them.
Package mempool stores transactions, orders them through pluggable ordering policies (with a built-in first-in-first-out policy), selects transactions for proposals and decides when to gossip them.
Package node assembles a consensus node: configuration parsing and start-up checks, profiles, the start-up handshake with the application, the node life cycle, the implementation of the Consensus service and of the RPC backend.
Package node assembles a consensus node: configuration parsing and start-up checks, profiles, the start-up handshake with the application, the node life cycle, the implementation of the Consensus service and of the RPC backend.
Package observe collects what the consensus layer does: event sinks (JSON lines files, ring buffers, subscriptions), frame dumps, metric registration, stores for double-sign evidence and rejected inputs, and a channel for application events.
Package observe collects what the consensus layer does: event sinks (JSON lines files, ring buffers, subscriptions), frame dumps, metric registration, stores for double-sign evidence and rejected inputs, and a channel for application events.
event
Package event defines the vocabulary of consensus events and a writer of the JSON Lines event stream.
Package event defines the vocabulary of consensus events and a writer of the JSON Lines event stream.
event/schema
Package schema holds the schema of the event records, events-v1.json, and checks records against it (observe.md 2.1).
Package schema holds the schema of the event records, events-v1.json, and checks records against it (observe.md 2.1).
evidence
Package evidence keeps the double-signing evidence a node detects: two messages of one signer for one view and code with different digests (observe.md 6, R-08).
Package evidence keeps the double-signing evidence a node detects: two messages of one signer for one view and code with different digests (observe.md 6, R-08).
journal
Package journal records the raw consensus messages a node sends and receives, with sender, receiver and monotonic time, so that runs can be analysed and replayed later.
Package journal records the raw consensus messages a node sends and receives, with sender, receiver and monotonic time, so that runs can be analysed and replayed later.
logcat
Package logcat holds the catalog of log messages and per-module log levels: a table of module names, a slog handler per module, the level table, and a handler that maps log records of the host node to modules.
Package logcat holds the catalog of log messages and per-module log levels: a table of module names, a slog handler per module, the level table, and a handler that maps log records of the host node to modules.
metrics
Package metrics is the metric registry of a wbft node (observe.md 5): counters, gauges and histograms with labels, named by the Prometheus rules (wbft_<what>_<unit>), and their export in the Prometheus text exposition format.
Package metrics is the metric registry of a wbft node (observe.md 5): counters, gauges and histograms with labels, named by the Prometheus rules (wbft_<what>_<unit>), and their export in the Prometheus text exposition format.
participation
Package participation keeps and serves node-local records of validator participation, built from consensus events: who sent what and when, and whose signatures went into seals.
Package participation keeps and serves node-local records of validator participation, built from consensus events: who sent what and when, and whose signatures went into seals.
rejection
Package rejection keeps the blocks and proposals a node rejected, with the step that failed, its error class and the path the block came by (observe.md 6, R-09).
Package rejection keeps the blocks and proposals a node rejected, with the step that failed, its error class and the path the block came by (observe.md 6, R-09).
window
Package window persists explicit observation checkpoints.
Package window persists explicit observation checkpoints.
p2p
devnet
Package devnet is a small TCP transport for development networks and tests.
Package devnet is a small TCP transport for development networks and tests.
transport
Package transport defines the consensus message transport interface, the deduplication decorator with its caches, the choice of gossip targets and the frame verdict of the consensus protocol.
Package transport defines the consensus message transport interface, the deduplication decorator with its caches, the choice of gossip targets and the frame verdict of the consensus protocol.
Package rpc serves the wbft_* RPC namespace and the compatible istanbul_* namespace.
Package rpc serves the wbft_* RPC namespace and the compatible istanbul_* namespace.
storage
kv
Package kv defines a small key-value store interface (get, put, batch, iterate) and its default implementation on pebble.
Package kv defines a small key-value store interface (get, put, batch, iterate) and its default implementation on pebble.
tools module
Package types defines the basic values of the protocol: addresses, hashes, heights, rounds and views, the Ethereum header and block layout, the WBFT extra data and its parts (aggregated seals, sealer sets, epoch information, candidates), protocol constants, the chain configuration with its height- dependent lookup, and the read-only chain interface.
Package types defines the basic values of the protocol: addresses, hashes, heights, rounds and views, the Ethereum header and block layout, the WBFT extra data and its parts (aggregated seals, sealer sets, epoch information, candidates), protocol constants, the chain configuration with its height- dependent lookup, and the read-only chain interface.

Jump to

Keyboard shortcuts

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