modules

package
v0.0.0-...-bd4e2c0 Latest Latest
Warning

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

Go to latest
Published: Sep 19, 2023 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package modules provides interfaces of modules that serve as building blocks of a Node. Implementations of those interfaces are not contained by this package and are expected to be provided by other packages.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type App

type App interface {

	// Apply a batch of Requests to the current stat of the application.
	// Note that the Batch only contains request references and it is up to the implementation of the App
	// to retrieve the corresponding payloads.
	// This can be done, for example, but having the App access the RequestStore (outside the Node).
	Apply(batch *requestpb.Batch) error

	// Snapshot returns a snapshot of the current application state in form of a byte slice.
	// The application must be able to restore its internal state when the returned value is passed to RestoreState.
	Snapshot() ([]byte, error)

	// RestoreState restores the internal application state based on data returned by Snapshot.
	RestoreState(snapshot []byte) error
}

App represents an application this library is used for replicating. It consumes Batches of Requests and executes them. This library makes sure that at each node the same sequence of Request Batches will be fed to all replicas of the application. The consumer of this library is responsible for providing each replica with an instance of the application in the same initial state, as well as for ensuring that processing of Requests is deterministic, in order to guarantee the state of the application to be consistent across all replicas.

type ClientTracker

type ClientTracker interface {
	ApplyEvent(event *eventpb.Event) *events.EventList
	Status() (s *statuspb.ClientTrackerStatus, err error)
}

type Crypto

type Crypto interface {

	// Sign signs the provided data and returns the resulting signature.
	// The data to be signed is the concatenation of all the passed byte slices.
	// A signature produced by Sign is verifiable using VerifyNodeSig or VerifyClientSig,
	// if, respectively, RegisterNodeKey or RegisterClientKey has been invoked with the corresponding public key.
	// Note that the private key used to produce the signature cannot be set ("registered") through this interface.
	// Storing and using the private key is completely implementation-dependent.
	Sign(data [][]byte) ([]byte, error)

	// RegisterNodeKey associates a public key with a numeric node ID.
	// The representation of the key is implementation-dependent.
	// Calls to VerifyNodeSig will fail until RegisterNodeKey is successfully called with the corresponding node ID.
	// Returns nil on success, a non-nil error on failure.
	RegisterNodeKey(pubKey []byte, nodeID t.NodeID) error

	// RegisterClientKey associates a public key with a numeric client ID.
	// The representation of the key is implementation-dependent.
	// Calls to VerifyClientSig will fail until RegisterClientKey is successfully called with the corresponding client ID.
	// Returns nil on success, a non-nil error on failure.
	RegisterClientKey(pubKey []byte, clientID t.ClientID) error

	// DeleteNodeKey removes the public key associated with nodeID from the module's state.
	// Any subsequent call to VerifyNodeSig(..., nodeID) will fail.
	DeleteNodeKey(nodeID t.NodeID)

	// DeleteClientKey removes the public key associated with clientID from the module's state.
	// Any subsequent call to VerifyClientSig(..., clientID) will fail.
	DeleteClientKey(clientID t.ClientID)

	// VerifyNodeSig verifies a signature produced by the node with numeric ID nodeID over data.
	// Returns nil on success (i.e., if the given signature is valid) and a non-nil error otherwise.
	// Note that RegisterNodeKey must be used to register the node's public key before calling VerifyNodeSig,
	// otherwise VerifyNodeSig will fail.
	VerifyNodeSig(data [][]byte, signature []byte, nodeID t.NodeID) error

	// VerifyClientSig verifies a signature produced by the client with numeric ID clientID over data.
	// Returns nil on success (i.e., if the given signature is valid) and a non-nil error otherwise.
	// Note that RegisterClientKey must be used to register the client's public key before calling VerifyClientSig,
	// otherwise VerifyClientSig will fail.
	VerifyClientSig(data [][]byte, signature []byte, clientID t.ClientID) error
}

The Crypto module is responsible for producing and verifying cryptographic signatures. It internally stores information about which clients and nodes are associated with which public keys. This information can be updated by the Node through appropriate events. Both clients and nodes are identified only by their numeric IDs.

type EventInterceptor

type EventInterceptor interface {

	// Intercept is called each time events are passed to a module, if an EventInterceptor is present in the node.
	// The expected behavior of Intercept is to add the intercepted events to a log for later analysis.
	// TODO: In the comment, also refer to the way events can be analyzed or replayed.
	Intercept(events *events.EventList) error
}

EventInterceptor provides a way to gain insight into the internal operation of the node. Before being passed to the respective target modules, events can be intercepted and logged for later analysis or replaying.

type Hasher

type Hasher interface {
	New() hash.Hash
}

TODO: Write comments.

type Modules

type Modules struct {
	Net           Net              // Sends messages produced by MirBFT through the network.
	Hasher        Hasher           // Computes hashes of requests and other data.
	Crypto        Crypto           // Performs cryptographic operations (except for computing hashes)
	App           App              // Implements user application logic. The user is expected to provide this module.
	WAL           WAL              // Implements a persistent write-ahead log for the case of crashes and restarts.
	ClientTracker ClientTracker    // Keeps the state related to clients and validates submitted requests.
	RequestStore  RequestStore     // Provides persistent storage for request data.
	Protocol      Protocol         // Implements the logic of the distributed protocol.
	Interceptor   EventInterceptor // Intercepts and logs all internal _Events_ for debugging purposes.
}

The Modules structs groups the modules a Node consists of.

func Defaults

func Defaults(m Modules) (*Modules, error)

Defaults takes a Modules object (as a value, not a pointer to it) and returns a pointer to a new Modules object with default modules inserted in fields where no module has been specified.

type Net

type Net interface {

	// Send sends msg to the node with ID dest.
	// Concurrent calls to Send are not (yet? TODO) supported.
	Send(dest t.NodeID, msg *messagepb.Message) error

	// ReceiveChan returns a channel to which the Net module writes all received messages and sender IDs
	// (Both the message itself and the sender ID are part of the ReceivedMessage struct.)
	ReceiveChan() <-chan ReceivedMessage
}

The Net module provides a simple abstract interface for sending messages to and receiving messages from other nodes. It abstracts away all network-related data and only deals with abstract numeric node IDs of senders and receivers at the interface. The messages returned from the Receive() must be (the library assumes them to be) authenticated. TODO: Deal explicitly with membership somewhere (not necessarily here). Note that the Net module's Receive() method is (currently? TODO) one of two ways of injecting messages in the Node. Alternatively, the Node.Step() method can be used directly to inject incoming messages. The user might choose to use the latter option, using the Net module only for sending. In such a case, the implementation of the Receive() method of the used Net module must simply block until interrupted by the passed channel (see documentation of Receive()).

type Protocol

type Protocol interface {

	// ApplyEvent applies a single input event to the protocol, making it advance its state
	// and potentially output a list of output events that the application of the input event results in.
	ApplyEvent(event *eventpb.Event) *events.EventList

	// Status returns the current state of the protocol.
	// Mostly for debugging purposes.
	Status() (s *statuspb.ProtocolStatus, err error)
}

Protocol represents the logic of a protocol. Similarly to the application, a protocol is modeled as a state machine. It takes a sequence of Events (e.g. incoming messages, ticks of the clock, etc.) as input. The processing of each input may result in a (deterministic) modification of the protocol state and a list of output events for further processing by other modules.

type ReceivedMessage

type ReceivedMessage struct {

	// Numeric ID of the node that sent the message.
	// Since the message is already authenticated by the Net module implementation, this information can be relied on.
	Sender t.NodeID

	// The received message itself.
	Msg *messagepb.Message
}

ReceivedMessage represents a message received over the network. This is the type of structures written to the channel returned by Net.ReceiveChan().

type RequestStore

type RequestStore interface {

	// PutRequest stores request the passed request data associated with the request reference.
	PutRequest(reqRef *requestpb.RequestRef, data []byte) error

	// GetRequest returns the stored request data associated with the passed request reference.
	// If no data is stored under the given reference, the returned error will be non-nil.
	GetRequest(reqRef *requestpb.RequestRef) ([]byte, error)

	// SetAuthenticated marks the referenced request as authenticated.
	// A request being authenticated means that the local node believes that
	// the request has indeed been sent by the client. This does not necessarily mean, however,
	// that the local node can convince other nodes about the request's authenticity
	// (e.g. if the local node received the request over an authenticated channel but the request is not signed).
	SetAuthenticated(reqRef *requestpb.RequestRef) error

	// IsAuthenticated returns true if the request is authenticated, false otherwise.
	IsAuthenticated(reqRef *requestpb.RequestRef) (bool, error)

	// PutAuthenticator stores an authenticator associated with the referenced request.
	// If an authenticator is already stored under the same reference, it will be overwritten.
	PutAuthenticator(reqRef *requestpb.RequestRef, auth []byte) error

	// GetAuthenticator returns the stored authenticator associated with the passed request reference.
	// If no authenticator is stored under the given reference, the returned error will be non-nil.
	GetAuthenticator(reqRef *requestpb.RequestRef) ([]byte, error)

	// GetDigestsByID returns a list of request Digests for which any information
	// (request data, authentication, or authenticator) is stored in the RequestStore.
	GetDigestsByID(clientID t.ClientID, reqNo t.ReqNo) ([][]byte, error)

	// Sync blocks until the effects of all preceding method invocations have been persisted.
	Sync() error
}

The RequestStore store persistently stores the payloads and authentication attributes of received requests. Each request is referenced using a msgs.RequestRef structure that contains the following fields: - ClientID: The numeric ID of the client that submitted the request. - ReqNo: The client-local sequence number of the request (how many request the client issued before this request). - Digest: A hash of the request payload. Along the request data (stored and retrieved using PutRequest() and GetRequest(), respectively), the RequestStore stores the authentication status of each request. A Boolean value (set and queried using SetAuthenticated() and IsAuthenticated(), respectively) signifies whether the request is authenticated to the local node. I.e, whether the local node believes that the request has indeed been sent by the client. This does not necessarily mean, however, that the local node can convince other nodes about the request's authenticity (e.g. if the local node received the request over an authenticated channel). Note that the authenticated flag cannot be cleared, as this would not be meaningful given its semantics.

For the purpose of convincing the rest of the system about a request's authenticity, an authenticator (e.g. a cryptographic signature) may be attached to the request. The authenticator is stored and retrieved using PutAuthenticator() and GetAuthenticator(), respectively. Note in particular that, in the context of a BFT TOB system, a node may accept a proposed request if the request is authenticated, even though the node does not have an authenticator (e.g. if the request has ben obtained directly from the client, but is not signed). For proposing a request, an authenticator is necessary to make sure that other correct nodes will accept the request.

All effects of method invocations that change the state of the RequestStore can only be guaranteed to be persisted When a subsequent invocation of Sync() returns. Without a call to Sync(), the effects may or may not be persisted.

TODO: Define functions for removal of old data.

type WAL

type WAL interface {

	// Append appends an entry with a retentionIndex to the WAL.
	// When Append returns, its effect on the WAL can, but might not have been persisted.
	// Persistence guarantees are only provided by the WAL after a call to Sync() returns.
	Append(entry *eventpb.Event, retentionIndex t.WALRetIndex) error

	// Truncate removes all entries from the WAL that have been appended
	// with a retentionIndex smaller than the specified one.
	// The effect of Truncate() is only guaranteed to be persisted to stable storage
	// after the next call to Sync() returns.
	Truncate(retentionIndex t.WALRetIndex) error

	// Sync persists the current state of the WAL to stable storage.
	// When Sync() returns, the effect of all previous calls to Append() and Truncate()
	// has been persisted to stable storage.
	Sync() error

	// LoadAll applies the provided forEach function to all WAL entries and their corresponding retentionIndexes
	// in the order in which they have been appended.
	LoadAll(forEach func(retentionIndex t.WALRetIndex, p *eventpb.Event)) error
}

The WAL (Write-Ahead Log) implements a persistent write-ahead log for the case of crashes and restarts. It simply persists (a serialized form of) events that are appended to it. The content of the WAL is used by the node to rebuild its state after, for example, a restart or a crash/recovery event. An integer retentionIndex is specified both when appending entries and when truncating the WAL. On truncation, the WAL removes all entries whose retentionIndex is smaller than the specified one.

Jump to

Keyboard shortcuts

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