qbft

package
v0.3.5 Latest Latest
Warning

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

Go to latest
Published: Jan 21, 2024 License: GPL-3.0 Imports: 10 Imported by: 0

README

QBFT

Introduction

This is a spec implementation for the QBFT protocol, following formal verification spec / github repo.

Important note on message processing

The spec only deals with message process logic but it's also very important the way controller.ProcessMsg is called. Message queueing and retry are important as there is no guarantee as to when a message is delivered. Examples:

  • A proposal message can be delivered after its respective prepare
  • A next round message can be delivered before the timer hits timeout
  • A late commit message can decide the instance even if it started the next round
  • A message can fail to process because it's "too early" or "too late"

Because of the above, there is a need to order and queue messages based on their round and type so to not lose message and make the protocol round change.

TODO

  • Support 4,7,10,13 committee sizes
  • Message encoding and validation spec tests
  • proposal/ prepare/ commit spec tests
  • round change spec tests
  • [//] Unified test suite, compatible with the formal verification spec
  • [//] Align according to spec and Roberto's comments
  • Remove round check from upon commit as it can be for any round?
  • Use data hashes instead of full data in msgs to save space in justifications

Documentation

Overview

Code generated by fastssz. DO NOT EDIT. Hash: e3a21c1a7686d6a8021a20e5f8a6ec7aed91023b3bfb5eafabc975f56726badb Version: 0.1.3

Index

Constants

View Source
const (
	NoRound     Round  = 0 // NoRound represents a nil/ zero round
	FirstRound  Round  = 1 // FirstRound value is the first round in any QBFT instance start
	FirstHeight Height = 0
)

Variables

View Source
var (

	// CutoffRound which round the instance should stop its timer and progress no further
	CutoffRound = 15 // stop processing instances after 8*2+120*6 = 14.2 min (~ 2 epochs)
)

Functions

func ControllerIdToMessageID added in v0.2.2

func ControllerIdToMessageID(identifier []byte) types.MessageID

func HasPartialQuorum

func HasPartialQuorum(share *types.Share, msgs []*SignedMessage) bool

HasPartialQuorum returns true if a unique set of signers has partial quorum

func HasQuorum

func HasQuorum(share *types.Share, msgs []*SignedMessage) bool

HasQuorum returns true if a unique set of signers has quorum

func HashDataRoot added in v0.3.0

func HashDataRoot(data []byte) ([32]byte, error)

HashDataRoot hashes input data to root

func IsDecidedMsg added in v0.3.0

func IsDecidedMsg(share *types.Share, signedDecided *SignedMessage) bool

IsDecidedMsg returns true if signed commit has all quorum sigs

func MarshalJustifications added in v0.3.0

func MarshalJustifications(msgs []*SignedMessage) ([][]byte, error)

func RoundRobinProposer

func RoundRobinProposer(state *State, round Round) types.OperatorID

RoundRobinProposer returns the proposer for the round. Each new height starts with the first proposer and increments by 1 with each following round. Each new height has a different first round proposer which is +1 from the previous height. First height starts with index 0

func ValidateDecided added in v0.3.0

func ValidateDecided(
	config IConfig,
	signedDecided *SignedMessage,
	share *types.Share,
) error

Types

type Config

type Config struct {
	Signer      types.SSVSigner
	SigningPK   []byte
	Domain      types.DomainType
	ValueCheckF ProposedValueCheckF
	ProposerF   ProposerF
	Network     Network
	Timer       Timer
}

func (*Config) GetNetwork

func (c *Config) GetNetwork() Network

GetNetwork returns a p2p Network instance

func (*Config) GetProposerF

func (c *Config) GetProposerF() ProposerF

GetProposerF returns func used to calculate proposer

func (*Config) GetSignatureDomainType

func (c *Config) GetSignatureDomainType() types.DomainType

GetSignatureDomainType returns the Domain type used for signatures

func (*Config) GetSigner

func (c *Config) GetSigner() types.SSVSigner

GetSigner returns a Signer instance

func (*Config) GetSigningPubKey

func (c *Config) GetSigningPubKey() []byte

GetSigningPubKey returns the public key used to sign all QBFT messages

func (*Config) GetTimer

func (c *Config) GetTimer() Timer

GetTimer returns round timer

func (*Config) GetValueCheckF

func (c *Config) GetValueCheckF() ProposedValueCheckF

GetValueCheckF returns value check instance

type Controller

type Controller struct {
	Identifier []byte
	Height     Height // incremental Height for InstanceContainer
	// StoredInstances stores the last HistoricalInstanceCapacity in an array for message processing purposes.
	StoredInstances InstanceContainer
	Share           *types.Share
	// contains filtered or unexported fields
}

Controller is a QBFT coordinator responsible for starting and following the entire life cycle of multiple QBFT InstanceContainer

func NewController

func NewController(
	identifier []byte,
	share *types.Share,
	config IConfig,
) *Controller

func (*Controller) BaseMsgValidation added in v0.3.0

func (c *Controller) BaseMsgValidation(msg *SignedMessage) error

BaseMsgValidation returns error if msg is invalid (base validation)

func (*Controller) Decode

func (c *Controller) Decode(data []byte) error

Decode implementation

func (*Controller) Encode

func (c *Controller) Encode() ([]byte, error)

Encode implementation

func (*Controller) GetConfig added in v0.2.7

func (c *Controller) GetConfig() IConfig

func (*Controller) GetIdentifier

func (c *Controller) GetIdentifier() []byte

GetIdentifier returns QBFT Identifier, used to identify messages

func (*Controller) GetRoot added in v0.2.7

func (c *Controller) GetRoot() ([32]byte, error)

GetRoot returns the state's deterministic root

func (*Controller) InstanceForHeight

func (c *Controller) InstanceForHeight(height Height) *Instance

func (*Controller) ProcessMsg

func (c *Controller) ProcessMsg(msg *SignedMessage) (*SignedMessage, error)

ProcessMsg processes a new msg, returns decided message or error

func (*Controller) StartNewInstance

func (c *Controller) StartNewInstance(height Height, value []byte) error

StartNewInstance will start a new QBFT instance, if can't will return error

func (*Controller) UponDecided added in v0.2.7

func (c *Controller) UponDecided(msg *SignedMessage) (*SignedMessage, error)

UponDecided returns decided msg if decided, nil otherwise

func (*Controller) UponExistingInstanceMsg added in v0.2.7

func (c *Controller) UponExistingInstanceMsg(msg *SignedMessage) (*SignedMessage, error)

type Height

type Height uint64

type IConfig

type IConfig interface {

	// GetValueCheckF returns value check function
	GetValueCheckF() ProposedValueCheckF
	// GetProposerF returns func used to calculate proposer
	GetProposerF() ProposerF
	// GetNetwork returns a p2p Network instance
	GetNetwork() Network
	// GetTimer returns round timer
	GetTimer() Timer
	// contains filtered or unexported methods
}

type Instance

type Instance struct {
	State *State

	StartValue []byte
	// contains filtered or unexported fields
}

Instance is a single QBFT instance that starts with a Start call (including a value). Every new msg the ProcessMsg function needs to be called

func NewInstance

func NewInstance(
	config IConfig,
	share *types.Share,
	identifier []byte,
	height Height,
) *Instance

func (*Instance) BaseMsgValidation added in v0.3.0

func (i *Instance) BaseMsgValidation(msg *SignedMessage) error

func (*Instance) Broadcast

func (i *Instance) Broadcast(msg *SignedMessage) error

func (*Instance) CanProcessMessages added in v0.3.1

func (i *Instance) CanProcessMessages() bool

CanProcessMessages will return true if instance can process messages

func (*Instance) Decode

func (i *Instance) Decode(data []byte) error

Decode implementation

func (*Instance) Encode

func (i *Instance) Encode() ([]byte, error)

Encode implementation

func (*Instance) ForceStop added in v0.3.1

func (i *Instance) ForceStop()

func (*Instance) GetConfig

func (i *Instance) GetConfig() IConfig

GetConfig returns the instance config

func (*Instance) GetHeight

func (i *Instance) GetHeight() Height

GetHeight interface implementation

func (*Instance) GetRoot added in v0.2.7

func (i *Instance) GetRoot() ([32]byte, error)

GetRoot returns the state's deterministic root

func (*Instance) IsDecided

func (i *Instance) IsDecided() (bool, []byte)

IsDecided interface implementation

func (*Instance) MarshalJSON added in v0.3.2

func (i *Instance) MarshalJSON() ([]byte, error)

MarshalJSON is a custom JSON marshaller for Instance

func (*Instance) ProcessMsg

func (i *Instance) ProcessMsg(msg *SignedMessage) (decided bool, decidedValue []byte, aggregatedCommit *SignedMessage, err error)

ProcessMsg processes a new QBFT msg, returns non nil error on msg processing error

func (*Instance) Start

func (i *Instance) Start(value []byte, height Height)

Start is an interface implementation

func (*Instance) UnmarshalJSON added in v0.3.2

func (i *Instance) UnmarshalJSON(data []byte) error

UnmarshalJSON is a custom JSON unmarshaller for Instance

func (*Instance) UponCommit

func (i *Instance) UponCommit(signedCommit *SignedMessage, commitMsgContainer *MsgContainer) (bool, []byte, *SignedMessage, error)

UponCommit returns true if a quorum of commit messages was received. Assumes commit message is valid!

func (*Instance) UponRoundTimeout added in v0.2.7

func (i *Instance) UponRoundTimeout() error

type InstanceContainer

type InstanceContainer []*Instance

func (InstanceContainer) FindInstance

func (i InstanceContainer) FindInstance(height Height) *Instance

type Message

type Message struct {
	MsgType    MessageType
	Height     Height // QBFT instance Height
	Round      Round  // QBFT round for which the msg is for
	Identifier []byte `ssz-max:"56"` // instance Identifier this msg belongs to

	Root                     [32]byte `ssz-size:"32"`
	DataRound                Round
	RoundChangeJustification [][]byte `ssz-max:"13,65536"` // 2^16
	PrepareJustification     [][]byte `ssz-max:"13,65536"` // 2^16
}

func (*Message) Decode

func (msg *Message) Decode(data []byte) error

Decode returns error if decoding failed

func (*Message) Encode

func (msg *Message) Encode() ([]byte, error)

Encode returns a msg encoded bytes or error

func (*Message) GetPrepareJustifications added in v0.3.0

func (msg *Message) GetPrepareJustifications() ([]*SignedMessage, error)

func (*Message) GetRoot

func (msg *Message) GetRoot() ([32]byte, error)

GetRoot returns the root used for signing and verification

func (*Message) GetRoundChangeJustifications added in v0.3.0

func (msg *Message) GetRoundChangeJustifications() ([]*SignedMessage, error)

func (*Message) GetTree added in v0.3.0

func (m *Message) GetTree() (*ssz.Node, error)

GetTree ssz hashes the Message object

func (*Message) HashTreeRoot added in v0.3.0

func (m *Message) HashTreeRoot() ([32]byte, error)

HashTreeRoot ssz hashes the Message object

func (*Message) HashTreeRootWith added in v0.3.0

func (m *Message) HashTreeRootWith(hh ssz.HashWalker) (err error)

HashTreeRootWith ssz hashes the Message object with a hasher

func (*Message) MarshalSSZ added in v0.3.0

func (m *Message) MarshalSSZ() ([]byte, error)

MarshalSSZ ssz marshals the Message object

func (*Message) MarshalSSZTo added in v0.3.0

func (m *Message) MarshalSSZTo(buf []byte) (dst []byte, err error)

MarshalSSZTo ssz marshals the Message object to a target array

func (*Message) RoundChangePrepared added in v0.3.0

func (msg *Message) RoundChangePrepared() bool

RoundChangePrepared returns true if message is a RoundChange and prepared

func (*Message) SizeSSZ added in v0.3.0

func (m *Message) SizeSSZ() (size int)

SizeSSZ returns the ssz encoded size in bytes for the Message object

func (*Message) UnmarshalSSZ added in v0.3.0

func (m *Message) UnmarshalSSZ(buf []byte) error

UnmarshalSSZ ssz unmarshals the Message object

func (*Message) Validate

func (msg *Message) Validate() error

Validate returns error if msg validation doesn't pass. Msg validation checks the msg, it's variables for validity.

type MessageType

type MessageType uint64
const (
	ProposalMsgType MessageType = iota
	PrepareMsgType
	CommitMsgType
	RoundChangeMsgType
)

type MsgContainer

type MsgContainer struct {
	Msgs map[Round][]*SignedMessage
}

func NewMsgContainer

func NewMsgContainer() *MsgContainer

func (*MsgContainer) AddFirstMsgForSignerAndRound added in v0.2.7

func (c *MsgContainer) AddFirstMsgForSignerAndRound(msg *SignedMessage) (bool, error)

AddFirstMsgForSignerAndRound will add the first msg for each signer for a specific round, consequent msgs will not be added

func (*MsgContainer) AddMsg added in v0.3.0

func (c *MsgContainer) AddMsg(msg *SignedMessage)

AddMsg will add any message regardless of signers

func (*MsgContainer) AllMessaged

func (c *MsgContainer) AllMessaged() []*SignedMessage

AllMessaged returns all messages

func (*MsgContainer) Decode

func (c *MsgContainer) Decode(data []byte) error

Decode returns error if decoding failed

func (*MsgContainer) Encode

func (c *MsgContainer) Encode() ([]byte, error)

Encode returns the encoded struct in bytes or error

func (*MsgContainer) LongestUniqueSignersForRoundAndRoot added in v0.3.0

func (c *MsgContainer) LongestUniqueSignersForRoundAndRoot(round Round, root [32]byte) ([]types.OperatorID, []*SignedMessage)

LongestUniqueSignersForRoundAndRoot returns the longest set of unique signers and msgs for a specific round and value

func (*MsgContainer) MessagesForRound

func (c *MsgContainer) MessagesForRound(round Round) []*SignedMessage

MessagesForRound returns all msgs for Height and round, empty slice otherwise

func (*MsgContainer) MessagesForRoundAndRoot added in v0.3.0

func (c *MsgContainer) MessagesForRoundAndRoot(round Round, root [32]byte) []*SignedMessage

MessagesForRoundAndRoot returns all msgs for round and value, empty slice otherwise

type Network

type Network interface {
	p2p.Broadcaster
}

Network is the interface for networking across QBFT components

type ProposedValueCheckF

type ProposedValueCheckF func(data []byte) error

type ProposerF

type ProposerF func(state *State, round Round) types.OperatorID

type Round

type Round uint64

type SignedMessage

type SignedMessage struct {
	Signature types.Signature    `ssz-size:"96"`
	Signers   []types.OperatorID `ssz-max:"13"`
	// Message max size is
	//			3*8 + 56 + 32 + 8
	//			13*SignedMessage(Nested types, estimated at 2^15)
	//			13*SignedMessage(Nested types, estimated at 2^15)
	//			= 852088 ~= 2^20
	Message Message // message for which this signature is for

	// Full data max value is ConsensusData max value ~= 2^8 + 8 + 2^20 + 2^22 = 5243144
	FullData []byte `ssz-max:"5243144"`
}

func CreateCommit

func CreateCommit(state *State, config IConfig, root [32]byte) (*SignedMessage, error)

CreateCommit * Commit(

signCommit(
    UnsignedCommit(
        |current.blockchain|,
        current.round,
        signHash(hashBlockForCommitSeal(proposedBlock), current.id),
        digest(proposedBlock)),
        current.id
    )
);

func CreatePrepare

func CreatePrepare(state *State, config IConfig, newRound Round, root [32]byte) (*SignedMessage, error)

CreatePrepare * Prepare(

    signPrepare(
        UnsignedPrepare(
            |current.blockchain|,
            newRound,
            digest(m.proposedBlock)),
        current.id
        )
);

func CreateProposal

func CreateProposal(state *State, config IConfig, fullData []byte, roundChanges, prepares []*SignedMessage) (*SignedMessage, error)

CreateProposal *

	Proposal(
                      signProposal(
                          UnsignedProposal(
                              |current.blockchain|,
                              newRound,
                              digest(block)),
                          current.id),
                      block,
                      extractSignedRoundChanges(roundChanges),
                      extractSignedPrepares(prepares));

func CreateRoundChange

func CreateRoundChange(state *State, config IConfig, newRound Round, instanceStartValue []byte) (*SignedMessage, error)

CreateRoundChange * RoundChange(

    signRoundChange(
        UnsignedRoundChange(
            |current.blockchain|,
            newRound,
            digestOptionalBlock(current.lastPreparedBlock),
            current.lastPreparedRound),
    current.id),
    current.lastPreparedBlock,
    getRoundChangeJustification(current)
)

func (*SignedMessage) Aggregate

func (signedMsg *SignedMessage) Aggregate(sig types.MessageSignature) error

Aggregate will aggregate the signed message if possible (unique signers, same digest, valid)

func (*SignedMessage) CommonSigners

func (signedMsg *SignedMessage) CommonSigners(ids []types.OperatorID) bool

CommonSigners returns true if there is at least 1 common signer

func (*SignedMessage) Decode

func (signedMsg *SignedMessage) Decode(data []byte) error

Decode returns error if decoding failed

func (*SignedMessage) DeepCopy

func (signedMsg *SignedMessage) DeepCopy() *SignedMessage

DeepCopy returns a new instance of SignedMessage, deep copied

func (*SignedMessage) Encode

func (signedMsg *SignedMessage) Encode() ([]byte, error)

Encode returns a msg encoded bytes or error

func (*SignedMessage) GetRoot

func (signedMsg *SignedMessage) GetRoot() ([32]byte, error)

GetRoot returns the root used for signing and verification

func (*SignedMessage) GetSignature

func (signedMsg *SignedMessage) GetSignature() types.Signature

func (*SignedMessage) GetSigners

func (signedMsg *SignedMessage) GetSigners() []types.OperatorID

func (*SignedMessage) GetTree added in v0.3.0

func (s *SignedMessage) GetTree() (*ssz.Node, error)

GetTree ssz hashes the SignedMessage object

func (*SignedMessage) HashTreeRoot added in v0.3.0

func (s *SignedMessage) HashTreeRoot() ([32]byte, error)

HashTreeRoot ssz hashes the SignedMessage object

func (*SignedMessage) HashTreeRootWith added in v0.3.0

func (s *SignedMessage) HashTreeRootWith(hh ssz.HashWalker) (err error)

HashTreeRootWith ssz hashes the SignedMessage object with a hasher

func (*SignedMessage) MarshalSSZ added in v0.3.0

func (s *SignedMessage) MarshalSSZ() ([]byte, error)

MarshalSSZ ssz marshals the SignedMessage object

func (*SignedMessage) MarshalSSZTo added in v0.3.0

func (s *SignedMessage) MarshalSSZTo(buf []byte) (dst []byte, err error)

MarshalSSZTo ssz marshals the SignedMessage object to a target array

func (*SignedMessage) MatchedSigners

func (signedMsg *SignedMessage) MatchedSigners(ids []types.OperatorID) bool

MatchedSigners returns true if the provided signer ids are equal to GetSignerIds() without order significance

func (*SignedMessage) SizeSSZ added in v0.3.0

func (s *SignedMessage) SizeSSZ() (size int)

SizeSSZ returns the ssz encoded size in bytes for the SignedMessage object

func (*SignedMessage) UnmarshalSSZ added in v0.3.0

func (s *SignedMessage) UnmarshalSSZ(buf []byte) error

UnmarshalSSZ ssz unmarshals the SignedMessage object

func (*SignedMessage) Validate

func (signedMsg *SignedMessage) Validate() error

Validate returns error if msg validation doesn't pass. Msg validation checks the msg, it's variables for validity.

func (*SignedMessage) WithoutFUllData added in v0.3.0

func (signedMsg *SignedMessage) WithoutFUllData() *SignedMessage

WithoutFUllData returns SignedMessage without full data

type State

type State struct {
	Share                           *types.Share
	ID                              []byte // instance Identifier
	Round                           Round
	Height                          Height
	LastPreparedRound               Round
	LastPreparedValue               []byte
	ProposalAcceptedForCurrentRound *SignedMessage
	Decided                         bool
	DecidedValue                    []byte

	ProposeContainer     *MsgContainer
	PrepareContainer     *MsgContainer
	CommitContainer      *MsgContainer
	RoundChangeContainer *MsgContainer
}

func (*State) Decode

func (s *State) Decode(data []byte) error

Decode returns error if decoding failed

func (*State) Encode

func (s *State) Encode() ([]byte, error)

Encode returns a msg encoded bytes or error

func (*State) GetRoot

func (s *State) GetRoot() ([32]byte, error)

GetRoot returns the state's deterministic root

type Timer

type Timer interface {
	// TimeoutForRound will reset running timer if exists and will start a new timer for a specific round
	TimeoutForRound(round Round)
}

Timer is an interface for a round timer, calling the UponRoundTimeout when times out

Jump to

Keyboard shortcuts

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