hotstuff2

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Dec 10, 2025 License: Apache-2.0 Imports: 11 Imported by: 0

README

HotStuff-2

A production-ready Go implementation of the HotStuff-2 Byzantine Fault Tolerant consensus protocol.

Go Version Tests License

Features

  • Two-phase BFT consensus - Optimistic responsiveness with 2 network delays
  • Linear view-change - O(n) data per message during leader changes (O(n²) total messages, amortized O(n))
  • Formally verified - TLA+ model checked (12 safety invariants, 4 liveness properties)
  • Dual signature schemes - Ed25519 (O(n) multi-sig) and BLS12-381 (O(1) aggregate sig)
  • Configurable block time - Target block time from milliseconds to minutes
  • Adaptive pacemaker - Exponential backoff timeouts for liveness
  • Generic design - Supports custom hash types and block structures
  • Payload-agnostic - Opaque block payload supports any execution model (transactions, DAG refs, rollup batches)
  • Byzantine testing - Twins framework for fault injection scenarios

Quick Start

Try the Interactive Demo

See HotStuff-2 in action with real consensus running on a simulated network:

go run demo/main.go
# Open http://localhost:8080 in your browser

Features:

  • Real-time visualization of nodes and message passing
  • Fault injection (crash nodes, create network partitions)
  • Configurable block time (optimistic vs 1-second target)
  • Event log with CSV export
  • Support for 4-16 node networks (f=1 to f=5)

See demo/README.md for details.

Integration Example
package main

import (
    "time"
    
    "github.com/edgedlt/hotstuff2"
    "github.com/edgedlt/hotstuff2/timer"
    "go.uber.org/zap"
)

func main() {
    // Configure consensus
    cfg, _ := hotstuff2.NewConfig[MyHash](
        hotstuff2.WithMyIndex[MyHash](0),
        hotstuff2.WithValidators[MyHash](validators),
        hotstuff2.WithPrivateKey[MyHash](privateKey),
        hotstuff2.WithStorage[MyHash](storage),
        hotstuff2.WithNetwork[MyHash](network),
        hotstuff2.WithExecutor[MyHash](executor),
        hotstuff2.WithTimer[MyHash](timer.NewRealTimer()),
        hotstuff2.WithLogger[MyHash](zap.NewProduction()),
        
        // Optional: Configure target block time (default is optimistically responsive)
        hotstuff2.WithTargetBlockTime[MyHash](5 * time.Second),
    )

    // Create and start consensus
    hs, _ := hotstuff2.NewHotStuff2(cfg, func(block hotstuff2.Block[MyHash]) {
        log.Printf("Committed block %d", block.Height())
    })

    hs.Start()
    defer hs.Stop()
}

See the Integration Guide for complete documentation.

Installation

go get github.com/edgedlt/hotstuff2

Requirements: Go 1.23+

Architecture

hotstuff2/
├── hotstuff2.go          # Core consensus state machine
├── config.go             # Configuration with functional options
├── context.go            # Consensus state management
├── types.go              # Core interfaces (Hash, Block, Storage, Network)
├── vote.go               # Vote creation and verification
├── quorum_cert.go        # Quorum certificate aggregation
├── pacemaker.go          # View synchronization and timeouts
├── payload.go            # Message serialization
├── timer/                # Timer implementations (Real, Mock, Adaptive)
├── internal/crypto/      # Ed25519 (multi-sig) and BLS12-381 (aggregate sig)
├── twins/                # Byzantine fault injection testing
└── formal-models/tla/    # TLA+ formal specification

Interfaces

Implement these interfaces to integrate with your blockchain:

// Your hash type
type Hash interface {
    Bytes() []byte
    Equals(other Hash) bool
    String() string
}

// Your block type (payload is opaque - consensus doesn't interpret it)
type Block[H Hash] interface {
    Hash() H
    Height() uint32
    PrevHash() H
    Payload() []byte  // Opaque: transactions, DAG refs, rollup batches, etc.
    ProposerIndex() uint16
    Timestamp() uint64
    Bytes() []byte
}

// Persistent storage (safety-critical)
type Storage[H Hash] interface {
    GetBlock(hash H) (Block[H], error)
    PutBlock(block Block[H]) error
    GetLastBlock() (Block[H], error)
    GetQC(nodeHash H) (QuorumCertificate[H], error)
    PutQC(qc QuorumCertificate[H]) error
    GetHighestLockedQC() (QuorumCertificate[H], error)
    PutHighestLockedQC(qc QuorumCertificate[H]) error
    GetView() (uint32, error)
    PutView(view uint32) error
    Close() error
}

// Network message delivery
type Network[H Hash] interface {
    Broadcast(payload ConsensusPayload[H])
    SendTo(validatorIndex uint16, payload ConsensusPayload[H])
    Receive() <-chan ConsensusPayload[H]
    Close() error
}

// Block execution
type Executor[H Hash] interface {
    Execute(block Block[H]) (stateHash H, err error)
    Verify(block Block[H]) error
    GetStateHash() H
    CreateBlock(height uint32, prevHash H, proposerIndex uint16) (Block[H], error)
}

Protocol Overview

HotStuff-2 achieves consensus in two phases:

  1. Propose: Leader broadcasts block with justification QC
  2. Vote: Replicas vote if proposal satisfies SafeNode rule
  3. QC Formation: Leader aggregates 2f+1 votes into QC
  4. Commit: Two consecutive QCs commit the first block (two-chain rule)

Safety: Replicas only vote for proposals that extend their locked QC or have a higher justification QC.

Liveness: Adaptive timeouts with exponential backoff ensure progress under partial synchrony.

For details, see the Protocol Guide.

Scalability

HotStuff-2's efficient view-change protocol enables scaling to hundreds of validators. During normal operation, communication is O(n) messages per view. View changes require O(n²) messages but occur rarely, keeping amortized complexity low. This makes HotStuff-2 suitable for both consortium networks and public proof-of-stake chains.

Validators Max Faults Quorum Use Case
4 1 3 Development, small teams
21 7 15 Consortium networks
100 33 67 Mid-size PoS chains
200+ 66+ 134+ Large validator sets

Byzantine fault tolerance: f < n/3 (tolerates up to f Byzantine validators)

Formal Verification

The protocol is formally verified using TLA+ model checking:

Safety Invariants (12 properties verified):

  • Agreement: No conflicting commits at same height
  • Validity: Only proposed blocks can be committed
  • No double voting: At most one vote per view per replica
  • QC integrity: Quorum certificates require 2f+1 distinct signers

Liveness Properties (4 properties verified under fairness):

  • Eventually some block commits
  • View synchronization achieved
  • Progress guaranteed under partial synchrony

Verification Results:

  • 2.6M states explored
  • 790K distinct states
  • All properties verified

See formal-models/tla/ for the TLA+ specification.

Testing

# Run all tests
go test ./...

# Run with race detection
go test -race ./...

# Run benchmarks
go test -bench=. -benchmem

# Run specific benchmark suite
./bench.sh quick
Test Categories
  • Unit tests: Individual component testing
  • Integration tests: Multi-node consensus scenarios
  • Twins tests: Byzantine fault injection (100+ scenarios)
  • Benchmarks: Performance validation

Performance

Benchmarked on AMD Ryzen 9 5900X, Go 1.24:

Cryptographic Operations
Operation Ed25519 BLS12-381 Notes
Sign 50.2K ops/sec (20μs) 7.3K ops/sec (137μs) Ed25519 ~7x faster
Verify 21.4K ops/sec (47μs) 914 ops/sec (1.1ms) Ed25519 ~23x faster
Aggregate N/A 190.7K ops/sec (5μs) BLS-only, O(1) signatures
QC Operations
Operation 4 nodes 7 nodes 10 nodes 22 nodes
QC Formation 506ns 682ns 864ns 2.5μs
QC Validation 47μs 47μs 47μs 47μs
Signature Scheme Selection
Validators Recommended QC Size Rationale
4-22 Ed25519 O(n) Faster verification
25-100 Either - Consider bandwidth needs
100+ BLS O(1) 75% bandwidth savings

Run benchmarks:

./bench.sh quick   # Quick benchmarks
./bench.sh full    # Full suite with HTML report

See bench/README.md for benchmark documentation.

Documentation

Document Description
Protocol Guide Algorithm explanation (quick overview + deep dive)
Integration Guide How to integrate HotStuff-2 into your protocol
Interactive Demo Web-based demo with network simulation and fault injection
TLA+ Specification Formal verification details
Implementation Mapping TLA+ to Go code mapping
Twins Testing Byzantine fault injection framework
Benchmarks Performance testing guide

Use Cases

HotStuff-2 is suitable for any blockchain requiring BFT consensus with fast finality:

Public Networks

  • Proof-of-stake L1 chains
  • Validator-based L2 networks
  • Cross-chain bridge committees

Consortium & Enterprise

  • Multi-organization networks
  • Financial settlement systems
  • Supply chain coordination

Why HotStuff-2?

  • Sub-second finality (no probabilistic confirmation)
  • Scales to hundreds of validators
  • Tolerates up to 1/3 Byzantine faults
  • Efficient view-change on leader failure

Contributing

Contributions welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass (go test ./...)
  5. Submit a pull request

References

Acknowledgments

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Documentation

Overview

Package hotstuff2 implements the HotStuff-2 consensus algorithm.

HotStuff-2 is a two-phase Byzantine Fault Tolerant (BFT) consensus protocol that achieves optimistic responsiveness and linear view-change complexity.

This implementation follows the architecture patterns from nspcc-dev/dbft for compatibility with NeoGo and uses generic types for flexibility.

Index

Constants

View Source
const (
	// CryptoSchemeEd25519 indicates Ed25519 multi-signature (O(n) size).
	CryptoSchemeEd25519 = "ed25519"

	// CryptoSchemeBLS indicates BLS12-381 aggregate signature (O(1) size).
	CryptoSchemeBLS = "bls"
)
View Source
const (
	// VoteTimestampWindow is the acceptable time window for vote timestamps.
	// Votes with timestamps outside this window are rejected (replay protection).
	VoteTimestampWindow = 60 * 1000 // 60 seconds in milliseconds
)

Variables

View Source
var (
	// ErrConfig indicates a configuration error that prevents startup.
	// These are hard errors that require fixing the configuration and restarting.
	// Examples: missing required fields, invalid validator index, insufficient validators.
	ErrConfig = errors.New("configuration error")

	// ErrInvalidMessage indicates a malformed or invalid message was received.
	// These are soft errors - the message should be dropped but consensus continues.
	// Examples: deserialization failure, message too short, unknown message type.
	ErrInvalidMessage = errors.New("invalid message")

	// ErrByzantine indicates potential Byzantine behavior from a peer.
	// Integrators may want to track these for peer scoring or slashing.
	// Examples: invalid signature, timestamp manipulation, duplicate votes.
	ErrByzantine = errors.New("byzantine behavior detected")

	// ErrInternal indicates an internal invariant violation.
	// These suggest bugs in the library or storage corruption.
	// Examples: QC validation failed on locally-created QC, missing expected block.
	ErrInternal = errors.New("internal error")
)

Error classes for consensus operations. These represent categories of errors that integrators can handle uniformly. Use errors.Is() to check error class, then inspect the error message for details.

Error Classification:

  • ErrConfig: Hard configuration errors - must fix and restart
  • ErrInvalidMessage: Malformed or invalid messages from peers - log and ignore
  • ErrByzantine: Potential Byzantine behavior detected - may warrant peer penalties
  • ErrInternal: Internal invariant violations - indicates bugs or corruption

Functions

This section is empty.

Types

type BLSSigner

type BLSSigner interface {
	// SignBLS signs a message using BLS12-381.
	SignBLS(message []byte) ([]byte, error)
}

BLSSigner is the interface for BLS signature creation. This is separate from PrivateKey to support BLS-specific signing.

type Block

type Block[H Hash] interface {
	// Hash returns the unique identifier for this block.
	// Also referred to as "nodeHash" in the HotStuff-2 paper.
	Hash() H

	// Height returns the block height (0 for genesis).
	// Also referred to as block number or sequence number.
	Height() uint32

	// PrevHash returns the hash of the parent block.
	// Must reference a valid block in the node tree.
	PrevHash() H

	// Payload returns the application-specific block content.
	// Consensus treats this as opaque bytes - interpretation is left
	// to the Executor implementation.
	//
	// Examples:
	//   - Serialized transaction list
	//   - DAG vertex references (Narwhal-style)
	//   - Rollup batch commitment
	//   - Empty (for empty blocks)
	Payload() []byte

	// ProposerIndex returns the validator index of the block proposer.
	ProposerIndex() uint16

	// Timestamp returns the block timestamp (milliseconds since epoch).
	Timestamp() uint64

	// Bytes returns the serialized form of the block.
	Bytes() []byte
}

Block represents a proposed block in the consensus protocol. Blocks form a tree structure via parent links.

The Block interface is intentionally minimal and payload-agnostic. Consensus treats block content as opaque bytes, allowing integration with various execution models:

  • Traditional transactions (serialize tx list into Payload)
  • DAG-based mempools like Narwhal (payload contains vertex references)
  • Rollup batches (payload contains batch commitments)
  • Any other application-specific data

type Config

type Config[H Hash] struct {
	// MyIndex is the validator index of this node.
	MyIndex uint16

	// Validators is the set of all validators in the network.
	Validators ValidatorSet

	// PrivateKey is the private key for signing messages.
	PrivateKey PrivateKey

	// Storage provides persistent storage for blocks and consensus state.
	Storage Storage[H]

	// Network provides message broadcasting and delivery.
	Network Network[H]

	// Executor handles block execution and validation.
	Executor Executor[H]

	// Timer provides timeout management for the pacemaker.
	Timer Timer

	// Pacemaker configures the timing behavior (timeouts, block time, backoff).
	// If nil, DefaultPacemakerConfig() is used.
	Pacemaker *PacemakerConfig

	// Logger for structured logging.
	Logger *zap.Logger

	// CryptoScheme specifies which signature scheme to use ("ed25519" or "bls").
	CryptoScheme string

	// EnableVerification enables runtime TLA+ model twins verification.
	EnableVerification bool
}

Config holds the configuration for a HotStuff2 consensus instance.

func NewConfig

func NewConfig[H Hash](opts ...ConfigOption[H]) (*Config[H], error)

NewConfig creates a new Config with the given options.

func (*Config[H]) IsLeader

func (c *Config[H]) IsLeader(view uint32) bool

IsLeader returns true if this node is the leader for the given view.

func (*Config[H]) Quorum

func (c *Config[H]) Quorum() int

Quorum returns the quorum size (2f+1).

type ConfigError

type ConfigError struct {
	Field   string
	Message string
}

ConfigError represents a configuration validation error.

func (*ConfigError) Error

func (e *ConfigError) Error() string

type ConfigOption

type ConfigOption[H Hash] func(*Config[H]) error

ConfigOption is a functional option for configuring HotStuff2.

func WithCryptoScheme

func WithCryptoScheme[H Hash](scheme string) ConfigOption[H]

WithCryptoScheme sets the signature scheme ("ed25519" or "bls").

func WithExecutor

func WithExecutor[H Hash](executor Executor[H]) ConfigOption[H]

WithExecutor sets the block executor.

func WithLogger

func WithLogger[H Hash](logger *zap.Logger) ConfigOption[H]

WithLogger sets the logger.

func WithMyIndex

func WithMyIndex[H Hash](index uint16) ConfigOption[H]

WithMyIndex sets the validator index for this node.

func WithNetwork

func WithNetwork[H Hash](network Network[H]) ConfigOption[H]

WithNetwork sets the network layer.

func WithPacemaker

func WithPacemaker[H Hash](config PacemakerConfig) ConfigOption[H]

WithPacemaker sets the pacemaker configuration. Use this to configure target block time, timeouts, and backoff behavior.

func WithPrivateKey

func WithPrivateKey[H Hash](key PrivateKey) ConfigOption[H]

WithPrivateKey sets the private key for signing.

func WithStorage

func WithStorage[H Hash](storage Storage[H]) ConfigOption[H]

WithStorage sets the storage backend.

func WithTargetBlockTime

func WithTargetBlockTime[H Hash](blockTime time.Duration) ConfigOption[H]

WithTargetBlockTime is a convenience method to configure pacemaker for a specific target block time. This sets up appropriate timeouts for production use.

func WithTimer

func WithTimer[H Hash](timer Timer) ConfigOption[H]

WithTimer sets the timer implementation.

func WithValidators

func WithValidators[H Hash](validators ValidatorSet) ConfigOption[H]

WithValidators sets the validator set.

func WithVerification

func WithVerification[H Hash](enabled bool) ConfigOption[H]

WithVerification enables or disables runtime TLA+ verification.

type ConsensusMessage

type ConsensusMessage[H Hash] struct {
	// contains filtered or unexported fields
}

ConsensusMessage represents a message in the HotStuff2 protocol.

Maps to TLA+ Message types: - PROPOSE: Leader broadcasts block with justification QC - VOTE: Replica votes for a proposal - NEWVIEW: Replica sends view change message with highQC

func MessageFromBytes

func MessageFromBytes[H Hash](
	data []byte,
	hashFromBytes func([]byte) (H, error),
	blockFromBytes func([]byte) (Block[H], error),
) (*ConsensusMessage[H], error)

MessageFromBytes deserializes a ConsensusMessage from bytes.

func NewNewViewMessage

func NewNewViewMessage[H Hash](
	view uint32,
	validatorIndex uint16,
	highQC *QC[H],
) *ConsensusMessage[H]

NewNewViewMessage creates a NEWVIEW message.

func NewProposeMessage

func NewProposeMessage[H Hash](
	view uint32,
	validatorIndex uint16,
	block Block[H],
	justifyQC *QC[H],
) *ConsensusMessage[H]

NewProposeMessage creates a PROPOSE message.

func NewVoteMessage

func NewVoteMessage[H Hash](
	view uint32,
	validatorIndex uint16,
	vote *Vote[H],
) *ConsensusMessage[H]

NewVoteMessage creates a VOTE message.

func (*ConsensusMessage[H]) Block

func (m *ConsensusMessage[H]) Block() Block[H]

Block returns the proposed block (PROPOSE only).

func (*ConsensusMessage[H]) Bytes

func (m *ConsensusMessage[H]) Bytes() []byte

Bytes serializes the message to bytes. Format: [type:1][view:4][validatorIndex:2][payload...]

func (*ConsensusMessage[H]) Hash

func (m *ConsensusMessage[H]) Hash() H

Hash returns the hash of this message (implementation depends on block hash).

func (*ConsensusMessage[H]) HighQC

func (m *ConsensusMessage[H]) HighQC() *QC[H]

HighQC returns the highest QC (NEWVIEW only).

func (*ConsensusMessage[H]) JustifyQC

func (m *ConsensusMessage[H]) JustifyQC() *QC[H]

JustifyQC returns the justification QC (PROPOSE only).

func (*ConsensusMessage[H]) Type

func (m *ConsensusMessage[H]) Type() MessageType

Type returns the message type.

func (*ConsensusMessage[H]) ValidatorIndex

func (m *ConsensusMessage[H]) ValidatorIndex() uint16

ValidatorIndex returns the sender's validator index.

func (*ConsensusMessage[H]) View

func (m *ConsensusMessage[H]) View() uint32

View returns the view number.

func (*ConsensusMessage[H]) Vote

func (m *ConsensusMessage[H]) Vote() *Vote[H]

Vote returns the vote (VOTE only).

type ConsensusPayload

type ConsensusPayload[H Hash] interface {
	// Type returns the message type (PROPOSE, VOTE, NEWVIEW).
	Type() MessageType

	// View returns the view number for this message.
	View() uint32

	// ValidatorIndex returns the index of the validator who created this message.
	ValidatorIndex() uint16

	// Bytes returns the serialized form of the payload.
	Bytes() []byte

	// Hash returns the hash of the payload for signing.
	Hash() H
}

ConsensusPayload represents a message in the consensus protocol.

type ConsensusState

type ConsensusState interface {
	// View returns the current view number.
	View() uint32

	// Height returns the height of the last committed block.
	Height() uint32

	// LockedQCView returns the view of the locked QC, or 0 if none.
	LockedQCView() uint32

	// HighQCView returns the view of the highest QC seen, or 0 if none.
	HighQCView() uint32

	// CommittedCount returns the number of committed blocks.
	CommittedCount() int
}

ConsensusState provides read-only access to consensus state. Use HotStuff2.State() to obtain an instance.

type Context

type Context[H Hash] struct {
	// contains filtered or unexported fields
}

Context maintains the consensus state for a HotStuff2 replica.

This maps to the TLA+ spec state variables: - view[r] -> view - lockedQC[r] -> lockedQC - highQC[r] -> highQC - committed[r] -> committed - votes tracking -> votes map - NEWVIEW tracking -> newviews map (for leader view change)

Thread-safe: All methods use mutex for concurrent access.

Note: Maps use string keys (hash.String()) instead of Hash type directly to avoid comparable constraint issues with generic interfaces.

func NewContext

func NewContext[H Hash](genesisBlock Block[H], genesisQC *QC[H]) *Context[H]

NewContext creates a new consensus context.

func (*Context[H]) AddBlock

func (c *Context[H]) AddBlock(block Block[H])

AddBlock stores a block in the context.

func (*Context[H]) AddNewView

func (c *Context[H]) AddNewView(view uint32, validatorIndex uint16, highQC *QC[H]) int

AddNewView records a NEWVIEW message from a validator for a view. Returns the count of unique validators who have sent NEWVIEW for this view. TLA+ spec: network' = network \cup { [type |-> "NEWVIEW", view |-> v, replica |-> r, qc |-> highQC[r]] }

func (*Context[H]) AddQC

func (c *Context[H]) AddQC(qc *QC[H])

AddQC stores a QC in the context.

func (*Context[H]) AddVote

func (c *Context[H]) AddVote(vote *Vote[H]) int

AddVote adds a vote to the vote tracking.

func (*Context[H]) CanCommit

func (c *Context[H]) CanCommit(block Block[H], qc *QC[H]) bool

CanCommit checks if a block can be committed using the two-chain rule.

TLA+ CanCommit(block, qc):

/\ blockParent[qc] = block
/\ blockView[qc] = blockView[block] + 1

Both conditions must hold: qcBlock must be child of block AND they must be in consecutive views.

func (*Context[H]) Commit

func (c *Context[H]) Commit(block Block[H])

Commit marks a block as committed and records the commit time.

func (*Context[H]) CommittedBlocks

func (c *Context[H]) CommittedBlocks() []Block[H]

CommittedBlocks returns all committed blocks.

func (*Context[H]) GetBlock

func (c *Context[H]) GetBlock(hash H) (Block[H], bool)

GetBlock retrieves a block by hash.

func (*Context[H]) GetCommitted

func (c *Context[H]) GetCommitted(height uint32) (Block[H], bool)

GetCommitted returns the committed block at the given height.

func (*Context[H]) GetQC

func (c *Context[H]) GetQC(nodeHash H) (*QC[H], bool)

GetQC retrieves a QC by node hash.

func (*Context[H]) GetVotes

func (c *Context[H]) GetVotes(view uint32, nodeHash H) []*Vote[H]

GetVotes returns all votes for a block in a view.

func (*Context[H]) HasNewViewQuorum

func (c *Context[H]) HasNewViewQuorum(view uint32, quorum int) bool

HasNewViewQuorum returns true if 2f+1 NEWVIEW messages have been received for a view. TLA+ spec: IsQuorum(NewViewReplicasFromNetwork(v))

func (*Context[H]) HasSentNewView

func (c *Context[H]) HasSentNewView(view uint32, validatorIndex uint16) bool

HasSentNewView returns true if this validator has already sent NEWVIEW for a view.

func (*Context[H]) HasVoted

func (c *Context[H]) HasVoted(view uint32, validatorIndex uint16) bool

HasVoted returns true if the validator has voted in this view.

func (*Context[H]) HighQC

func (c *Context[H]) HighQC() *QC[H]

HighQC returns the highest QC seen.

func (*Context[H]) HighestQCFromNewViews

func (c *Context[H]) HighestQCFromNewViews(view uint32) *QC[H]

HighestQCFromNewViews returns the highest QC from all NEWVIEW messages in a view. TLA+ spec: HighestQCFromNetwork(v)

func (*Context[H]) IsAncestor

func (c *Context[H]) IsAncestor(ancestor H, descendant H) bool

IsAncestor checks if ancestor is an ancestor of descendant in the block tree.

func (*Context[H]) IsCommitted

func (c *Context[H]) IsCommitted(height uint32) bool

IsCommitted returns true if a block at the given height is committed.

func (*Context[H]) LastCommitTime

func (c *Context[H]) LastCommitTime() time.Time

LastCommitTime returns the time of the last commit.

func (*Context[H]) LockedQC

func (c *Context[H]) LockedQC() *QC[H]

LockedQC returns the current locked QC.

func (*Context[H]) NewViewCount

func (c *Context[H]) NewViewCount(view uint32) int

NewViewCount returns the number of NEWVIEW messages received for a view. TLA+ spec: Cardinality(NewViewReplicasFromNetwork(v))

func (*Context[H]) PruneVotes

func (c *Context[H]) PruneVotes(currentView uint32, keepViews uint32)

PruneVotes removes old votes and newviews to free memory.

func (*Context[H]) SafeToVote

func (c *Context[H]) SafeToVote(block Block[H], justifyQC *QC[H]) bool

SafeToVote implements the TLA+ SafeNodeRule predicate.

func (*Context[H]) SetView

func (c *Context[H]) SetView(view uint32)

SetView updates the current view.

func (*Context[H]) State

func (c *Context[H]) State() ConsensusState

State returns a read-only ConsensusState for the context.

func (*Context[H]) Stats

func (c *Context[H]) Stats() map[string]interface{}

Stats returns statistics about the context state.

func (*Context[H]) TimeSinceLastCommit

func (c *Context[H]) TimeSinceLastCommit() time.Duration

TimeSinceLastCommit returns the duration since the last commit.

func (*Context[H]) UpdateHighQC

func (c *Context[H]) UpdateHighQC(qc *QC[H]) bool

UpdateHighQC updates the highest QC if the new QC has higher view.

func (*Context[H]) UpdateLockedQC

func (c *Context[H]) UpdateLockedQC(qc *QC[H]) bool

UpdateLockedQC updates the locked QC if the new QC has higher view.

func (*Context[H]) View

func (c *Context[H]) View() uint32

View returns the current view number.

func (*Context[H]) VoteCount

func (c *Context[H]) VoteCount(view uint32, nodeHash H) int

VoteCount returns the number of votes for a block in a view.

type Executor

type Executor[H Hash] interface {
	// Execute applies a block's payload and returns the resulting state hash.
	// Must be deterministic - same input always produces same output.
	Execute(block Block[H]) (stateHash H, err error)

	// Verify checks if a block is valid before voting.
	// Should validate:
	//   - Parent block exists
	//   - Payload is well-formed (application-specific)
	//   - Block height is correct
	//   - Any application-specific rules
	Verify(block Block[H]) error

	// GetStateHash returns the current state hash after all executed blocks.
	GetStateHash() H

	// CreateBlock creates a new block proposal.
	// Called when this validator is the leader.
	// The implementation decides what payload to include (e.g., transactions
	// from mempool, DAG references, rollup batch, etc.).
	CreateBlock(height uint32, prevHash H, proposerIndex uint16) (Block[H], error)
}

Executor handles block execution and validation.

func NewMockExecutor

func NewMockExecutor[H Hash]() Executor[H]

NewMockExecutor creates a generic mock executor.

type GenericValidatorSet

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

GenericValidatorSet implements ValidatorSet for testing with any key type.

func NewGenericValidatorSet

func NewGenericValidatorSet(n int, keys map[uint16]PublicKey) *GenericValidatorSet

NewGenericValidatorSet creates a generic validator set with any key type.

func (*GenericValidatorSet) Contains

func (vs *GenericValidatorSet) Contains(index uint16) bool

func (*GenericValidatorSet) Count

func (vs *GenericValidatorSet) Count() int

func (*GenericValidatorSet) F

func (vs *GenericValidatorSet) F() int

func (*GenericValidatorSet) GetByIndex

func (vs *GenericValidatorSet) GetByIndex(index uint16) (PublicKey, error)

func (*GenericValidatorSet) GetLeader

func (vs *GenericValidatorSet) GetLeader(view uint32) uint16

func (*GenericValidatorSet) GetPublicKeys

func (vs *GenericValidatorSet) GetPublicKeys(indices []uint16) ([]PublicKey, error)

type Hash

type Hash interface {
	// Bytes returns the raw byte representation of the hash.
	Bytes() []byte

	// Equals returns true if this hash equals the other hash.
	// Must be consistent with Bytes() comparison.
	Equals(other Hash) bool

	// String returns a human-readable representation (typically hex-encoded).
	String() string
}

Hash represents a cryptographic hash function output. Implementations must be comparable and suitable for use as map keys.

type Hooks

type Hooks[H Hash] struct {
	// OnPropose is called when this node proposes a block (leader only).
	OnPropose func(view uint32, block Block[H])

	// OnVote is called when this node votes for a proposal.
	OnVote func(view uint32, blockHash H)

	// OnQCFormed is called when a quorum certificate is formed.
	OnQCFormed func(view uint32, qc QuorumCertificate[H])

	// OnCommit is called when a block is committed (finalized).
	OnCommit func(block Block[H])

	// OnViewChange is called when the view changes.
	OnViewChange func(oldView, newView uint32)

	// OnTimeout is called when a view times out.
	OnTimeout func(view uint32)
}

Hooks provides callbacks for consensus events. All callbacks are optional - nil callbacks are safely ignored. Callbacks are invoked synchronously, so implementations should be fast or dispatch to a goroutine to avoid blocking consensus.

type HotStuff2

type HotStuff2[H Hash] struct {
	// contains filtered or unexported fields
}

HotStuff2 implements the HotStuff-2 consensus protocol.

This implementation follows the TLA+ specification in formal-models/tla/hotstuff2.tla Key protocol actions: - LeaderPropose: Leader broadcasts proposal with justification QC - ReplicaVote: Replica votes if proposal extends locked QC (SafeNode rule) - LeaderFormQC: Leader forms QC from 2f+1 votes - ReplicaUpdateOnQC: Replica updates state on seeing QC - ReplicaTimeout: Replica advances view on timeout

Safety: Lock mechanism prevents voting for conflicting blocks Liveness: Adaptive timeouts with exponential backoff

func NewHotStuff2

func NewHotStuff2[H Hash](cfg *Config[H], onCommit func(Block[H])) (*HotStuff2[H], error)

NewHotStuff2 creates a new HotStuff2 consensus instance. Deprecated: Use NewHotStuff2WithHooks for full observability support. This function is kept for backwards compatibility.

func NewHotStuff2WithHooks

func NewHotStuff2WithHooks[H Hash](cfg *Config[H], hooks *Hooks[H]) (*HotStuff2[H], error)

NewHotStuff2WithHooks creates a new HotStuff2 consensus instance with event hooks.

func (*HotStuff2[H]) Height

func (hs *HotStuff2[H]) Height() uint32

Height returns the height of the latest committed block.

func (*HotStuff2[H]) Start

func (hs *HotStuff2[H]) Start() error

Start starts the consensus protocol.

func (*HotStuff2[H]) State

func (hs *HotStuff2[H]) State() ConsensusState

State returns a read-only view of consensus state for monitoring.

func (*HotStuff2[H]) Stop

func (hs *HotStuff2[H]) Stop()

Stop stops the consensus protocol.

func (*HotStuff2[H]) View

func (hs *HotStuff2[H]) View() uint32

View returns the current view.

type MessageCodec

type MessageCodec[H Hash] struct {
	// HashFromBytes deserializes a hash from bytes.
	HashFromBytes func([]byte) (H, error)

	// BlockFromBytes deserializes a block from bytes.
	BlockFromBytes func([]byte) (Block[H], error)
}

MessageCodec provides convenient encoding/decoding of consensus messages. Create once and reuse for all message operations.

func NewMessageCodec

func NewMessageCodec[H Hash](
	hashFromBytes func([]byte) (H, error),
	blockFromBytes func([]byte) (Block[H], error),
) *MessageCodec[H]

NewMessageCodec creates a new message codec with the given deserializers.

func (*MessageCodec[H]) Decode

func (c *MessageCodec[H]) Decode(data []byte) (*ConsensusMessage[H], error)

Decode deserializes a consensus message from bytes.

func (*MessageCodec[H]) Encode

func (c *MessageCodec[H]) Encode(msg *ConsensusMessage[H]) []byte

Encode serializes a consensus message to bytes.

type MessageType

type MessageType uint8

MessageType represents the type of consensus message.

const (
	// MessageProposal represents a block proposal from the leader.
	MessageProposal MessageType = iota

	// MessageVote represents a vote for a proposal.
	MessageVote

	// MessageNewView represents a new view message during view change.
	MessageNewView
)

func (MessageType) String

func (mt MessageType) String() string

String returns the string representation of the message type.

type Network

type Network[H Hash] interface {
	// Broadcast sends a message to all validators.
	Broadcast(payload ConsensusPayload[H])

	// SendTo sends a message to a specific validator.
	SendTo(validatorIndex uint16, payload ConsensusPayload[H])

	// Receive returns a channel for receiving consensus messages.
	// The channel should be buffered to prevent message loss.
	Receive() <-chan ConsensusPayload[H]

	// Close releases any resources held by the network.
	Close() error
}

Network provides message broadcasting and delivery.

func NewMockNetwork

func NewMockNetwork[H Hash]() Network[H]

NewMockNetwork creates a generic mock network.

type Pacemaker

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

Pacemaker manages view synchronization and timeouts for HotStuff2.

The pacemaker triggers view changes when the current view doesn't make progress. It uses adaptive timeouts with exponential backoff for liveness under asynchrony.

The timing model follows standard BFT practices:

  • TimeoutPropose: Wait for leader to propose
  • TimeoutVote: Wait for votes (unused in two-phase HotStuff-2)
  • TimeoutCommit: Minimum delay between blocks (target block time)

func NewPacemaker

func NewPacemaker(timer Timer, logger *zap.Logger, onTimeout func(view uint32)) *Pacemaker

NewPacemaker creates a new Pacemaker with the given configuration.

func NewPacemakerWithConfig

func NewPacemakerWithConfig(timer Timer, logger *zap.Logger, onTimeout func(view uint32), config PacemakerConfig) *Pacemaker

NewPacemakerWithConfig creates a new Pacemaker with explicit configuration.

func (*Pacemaker) CanAcceptProposal

func (pm *Pacemaker) CanAcceptProposal(lastCommitTime time.Time) bool

CanAcceptProposal checks if a proposal can be accepted based on minimum block time. Returns true if MinBlockTime is not configured, or if enough time has elapsed. The skew parameter allows for clock drift tolerance.

func (*Pacemaker) Config

func (pm *Pacemaker) Config() PacemakerConfig

Config returns the pacemaker configuration.

func (*Pacemaker) CurrentTimeout

func (pm *Pacemaker) CurrentTimeout() uint64

CurrentTimeout returns the current timeout duration.

func (*Pacemaker) IncreaseTimeout

func (pm *Pacemaker) IncreaseTimeout()

IncreaseTimeout increases the timeout duration using exponential backoff. This should be called after a view change (timeout without commit).

func (*Pacemaker) MinBlockTimeEnabled

func (pm *Pacemaker) MinBlockTimeEnabled() bool

MinBlockTimeEnabled returns true if minimum block time enforcement is enabled.

func (*Pacemaker) OnCommit

func (pm *Pacemaker) OnCommit(view uint32) <-chan struct{}

OnCommit should be called when a block is committed. It enforces the minimum block time (TimeoutCommit) before allowing the next view to start proposing. Returns a channel that will be closed when the commit delay is complete.

func (*Pacemaker) OnProgress

func (pm *Pacemaker) OnProgress(view uint32)

OnProgress should be called when consensus makes progress (receives valid proposal/votes). This resets the view timeout but does NOT affect the commit delay.

func (*Pacemaker) OnTimeout

func (pm *Pacemaker) OnTimeout(view uint32)

OnTimeout handles timer expiration (triggers view change).

func (*Pacemaker) ResetTimeout

func (pm *Pacemaker) ResetTimeout()

ResetTimeout resets the timeout to the base value. This is typically called after successful commit.

func (*Pacemaker) Start

func (pm *Pacemaker) Start(view uint32)

Start starts the pacemaker timer for the given view.

func (*Pacemaker) Stop

func (pm *Pacemaker) Stop()

Stop stops the pacemaker.

func (*Pacemaker) TimeUntilCanPropose

func (pm *Pacemaker) TimeUntilCanPropose(lastCommitTime time.Time) time.Duration

TimeUntilCanPropose returns how long until a proposal can be made. Returns 0 if MinBlockTime is not configured or already past threshold.

func (*Pacemaker) WaitForCommitDelay

func (pm *Pacemaker) WaitForCommitDelay()

WaitForCommitDelay waits for the commit delay to complete. This is a convenience method that blocks until the delay is done.

func (*Pacemaker) WaitForMinBlockTime

func (pm *Pacemaker) WaitForMinBlockTime(lastCommitTime time.Time)

WaitForMinBlockTime waits until the minimum block time has elapsed since lastCommitTime. This should be called by leaders before proposing a new block. Returns immediately if MinBlockTime is not configured (0).

type PacemakerConfig

type PacemakerConfig struct {
	// TimeoutPropose is how long to wait for a proposal before timing out.
	// This is the initial timeout for each view.
	// Default: 1000ms (1 second)
	TimeoutPropose time.Duration

	// TimeoutVote is how long to wait for votes after receiving +2/3 prevotes.
	// In HotStuff-2, this is mainly used for the two-chain commit rule.
	// Default: 1000ms (1 second)
	TimeoutVote time.Duration

	// TimeoutCommit is the minimum delay after committing a block before
	// starting the next view. This is the primary control for target block time.
	// Setting this to 0 allows blocks to be produced as fast as possible.
	// Default: 0 (immediate, optimistically responsive)
	TimeoutCommit time.Duration

	// MinBlockTime enforces a minimum time between blocks at the consensus level.
	// When set (> 0):
	//   - Leaders wait until last_commit_time + MinBlockTime before proposing
	//   - Validators reject proposals that arrive before last_commit_time + MinBlockTime - MinBlockTimeSkew
	//
	// This provides stronger guarantees than TimeoutCommit alone, as it prevents
	// malicious leaders from proposing blocks faster than the target rate.
	//
	// Default: 0 (disabled, use TimeoutCommit for block pacing)
	MinBlockTime time.Duration

	// MinBlockTimeSkew is the clock skew tolerance for MinBlockTime enforcement.
	// Validators will accept proposals that arrive up to this much before the
	// minimum block time. This accounts for clock drift between nodes.
	//
	// Only used when MinBlockTime > 0.
	// Default: 500ms
	MinBlockTimeSkew time.Duration

	// BackoffMultiplier is the factor by which timeouts increase after each
	// failed round (view change without commit). Values > 1.0 provide exponential
	// backoff which helps the network synchronize during periods of instability.
	// Default: 1.5 (50% increase per failed round)
	BackoffMultiplier float64

	// MaxTimeout is the maximum timeout duration. Timeouts will not grow beyond
	// this value regardless of backoff.
	// Default: 30s
	MaxTimeout time.Duration

	// SkipTimeoutCommit, if true, skips the commit delay when we receive
	// all votes (fast path). This allows faster block times when the network
	// is healthy while still enforcing the target block time under degraded
	// conditions.
	// Default: false
	SkipTimeoutCommit bool
}

PacemakerConfig configures the pacemaker timing behavior.

This follows the standard BFT timing model used by production systems like CometBFT (Tendermint) and Aptos. The key parameters are:

  • TimeoutPropose: How long to wait for a block proposal before timing out
  • TimeoutVote: How long to wait for votes after receiving a proposal
  • TimeoutCommit: Minimum time between blocks (target block time)
  • BackoffMultiplier: Exponential backoff factor for failed rounds

For public blockchains, TimeoutCommit is typically the primary control for block time. For example, setting TimeoutCommit to 5 seconds results in approximately 5-second blocks under normal conditions.

Minimum Block Time Enforcement

For stronger block time guarantees, enable MinBlockTime. When set:

  • Leaders wait until last_commit_time + MinBlockTime before proposing
  • Validators refuse to vote for proposals that arrive too early

This prevents malicious/fast leaders from speeding up the chain while preserving HotStuff-2 safety and remaining compatible with the liveness model.

func DefaultPacemakerConfig

func DefaultPacemakerConfig() PacemakerConfig

DefaultPacemakerConfig returns the default pacemaker configuration. This is tuned for low-latency networks with optimistic responsiveness.

func DemoPacemakerConfig

func DemoPacemakerConfig() PacemakerConfig

DemoPacemakerConfig returns a configuration suitable for demos and testing with visible block production (approximately 1 block per second).

func ProductionPacemakerConfig

func ProductionPacemakerConfig(targetBlockTime time.Duration) PacemakerConfig

ProductionPacemakerConfig returns a configuration suitable for production public blockchains with a target block time.

This enables MinBlockTime enforcement, which ensures:

  • Leaders wait until last_commit_time + targetBlockTime before proposing
  • Validators reject proposals that arrive too early

This provides stronger guarantees against malicious leaders trying to speed up block production.

func (PacemakerConfig) Validate

func (c PacemakerConfig) Validate() error

Validate checks that the configuration values are sensible.

type PrivateKey

type PrivateKey interface {
	// PublicKey returns the corresponding public key.
	// Returns a type that implements PublicKey interface methods.
	PublicKey() interface {
		Bytes() []byte
		Verify(message []byte, signature []byte) bool
		Equals(other interface{ Bytes() []byte }) bool
		String() string
	}

	// Sign signs the given message and returns the signature.
	// Returns an error if signing fails.
	Sign(message []byte) ([]byte, error)

	// Bytes returns the raw byte representation of the private key.
	// WARNING: Handle with care - this exposes sensitive key material.
	Bytes() []byte
}

PrivateKey represents a validator's private key for signing messages.

type PublicKey

type PublicKey interface {
	// Bytes returns the raw byte representation of the public key.
	Bytes() []byte

	// Verify verifies a signature over the given message.
	// Returns true if the signature is valid for this public key.
	Verify(message []byte, signature []byte) bool

	// Equals returns true if this public key equals the other.
	// Accepts interface{} for flexibility with different implementations.
	Equals(other interface{ Bytes() []byte }) bool

	// String returns a human-readable representation of the public key.
	String() string
}

PublicKey represents a validator's public key for signature verification.

type QC

type QC[H Hash] struct {
	// contains filtered or unexported fields
}

QC represents a Quorum Certificate - aggregated proof that 2f+1 validators voted for a block in a specific view.

CRITICAL SAFETY: QC validation is the cornerstone of consensus safety. A forged QC can cause Byzantine faults and break consensus.

Safety rules (from TLA+ spec): - MUST have exactly 2f+1 distinct signers (deduplicated) - MUST verify aggregate signature - NEVER accept QC without full validation

Attack scenario: Without deduplication, Byzantine nodes could create valid-looking QCs with only f+1 real votes by duplicating signatures.

func NewQC

func NewQC[H Hash](
	view uint32,
	nodeHash H,
	votes []*Vote[H],
	validators ValidatorSet,
	cryptoScheme string,
) (*QC[H], error)

NewQC creates a new QC from a set of votes.

CRITICAL: This function deduplicates signers before forming the QC. Duplicate votes from the same validator MUST be rejected to prevent Byzantine attacks where <2f+1 real votes appear as a valid QC.

The cryptoScheme parameter determines how signatures are aggregated: - "ed25519": Concatenate signatures (O(n) size) - "bls": Aggregate signatures using BLS12-381 (O(1) size)

func QCFromBytes

func QCFromBytes[H Hash](data []byte, hashFromBytes func([]byte) (H, error)) (*QC[H], error)

QCFromBytes reconstructs a QC from serialized bytes.

func (*QC[H]) AggregateSignature

func (qc *QC[H]) AggregateSignature() []byte

AggregateSignature returns the aggregated signature bytes.

func (*QC[H]) Bytes

func (qc *QC[H]) Bytes() []byte

Bytes serializes the QC to bytes. Format: [view:4][nodeHashLen:2][nodeHash][signerCount:2][signer1:2]...[timestamp1:8]...[schemeLen:1][scheme][aggSigLen:2][aggSig]

func (*QC[H]) CryptoScheme

func (qc *QC[H]) CryptoScheme() string

CryptoScheme returns the cryptographic scheme used ("ed25519" or "bls").

func (*QC[H]) Node

func (qc *QC[H]) Node() H

Node returns the hash of the block this QC certifies.

func (*QC[H]) Signers

func (qc *QC[H]) Signers() []uint16

Signers returns the list of validator indices who signed. Guaranteed to be deduplicated and sorted.

func (*QC[H]) Validate

func (qc *QC[H]) Validate(validators ValidatorSet) error

Validate verifies the QC is well-formed and signatures are valid.

CRITICAL SAFETY: This implements the complete QC validation logic. Failures here can cause Byzantine faults.

Validation steps: 1. Check quorum size (2f+1) 2. Verify all signers are valid validators 3. Check signers are deduplicated 4. Verify aggregate signature

Returns error if any validation fails.

func (*QC[H]) View

func (qc *QC[H]) View() uint32

View returns the view number of this QC.

type QuorumCertificate

type QuorumCertificate[H Hash] interface {
	// Node returns the hash of the block this QC certifies.
	Node() H

	// View returns the view number in which this QC was formed.
	View() uint32

	// Signers returns the list of validator indices who signed.
	// MUST be deduplicated and sorted.
	Signers() []uint16

	// AggregateSignature returns the aggregated signature.
	AggregateSignature() []byte

	// Bytes returns the serialized form of the QC.
	Bytes() []byte

	// Validate verifies the QC is well-formed and signatures are valid.
	// Returns error if:
	//   - Signer count < 2f+1
	//   - Duplicate signers found
	//   - Invalid signer indices
	//   - Aggregate signature verification fails
	Validate(validators ValidatorSet) error
}

QuorumCertificate represents an aggregated certificate from 2f+1 validators.

CRITICAL SAFETY RULES: QC validation is the most critical part of consensus safety. A forged QC can cause replicas to commit conflicting blocks, violating Byzantine fault tolerance.

Implementations MUST:

  • Validate signer set has exactly 2f+1 distinct validators (not more, not less)
  • Deduplicate signers before accepting QC (prevents forged QCs via duplicate votes)
  • Verify aggregate signature matches node digest + view number
  • NEVER accept QC without full validation - even from trusted sources

Attack scenario: If duplicate signers are allowed, a Byzantine node could create a valid-looking QC with only f+1 real votes by duplicating each signature.

type Storage

type Storage[H Hash] interface {
	// GetBlock retrieves a block by its hash.
	GetBlock(hash H) (Block[H], error)

	// PutBlock persists a block.
	// Must complete durably before returning.
	PutBlock(block Block[H]) error

	// GetLastBlock returns the most recently committed block.
	GetLastBlock() (Block[H], error)

	// GetQC retrieves the QC that certifies the given block.
	GetQC(nodeHash H) (QuorumCertificate[H], error)

	// PutQC persists a QC.
	// Must complete durably before returning.
	PutQC(qc QuorumCertificate[H]) error

	// GetHighestLockedQC returns the highest locked QC.
	// This is safety-critical for crash recovery.
	GetHighestLockedQC() (QuorumCertificate[H], error)

	// PutHighestLockedQC persists the highest locked QC.
	// Must complete durably before returning.
	PutHighestLockedQC(qc QuorumCertificate[H]) error

	// GetView returns the current view number.
	GetView() (uint32, error)

	// PutView persists the current view number.
	// Must complete durably before returning.
	PutView(view uint32) error

	// Close releases any resources held by the storage.
	Close() error
}

Storage provides persistent storage for consensus state.

CRITICAL SAFETY: All Put operations must be durable before returning. The locked QC and view number must be persisted atomically to prevent safety violations after crash recovery.

func NewMockStorage

func NewMockStorage[H Hash]() Storage[H]

NewMockStorage creates a generic mock storage.

type TestBlock

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

TestBlock implements Block for testing.

func NewTestBlock

func NewTestBlock(height uint32, prevHash TestHash, proposer uint16) *TestBlock

func (*TestBlock) Bytes

func (b *TestBlock) Bytes() []byte

func (*TestBlock) Hash

func (b *TestBlock) Hash() TestHash

func (*TestBlock) Height

func (b *TestBlock) Height() uint32

func (*TestBlock) Payload

func (b *TestBlock) Payload() []byte

func (*TestBlock) PrevHash

func (b *TestBlock) PrevHash() TestHash

func (*TestBlock) ProposerIndex

func (b *TestBlock) ProposerIndex() uint16

func (*TestBlock) Timestamp

func (b *TestBlock) Timestamp() uint64

type TestExecutor

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

TestExecutor implements Executor for testing.

func NewTestExecutor

func NewTestExecutor() *TestExecutor

func (*TestExecutor) CreateBlock

func (e *TestExecutor) CreateBlock(height uint32, prevHash TestHash, proposerIndex uint16) (Block[TestHash], error)

func (*TestExecutor) Execute

func (e *TestExecutor) Execute(block Block[TestHash]) (TestHash, error)

func (*TestExecutor) GetStateHash

func (e *TestExecutor) GetStateHash() TestHash

func (*TestExecutor) Verify

func (e *TestExecutor) Verify(block Block[TestHash]) error

type TestHash

type TestHash [32]byte

TestHash implements Hash for testing using SHA256.

func NewTestHash

func NewTestHash(data string) TestHash

NewTestHash creates a test hash from a string.

func (TestHash) Bytes

func (h TestHash) Bytes() []byte

func (TestHash) Equals

func (h TestHash) Equals(other Hash) bool

func (TestHash) String

func (h TestHash) String() string

type TestNetwork

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

TestNetwork implements Network for testing.

func NewTestNetwork

func NewTestNetwork() *TestNetwork

func (*TestNetwork) Broadcast

func (n *TestNetwork) Broadcast(payload ConsensusPayload[TestHash])

func (*TestNetwork) Close

func (n *TestNetwork) Close() error

func (*TestNetwork) Receive

func (n *TestNetwork) Receive() <-chan ConsensusPayload[TestHash]

func (*TestNetwork) SendTo

func (n *TestNetwork) SendTo(validatorIndex uint16, payload ConsensusPayload[TestHash])

func (*TestNetwork) SentMessages

func (n *TestNetwork) SentMessages() []ConsensusPayload[TestHash]

type TestStorage

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

TestStorage implements Storage for testing.

func NewTestStorage

func NewTestStorage() *TestStorage

func (*TestStorage) Close

func (s *TestStorage) Close() error

func (*TestStorage) GetBlock

func (s *TestStorage) GetBlock(hash TestHash) (Block[TestHash], error)

func (*TestStorage) GetHighestLockedQC

func (s *TestStorage) GetHighestLockedQC() (QuorumCertificate[TestHash], error)

func (*TestStorage) GetLastBlock

func (s *TestStorage) GetLastBlock() (Block[TestHash], error)

func (*TestStorage) GetQC

func (s *TestStorage) GetQC(nodeHash TestHash) (QuorumCertificate[TestHash], error)

func (*TestStorage) GetView

func (s *TestStorage) GetView() (uint32, error)

func (*TestStorage) PutBlock

func (s *TestStorage) PutBlock(block Block[TestHash]) error

func (*TestStorage) PutHighestLockedQC

func (s *TestStorage) PutHighestLockedQC(qc QuorumCertificate[TestHash]) error

func (*TestStorage) PutQC

func (*TestStorage) PutView

func (s *TestStorage) PutView(view uint32) error

type TestValidatorSet

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

TestValidatorSet implements ValidatorSet for testing.

func NewTestValidatorSet

func NewTestValidatorSet(n int) *TestValidatorSet

func NewTestValidatorSetWithKeys

func NewTestValidatorSetWithKeys(n int) (*TestValidatorSet, []*crypto.Ed25519PrivateKey)

NewTestValidatorSetWithKeys creates a validator set and returns the private keys.

func (*TestValidatorSet) Contains

func (vs *TestValidatorSet) Contains(index uint16) bool

func (*TestValidatorSet) Count

func (vs *TestValidatorSet) Count() int

func (*TestValidatorSet) F

func (vs *TestValidatorSet) F() int

func (*TestValidatorSet) GetByIndex

func (vs *TestValidatorSet) GetByIndex(index uint16) (PublicKey, error)

func (*TestValidatorSet) GetLeader

func (vs *TestValidatorSet) GetLeader(view uint32) uint16

func (*TestValidatorSet) GetPublicKeys

func (vs *TestValidatorSet) GetPublicKeys(indices []uint16) ([]PublicKey, error)

type Timer

type Timer interface {
	// Start starts the timer with the given duration in milliseconds.
	Start(duration uint64)

	// Stop stops the timer.
	Stop()

	// Reset resets the timer with a new duration.
	Reset(duration uint64)

	// C returns a channel that receives when the timer expires.
	C() <-chan struct{}
}

Timer provides timeout management for the pacemaker.

type ValidatorSet

type ValidatorSet interface {
	// Count returns the total number of validators.
	Count() int

	// GetByIndex returns the public key for a validator by index.
	// Returns error if index is out of bounds.
	GetByIndex(index uint16) (PublicKey, error)

	// Contains returns true if the validator index is valid.
	Contains(index uint16) bool

	// GetPublicKeys returns public keys for the given validator indices.
	// Used for aggregate signature verification.
	GetPublicKeys(indices []uint16) ([]PublicKey, error)

	// GetLeader returns the validator index of the leader for a given view.
	// Typically implements round-robin: view % count.
	GetLeader(view uint32) uint16

	// F returns the maximum number of Byzantine validators tolerated.
	// Must return (n-1)/3 where n is the total validator count.
	F() int
}

ValidatorSet represents the current set of consensus validators.

type Vote

type Vote[H Hash] struct {
	// contains filtered or unexported fields
}

Vote represents a replica's vote for a block proposal.

Votes are the fundamental building blocks of QCs (Quorum Certificates). A QC is formed when 2f+1 replicas vote for the same block in the same view.

CRITICAL SAFETY: Votes include replay protection via timestamp validation. Replicas must reject votes with timestamps outside acceptable window (60s).

func NewBLSVote

func NewBLSVote[H Hash](
	view uint32,
	nodeHash H,
	validatorIndex uint16,
	signer BLSSigner,
) (*Vote[H], error)

NewBLSVote creates a new vote using BLS signature scheme. BLS votes sign the common message (view + nodeHash) without validatorIndex or timestamp. This enables O(1) signature aggregation in QC formation.

func NewVote

func NewVote[H Hash](
	view uint32,
	nodeHash H,
	validatorIndex uint16,
	privateKey PrivateKey,
) (*Vote[H], error)

NewVote creates a new vote for a block at a given view (Ed25519). For BLS votes, use NewBLSVote instead.

func VoteFromBytes

func VoteFromBytes[H Hash](data []byte, hashFromBytes func([]byte) (H, error)) (*Vote[H], error)

VoteFromBytes reconstructs a Vote from serialized bytes.

func (*Vote[H]) BLSDigest

func (v *Vote[H]) BLSDigest() []byte

BLSDigest returns the common message for BLS signing. Format: [view:4][nodeHash] This is the message ALL validators sign when using BLS, enabling aggregation. Unlike Digest(), this does NOT include validatorIndex or timestamp.

func (*Vote[H]) Bytes

func (v *Vote[H]) Bytes() []byte

Bytes serializes the vote to bytes. Format: [view:4][nodeHashLen:2][nodeHash][validatorIndex:2][timestamp:8][sigLen:2][signature]

func (*Vote[H]) Digest

func (v *Vote[H]) Digest() []byte

Digest returns the digest of the vote for signing/verification (Ed25519). Digest includes: view + nodeHash + validatorIndex + timestamp This is used for Ed25519 signatures which include per-validator data.

func (*Vote[H]) NodeHash

func (v *Vote[H]) NodeHash() H

NodeHash returns the hash of the block being voted for.

func (*Vote[H]) Signature

func (v *Vote[H]) Signature() []byte

Signature returns the signature of this vote.

func (*Vote[H]) Timestamp

func (v *Vote[H]) Timestamp() uint64

Timestamp returns the Unix timestamp (milliseconds) when this vote was created.

func (*Vote[H]) ValidatorIndex

func (v *Vote[H]) ValidatorIndex() uint16

ValidatorIndex returns the index of the validator who created this vote.

func (*Vote[H]) Verify

func (v *Vote[H]) Verify(publicKey PublicKey) error

Verify verifies the vote signature with the given public key. Also validates timestamp is within acceptable window (Ed25519 only). BLS votes use timestamp=0 and skip timestamp validation since all validators sign the same message (view + nodeHash only) for aggregation.

func (*Vote[H]) View

func (v *Vote[H]) View() uint32

View returns the view number of this vote.

Directories

Path Synopsis
Package bench provides benchmarking utilities for HotStuff-2.
Package bench provides benchmarking utilities for HotStuff-2.
cmd
benchgen command
Command benchgen generates HTML benchmark reports.
Command benchgen generates HTML benchmark reports.
Demo is a web-based visualization of HotStuff-2 consensus.
Demo is a web-based visualization of HotStuff-2 consensus.
simulator
Package simulator provides a local network simulation for HotStuff-2 consensus.
Package simulator provides a local network simulation for HotStuff-2 consensus.
internal
crypto
Package crypto provides cryptographic primitives for HotStuff-2.
Package crypto provides cryptographic primitives for HotStuff-2.
Package timer provides timer implementations for HotStuff-2 pacemaker.
Package timer provides timer implementations for HotStuff-2 pacemaker.
Package twins implements the Twins framework for Byzantine fault testing.
Package twins implements the Twins framework for Byzantine fault testing.

Jump to

Keyboard shortcuts

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