45627c7a6a
Co-authored-by: Brannon King <countprimes@gmail.com>
466 lines
21 KiB
Go
466 lines
21 KiB
Go
// Copyright (c) 2015-2016 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Parts of this interface were inspired heavily by the excellent boltdb project
|
|
// at https://github.com/boltdb/bolt by Ben B. Johnson.
|
|
|
|
package database
|
|
|
|
import (
|
|
"github.com/lbryio/lbcd/chaincfg/chainhash"
|
|
btcutil "github.com/lbryio/lbcutil"
|
|
)
|
|
|
|
// Cursor represents a cursor over key/value pairs and nested buckets of a
|
|
// bucket.
|
|
//
|
|
// Note that open cursors are not tracked on bucket changes and any
|
|
// modifications to the bucket, with the exception of Cursor.Delete, invalidates
|
|
// the cursor. After invalidation, the cursor must be repositioned, or the keys
|
|
// and values returned may be unpredictable.
|
|
type Cursor interface {
|
|
// Bucket returns the bucket the cursor was created for.
|
|
Bucket() Bucket
|
|
|
|
// Delete removes the current key/value pair the cursor is at without
|
|
// invalidating the cursor.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrIncompatibleValue if attempted when the cursor points to a
|
|
// nested bucket
|
|
// - ErrTxNotWritable if attempted against a read-only transaction
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
Delete() error
|
|
|
|
// First positions the cursor at the first key/value pair and returns
|
|
// whether or not the pair exists.
|
|
First() bool
|
|
|
|
// Last positions the cursor at the last key/value pair and returns
|
|
// whether or not the pair exists.
|
|
Last() bool
|
|
|
|
// Next moves the cursor one key/value pair forward and returns whether
|
|
// or not the pair exists.
|
|
Next() bool
|
|
|
|
// Prev moves the cursor one key/value pair backward and returns whether
|
|
// or not the pair exists.
|
|
Prev() bool
|
|
|
|
// Seek positions the cursor at the first key/value pair that is greater
|
|
// than or equal to the passed seek key. Returns whether or not the
|
|
// pair exists.
|
|
Seek(seek []byte) bool
|
|
|
|
// Key returns the current key the cursor is pointing to.
|
|
Key() []byte
|
|
|
|
// Value returns the current value the cursor is pointing to. This will
|
|
// be nil for nested buckets.
|
|
Value() []byte
|
|
}
|
|
|
|
// Bucket represents a collection of key/value pairs.
|
|
type Bucket interface {
|
|
// Bucket retrieves a nested bucket with the given key. Returns nil if
|
|
// the bucket does not exist.
|
|
Bucket(key []byte) Bucket
|
|
|
|
// CreateBucket creates and returns a new nested bucket with the given
|
|
// key.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBucketExists if the bucket already exists
|
|
// - ErrBucketNameRequired if the key is empty
|
|
// - ErrIncompatibleValue if the key is otherwise invalid for the
|
|
// particular implementation
|
|
// - ErrTxNotWritable if attempted against a read-only transaction
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
CreateBucket(key []byte) (Bucket, error)
|
|
|
|
// CreateBucketIfNotExists creates and returns a new nested bucket with
|
|
// the given key if it does not already exist.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBucketNameRequired if the key is empty
|
|
// - ErrIncompatibleValue if the key is otherwise invalid for the
|
|
// particular implementation
|
|
// - ErrTxNotWritable if attempted against a read-only transaction
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
CreateBucketIfNotExists(key []byte) (Bucket, error)
|
|
|
|
// DeleteBucket removes a nested bucket with the given key. This also
|
|
// includes removing all nested buckets and keys under the bucket being
|
|
// deleted.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBucketNotFound if the specified bucket does not exist
|
|
// - ErrTxNotWritable if attempted against a read-only transaction
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
DeleteBucket(key []byte) error
|
|
|
|
// ForEach invokes the passed function with every key/value pair in the
|
|
// bucket. This does not include nested buckets or the key/value pairs
|
|
// within those nested buckets.
|
|
//
|
|
// WARNING: It is not safe to mutate data while iterating with this
|
|
// method. Doing so may cause the underlying cursor to be invalidated
|
|
// and return unexpected keys and/or values.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
//
|
|
// NOTE: The slices returned by this function are only valid during a
|
|
// transaction. Attempting to access them after a transaction has ended
|
|
// results in undefined behavior. Additionally, the slices must NOT
|
|
// be modified by the caller. These constraints prevent additional data
|
|
// copies and allows support for memory-mapped database implementations.
|
|
ForEach(func(k, v []byte) error) error
|
|
|
|
// ForEachBucket invokes the passed function with the key of every
|
|
// nested bucket in the current bucket. This does not include any
|
|
// nested buckets within those nested buckets.
|
|
//
|
|
// WARNING: It is not safe to mutate data while iterating with this
|
|
// method. Doing so may cause the underlying cursor to be invalidated
|
|
// and return unexpected keys and/or values.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
//
|
|
// NOTE: The keys returned by this function are only valid during a
|
|
// transaction. Attempting to access them after a transaction has ended
|
|
// results in undefined behavior. This constraint prevents additional
|
|
// data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
ForEachBucket(func(k []byte) error) error
|
|
|
|
// Cursor returns a new cursor, allowing for iteration over the bucket's
|
|
// key/value pairs and nested buckets in forward or backward order.
|
|
//
|
|
// You must seek to a position using the First, Last, or Seek functions
|
|
// before calling the Next, Prev, Key, or Value functions. Failure to
|
|
// do so will result in the same return values as an exhausted cursor,
|
|
// which is false for the Prev and Next functions and nil for Key and
|
|
// Value functions.
|
|
Cursor() Cursor
|
|
|
|
// Writable returns whether or not the bucket is writable.
|
|
Writable() bool
|
|
|
|
// Put saves the specified key/value pair to the bucket. Keys that do
|
|
// not already exist are added and keys that already exist are
|
|
// overwritten.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrKeyRequired if the key is empty
|
|
// - ErrIncompatibleValue if the key is the same as an existing bucket
|
|
// - ErrTxNotWritable if attempted against a read-only transaction
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
//
|
|
// NOTE: The slices passed to this function must NOT be modified by the
|
|
// caller. This constraint prevents the requirement for additional data
|
|
// copies and allows support for memory-mapped database implementations.
|
|
Put(key, value []byte) error
|
|
|
|
// Get returns the value for the given key. Returns nil if the key does
|
|
// not exist in this bucket. An empty slice is returned for keys that
|
|
// exist but have no value assigned.
|
|
//
|
|
// NOTE: The value returned by this function is only valid during a
|
|
// transaction. Attempting to access it after a transaction has ended
|
|
// results in undefined behavior. Additionally, the value must NOT
|
|
// be modified by the caller. These constraints prevent additional data
|
|
// copies and allows support for memory-mapped database implementations.
|
|
Get(key []byte) []byte
|
|
|
|
// Delete removes the specified key from the bucket. Deleting a key
|
|
// that does not exist does not return an error.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrKeyRequired if the key is empty
|
|
// - ErrIncompatibleValue if the key is the same as an existing bucket
|
|
// - ErrTxNotWritable if attempted against a read-only transaction
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
Delete(key []byte) error
|
|
}
|
|
|
|
// BlockRegion specifies a particular region of a block identified by the
|
|
// specified hash, given an offset and length.
|
|
type BlockRegion struct {
|
|
Hash *chainhash.Hash
|
|
Offset uint32
|
|
Len uint32
|
|
}
|
|
|
|
// Tx represents a database transaction. It can either by read-only or
|
|
// read-write. The transaction provides a metadata bucket against which all
|
|
// read and writes occur.
|
|
//
|
|
// As would be expected with a transaction, no changes will be saved to the
|
|
// database until it has been committed. The transaction will only provide a
|
|
// view of the database at the time it was created. Transactions should not be
|
|
// long running operations.
|
|
type Tx interface {
|
|
// Metadata returns the top-most bucket for all metadata storage.
|
|
Metadata() Bucket
|
|
|
|
// StoreBlock stores the provided block into the database. There are no
|
|
// checks to ensure the block connects to a previous block, contains
|
|
// double spends, or any additional functionality such as transaction
|
|
// indexing. It simply stores the block in the database.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBlockExists when the block hash already exists
|
|
// - ErrTxNotWritable if attempted against a read-only transaction
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
//
|
|
// Other errors are possible depending on the implementation.
|
|
StoreBlock(block *btcutil.Block) error
|
|
|
|
// HasBlock returns whether or not a block with the given hash exists
|
|
// in the database.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
//
|
|
// Other errors are possible depending on the implementation.
|
|
HasBlock(hash *chainhash.Hash) (bool, error)
|
|
|
|
// HasBlocks returns whether or not the blocks with the provided hashes
|
|
// exist in the database.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
//
|
|
// Other errors are possible depending on the implementation.
|
|
HasBlocks(hashes []chainhash.Hash) ([]bool, error)
|
|
|
|
// FetchBlockHeader returns the raw serialized bytes for the block
|
|
// header identified by the given hash. The raw bytes are in the format
|
|
// returned by Serialize on a wire.BlockHeader.
|
|
//
|
|
// It is highly recommended to use this function (or FetchBlockHeaders)
|
|
// to obtain block headers over the FetchBlockRegion(s) functions since
|
|
// it provides the backend drivers the freedom to perform very specific
|
|
// optimizations which can result in significant speed advantages when
|
|
// working with headers.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBlockNotFound if the requested block hash does not exist
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
// - ErrCorruption if the database has somehow become corrupted
|
|
//
|
|
// NOTE: The data returned by this function is only valid during a
|
|
// database transaction. Attempting to access it after a transaction
|
|
// has ended results in undefined behavior. This constraint prevents
|
|
// additional data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
FetchBlockHeader(hash *chainhash.Hash) ([]byte, error)
|
|
|
|
// FetchBlockHeaders returns the raw serialized bytes for the block
|
|
// headers identified by the given hashes. The raw bytes are in the
|
|
// format returned by Serialize on a wire.BlockHeader.
|
|
//
|
|
// It is highly recommended to use this function (or FetchBlockHeader)
|
|
// to obtain block headers over the FetchBlockRegion(s) functions since
|
|
// it provides the backend drivers the freedom to perform very specific
|
|
// optimizations which can result in significant speed advantages when
|
|
// working with headers.
|
|
//
|
|
// Furthermore, depending on the specific implementation, this function
|
|
// can be more efficient for bulk loading multiple block headers than
|
|
// loading them one-by-one with FetchBlockHeader.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBlockNotFound if any of the request block hashes do not exist
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
// - ErrCorruption if the database has somehow become corrupted
|
|
//
|
|
// NOTE: The data returned by this function is only valid during a
|
|
// database transaction. Attempting to access it after a transaction
|
|
// has ended results in undefined behavior. This constraint prevents
|
|
// additional data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
FetchBlockHeaders(hashes []chainhash.Hash) ([][]byte, error)
|
|
|
|
// FetchBlock returns the raw serialized bytes for the block identified
|
|
// by the given hash. The raw bytes are in the format returned by
|
|
// Serialize on a wire.MsgBlock.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBlockNotFound if the requested block hash does not exist
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
// - ErrCorruption if the database has somehow become corrupted
|
|
//
|
|
// NOTE: The data returned by this function is only valid during a
|
|
// database transaction. Attempting to access it after a transaction
|
|
// has ended results in undefined behavior. This constraint prevents
|
|
// additional data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
FetchBlock(hash *chainhash.Hash) ([]byte, error)
|
|
|
|
// FetchBlocks returns the raw serialized bytes for the blocks
|
|
// identified by the given hashes. The raw bytes are in the format
|
|
// returned by Serialize on a wire.MsgBlock.
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBlockNotFound if the any of the requested block hashes do not
|
|
// exist
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
// - ErrCorruption if the database has somehow become corrupted
|
|
//
|
|
// NOTE: The data returned by this function is only valid during a
|
|
// database transaction. Attempting to access it after a transaction
|
|
// has ended results in undefined behavior. This constraint prevents
|
|
// additional data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
FetchBlocks(hashes []chainhash.Hash) ([][]byte, error)
|
|
|
|
// FetchBlockRegion returns the raw serialized bytes for the given
|
|
// block region.
|
|
//
|
|
// For example, it is possible to directly extract Bitcoin transactions
|
|
// and/or scripts from a block with this function. Depending on the
|
|
// backend implementation, this can provide significant savings by
|
|
// avoiding the need to load entire blocks.
|
|
//
|
|
// The raw bytes are in the format returned by Serialize on a
|
|
// wire.MsgBlock and the Offset field in the provided BlockRegion is
|
|
// zero-based and relative to the start of the block (byte 0).
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBlockNotFound if the requested block hash does not exist
|
|
// - ErrBlockRegionInvalid if the region exceeds the bounds of the
|
|
// associated block
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
// - ErrCorruption if the database has somehow become corrupted
|
|
//
|
|
// NOTE: The data returned by this function is only valid during a
|
|
// database transaction. Attempting to access it after a transaction
|
|
// has ended results in undefined behavior. This constraint prevents
|
|
// additional data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
FetchBlockRegion(region *BlockRegion) ([]byte, error)
|
|
|
|
// FetchBlockRegions returns the raw serialized bytes for the given
|
|
// block regions.
|
|
//
|
|
// For example, it is possible to directly extract Bitcoin transactions
|
|
// and/or scripts from various blocks with this function. Depending on
|
|
// the backend implementation, this can provide significant savings by
|
|
// avoiding the need to load entire blocks.
|
|
//
|
|
// The raw bytes are in the format returned by Serialize on a
|
|
// wire.MsgBlock and the Offset fields in the provided BlockRegions are
|
|
// zero-based and relative to the start of the block (byte 0).
|
|
//
|
|
// The interface contract guarantees at least the following errors will
|
|
// be returned (other implementation-specific errors are possible):
|
|
// - ErrBlockNotFound if any of the requested block hashed do not
|
|
// exist
|
|
// - ErrBlockRegionInvalid if one or more region exceed the bounds of
|
|
// the associated block
|
|
// - ErrTxClosed if the transaction has already been closed
|
|
// - ErrCorruption if the database has somehow become corrupted
|
|
//
|
|
// NOTE: The data returned by this function is only valid during a
|
|
// database transaction. Attempting to access it after a transaction
|
|
// has ended results in undefined behavior. This constraint prevents
|
|
// additional data copies and allows support for memory-mapped database
|
|
// implementations.
|
|
FetchBlockRegions(regions []BlockRegion) ([][]byte, error)
|
|
|
|
// ******************************************************************
|
|
// Methods related to both atomic metadata storage and block storage.
|
|
// ******************************************************************
|
|
|
|
// Commit commits all changes that have been made to the metadata or
|
|
// block storage. Depending on the backend implementation this could be
|
|
// to a cache that is periodically synced to persistent storage or
|
|
// directly to persistent storage. In any case, all transactions which
|
|
// are started after the commit finishes will include all changes made
|
|
// by this transaction. Calling this function on a managed transaction
|
|
// will result in a panic.
|
|
Commit() error
|
|
|
|
// Rollback undoes all changes that have been made to the metadata or
|
|
// block storage. Calling this function on a managed transaction will
|
|
// result in a panic.
|
|
Rollback() error
|
|
}
|
|
|
|
// DB provides a generic interface that is used to store bitcoin blocks and
|
|
// related metadata. This interface is intended to be agnostic to the actual
|
|
// mechanism used for backend data storage. The RegisterDriver function can be
|
|
// used to add a new backend data storage method.
|
|
//
|
|
// This interface is divided into two distinct categories of functionality.
|
|
//
|
|
// The first category is atomic metadata storage with bucket support. This is
|
|
// accomplished through the use of database transactions.
|
|
//
|
|
// The second category is generic block storage. This functionality is
|
|
// intentionally separate because the mechanism used for block storage may or
|
|
// may not be the same mechanism used for metadata storage. For example, it is
|
|
// often more efficient to store the block data as flat files while the metadata
|
|
// is kept in a database. However, this interface aims to be generic enough to
|
|
// support blocks in the database too, if needed by a particular backend.
|
|
type DB interface {
|
|
// Type returns the database driver type the current database instance
|
|
// was created with.
|
|
Type() string
|
|
|
|
// Begin starts a transaction which is either read-only or read-write
|
|
// depending on the specified flag. Multiple read-only transactions
|
|
// can be started simultaneously while only a single read-write
|
|
// transaction can be started at a time. The call will block when
|
|
// starting a read-write transaction when one is already open.
|
|
//
|
|
// NOTE: The transaction must be closed by calling Rollback or Commit on
|
|
// it when it is no longer needed. Failure to do so can result in
|
|
// unclaimed memory and/or inablity to close the database due to locks
|
|
// depending on the specific database implementation.
|
|
Begin(writable bool) (Tx, error)
|
|
|
|
// View invokes the passed function in the context of a managed
|
|
// read-only transaction. Any errors returned from the user-supplied
|
|
// function are returned from this function.
|
|
//
|
|
// Calling Rollback or Commit on the transaction passed to the
|
|
// user-supplied function will result in a panic.
|
|
View(fn func(tx Tx) error) error
|
|
|
|
// Update invokes the passed function in the context of a managed
|
|
// read-write transaction. Any errors returned from the user-supplied
|
|
// function will cause the transaction to be rolled back and are
|
|
// returned from this function. Otherwise, the transaction is committed
|
|
// when the user-supplied function returns a nil error.
|
|
//
|
|
// Calling Rollback or Commit on the transaction passed to the
|
|
// user-supplied function will result in a panic.
|
|
Update(fn func(tx Tx) error) error
|
|
|
|
// Close cleanly shuts down the database and syncs all data. It will
|
|
// block until all database transactions have been finalized (rolled
|
|
// back or committed).
|
|
Close() error
|
|
}
|