fcccae3d1a
This changes the wallet.Open function signature to remove the database namespace parameters. This is done so that the wallet package itself is responsible for the location and opening of these namespaces from the database, rather than requiring the caller to open these ahead of time. A new wallet.Create function has also been added. This function initializes a new wallet in an empty database, using the same namespaces as wallet.Open will eventually use. This relieves the caller from needing to manage wallet database namespaces explicitly. Fixes #397.
278 lines
11 KiB
Go
278 lines
11 KiB
Go
// Copyright (c) 2014 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// This interface was inspired heavily by the excellent boltdb project at
|
|
// https://github.com/boltdb/bolt by Ben B. Johnson.
|
|
|
|
package walletdb
|
|
|
|
import "io"
|
|
|
|
// 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. Returns ErrBucketExists if the bucket already exists,
|
|
// ErrBucketNameRequired if the key is empty, or ErrIncompatibleValue
|
|
// if the key value is otherwise invalid for the particular database
|
|
// implementation. Other errors are possible depending on the
|
|
// implementation.
|
|
CreateBucket(key []byte) (Bucket, error)
|
|
|
|
// CreateBucketIfNotExists creates and returns a new nested bucket with
|
|
// the given key if it does not already exist. Returns
|
|
// ErrBucketNameRequired if the key is empty or ErrIncompatibleValue
|
|
// if the key value is otherwise invalid for the particular database
|
|
// backend. Other errors are possible depending on the implementation.
|
|
CreateBucketIfNotExists(key []byte) (Bucket, error)
|
|
|
|
// DeleteBucket removes a nested bucket with the given key. Returns
|
|
// ErrTxNotWritable if attempted against a read-only transaction and
|
|
// ErrBucketNotFound if the specified bucket does not exist.
|
|
DeleteBucket(key []byte) error
|
|
|
|
// ForEach invokes the passed function with every key/value pair in
|
|
// the bucket. This includes nested buckets, in which case the value
|
|
// is nil, but it does not include the key/value pairs within those
|
|
// nested buckets.
|
|
//
|
|
// NOTE: The values 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.
|
|
ForEach(func(k, v []byte) error) error
|
|
|
|
// 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. Returns ErrTxNotWritable if attempted against a
|
|
// read-only transaction.
|
|
Put(key, value []byte) error
|
|
|
|
// Get returns the value for the given key. Returns nil if the key does
|
|
// not exist in this bucket (or nested buckets).
|
|
//
|
|
// 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. This constraint prevents 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. Returns
|
|
// ErrTxNotWritable if attempted against a read-only transaction.
|
|
Delete(key []byte) error
|
|
|
|
// Cursor returns a new cursor, allowing for iteration over the bucket's
|
|
// key/value pairs and nested buckets in forward or backward order.
|
|
Cursor() Cursor
|
|
}
|
|
|
|
// 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, invalidate
|
|
// 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. Returns ErrTxNotWritable if attempted on a
|
|
// read-only transaction, or ErrIncompatibleValue if attempted when the
|
|
// cursor points to a nested bucket.
|
|
Delete() error
|
|
|
|
// First positions the cursor at the first key/value pair and returns
|
|
// the pair.
|
|
First() (key, value []byte)
|
|
|
|
// Last positions the cursor at the last key/value pair and returns the
|
|
// pair.
|
|
Last() (key, value []byte)
|
|
|
|
// Next moves the cursor one key/value pair forward and returns the new
|
|
// pair.
|
|
Next() (key, value []byte)
|
|
|
|
// Prev moves the cursor one key/value pair backward and returns the new
|
|
// pair.
|
|
Prev() (key, value []byte)
|
|
|
|
// Seek positions the cursor at the passed seek key. If the key does
|
|
// not exist, the cursor is moved to the next key after seek. Returns
|
|
// the new pair.
|
|
Seek(seek []byte) (key, value []byte)
|
|
}
|
|
|
|
// Tx represents a database transaction. It can either by read-only or
|
|
// read-write. The transaction provides a root 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 {
|
|
// RootBucket returns the top-most bucket for the namespace the
|
|
// transaction was created from.
|
|
RootBucket() Bucket
|
|
|
|
// Commit commits all changes that have been made through the root
|
|
// bucket and all of its sub-buckets to persistent storage.
|
|
Commit() error
|
|
|
|
// Rollback undoes all changes that have been made to the root bucket
|
|
// and all of its sub-buckets.
|
|
Rollback() error
|
|
}
|
|
|
|
// Namespace represents a database namespace that is inteded to support the
|
|
// concept of a single entity that controls the opening, creating, and closing
|
|
// of a database while providing other entities their own namespace to work in.
|
|
type Namespace interface {
|
|
// 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 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 on the transaction passed to the user-supplied
|
|
// function will result in a panic.
|
|
View(fn func(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 commited
|
|
// when the user-supplied function returns a nil error.
|
|
//
|
|
// Calling Rollback on the transaction passed to the user-supplied
|
|
// function will result in a panic.
|
|
Update(fn func(Tx) error) error
|
|
}
|
|
|
|
// NamespaceIsEmpty returns whether the namespace is empty, that is, whether there
|
|
// are no key/value pairs or nested buckets.
|
|
func NamespaceIsEmpty(namespace Namespace) (bool, error) {
|
|
var empty bool
|
|
err := namespace.View(func(tx Tx) error {
|
|
k, v := tx.RootBucket().Cursor().First()
|
|
empty = k == nil && v == nil
|
|
return nil
|
|
})
|
|
return empty, err
|
|
}
|
|
|
|
// DB represents a collection of namespaces which are persisted. All database
|
|
// access is performed through transactions which are obtained through the
|
|
// specific Namespace.
|
|
type DB interface {
|
|
// Namespace returns a Namespace interface for the provided key. See
|
|
// the Namespace interface documentation for more details. Attempting
|
|
// to access a Namespace on a database that is not open yet or has been
|
|
// closed will result in ErrDbNotOpen. Namespaces are created in the
|
|
// database on first access.
|
|
Namespace(key []byte) (Namespace, error)
|
|
|
|
// DeleteNamespace deletes the namespace for the passed key.
|
|
// ErrBucketNotFound will be returned if the namespace does not exist.
|
|
DeleteNamespace(key []byte) error
|
|
|
|
// Copy writes a copy of the database to the provided writer. This
|
|
// call will start a read-only transaction to perform all operations.
|
|
Copy(w io.Writer) error
|
|
|
|
// Close cleanly shuts down the database and syncs all data.
|
|
Close() error
|
|
}
|
|
|
|
// Driver defines a structure for backend drivers to use when they registered
|
|
// themselves as a backend which implements the Db interface.
|
|
type Driver struct {
|
|
// DbType is the identifier used to uniquely identify a specific
|
|
// database driver. There can be only one driver with the same name.
|
|
DbType string
|
|
|
|
// Create is the function that will be invoked with all user-specified
|
|
// arguments to create the database. This function must return
|
|
// ErrDbExists if the database already exists.
|
|
Create func(args ...interface{}) (DB, error)
|
|
|
|
// Open is the function that will be invoked with all user-specified
|
|
// arguments to open the database. This function must return
|
|
// ErrDbDoesNotExist if the database has not already been created.
|
|
Open func(args ...interface{}) (DB, error)
|
|
}
|
|
|
|
// driverList holds all of the registered database backends.
|
|
var drivers = make(map[string]*Driver)
|
|
|
|
// RegisterDriver adds a backend database driver to available interfaces.
|
|
// ErrDbTypeRegistered will be retruned if the database type for the driver has
|
|
// already been registered.
|
|
func RegisterDriver(driver Driver) error {
|
|
if _, exists := drivers[driver.DbType]; exists {
|
|
return ErrDbTypeRegistered
|
|
}
|
|
|
|
drivers[driver.DbType] = &driver
|
|
return nil
|
|
}
|
|
|
|
// SupportedDrivers returns a slice of strings that represent the database
|
|
// drivers that have been registered and are therefore supported.
|
|
func SupportedDrivers() []string {
|
|
supportedDBs := make([]string, 0, len(drivers))
|
|
for _, drv := range drivers {
|
|
supportedDBs = append(supportedDBs, drv.DbType)
|
|
}
|
|
return supportedDBs
|
|
}
|
|
|
|
// Create intializes and opens a database for the specified type. The arguments
|
|
// are specific to the database type driver. See the documentation for the
|
|
// database driver for further details.
|
|
//
|
|
// ErrDbUnknownType will be returned if the the database type is not registered.
|
|
func Create(dbType string, args ...interface{}) (DB, error) {
|
|
drv, exists := drivers[dbType]
|
|
if !exists {
|
|
return nil, ErrDbUnknownType
|
|
}
|
|
|
|
return drv.Create(args...)
|
|
}
|
|
|
|
// Open opens an existing database for the specified type. The arguments are
|
|
// specific to the database type driver. See the documentation for the database
|
|
// driver for further details.
|
|
//
|
|
// ErrDbUnknownType will be returned if the the database type is not registered.
|
|
func Open(dbType string, args ...interface{}) (DB, error) {
|
|
drv, exists := drivers[dbType]
|
|
if !exists {
|
|
return nil, ErrDbUnknownType
|
|
}
|
|
|
|
return drv.Open(args...)
|
|
}
|