Release v0.1.0

This commit is contained in:
Manu Herrera
2019-10-01 12:22:30 -03:00
parent 41e6aad190
commit d301c63596
915 changed files with 378049 additions and 11 deletions

16
vendor/github.com/btcsuite/btcwallet/walletdb/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,16 @@
ISC License
Copyright (c) 2013-2017 The btcsuite developers
Copyright (c) 2015-2016 The Decred developers
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,75 @@
walletdb
========
[![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)]
(https://travis-ci.org/btcsuite/btcwallet)
Package walletdb provides a namespaced database interface for btcwallet.
A wallet essentially consists of a multitude of stored data such as private
and public keys, key derivation bits, pay-to-script-hash scripts, and various
metadata. One of the issues with many wallets is they are tightly integrated.
Designing a wallet with loosely coupled components that provide specific
functionality is ideal, however it presents a challenge in regards to data
storage since each component needs to store its own data without knowing the
internals of other components or breaking atomicity.
This package solves this issue by providing a namespaced database interface that
is intended to be used by the main wallet daemon. This allows the potential for
any backend database type with a suitable driver. Each component, which will
typically be a package, can then implement various functionality such as address
management, voting pools, and colored coin metadata in their own namespace
without having to worry about conflicts with other packages even though they are
sharing the same database that is managed by the wallet.
A suite of tests is provided to ensure proper functionality. See
`test_coverage.txt` for the gocov coverage report. Alternatively, if you are
running a POSIX OS, you can run the `cov_report.sh` script for a real-time
report. Package walletdb is licensed under the copyfree ISC license.
This interfaces provided by this package were heavily inspired by the excellent
boltdb project at https://github.com/boltdb/bolt by Ben B. Johnson.
## Feature Overview
- Key/value store
- Namespace support
- Allows multiple packages to have their own area in the database without
worrying about conflicts
- Read-only and read-write transactions with both manual and managed modes
- Nested buckets
- Supports registration of backend databases
- Comprehensive test coverage
## Documentation
[![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/walletdb?status.png)]
(http://godoc.org/github.com/btcsuite/btcwallet/walletdb)
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/btcsuite/btcwallet/walletdb
You can also view the documentation locally once the package is installed with
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
http://localhost:6060/pkg/github.com/btcsuite/btcwallet/walletdb
## Installation
```bash
$ go get github.com/btcsuite/btcwallet/walletdb
```
## Examples
* [Basic Usage Example]
(http://godoc.org/github.com/btcsuite/btcwallet/walletdb#example-package--BasicUsage)
Demonstrates creating a new database, getting a namespace from it, and using a
managed read-write transaction against the namespace to store and retrieve
data.
## License
Package walletdb is licensed under the [copyfree](http://copyfree.org) ISC
License.

View File

@@ -0,0 +1,46 @@
bdb
===
[![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)]
(https://travis-ci.org/btcsuite/btcwallet)
Package bdb implements an driver for walletdb that uses boltdb for the backing
datastore. Package bdb is licensed under the copyfree ISC license.
## Usage
This package is only a driver to the walletdb package and provides the database
type of "bdb". The only parameter the Open and Create functions take is the
database path as a string:
```Go
db, err := walletdb.Open("bdb", "path/to/database.db")
if err != nil {
// Handle error
}
```
```Go
db, err := walletdb.Create("bdb", "path/to/database.db")
if err != nil {
// Handle error
}
```
## Documentation
[![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb?status.png)]
(http://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb)
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/btcsuite/btcwallet/walletdb/bdb
You can also view the documentation locally once the package is installed with
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
http://localhost:6060/pkg/github.com/btcsuite/btcwallet/walletdb/bdb
## License
Package bdb is licensed under the [copyfree](http://copyfree.org) ISC
License.

356
vendor/github.com/btcsuite/btcwallet/walletdb/bdb/db.go generated vendored Normal file
View File

@@ -0,0 +1,356 @@
// 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.
package bdb
import (
"io"
"os"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/coreos/bbolt"
)
// convertErr converts some bolt errors to the equivalent walletdb error.
func convertErr(err error) error {
switch err {
// Database open/create errors.
case bbolt.ErrDatabaseNotOpen:
return walletdb.ErrDbNotOpen
case bbolt.ErrInvalid:
return walletdb.ErrInvalid
// Transaction errors.
case bbolt.ErrTxNotWritable:
return walletdb.ErrTxNotWritable
case bbolt.ErrTxClosed:
return walletdb.ErrTxClosed
// Value/bucket errors.
case bbolt.ErrBucketNotFound:
return walletdb.ErrBucketNotFound
case bbolt.ErrBucketExists:
return walletdb.ErrBucketExists
case bbolt.ErrBucketNameRequired:
return walletdb.ErrBucketNameRequired
case bbolt.ErrKeyRequired:
return walletdb.ErrKeyRequired
case bbolt.ErrKeyTooLarge:
return walletdb.ErrKeyTooLarge
case bbolt.ErrValueTooLarge:
return walletdb.ErrValueTooLarge
case bbolt.ErrIncompatibleValue:
return walletdb.ErrIncompatibleValue
}
// Return the original error if none of the above applies.
return err
}
// transaction represents a database transaction. It can either by read-only or
// read-write and implements the walletdb Tx interfaces. The transaction
// provides a root bucket against which all read and writes occur.
type transaction struct {
boltTx *bbolt.Tx
}
func (tx *transaction) ReadBucket(key []byte) walletdb.ReadBucket {
return tx.ReadWriteBucket(key)
}
func (tx *transaction) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
boltBucket := tx.boltTx.Bucket(key)
if boltBucket == nil {
return nil
}
return (*bucket)(boltBucket)
}
func (tx *transaction) CreateTopLevelBucket(key []byte) (walletdb.ReadWriteBucket, error) {
boltBucket, err := tx.boltTx.CreateBucket(key)
if err != nil {
return nil, convertErr(err)
}
return (*bucket)(boltBucket), nil
}
func (tx *transaction) DeleteTopLevelBucket(key []byte) error {
err := tx.boltTx.DeleteBucket(key)
if err != nil {
return convertErr(err)
}
return nil
}
// Commit commits all changes that have been made through the root bucket and
// all of its sub-buckets to persistent storage.
//
// This function is part of the walletdb.ReadWriteTx interface implementation.
func (tx *transaction) Commit() error {
return convertErr(tx.boltTx.Commit())
}
// Rollback undoes all changes that have been made to the root bucket and all of
// its sub-buckets.
//
// This function is part of the walletdb.ReadTx interface implementation.
func (tx *transaction) Rollback() error {
return convertErr(tx.boltTx.Rollback())
}
// OnCommit takes a function closure that will be executed when the transaction
// successfully gets committed.
//
// This function is part of the walletdb.ReadWriteTx interface implementation.
func (tx *transaction) OnCommit(f func()) {
tx.boltTx.OnCommit(f)
}
// bucket is an internal type used to represent a collection of key/value pairs
// and implements the walletdb Bucket interfaces.
type bucket bbolt.Bucket
// Enforce bucket implements the walletdb Bucket interfaces.
var _ walletdb.ReadWriteBucket = (*bucket)(nil)
// NestedReadWriteBucket retrieves a nested bucket with the given key. Returns
// nil if the bucket does not exist.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) NestedReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
boltBucket := (*bbolt.Bucket)(b).Bucket(key)
// Don't return a non-nil interface to a nil pointer.
if boltBucket == nil {
return nil
}
return (*bucket)(boltBucket)
}
func (b *bucket) NestedReadBucket(key []byte) walletdb.ReadBucket {
return b.NestedReadWriteBucket(key)
}
// 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.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) CreateBucket(key []byte) (walletdb.ReadWriteBucket, error) {
boltBucket, err := (*bbolt.Bucket)(b).CreateBucket(key)
if err != nil {
return nil, convertErr(err)
}
return (*bucket)(boltBucket), nil
}
// 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.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) CreateBucketIfNotExists(key []byte) (walletdb.ReadWriteBucket, error) {
boltBucket, err := (*bbolt.Bucket)(b).CreateBucketIfNotExists(key)
if err != nil {
return nil, convertErr(err)
}
return (*bucket)(boltBucket), nil
}
// DeleteNestedBucket 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.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) DeleteNestedBucket(key []byte) error {
return convertErr((*bbolt.Bucket)(b).DeleteBucket(key))
}
// 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 will
// likely result in an access violation.
//
// This function is part of the walletdb.ReadBucket interface implementation.
func (b *bucket) ForEach(fn func(k, v []byte) error) error {
return convertErr((*bbolt.Bucket)(b).ForEach(fn))
}
// 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.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) Put(key, value []byte) error {
return convertErr((*bbolt.Bucket)(b).Put(key, value))
}
// 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
// will likely result in an access violation.
//
// This function is part of the walletdb.ReadBucket interface implementation.
func (b *bucket) Get(key []byte) []byte {
return (*bbolt.Bucket)(b).Get(key)
}
// 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.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) Delete(key []byte) error {
return convertErr((*bbolt.Bucket)(b).Delete(key))
}
func (b *bucket) ReadCursor() walletdb.ReadCursor {
return b.ReadWriteCursor()
}
// ReadWriteCursor returns a new cursor, allowing for iteration over the bucket's
// key/value pairs and nested buckets in forward or backward order.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) ReadWriteCursor() walletdb.ReadWriteCursor {
return (*cursor)((*bbolt.Bucket)(b).Cursor())
}
// Tx returns the bucket's transaction.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) Tx() walletdb.ReadWriteTx {
return &transaction{
(*bbolt.Bucket)(b).Tx(),
}
}
// 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 bbolt.Cursor
// 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.
//
// This function is part of the walletdb.ReadWriteCursor interface implementation.
func (c *cursor) Delete() error {
return convertErr((*bbolt.Cursor)(c).Delete())
}
// First positions the cursor at the first key/value pair and returns the pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) First() (key, value []byte) {
return (*bbolt.Cursor)(c).First()
}
// Last positions the cursor at the last key/value pair and returns the pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) Last() (key, value []byte) {
return (*bbolt.Cursor)(c).Last()
}
// Next moves the cursor one key/value pair forward and returns the new pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) Next() (key, value []byte) {
return (*bbolt.Cursor)(c).Next()
}
// Prev moves the cursor one key/value pair backward and returns the new pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) Prev() (key, value []byte) {
return (*bbolt.Cursor)(c).Prev()
}
// 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.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) Seek(seek []byte) (key, value []byte) {
return (*bbolt.Cursor)(c).Seek(seek)
}
// db represents a collection of namespaces which are persisted and implements
// the walletdb.Db interface. All database access is performed through
// transactions which are obtained through the specific Namespace.
type db bbolt.DB
// Enforce db implements the walletdb.Db interface.
var _ walletdb.DB = (*db)(nil)
func (db *db) beginTx(writable bool) (*transaction, error) {
boltTx, err := (*bbolt.DB)(db).Begin(writable)
if err != nil {
return nil, convertErr(err)
}
return &transaction{boltTx: boltTx}, nil
}
func (db *db) BeginReadTx() (walletdb.ReadTx, error) {
return db.beginTx(false)
}
func (db *db) BeginReadWriteTx() (walletdb.ReadWriteTx, error) {
return db.beginTx(true)
}
// Copy writes a copy of the database to the provided writer. This call will
// start a read-only transaction to perform all operations.
//
// This function is part of the walletdb.Db interface implementation.
func (db *db) Copy(w io.Writer) error {
return convertErr((*bbolt.DB)(db).View(func(tx *bbolt.Tx) error {
return tx.Copy(w)
}))
}
// Close cleanly shuts down the database and syncs all data.
//
// This function is part of the walletdb.Db interface implementation.
func (db *db) Close() error {
return convertErr((*bbolt.DB)(db).Close())
}
// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// openDB opens the database at the provided path. walletdb.ErrDbDoesNotExist
// is returned if the database doesn't exist and the create flag is not set.
func openDB(dbPath string, create bool) (walletdb.DB, error) {
if !create && !fileExists(dbPath) {
return nil, walletdb.ErrDbDoesNotExist
}
// Specify bbolt freelist options to reduce heap pressure in case the
// freelist grows to be very large.
options := &bbolt.Options{
NoFreelistSync: true,
FreelistType: bbolt.FreelistMapType,
}
boltDB, err := bbolt.Open(dbPath, 0600, options)
return (*db)(boltDB), convertErr(err)
}

View File

@@ -0,0 +1,25 @@
// 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.
/*
Package bdb implements an instance of walletdb that uses boltdb for the backing
datastore.
Usage
This package is only a driver to the walletdb package and provides the database
type of "bdb". The only parameter the Open and Create functions take is the
database path as a string:
db, err := walletdb.Open("bdb", "path/to/database.db")
if err != nil {
// Handle error
}
db, err := walletdb.Create("bdb", "path/to/database.db")
if err != nil {
// Handle error
}
*/
package bdb

View File

@@ -0,0 +1,66 @@
// 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.
package bdb
import (
"fmt"
"github.com/btcsuite/btcwallet/walletdb"
)
const (
dbType = "bdb"
)
// parseArgs parses the arguments from the walletdb Open/Create methods.
func parseArgs(funcName string, args ...interface{}) (string, error) {
if len(args) != 1 {
return "", fmt.Errorf("invalid arguments to %s.%s -- "+
"expected database path", dbType, funcName)
}
dbPath, ok := args[0].(string)
if !ok {
return "", fmt.Errorf("first argument to %s.%s is invalid -- "+
"expected database path string", dbType, funcName)
}
return dbPath, nil
}
// openDBDriver is the callback provided during driver registration that opens
// an existing database for use.
func openDBDriver(args ...interface{}) (walletdb.DB, error) {
dbPath, err := parseArgs("Open", args...)
if err != nil {
return nil, err
}
return openDB(dbPath, false)
}
// createDBDriver is the callback provided during driver registration that
// creates, initializes, and opens a database for use.
func createDBDriver(args ...interface{}) (walletdb.DB, error) {
dbPath, err := parseArgs("Create", args...)
if err != nil {
return nil, err
}
return openDB(dbPath, true)
}
func init() {
// Register the driver.
driver := walletdb.Driver{
DbType: dbType,
Create: createDBDriver,
Open: openDBDriver,
}
if err := walletdb.RegisterDriver(driver); err != nil {
panic(fmt.Sprintf("Failed to regiser database driver '%s': %v",
dbType, err))
}
}

View File

@@ -0,0 +1,7 @@
#!/bin/sh
# This script uses go tool cover to generate a test coverage report.
go test -coverprofile=cov.out && go tool cover -func=cov.out && rm -f cov.out
echo "============================================================"
(cd bdb && go test -coverprofile=cov.out && go tool cover -func=cov.out && \
rm -f cov.out)

103
vendor/github.com/btcsuite/btcwallet/walletdb/doc.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
// 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.
/*
Package walletdb provides a namespaced database interface for btcwallet.
Overview
A wallet essentially consists of a multitude of stored data such as private
and public keys, key derivation bits, pay-to-script-hash scripts, and various
metadata. One of the issues with many wallets is they are tightly integrated.
Designing a wallet with loosely coupled components that provide specific
functionality is ideal, however it presents a challenge in regards to data
storage since each component needs to store its own data without knowing the
internals of other components or breaking atomicity.
This package solves this issue by providing a pluggable driver, namespaced
database interface that is intended to be used by the main wallet daemon. This
allows the potential for any backend database type with a suitable driver. Each
component, which will typically be a package, can then implement various
functionality such as address management, voting pools, and colored coin
metadata in their own namespace without having to worry about conflicts with
other packages even though they are sharing the same database that is managed by
the wallet.
A quick overview of the features walletdb provides are as follows:
- Key/value store
- Namespace support
- Allows multiple packages to have their own area in the database without
worrying about conflicts
- Read-only and read-write transactions with both manual and managed modes
- Nested buckets
- Supports registration of backend databases
- Comprehensive test coverage
Database
The main entry point is the DB interface. It exposes functionality for
creating, retrieving, and removing namespaces. It is obtained via the Create
and Open functions which take a database type string that identifies the
specific database driver (backend) to use as well as arguments specific to the
specified driver.
Namespaces
The Namespace interface is an abstraction that provides facilities for obtaining
transactions (the Tx interface) that are the basis of all database reads and
writes. Unlike some database interfaces that support reading and writing
without transactions, this interface requires transactions even when only
reading or writing a single key.
The Begin function provides an unmanaged transaction while the View and Update
functions provide a managed transaction. These are described in more detail
below.
Transactions
The Tx interface provides facilities for rolling back or commiting changes that
took place while the transaction was active. It also provides the root bucket
under which all keys, values, and nested buckets are stored. A transaction
can either be read-only or read-write and managed or unmanaged.
Managed versus Unmanaged Transactions
A managed transaction is one where the caller provides a function to execute
within the context of the transaction and the commit or rollback is handled
automatically depending on whether or not the provided function returns an
error. Attempting to manually call Rollback or Commit on the managed
transaction will result in a panic.
An unmanaged transaction, on the other hand, requires the caller to manually
call Commit or Rollback when they are finished with it. Leaving transactions
open for long periods of time can have several adverse effects, so it is
recommended that managed transactions are used instead.
Buckets
The Bucket interface provides the ability to manipulate key/value pairs and
nested buckets as well as iterate through them.
The Get, Put, and Delete functions work with key/value pairs, while the Bucket,
CreateBucket, CreateBucketIfNotExists, and DeleteBucket functions work with
buckets. The ForEach function allows the caller to provide a function to be
called with each key/value pair and nested bucket in the current bucket.
Root Bucket
As discussed above, all of the functions which are used to manipulate key/value
pairs and nested buckets exist on the Bucket interface. The root bucket is the
upper-most bucket in a namespace under which data is stored and is created at
the same time as the namespace. Use the RootBucket function on the Tx interface
to retrieve it.
Nested Buckets
The CreateBucket and CreateBucketIfNotExists functions on the Bucket interface
provide the ability to create an arbitrary number of nested buckets. It is
a good idea to avoid a lot of buckets with little data in them as it could lead
to poor page utilization depending on the specific driver in use.
*/
package walletdb

80
vendor/github.com/btcsuite/btcwallet/walletdb/error.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
// 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.
package walletdb
import (
"errors"
)
// Errors that can occur during driver registration.
var (
// ErrDbTypeRegistered is returned when two different database drivers
// attempt to register with the name database type.
ErrDbTypeRegistered = errors.New("database type already registered")
)
// Errors that the various database functions may return.
var (
// ErrDbUnknownType is returned when there is no driver registered for
// the specified database type.
ErrDbUnknownType = errors.New("unknown database type")
// ErrDbDoesNotExist is returned when open is called for a database that
// does not exist.
ErrDbDoesNotExist = errors.New("database does not exist")
// ErrDbExists is returned when create is called for a database that
// already exists.
ErrDbExists = errors.New("database already exists")
// ErrDbNotOpen is returned when a database instance is accessed before
// it is opened or after it is closed.
ErrDbNotOpen = errors.New("database not open")
// ErrDbAlreadyOpen is returned when open is called on a database that
// is already open.
ErrDbAlreadyOpen = errors.New("database already open")
// ErrInvalid is returned if the specified database is not valid.
ErrInvalid = errors.New("invalid database")
)
// Errors that can occur when beginning or committing a transaction.
var (
// ErrTxClosed is returned when attempting to commit or rollback a
// transaction that has already had one of those operations performed.
ErrTxClosed = errors.New("tx closed")
// ErrTxNotWritable is returned when an operation that requires write
// access to the database is attempted against a read-only transaction.
ErrTxNotWritable = errors.New("tx not writable")
)
// Errors that can occur when putting or deleting a value or bucket.
var (
// ErrBucketNotFound is returned when trying to access a bucket that has
// not been created yet.
ErrBucketNotFound = errors.New("bucket not found")
// ErrBucketExists is returned when creating a bucket that already exists.
ErrBucketExists = errors.New("bucket already exists")
// ErrBucketNameRequired is returned when creating a bucket with a blank name.
ErrBucketNameRequired = errors.New("bucket name required")
// ErrKeyRequired is returned when inserting a zero-length key.
ErrKeyRequired = errors.New("key required")
// ErrKeyTooLarge is returned when inserting a key that is larger than MaxKeySize.
ErrKeyTooLarge = errors.New("key too large")
// ErrValueTooLarge is returned when inserting a value that is larger than MaxValueSize.
ErrValueTooLarge = errors.New("value too large")
// ErrIncompatibleValue is returned when trying create or delete a
// bucket on an existing non-bucket key or when trying to create or
// delete a non-bucket key on an existing bucket key.
ErrIncompatibleValue = errors.New("incompatible value")
)

11
vendor/github.com/btcsuite/btcwallet/walletdb/go.mod generated vendored Normal file
View File

@@ -0,0 +1,11 @@
module github.com/btcsuite/btcwallet/walletdb
go 1.12
require (
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/coreos/bbolt v1.3.3
github.com/davecgh/go-spew v1.1.1
go.etcd.io/bbolt v1.3.3 // indirect
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd // indirect
)

10
vendor/github.com/btcsuite/btcwallet/walletdb/go.sum generated vendored Normal file
View File

@@ -0,0 +1,10 @@
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -0,0 +1,304 @@
// 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"
// ReadTx represents a database transaction that can only be used for reads. If
// a database update must occur, use a ReadWriteTx.
type ReadTx interface {
// ReadBucket opens the root bucket for read only access. If the bucket
// described by the key does not exist, nil is returned.
ReadBucket(key []byte) ReadBucket
// Rollback closes the transaction, discarding changes (if any) if the
// database was modified by a write transaction.
Rollback() error
}
// ReadWriteTx represents a database transaction that can be used for both reads
// and writes. When only reads are necessary, consider using a ReadTx instead.
type ReadWriteTx interface {
ReadTx
// ReadWriteBucket opens the root bucket for read/write access. If the
// bucket described by the key does not exist, nil is returned.
ReadWriteBucket(key []byte) ReadWriteBucket
// CreateTopLevelBucket creates the top level bucket for a key if it
// does not exist. The newly-created bucket it returned.
CreateTopLevelBucket(key []byte) (ReadWriteBucket, error)
// DeleteTopLevelBucket deletes the top level bucket for a key. This
// errors if the bucket can not be found or the key keys a single value
// instead of a bucket.
DeleteTopLevelBucket(key []byte) error
// Commit commits all changes that have been on the transaction's root
// buckets and all of their sub-buckets to persistent storage.
Commit() error
// OnCommit takes a function closure that will be executed when the
// transaction successfully gets committed.
OnCommit(func())
}
// ReadBucket represents a bucket (a hierarchical structure within the database)
// that is only allowed to perform read operations.
type ReadBucket interface {
// NestedReadBucket retrieves a nested bucket with the given key.
// Returns nil if the bucket does not exist.
NestedReadBucket(key []byte) ReadBucket
// 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
// 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
ReadCursor() ReadCursor
}
// ReadWriteBucket represents a bucket (a hierarchical structure within the
// database) that is allowed to perform both read and write operations.
type ReadWriteBucket interface {
ReadBucket
// NestedReadWriteBucket retrieves a nested bucket with the given key.
// Returns nil if the bucket does not exist.
NestedReadWriteBucket(key []byte) ReadWriteBucket
// 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) (ReadWriteBucket, 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) (ReadWriteBucket, error)
// DeleteNestedBucket 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.
DeleteNestedBucket(key []byte) error
// 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
// 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.
ReadWriteCursor() ReadWriteCursor
// Tx returns the bucket's transaction.
Tx() ReadWriteTx
}
// ReadCursor represents a bucket cursor that can be positioned at the start or
// end of the bucket's key/value pairs and iterate over pairs in the bucket.
// This type is only allowed to perform database read operations.
type ReadCursor interface {
// 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)
}
// ReadWriteCursor represents a bucket cursor that can be positioned at the
// start or end of the bucket's key/value pairs and iterate over pairs in the
// bucket. This abstraction is allowed to perform both database read and write
// operations.
type ReadWriteCursor interface {
ReadCursor
// Delete removes the current key/value pair the cursor is at without
// invalidating the cursor. Returns ErrIncompatibleValue if attempted
// when the cursor points to a nested bucket.
Delete() error
}
// BucketIsEmpty returns whether the bucket is empty, that is, whether there are
// no key/value pairs or nested buckets.
func BucketIsEmpty(bucket ReadBucket) bool {
k, v := bucket.ReadCursor().First()
return k == nil && v == nil
}
// DB represents an ACID database. All database access is performed through
// read or read+write transactions.
type DB interface {
// BeginReadTx opens a database read transaction.
BeginReadTx() (ReadTx, error)
// BeginReadWriteTx opens a database read+write transaction.
BeginReadWriteTx() (ReadWriteTx, 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
}
// View opens a database read transaction and executes the function f with the
// transaction passed as a parameter. After f exits, the transaction is rolled
// back. If f errors, its error is returned, not a rollback error (if any
// occur).
func View(db DB, f func(tx ReadTx) error) error {
tx, err := db.BeginReadTx()
if err != nil {
return err
}
err = f(tx)
rollbackErr := tx.Rollback()
if err != nil {
return err
}
if rollbackErr != nil {
return rollbackErr
}
return nil
}
// Update opens a database read/write transaction and executes the function f
// with the transaction passed as a parameter. After f exits, if f did not
// error, the transaction is committed. Otherwise, if f did error, the
// transaction is rolled back. If the rollback fails, the original error
// returned by f is still returned. If the commit fails, the commit error is
// returned.
func Update(db DB, f func(tx ReadWriteTx) error) error {
tx, err := db.BeginReadWriteTx()
if err != nil {
return err
}
err = f(tx)
if err != nil {
// Want to return the original error, not a rollback error if
// any occur.
_ = tx.Rollback()
return err
}
return tx.Commit()
}
// 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...)
}

View File

@@ -0,0 +1,43 @@
package migration
import "github.com/btcsuite/btclog"
// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
// requests it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
DisableLog()
}
// DisableLog disables all library log output. Logging output is disabled
// by default until either UseLogger or SetLogWriter are called.
func DisableLog() {
UseLogger(btclog.Disabled)
}
// UseLogger uses a specified Logger to output package logging info.
// This should be used in preference to SetLogWriter if the caller is also
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
}
// LogClosure is a closure that can be printed with %v to be used to
// generate expensive-to-create data for a detailed log level and avoid doing
// the work if the data isn't printed.
type logClosure func() string
// String invokes the log closure and returns the results string.
func (c logClosure) String() string {
return c()
}
// newLogClosure returns a new closure over the passed function which allows
// it to be used as a parameter in a logging function that is only invoked when
// the logging level is such that the message will actually be logged.
func newLogClosure(c func() string) logClosure {
return logClosure(c)
}

View File

@@ -0,0 +1,162 @@
package migration
import (
"errors"
"sort"
"github.com/btcsuite/btcwallet/walletdb"
)
var (
// ErrReversion is an error returned when an attempt to revert to a
// previous version is detected. This is done to provide safety to users
// as some upgrades may not be backwards-compatible.
ErrReversion = errors.New("reverting to a previous version is not " +
"supported")
)
// Version denotes the version number of the database. A migration can be used
// to bring a previous version of the database to a later one.
type Version struct {
// Number represents the number of this version.
Number uint32
// Migration represents a migration function that modifies the database
// state. Care must be taken so that consequent migrations build off of
// the previous one in order to ensure the consistency of the database.
Migration func(walletdb.ReadWriteBucket) error
}
// Manager is an interface that exposes the necessary methods needed in order to
// migrate/upgrade a service. Each service (i.e., an implementation of this
// interface) can then use the Upgrade function to perform any required database
// migrations.
type Manager interface {
// Name returns the name of the service we'll be attempting to upgrade.
Name() string
// Namespace returns the top-level bucket of the service.
Namespace() walletdb.ReadWriteBucket
// CurrentVersion returns the current version of the service's database.
CurrentVersion(walletdb.ReadBucket) (uint32, error)
// SetVersion sets the version of the service's database.
SetVersion(walletdb.ReadWriteBucket, uint32) error
// Versions returns all of the available database versions of the
// service.
Versions() []Version
}
// GetLatestVersion returns the latest version available from the given slice.
func GetLatestVersion(versions []Version) uint32 {
if len(versions) == 0 {
return 0
}
// Before determining the latest version number, we'll sort the slice to
// ensure it reflects the last element.
sort.Slice(versions, func(i, j int) bool {
return versions[i].Number < versions[j].Number
})
return versions[len(versions)-1].Number
}
// VersionsToApply determines which versions should be applied as migrations
// based on the current version.
func VersionsToApply(currentVersion uint32, versions []Version) []Version {
// Assuming the migration versions are in increasing order, we'll apply
// any migrations that have a version number lower than our current one.
var upgradeVersions []Version
for _, version := range versions {
if version.Number > currentVersion {
upgradeVersions = append(upgradeVersions, version)
}
}
// Before returning, we'll sort the slice by its version number to
// ensure the migrations are applied in their intended order.
sort.Slice(upgradeVersions, func(i, j int) bool {
return upgradeVersions[i].Number < upgradeVersions[j].Number
})
return upgradeVersions
}
// Upgrade attempts to upgrade a group of services exposed through the Manager
// interface. Each service will go through its available versions and determine
// whether it needs to apply any.
//
// NOTE: In order to guarantee fault-tolerance, each service upgrade should
// happen within the same database transaction.
func Upgrade(mgrs ...Manager) error {
for _, mgr := range mgrs {
if err := upgrade(mgr); err != nil {
return err
}
}
return nil
}
// upgrade attempts to upgrade a service expose through its implementation of
// the Manager interface. This function will determine whether any new versions
// need to be applied based on the service's current version and latest
// available one.
func upgrade(mgr Manager) error {
// We'll start by fetching the service's current and latest version.
ns := mgr.Namespace()
currentVersion, err := mgr.CurrentVersion(ns)
if err != nil {
return err
}
versions := mgr.Versions()
latestVersion := GetLatestVersion(versions)
switch {
// If the current version is greater than the latest, then the service
// is attempting to revert to a previous version that's possibly
// backwards-incompatible. To prevent this, we'll return an error
// indicating so.
case currentVersion > latestVersion:
return ErrReversion
// If the current version is behind the latest version, we'll need to
// apply all of the newer versions in order to catch up to the latest.
case currentVersion < latestVersion:
versions := VersionsToApply(currentVersion, versions)
mgrName := mgr.Name()
ns := mgr.Namespace()
for _, version := range versions {
log.Infof("Applying %v migration #%d", mgrName,
version.Number)
// We'll only run a migration if there is one available
// for this version.
if version.Migration != nil {
err := version.Migration(ns)
if err != nil {
log.Errorf("Unable to apply %v "+
"migration #%d: %v", mgrName,
version.Number, err)
return err
}
}
}
// With all of the versions applied, we can now reflect the
// latest version upon the service.
if err := mgr.SetVersion(ns, latestVersion); err != nil {
return err
}
// If the current version matches the latest one, there's no upgrade
// needed and we can safely exit.
case currentVersion == latestVersion:
}
return nil
}

View File

@@ -0,0 +1,39 @@
PASS
coverage: 100.0% of statements
ok github.com/conformal/btcwallet/walletdb 0.130s
github.com\conformal\btcwallet\walletdb\interface.go:190: RegisterDriver 100.0%
github.com\conformal\btcwallet\walletdb\interface.go:201: SupportedDrivers 100.0%
github.com\conformal\btcwallet\walletdb\interface.go:214: Create 100.0%
github.com\conformal\btcwallet\walletdb\interface.go:228: Open 100.0%
total: (statements) 100.0%
============================================================
PASS
coverage: 91.7% of statements
ok github.com/conformal/btcwallet/walletdb/bdb 0.149s
github.com\conformal\btcwallet\walletdb\bdb\db.go:28: convertErr 76.9%
github.com\conformal\btcwallet\walletdb\bdb\db.go:74: Bucket 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:90: CreateBucket 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:103: CreateBucketIfNotExists 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:116: DeleteBucket 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:129: ForEach 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:136: Writable 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:145: Put 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:157: Get 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:166: Delete 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:185: RootBucket 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:193: Commit 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:201: Rollback 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:227: Begin 85.7%
github.com\conformal\btcwallet\walletdb\bdb\db.go:249: View 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:270: Update 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:294: Namespace 93.3%
github.com\conformal\btcwallet\walletdb\bdb\db.go:329: DeleteNamespace 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:339: WriteTo 0.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:348: Close 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:353: fileExists 100.0%
github.com\conformal\btcwallet\walletdb\bdb\db.go:364: openDB 100.0%
github.com\conformal\btcwallet\walletdb\bdb\driver.go:34: parseArgs 100.0%
github.com\conformal\btcwallet\walletdb\bdb\driver.go:50: openDBDriver 100.0%
github.com\conformal\btcwallet\walletdb\bdb\driver.go:60: createDBDriver 100.0%
github.com\conformal\btcwallet\walletdb\bdb\driver.go:69: init 66.7%
total: (statements) 91.7%