consensus

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Apr 27, 2020 License: MIT Imports: 19 Imported by: 0

README

SBA* Consensus

Intro

SBA is the first implementation of a novel consensus algorithm called Proof-Of-Blind-Bid, a privacy oriented, hybrid Proof-of-Stake like protocol with instant, statistical finality guarantees. The protocol is based on an Honest Majority of Money (an Adversary can corrupt nodes controlling up to f percent of the total stake value ≥ 3f + 1) and weak network synchrony assumptions.

The roles in the protocol are split between two different types: Block Generators and Provisioners. Block Generators retain their privacy, with the proofs of stake computed in zero-knowledge to preserve the anonymity of the Block Generator. On the other hand, Provisioners are required to deanonymize their stakes and remain transparent about their activities in the consensus while their stake remains valid.

Both bids and stakes have a registration period of t, which begins when a Bid or Stake transaction is included in a final block and is required to elapse before the full-node is eligible to participate in the consensus.

SBA protocol can be conceptually defined with an inner loop (Block Loop). Block Loop is responsible for reaching a consensus on a uniform block.

Security Model

The security model of SBA is based on Snow White, a provably secure Proof-of-Stake protocol. SBA is secure under a ∆-delayed mildly adaptive adversary (an adversary who is required to choose the nodes controlling a maximum of f percent of the total stake he/she is willing to corrupt ∆ rounds before the actual corruption) and in a weakly synchronous network with a propagation delay of up to δ seconds.

Block Generator

Block Generator is the first of the two full-node types eligible to participate in the consensus. To become a Block Generator, a full-node has to submit a Bid Transaction. The Block Generator is eligible to participate in one phase:

  • Block Generation

In the aforementioned phase, Block Generators participate in a non-interactive lottery to be able to forge a candidate block.

Provisioner

Provisioner is the second of the two full-node types eligible to participate in the consensus.

Unlike a Block Generator, a Provisioner node is required to deanonymize the value of the stake to be able to participate in the consensus. While it is technically possible to obfuscate the stake value, the team has decided against the latter as the addition of stake value obfuscation would have slowed down the consensus, and simultaneously increased the block size.

The Provisioner is eligible to participate in two phases:

  • Block Reduction
  • Block Agreement

The two mentioned phases are the way for Provisioners to reach consensus on a single block, which can then be added to the chain.

Values

Message Header
Field Type
pubkeyBLS BLS Signature
round uint64
step uint64
blockhash uint256

Redux-like architecture

The architecture for the implementation is based on a Flux or Redux like approach, where a single component holds the right to mutate the state, and any peripheral components/processes merely receive read-only copies of it. These peripheral components can request updates to the state, but the central Coordinator ultimately decides whether or not this state change will be enacted.

This Coordinator is accompanied by a roundStore which facilitates the dispatching of incoming events to other consensus components. As the Coordinator holds the single source of truth regarding consensus state, it redirects incoming events to the correct endpoint depending on their own state (included in the message header). The consensus components are then given the correct events, who apply further, more specific processing.

Event streaming

The Coordinator and roundStore are abstracted through the EventPlayer interface, exposing three methods:

  • Forward(id)
  • Play(id)
  • Pause(id)

Each method takes in a specific identifier, which should correspond to one of the Listeners saved on the roundStore. This ensures that requests are made by components which are considered valid.

Forward is a way for a component to request a state change, namely incrementing the step. If the given ID is found in the roundStore, the step is updated and returned to the caller. Play and Pause are used to enable or disable receiving of events from the Coordinator. This is mainly used for directing incoming events to the correct component in the case of multiple listeners, and for disconnecting consensus components when a round is finalized.

Event propagation

Since the Coordinator has full knowledge of the state, it is also responsible for signing outgoing messages. This functionality is abstracted through the Signer interface and exposes two methods:

  • Gossip(topic, hash, payload, id)
  • SendInternally(topic, hash, payload, id)

Both methods take an id, which allows the Coordinator to refuse requests for sending messages from obsolete components. Gossip is intended for propagation to the network, while SendInternally is intended for internal propagation.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func InitAcceptedBlockUpdate

func InitAcceptedBlockUpdate(subscriber eventbus.Subscriber) (chan block.Block, uint32)

InitAcceptedBlockUpdate init listener to get updates about lastly accepted block in the chain

func InitRoundUpdate

func InitRoundUpdate(subscriber eventbus.Subscriber) <-chan RoundUpdate

InitRoundUpdate initializes a Round update channel and fires up the TopicListener as well. Its purpose is to lighten up a bit the amount of arguments in creating the handler for the collectors. Also it removes the need to store subscribers on the consensus process

func MockBidList added in v0.2.0

func MockBidList(amount int) user.BidList

MockBidList mocks a bid list

func MockMember added in v0.2.0

func MockMember(keys key.Keys) *user.Member

MockMember mocks a Provisioner

func MockProvisioners added in v0.2.0

func MockProvisioners(amount int) (*user.Provisioners, []key.Keys)

MockProvisioners mock a Provisioner set

Types

type AsyncState

type AsyncState struct {
	Round uint64
	Step  uint8
}

AsyncState is a representation of the consensus state at any given point in time. Can be used to 'date' messages that are passed between consensus components.

type Component added in v0.2.0

type Component interface {
	// Initialize a Component with data relevant to the current Round
	Initialize(EventPlayer, Signer, RoundUpdate) []TopicListener
	// Finalize allows a Component to perform cleanup operations before begin garbage collected
	Finalize()
	// ID allows the Coordinator to differentiate between components and
	// establish relevance or problems
	ID() uint32
}

Component is an ephemeral instance that lives solely for a round

type ComponentFactory added in v0.2.0

type ComponentFactory interface {
	// Instantiate a new Component without initializing it
	Instantiate() Component
}

ComponentFactory holds the data to create a Component (i.e. Signer, EventPublisher, RPCBus). Its responsibility is to recreate it on demand

type Coordinator added in v0.2.0

type Coordinator struct {
	*SyncState
	// contains filtered or unexported fields
}

Coordinator encapsulates the information about the Round and the Step of the coordinator. It also manages the roundStore, which aim is to centralize the state of the coordinator Component while decoupling them from each other and the EventBus

func Start added in v0.2.0

func Start(eventBus *eventbus.EventBus, keys key.Keys, factories ...ComponentFactory) *Coordinator

Start the coordinator by wiring the listener to the RoundUpdate

func (*Coordinator) CollectEvent added in v0.2.0

func (c *Coordinator) CollectEvent(m message.Message) error

CollectEvent collects the consensus message and reroutes it to the proper component. It is the callback passed to the eventbus.Multicaster

func (*Coordinator) CollectFinalize added in v0.2.0

func (c *Coordinator) CollectFinalize(m bytes.Buffer) error

CollectFinalize is triggered when the Agreement reaches quorum, and pre-emptively finalizes all consensus components, as they are no longer needed after this point.

func (*Coordinator) CollectRoundUpdate added in v0.2.0

func (c *Coordinator) CollectRoundUpdate(m message.Message) error

CollectRoundUpdate is triggered when the Chain propagates a new round update. The consensus components are swapped out, initialized, and the state will be updated to the new round.

func (*Coordinator) Compose added in v0.3.0

func (c *Coordinator) Compose(pf PacketFactory) InternalPacket

Compose complies with the consensus.Signer interface. It is a callback used by the consensus components to create the appropriate Header for the Consensus

func (*Coordinator) FinalizeRound added in v0.2.0

func (c *Coordinator) FinalizeRound()

FinalizeRound triggers the store to dispatch a finalize to the Components and clear the internal EventQueue

func (*Coordinator) Forward added in v0.2.0

func (c *Coordinator) Forward(id uint32) uint8

Forward complies to the EventPlayer interface. It increments the internal step count and returns it. It is used as a callback by the consensus components

func (*Coordinator) Gossip added in v0.2.0

func (c *Coordinator) Gossip(msg message.Message, id uint32) error

Gossip concatenates the topic, the header and the payload, and gossips it to the rest of the network. TODO: interface - marshaling should actually be done after the Gossip to respect the symmetry of the architecture

func (*Coordinator) Pause added in v0.2.0

func (c *Coordinator) Pause(id uint32)

Pause event streaming for the listener with the specified ID.

func (*Coordinator) Play added in v0.2.0

func (c *Coordinator) Play(id uint32)

Play will resume event streaming for the listener with the specified ID.

func (*Coordinator) SendInternally added in v0.2.0

func (c *Coordinator) SendInternally(topic topics.Topic, msg message.Message, id uint32) error

SendInternally publish a message for internal consumption (and therefore does not carry the topic, nor needs binary de- serialization)

func (*Coordinator) Sign added in v0.2.0

func (c *Coordinator) Sign(h header.Header) ([]byte, error)

Sign uses the blockhash (which is lost when decoupling the Header and the Payload) to recompose the Header and sign the Payload by adding it to the signature. Argument packet can be nil XXX: adjust the signature verification on reduction (and agreement)

func (*Coordinator) StopConsensus added in v0.2.0

func (c *Coordinator) StopConsensus(m message.Message) error

StopConsensus stop the consensus for this round, finalizes the Round, instantiate a new Store

type EventPlayer added in v0.2.0

type EventPlayer interface {
	// Forward signals the Coordinator that a component wishes to further the step
	// of the consensus. An ID needs to be supplied in order for the Coordinator to
	// decide if this request is valid.
	Forward(uint32) uint8
	// Pause signals the Coordinator to temporarily pause Event forwarding for
	// a Listener specified through its ID.
	Pause(uint32)
	// Play resumes the Event forwarding for a Listener with the given ID.
	Play(uint32)
}

EventPlayer is the interface used by Components to signal their intention to get, pause or resume events for a given Step

type FilteringListener added in v0.2.0

type FilteringListener struct {
	*SimpleListener
	// contains filtered or unexported fields
}

FilteringListener is a Listener that performs filtering before triggering the callback specified by the component Normally it is used to filter out events sent by Provisioners not being part of a committee or invalid messages. Filtering is applied to the `header.Header`

func (*FilteringListener) NotifyPayload added in v0.2.0

func (cb *FilteringListener) NotifyPayload(ev InternalPacket) error

NotifyPayload uses the filtering function to let only relevant events through

type InternalPacket added in v0.3.0

type InternalPacket interface {
	State() header.Header
}

InternalPacket is a specialization of the Payload of message.Message. It is used to unify messages used by the consensus, which need to carry the header.Header for consensus specific operations TODO: interface - consider breaking the Header down into AsyncState/SignedState/Header

func EmptyPacket added in v0.3.0

func EmptyPacket() InternalPacket

EmptyPacket returns an empty InternalPacket

type Listener added in v0.2.0

type Listener interface {
	// NotifyPayload forwards consensus events to the component
	NotifyPayload(InternalPacket) error
	// ID is used to later unsubscribe from the Coordinator. This is useful for components active throughout
	// multiple steps
	ID() uint32
	// Priority indicates the Priority of a Listener
	Priority() Priority
	Paused() bool
	Pause()
	Resume()
}

Listener subscribes to the Coordinator and forwards consensus events to the components

func NewFilteringListener added in v0.2.0

func NewFilteringListener(callback func(InternalPacket) error, filter func(header.Header) bool, priority Priority, paused bool) Listener

NewFilteringListener creates a FilteringListener

func NewSimpleListener added in v0.2.0

func NewSimpleListener(callback func(InternalPacket) error, priority Priority, paused bool) Listener

NewSimpleListener creates a SimpleListener

type Packet added in v0.3.0

type Packet interface {
	InternalPacket
	Sender() []byte
}

Packet is a consensus message payload with a full Header

type PacketFactory added in v0.3.0

type PacketFactory interface {
	Create([]byte, uint64, uint8) InternalPacket
}

PacketFactory is used by the signer/coordinator to create internal messages

type Priority added in v0.2.0

type Priority uint8

Priority indicates a rough order among components subscribed to the same topic

const (
	// HighPriority indicates precedence
	HighPriority Priority = iota
	// LowPriority indicates that the component should be the last to get updates
	LowPriority
)

type Queue added in v0.2.0

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

Queue is a Queue of Events grouped by rounds and steps. It is threadsafe through a sync.RWMutex.

func NewQueue added in v0.2.0

func NewQueue() *Queue

NewQueue creates a new Queue. It is primarily used by Collectors to temporarily store messages not yet relevant to the collection process.

func (*Queue) Clear added in v0.2.0

func (eq *Queue) Clear(round uint64)

Clear the queue.

func (*Queue) Flush added in v0.2.0

func (eq *Queue) Flush(round uint64) []message.Message

Flush all events stored for a specific round from the queue, and return them.

func (*Queue) GetEvents added in v0.2.0

func (eq *Queue) GetEvents(round uint64, step uint8) []message.Message

GetEvents returns the events for a round and step.

func (*Queue) PutEvent added in v0.2.0

func (eq *Queue) PutEvent(round uint64, step uint8, m message.Message)

PutEvent stores an Event at a given round and step.

type Restarter added in v0.3.0

type Restarter struct{}

Restarter creates the Restart message used by the Generator and the Reduction

func (Restarter) Create added in v0.3.0

func (r Restarter) Create(sender []byte, round uint64, step uint8) InternalPacket

Create a Restart message to restart the consensus

type RoundUpdate added in v0.2.0

type RoundUpdate struct {
	Round   uint64
	P       user.Provisioners
	BidList user.BidList
	Seed    []byte
	Hash    []byte
}

RoundUpdate carries the data about the new Round, such as the active Provisioners, the BidList, the Seed and the Hash

func MockRoundUpdate added in v0.2.0

func MockRoundUpdate(round uint64, p *user.Provisioners, bidList user.BidList) RoundUpdate

MockRoundUpdate mocks a round update

type Signer added in v0.2.0

type Signer interface {
	// Sign a payload. The first is parameter is a block hash
	Sign(header.Header) ([]byte, error)

	// Gossip concatenates all information before gossiping it to the
	// rest of the network.
	// It accepts a topic, a blockhash, a payload and the ID of the requesting
	// component
	Gossip(message.Message, uint32) error

	// Compose is used to inject authentication data to a component specific
	// packet. It is supposed to be used whenever a component needs to create a
	// **new** Packet for internal propagation
	Compose(PacketFactory) InternalPacket

	// SendInternally is used for internal forwarding
	SendInternally(topics.Topic, message.Message, uint32) error
}

Signer encapsulate the credentials to sign or authenticate outgoing events

type SimpleListener added in v0.2.0

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

SimpleListener implements Listener and uses a callback for notifying events

func (*SimpleListener) ID added in v0.2.0

func (s *SimpleListener) ID() uint32

ID returns the id to allow Component to unsubscribe

func (*SimpleListener) NotifyPayload added in v0.2.0

func (s *SimpleListener) NotifyPayload(ev InternalPacket) error

NotifyPayload triggers the callback specified during instantiation

func (*SimpleListener) Pause added in v0.2.0

func (s *SimpleListener) Pause()

Pause the SimpleListener

func (*SimpleListener) Paused added in v0.2.0

func (s *SimpleListener) Paused() bool

Paused returns whether this Listener is Paused

func (*SimpleListener) Priority added in v0.2.0

func (s *SimpleListener) Priority() Priority

Priority as indicated by the Listener interface

func (*SimpleListener) Resume added in v0.2.0

func (s *SimpleListener) Resume()

Resume the SimpleListener

type SimplePlayer added in v0.2.0

type SimplePlayer struct {
	Round uint64
	// contains filtered or unexported fields
}

SimplePlayer is used within tests to simulate the behavior of the consensus.EventPlayer

func NewSimplePlayer added in v0.2.0

func NewSimplePlayer() *SimplePlayer

NewSimplePlayer creates a SimplePlayer

func (*SimplePlayer) Forward added in v0.2.0

func (s *SimplePlayer) Forward(uint32) uint8

Forward upticks the step

func (*SimplePlayer) Pause added in v0.2.0

func (s *SimplePlayer) Pause(id uint32)

Pause as specified by the EventPlayer interface

func (*SimplePlayer) Play added in v0.2.0

func (s *SimplePlayer) Play(id uint32)

Play as specified by the EventPlayer interface

func (*SimplePlayer) State added in v0.2.0

func (s *SimplePlayer) State() State

State s a threadsafe method to return whether the player is paused or not

func (*SimplePlayer) Step added in v0.2.0

func (s *SimplePlayer) Step() uint8

Step guards the step with a lock

type State

type State uint8

State indicates the status of the EventPlayer

const (
	// PAUSED player
	PAUSED State = iota
	// RUNNING player
	RUNNING
)

type SyncState

type SyncState struct {
	Lock sync.RWMutex
	// contains filtered or unexported fields
}

SyncState is an implementation of State which can be shared by multiple processes. It also notifies subscribers of changes in the state's step.

func NewState

func NewState() *SyncState

NewState returns an initialized SyncState.

func (*SyncState) IncrementStep

func (s *SyncState) IncrementStep()

IncrementStep increments the SyncState step by 1. It also notifies any subscribers of the state change.

func (*SyncState) Round

func (s *SyncState) Round() uint64

Round returns the round that the SyncState is on.

func (*SyncState) Step

func (s *SyncState) Step() uint8

Step returns the step that the SyncState is on.

func (*SyncState) String

func (s *SyncState) String() string

func (*SyncState) ToBuffer added in v0.2.0

func (s *SyncState) ToBuffer() bytes.Buffer

ToBuffer returns a binary representation of the round and the step

func (*SyncState) Update

func (s *SyncState) Update(round uint64)

Update the round of the SyncState.

type Threshold

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

Threshold is a number which proof scores should be compared against. If a proof score does not exceed the Threshold value, it should be discarded.

func NewThreshold

func NewThreshold() *Threshold

NewThreshold returns an initialized Threshold.

func (*Threshold) Exceeds

func (t *Threshold) Exceeds(score []byte) bool

Exceeds checks whether the Threshold exceeds a given score.

func (*Threshold) Lower

func (t *Threshold) Lower()

Lower the Threshold by cutting it in half.

func (*Threshold) Reset

func (t *Threshold) Reset()

Reset the Threshold to its normal lower limit.

type TopicListener added in v0.2.0

type TopicListener struct {
	Listener
	Topic topics.Topic
}

TopicListener is Listener carrying a Topic

Jump to

Keyboard shortcuts

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