Package api implements the storage backend API.



const (
	// ModuleName is the storage module name.
	ModuleName = "storage"

	// WriteLogIteratorChunkSize defines the chunk size of write log entries
	// for the GetDiff method.
	WriteLogIteratorChunkSize = 10


var (
	// ErrCantProve is the error returned when the backend is incapable
	// of generating proofs (unsupported, no key, etc).
	ErrCantProve = errors.New(ModuleName, 1, "storage: unable to provide proofs")
	// ErrNoRoots is the error returned when the generated receipt would
	// not contain any roots.
	ErrNoRoots = errors.New(ModuleName, 2, "storage: no roots to generate receipt for")
	// ErrExpectedRootMismatch is the error returned when the expected root
	// does not match the computed root.
	ErrExpectedRootMismatch = errors.New(ModuleName, 3, "storage: expected root mismatch")
	// ErrUnsupported is the error returned when the called method is not
	// supported by the given backend.
	ErrUnsupported = errors.New(ModuleName, 4, "storage: method not supported by backend")
	// ErrLimitReached means that a configured limit has been reached.
	ErrLimitReached = errors.New(ModuleName, 5, "storage: limit reached")

	// ErrNodeNotFound indicates that a node with the specified hash couldn't be found
	// in the database.
	ErrNodeNotFound = nodedb.ErrNodeNotFound
	// ErrWriteLogNotFound indicates that a write log for the specified storage hashes
	// couldn't be found.
	ErrWriteLogNotFound = nodedb.ErrWriteLogNotFound
	// ErrNotFinalized indicates that the operation requires a version to be finalized
	// but the version is not yet finalized.
	ErrNotFinalized = nodedb.ErrNotFinalized
	// ErrAlreadyFinalized indicates that the given version has already been finalized.
	ErrAlreadyFinalized = nodedb.ErrAlreadyFinalized
	// ErrVersionNotFound indicates that the given version cannot be found.
	ErrVersionNotFound = nodedb.ErrVersionNotFound
	// ErrPreviousVersionMismatch indicates that the version given for the old root does
	// not match the previous version.
	ErrPreviousVersionMismatch = nodedb.ErrPreviousVersionMismatch
	// ErrVersionWentBackwards indicates that the new version is earlier than an already
	// inserted version.
	ErrVersionWentBackwards = nodedb.ErrVersionWentBackwards
	// ErrRootNotFound indicates that the given root cannot be found.
	ErrRootNotFound = nodedb.ErrRootNotFound
	// ErrRootMustFollowOld indicates that the passed new root does not follow old root.
	ErrRootMustFollowOld = nodedb.ErrRootMustFollowOld
	// ErrReadOnly indicates that the storage backend is read-only.
	ErrReadOnly = nodedb.ErrReadOnly

	// ReceiptSignatureContext is the signature context used for verifying MKVS receipts.
	ReceiptSignatureContext = signature.NewContext("oasis-core/storage: receipt", signature.WithChainSeparation())

var (

	// ServiceName is the gRPC service name.
	ServiceName = cmnGrpc.NewServiceName("Storage")

	// MethodSyncGet is the SyncGet method.
	MethodSyncGet = ServiceName.NewMethod("SyncGet", GetRequest{})
	// MethodSyncGetPrefixes is the SyncGetPrefixes method.
	MethodSyncGetPrefixes = ServiceName.NewMethod("SyncGetPrefixes", GetPrefixesRequest{})
	// MethodSyncIterate is the SyncIterate method.
	MethodSyncIterate = ServiceName.NewMethod("SyncIterate", IterateRequest{})
	// MethodApply is the Apply method.
	MethodApply = ServiceName.NewMethod("Apply", ApplyRequest{}).
				WithNamespaceExtractor(func(ctx context.Context, req interface{}) (common.Namespace, error) {
			r, ok := req.(*ApplyRequest)
			if !ok {
				return common.Namespace{}, errInvalidRequestType
			return r.Namespace, nil
		WithAccessControl(func(ctx context.Context, req interface{}) (bool, error) {
			return true, nil

	// MethodApplyBatch is the ApplyBatch method.
	MethodApplyBatch = ServiceName.NewMethod("ApplyBatch", ApplyBatchRequest{}).
						WithNamespaceExtractor(func(ctx context.Context, req interface{}) (common.Namespace, error) {
			r, ok := req.(*ApplyBatchRequest)
			if !ok {
				return common.Namespace{}, errInvalidRequestType
			return r.Namespace, nil
		WithAccessControl(func(ctx context.Context, req interface{}) (bool, error) {
			return true, nil

	// MethodGetDiff is the GetDiff method.
	MethodGetDiff = ServiceName.NewMethod("GetDiff", GetDiffRequest{}).
					WithNamespaceExtractor(func(ctx context.Context, req interface{}) (common.Namespace, error) {
			r, ok := req.(*GetDiffRequest)
			if !ok {
				return common.Namespace{}, errInvalidRequestType
			return r.StartRoot.Namespace, nil
		WithAccessControl(func(ctx context.Context, req interface{}) (bool, error) {
			return true, nil

	// MethodGetCheckpoints is the GetCheckpoints method.
	MethodGetCheckpoints = ServiceName.NewMethod("GetCheckpoints", checkpoint.GetCheckpointsRequest{}).
							WithNamespaceExtractor(func(ctx context.Context, req interface{}) (common.Namespace, error) {
			r, ok := req.(*checkpoint.GetCheckpointsRequest)
			if !ok {
				return common.Namespace{}, errInvalidRequestType
			return r.Namespace, nil
		WithAccessControl(func(ctx context.Context, req interface{}) (bool, error) {
			return true, nil

	// MethodGetCheckpointChunk is the GetCheckpointChunk method.
	MethodGetCheckpointChunk = ServiceName.NewMethod("GetCheckpointChunk", checkpoint.ChunkMetadata{}).
								WithNamespaceExtractor(func(ctx context.Context, req interface{}) (common.Namespace, error) {
			cm, ok := req.(*checkpoint.ChunkMetadata)
			if !ok {
				return common.Namespace{}, errInvalidRequestType
			return cm.Root.Namespace, nil
		WithAccessControl(func(ctx context.Context, req interface{}) (bool, error) {
			return true, nil


func RegisterService

func RegisterService(server *grpc.Server, service Backend)

RegisterService registers a new sentry service with the given gRPC server.


type ApplyBatchRequest

type ApplyBatchRequest struct {
	Namespace common.Namespace `json:"namespace"`
	DstRound  uint64           `json:"dst_round"`
	Ops       []ApplyOp        `json:"ops"`

ApplyBatchRequest is an ApplyBatch request.

type ApplyOp

type ApplyOp struct {
	// SrcRound is the source root round.
	SrcRound uint64 `json:"src_round"`
	// SrcRoot is the merkle root to apply the operations against. It may
	// refer to a nil node (empty hash) in which case a new root will be
	// created.
	SrcRoot hash.Hash `json:"src_root"`
	// DstRoot is the expected merkle root after applying the write log.
	DstRoot hash.Hash `json:"dst_root"`
	// WriteLog is a write log of operations to apply.
	WriteLog WriteLog `json:"writelog"`

ApplyOp is an apply operation within a batch of apply operations.

type ApplyRequest

type ApplyRequest struct {
	Namespace common.Namespace `json:"namespace"`
	SrcRound  uint64           `json:"src_round"`
	SrcRoot   hash.Hash        `json:"src_root"`
	DstRound  uint64           `json:"dst_round"`
	DstRoot   hash.Hash        `json:"dst_root"`
	WriteLog  WriteLog         `json:"writelog"`

ApplyRequest is an Apply request.

type Backend

type Backend interface {

	// Apply applies a set of operations against the MKVS.  The root may refer
	// to a nil node, in which case a new root will be created.
	// The expected new root is used to check if the new root after all the
	// operations are applied already exists in the local DB.  If it does, the
	// Apply is ignored.
	Apply(ctx context.Context, request *ApplyRequest) ([]*Receipt, error)

	// ApplyBatch applies multiple sets of operations against the MKVS and
	// returns a single receipt covering all applied roots.
	// See Apply for more details.
	ApplyBatch(ctx context.Context, request *ApplyBatchRequest) ([]*Receipt, error)

	// GetDiff returns an iterator of write log entries that must be applied
	// to get from the first given root to the second one.
	GetDiff(ctx context.Context, request *GetDiffRequest) (WriteLogIterator, error)

	// Cleanup closes/cleans up the storage backend.

	// Initialized returns a channel that will be closed when the
	// backend is initialized and ready to service requests.
	Initialized() <-chan struct{}

Backend is a storage backend implementation.

func NewStorageClient

func NewStorageClient(c *grpc.ClientConn) Backend

NewStorageClient creates a new gRPC storage client service.

type ClientBackend

type ClientBackend interface {

	// GetConnectedNodes returns currently connected storage nodes.
	GetConnectedNodes() []*node.Node

	// EnsureCommitteeVersion waits for the storage committee client to be fully synced to the
	// given version.
	EnsureCommitteeVersion(ctx context.Context, version int64) error

ClientBackend is a storage client backend implementation.

type Config

type Config struct {
	// Backend is the database backend.
	Backend string

	// DB is the path to the database.
	DB string

	// Signer is the signing key to use for generating recipts.
	Signer signature.Signer

	// ApplyLockLRUSlots is the number of LRU slots to use for Apply call locks.
	ApplyLockLRUSlots uint64

	// InsecureSkipChecks bypasses the known root checks.
	InsecureSkipChecks bool

	// Namespace is the namespace contained within the database.
	Namespace common.Namespace

	// MaxCacheSize is the maximum in-memory cache size for the database.
	MaxCacheSize int64

	// DiscardWriteLogs will cause all write logs to be discarded.
	DiscardWriteLogs bool

	// NoFsync will disable fsync() where possible.
	NoFsync bool

	// MemoryOnly will make the storage memory-only (if the backend supports it).
	MemoryOnly bool

	// ReadOnly will make the storage read-only.
	ReadOnly bool

Config is the storage backend configuration.

func (*Config) ToNodeDB

func (cfg *Config) ToNodeDB() *nodedb.Config

ToNodeDB converts from a Config to a node DB Config.

type Depth

type Depth = mkvsNode.Depth

Depth determines the node's (bit) depth in the tree. It is also used for storing the Key length in bits.

type GetDiffRequest

type GetDiffRequest struct {
	StartRoot Root        `json:"start_root"`
	EndRoot   Root        `json:"end_root"`
	Options   SyncOptions `json:"options"`

GetDiffRequest is a GetDiff request.

type GetPrefixesRequest

type GetPrefixesRequest = syncer.GetPrefixesRequest

GetPrefixesRequest is a request for the SyncGetPrefixes operation.

type GetRequest

type GetRequest = syncer.GetRequest

GetRequest is a request for the SyncGet operation.

type InternalNode

type InternalNode = mkvsNode.InternalNode

InternalNode is an internal node with two children.

type IterateRequest

type IterateRequest = syncer.IterateRequest

IterateRequest is a request for the SyncIterate operation.

type Key

type Key = mkvsNode.Key

Key is a node's key spelled out from the root to the node.

type LeafNode

type LeafNode = mkvsNode.LeafNode

LeafNode is a leaf node containing a key/value pair.

type LocalBackend

type LocalBackend interface {

	// Checkpointer returns the checkpoint creator/restorer for this storage backend.
	Checkpointer() checkpoint.CreateRestorer

	// NodeDB returns the underlying node database.
	NodeDB() nodedb.NodeDB

LocalBackend is a storage implementation with a local backing store.

type LogEntry

type LogEntry = writelog.LogEntry

LogEntry is a write log entry.

type Node

type Node = mkvsNode.Node

Node is either an InternalNode or a LeafNode.

type NodeDB

type NodeDB = nodedb.NodeDB

NodeDB is a node database.

type Pointer

type Pointer = mkvsNode.Pointer

Pointer is a pointer to another node.

type Proof

type Proof = syncer.Proof

Proof is a Merkle proof for a subtree.

type ProofResponse

type ProofResponse = syncer.ProofResponse

ProofResponse is a response for requests that produce proofs.

type Receipt

type Receipt struct {

Receipt is a signed ReceiptBody.

func SignReceipt

func SignReceipt(signer signature.Signer, ns common.Namespace, round uint64, roots []hash.Hash) (*Receipt, error)

SignReceipt signs a storage receipt for the given roots.

func (*Receipt) Open

func (s *Receipt) Open(receipt *ReceiptBody) error

Open first verifies the blob signature then unmarshals the blob.

type ReceiptBody

type ReceiptBody struct {
	// Version is the storage data structure version.
	Version uint16 `json:"version"`
	// Namespace is the chain namespace under which the root(s) are stored.
	Namespace common.Namespace `json:"ns"`
	// Round is the chain round in which the root(s) are stored.
	Round uint64 `json:"round"`
	// Roots are the merkle roots of the merklized data structure that the
	// storage node is certifying to store.
	Roots []hash.Hash `json:"roots"`

ReceiptBody is the body of a receipt.

type Root

type Root = mkvsNode.Root

Root is a storage root.

type RootCache

type RootCache struct {
	// contains filtered or unexported fields

RootCache is a LRU based tree cache.

func NewRootCache

func NewRootCache(
	localDB nodedb.NodeDB,
	remoteSyncer syncer.ReadSyncer,
	applyLockLRUSlots uint64,
	insecureSkipChecks bool,
) (*RootCache, error)

func (*RootCache) Apply

func (rc *RootCache) Apply(
	ctx context.Context,
	ns common.Namespace,
	srcVersion uint64,
	srcRoot hash.Hash,
	dstVersion uint64,
	dstRoot hash.Hash,
	writeLog WriteLog,
) (*hash.Hash, error)

Apply applies the write log, bypassing the apply operation iff the new root already is in the node database.

func (*RootCache) GetTree

func (rc *RootCache) GetTree(ctx context.Context, root Root) (mkvs.Tree, error)

GetTree gets a tree entry from the cache by the root iff present, or creates a new tree with the specified root in the node database.

func (*RootCache) HasRoot

func (rc *RootCache) HasRoot(root Root) bool

type SyncChunk

type SyncChunk struct {
	Final    bool     `json:"final"`
	WriteLog WriteLog `json:"writelog"`

SyncChunk is a chunk of write log entries sent during GetDiff operation.

type SyncOptions

type SyncOptions struct {
	OffsetKey []byte `json:"offset_key"`
	Limit     uint64 `json:"limit"`

SyncOptions are the sync options.

type TreeID

type TreeID = syncer.TreeID

TreeID identifies a specific tree and a position within that tree.

type WriteLog

type WriteLog = writelog.WriteLog

WriteLog is a write log.

The keys in the write log must be unique.

type WriteLogIterator

type WriteLogIterator = writelog.Iterator

WriteLogIterator iterates over write log entries.