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

View File

@@ -0,0 +1,62 @@
waddrmgr
========
[![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)]
(https://travis-ci.org/btcsuite/btcwallet)
Package waddrmgr provides a secure hierarchical deterministic wallet address
manager.
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 waddrmgr is licensed under the liberal ISC license.
## Feature Overview
- BIP0032 hierarchical deterministic keys
- BIP0043/BIP0044 multi-account hierarchy
- Strong focus on security:
- Fully encrypted database including public information such as addresses as
well as private information such as private keys and scripts needed to
redeem pay-to-script-hash transactions
- Hardened against memory scraping through the use of actively clearing
private material from memory when locked
- Different crypto keys used for public, private, and script data
- Ability for different passphrases for public and private data
- Scrypt-based key derivation
- NaCl-based secretbox cryptography (XSalsa20 and Poly1305)
- Scalable design:
- Multi-tier key design to allow instant password changes regardless of the
number of addresses stored
- Import WIF keys
- Import pay-to-script-hash scripts for things such as multi-signature
transactions
- Ability to export a watching-only version which does not contain any private
key material
- Programmatically detectable errors, including encapsulation of errors from
packages it relies on
- Address synchronization capabilities
- Comprehensive test coverage
## Documentation
[![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/waddrmgr?status.png)]
(http://godoc.org/github.com/btcsuite/btcwallet/waddrmgr)
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/waddrmgr
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/waddrmgr
## Installation
```bash
$ go get github.com/btcsuite/btcwallet/waddrmgr
```
Package waddrmgr is licensed under the [copyfree](http://copyfree.org) ISC
License.

View File

@@ -0,0 +1,648 @@
// 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 waddrmgr
import (
"encoding/hex"
"fmt"
"sync"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/btcsuite/btcwallet/internal/zero"
"github.com/btcsuite/btcwallet/walletdb"
)
// AddressType represents the various address types waddrmgr is currently able
// to generate, and maintain.
//
// NOTE: These MUST be stable as they're used for scope address schema
// recognition within the database.
type AddressType uint8
const (
// PubKeyHash is a regular p2pkh address.
PubKeyHash AddressType = iota
// Script reprints a raw script address.
Script
// RawPubKey is just raw public key to be used within scripts, This
// type indicates that a scoped manager with this address type
// shouldn't be consulted during historical rescans.
RawPubKey
// NestedWitnessPubKey represents a p2wkh output nested within a p2sh
// output. Using this address type, the wallet can receive funds from
// other wallet's which don't yet recognize the new segwit standard
// output types. Receiving funds to this address maintains the
// scalability, and malleability fixes due to segwit in a backwards
// compatible manner.
NestedWitnessPubKey
// WitnessPubKey represents a p2wkh (pay-to-witness-key-hash) address
// type.
WitnessPubKey
)
// ManagedAddress is an interface that provides acces to information regarding
// an address managed by an address manager. Concrete implementations of this
// type may provide further fields to provide information specific to that type
// of address.
type ManagedAddress interface {
// Account returns the account the address is associated with.
Account() uint32
// Address returns a btcutil.Address for the backing address.
Address() btcutil.Address
// AddrHash returns the key or script hash related to the address
AddrHash() []byte
// Imported returns true if the backing address was imported instead
// of being part of an address chain.
Imported() bool
// Internal returns true if the backing address was created for internal
// use such as a change output of a transaction.
Internal() bool
// Compressed returns true if the backing address is compressed.
Compressed() bool
// Used returns true if the backing address has been used in a transaction.
Used(ns walletdb.ReadBucket) bool
// AddrType returns the address type of the managed address. This can
// be used to quickly discern the address type without further
// processing
AddrType() AddressType
}
// ManagedPubKeyAddress extends ManagedAddress and additionally provides the
// public and private keys for pubkey-based addresses.
type ManagedPubKeyAddress interface {
ManagedAddress
// PubKey returns the public key associated with the address.
PubKey() *btcec.PublicKey
// ExportPubKey returns the public key associated with the address
// serialized as a hex encoded string.
ExportPubKey() string
// PrivKey returns the private key for the address. It can fail if the
// address manager is watching-only or locked, or the address does not
// have any keys.
PrivKey() (*btcec.PrivateKey, error)
// ExportPrivKey returns the private key associated with the address
// serialized as Wallet Import Format (WIF).
ExportPrivKey() (*btcutil.WIF, error)
// DerivationInfo contains the information required to derive the key
// that backs the address via traditional methods from the HD root. For
// imported keys, the first value will be set to false to indicate that
// we don't know exactly how the key was derived.
DerivationInfo() (KeyScope, DerivationPath, bool)
}
// ManagedScriptAddress extends ManagedAddress and represents a pay-to-script-hash
// style of bitcoin addresses. It additionally provides information about the
// script.
type ManagedScriptAddress interface {
ManagedAddress
// Script returns the script associated with the address.
Script() ([]byte, error)
}
// managedAddress represents a public key address. It also may or may not have
// the private key associated with the public key.
type managedAddress struct {
manager *ScopedKeyManager
derivationPath DerivationPath
address btcutil.Address
imported bool
internal bool
compressed bool
used bool
addrType AddressType
pubKey *btcec.PublicKey
privKeyEncrypted []byte
privKeyCT []byte // non-nil if unlocked
privKeyMutex sync.Mutex
}
// Enforce managedAddress satisfies the ManagedPubKeyAddress interface.
var _ ManagedPubKeyAddress = (*managedAddress)(nil)
// unlock decrypts and stores a pointer to the associated private key. It will
// fail if the key is invalid or the encrypted private key is not available.
// The returned clear text private key will always be a copy that may be safely
// used by the caller without worrying about it being zeroed during an address
// lock.
func (a *managedAddress) unlock(key EncryptorDecryptor) ([]byte, error) {
// Protect concurrent access to clear text private key.
a.privKeyMutex.Lock()
defer a.privKeyMutex.Unlock()
if len(a.privKeyCT) == 0 {
privKey, err := key.Decrypt(a.privKeyEncrypted)
if err != nil {
str := fmt.Sprintf("failed to decrypt private key for "+
"%s", a.address)
return nil, managerError(ErrCrypto, str, err)
}
a.privKeyCT = privKey
}
privKeyCopy := make([]byte, len(a.privKeyCT))
copy(privKeyCopy, a.privKeyCT)
return privKeyCopy, nil
}
// lock zeroes the associated clear text private key.
func (a *managedAddress) lock() {
// Zero and nil the clear text private key associated with this
// address.
a.privKeyMutex.Lock()
zero.Bytes(a.privKeyCT)
a.privKeyCT = nil
a.privKeyMutex.Unlock()
}
// Account returns the account number the address is associated with.
//
// This is part of the ManagedAddress interface implementation.
func (a *managedAddress) Account() uint32 {
return a.derivationPath.Account
}
// AddrType returns the address type of the managed address. This can be used
// to quickly discern the address type without further processing
//
// This is part of the ManagedAddress interface implementation.
func (a *managedAddress) AddrType() AddressType {
return a.addrType
}
// Address returns the btcutil.Address which represents the managed address.
// This will be a pay-to-pubkey-hash address.
//
// This is part of the ManagedAddress interface implementation.
func (a *managedAddress) Address() btcutil.Address {
return a.address
}
// AddrHash returns the public key hash for the address.
//
// This is part of the ManagedAddress interface implementation.
func (a *managedAddress) AddrHash() []byte {
var hash []byte
switch n := a.address.(type) {
case *btcutil.AddressPubKeyHash:
hash = n.Hash160()[:]
case *btcutil.AddressScriptHash:
hash = n.Hash160()[:]
case *btcutil.AddressWitnessPubKeyHash:
hash = n.Hash160()[:]
}
return hash
}
// Imported returns true if the address was imported instead of being part of an
// address chain.
//
// This is part of the ManagedAddress interface implementation.
func (a *managedAddress) Imported() bool {
return a.imported
}
// Internal returns true if the address was created for internal use such as a
// change output of a transaction.
//
// This is part of the ManagedAddress interface implementation.
func (a *managedAddress) Internal() bool {
return a.internal
}
// Compressed returns true if the address is compressed.
//
// This is part of the ManagedAddress interface implementation.
func (a *managedAddress) Compressed() bool {
return a.compressed
}
// Used returns true if the address has been used in a transaction.
//
// This is part of the ManagedAddress interface implementation.
func (a *managedAddress) Used(ns walletdb.ReadBucket) bool {
return a.manager.fetchUsed(ns, a.AddrHash())
}
// PubKey returns the public key associated with the address.
//
// This is part of the ManagedPubKeyAddress interface implementation.
func (a *managedAddress) PubKey() *btcec.PublicKey {
return a.pubKey
}
// pubKeyBytes returns the serialized public key bytes for the managed address
// based on whether or not the managed address is marked as compressed.
func (a *managedAddress) pubKeyBytes() []byte {
if a.compressed {
return a.pubKey.SerializeCompressed()
}
return a.pubKey.SerializeUncompressed()
}
// ExportPubKey returns the public key associated with the address
// serialized as a hex encoded string.
//
// This is part of the ManagedPubKeyAddress interface implementation.
func (a *managedAddress) ExportPubKey() string {
return hex.EncodeToString(a.pubKeyBytes())
}
// PrivKey returns the private key for the address. It can fail if the address
// manager is watching-only or locked, or the address does not have any keys.
//
// This is part of the ManagedPubKeyAddress interface implementation.
func (a *managedAddress) PrivKey() (*btcec.PrivateKey, error) {
// No private keys are available for a watching-only address manager.
if a.manager.rootManager.WatchOnly() {
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
}
a.manager.mtx.Lock()
defer a.manager.mtx.Unlock()
// Account manager must be unlocked to decrypt the private key.
if a.manager.rootManager.IsLocked() {
return nil, managerError(ErrLocked, errLocked, nil)
}
// Decrypt the key as needed. Also, make sure it's a copy since the
// private key stored in memory can be cleared at any time. Otherwise
// the returned private key could be invalidated from under the caller.
privKeyCopy, err := a.unlock(a.manager.rootManager.cryptoKeyPriv)
if err != nil {
return nil, err
}
privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyCopy)
zero.Bytes(privKeyCopy)
return privKey, nil
}
// ExportPrivKey returns the private key associated with the address in Wallet
// Import Format (WIF).
//
// This is part of the ManagedPubKeyAddress interface implementation.
func (a *managedAddress) ExportPrivKey() (*btcutil.WIF, error) {
pk, err := a.PrivKey()
if err != nil {
return nil, err
}
return btcutil.NewWIF(pk, a.manager.rootManager.chainParams, a.compressed)
}
// Derivationinfo contains the information required to derive the key that
// backs the address via traditional methods from the HD root. For imported
// keys, the first value will be set to false to indicate that we don't know
// exactly how the key was derived.
//
// This is part of the ManagedPubKeyAddress interface implementation.
func (a *managedAddress) DerivationInfo() (KeyScope, DerivationPath, bool) {
var (
scope KeyScope
path DerivationPath
)
// If this key is imported, then we can't return any information as we
// don't know precisely how the key was derived.
if a.imported {
return scope, path, false
}
return a.manager.Scope(), a.derivationPath, true
}
// newManagedAddressWithoutPrivKey returns a new managed address based on the
// passed account, public key, and whether or not the public key should be
// compressed.
func newManagedAddressWithoutPrivKey(m *ScopedKeyManager,
derivationPath DerivationPath, pubKey *btcec.PublicKey, compressed bool,
addrType AddressType) (*managedAddress, error) {
// Create a pay-to-pubkey-hash address from the public key.
var pubKeyHash []byte
if compressed {
pubKeyHash = btcutil.Hash160(pubKey.SerializeCompressed())
} else {
pubKeyHash = btcutil.Hash160(pubKey.SerializeUncompressed())
}
var address btcutil.Address
var err error
switch addrType {
case NestedWitnessPubKey:
// For this address type we'l generate an address which is
// backwards compatible to Bitcoin nodes running 0.6.0 onwards, but
// allows us to take advantage of segwit's scripting improvments,
// and malleability fixes.
// First, we'll generate a normal p2wkh address from the pubkey hash.
witAddr, err := btcutil.NewAddressWitnessPubKeyHash(
pubKeyHash, m.rootManager.chainParams,
)
if err != nil {
return nil, err
}
// Next we'll generate the witness program which can be used as a
// pkScript to pay to this generated address.
witnessProgram, err := txscript.PayToAddrScript(witAddr)
if err != nil {
return nil, err
}
// Finally, we'll use the witness program itself as the pre-image
// to a p2sh address. In order to spend, we first use the
// witnessProgram as the sigScript, then present the proper
// <sig, pubkey> pair as the witness.
address, err = btcutil.NewAddressScriptHash(
witnessProgram, m.rootManager.chainParams,
)
if err != nil {
return nil, err
}
case PubKeyHash:
address, err = btcutil.NewAddressPubKeyHash(
pubKeyHash, m.rootManager.chainParams,
)
if err != nil {
return nil, err
}
case WitnessPubKey:
address, err = btcutil.NewAddressWitnessPubKeyHash(
pubKeyHash, m.rootManager.chainParams,
)
if err != nil {
return nil, err
}
}
return &managedAddress{
manager: m,
address: address,
derivationPath: derivationPath,
imported: false,
internal: false,
addrType: addrType,
compressed: compressed,
pubKey: pubKey,
privKeyEncrypted: nil,
privKeyCT: nil,
}, nil
}
// newManagedAddress returns a new managed address based on the passed account,
// private key, and whether or not the public key is compressed. The managed
// address will have access to the private and public keys.
func newManagedAddress(s *ScopedKeyManager, derivationPath DerivationPath,
privKey *btcec.PrivateKey, compressed bool,
addrType AddressType) (*managedAddress, error) {
// Encrypt the private key.
//
// NOTE: The privKeyBytes here are set into the managed address which
// are cleared when locked, so they aren't cleared here.
privKeyBytes := privKey.Serialize()
privKeyEncrypted, err := s.rootManager.cryptoKeyPriv.Encrypt(privKeyBytes)
if err != nil {
str := "failed to encrypt private key"
return nil, managerError(ErrCrypto, str, err)
}
// Leverage the code to create a managed address without a private key
// and then add the private key to it.
ecPubKey := (*btcec.PublicKey)(&privKey.PublicKey)
managedAddr, err := newManagedAddressWithoutPrivKey(
s, derivationPath, ecPubKey, compressed, addrType,
)
if err != nil {
return nil, err
}
managedAddr.privKeyEncrypted = privKeyEncrypted
managedAddr.privKeyCT = privKeyBytes
return managedAddr, nil
}
// newManagedAddressFromExtKey returns a new managed address based on the passed
// account and extended key. The managed address will have access to the
// private and public keys if the provided extended key is private, otherwise it
// will only have access to the public key.
func newManagedAddressFromExtKey(s *ScopedKeyManager,
derivationPath DerivationPath, key *hdkeychain.ExtendedKey,
addrType AddressType) (*managedAddress, error) {
// Create a new managed address based on the public or private key
// depending on whether the generated key is private.
var managedAddr *managedAddress
if key.IsPrivate() {
privKey, err := key.ECPrivKey()
if err != nil {
return nil, err
}
// Ensure the temp private key big integer is cleared after
// use.
managedAddr, err = newManagedAddress(
s, derivationPath, privKey, true, addrType,
)
if err != nil {
return nil, err
}
} else {
pubKey, err := key.ECPubKey()
if err != nil {
return nil, err
}
managedAddr, err = newManagedAddressWithoutPrivKey(
s, derivationPath, pubKey, true,
addrType,
)
if err != nil {
return nil, err
}
}
return managedAddr, nil
}
// scriptAddress represents a pay-to-script-hash address.
type scriptAddress struct {
manager *ScopedKeyManager
account uint32
address *btcutil.AddressScriptHash
scriptEncrypted []byte
scriptCT []byte
scriptMutex sync.Mutex
used bool
}
// Enforce scriptAddress satisfies the ManagedScriptAddress interface.
var _ ManagedScriptAddress = (*scriptAddress)(nil)
// unlock decrypts and stores the associated script. It will fail if the key is
// invalid or the encrypted script is not available. The returned clear text
// script will always be a copy that may be safely used by the caller without
// worrying about it being zeroed during an address lock.
func (a *scriptAddress) unlock(key EncryptorDecryptor) ([]byte, error) {
// Protect concurrent access to clear text script.
a.scriptMutex.Lock()
defer a.scriptMutex.Unlock()
if len(a.scriptCT) == 0 {
script, err := key.Decrypt(a.scriptEncrypted)
if err != nil {
str := fmt.Sprintf("failed to decrypt script for %s",
a.address)
return nil, managerError(ErrCrypto, str, err)
}
a.scriptCT = script
}
scriptCopy := make([]byte, len(a.scriptCT))
copy(scriptCopy, a.scriptCT)
return scriptCopy, nil
}
// lock zeroes the associated clear text private key.
func (a *scriptAddress) lock() {
// Zero and nil the clear text script associated with this address.
a.scriptMutex.Lock()
zero.Bytes(a.scriptCT)
a.scriptCT = nil
a.scriptMutex.Unlock()
}
// Account returns the account the address is associated with. This will always
// be the ImportedAddrAccount constant for script addresses.
//
// This is part of the ManagedAddress interface implementation.
func (a *scriptAddress) Account() uint32 {
return a.account
}
// AddrType returns the address type of the managed address. This can be used
// to quickly discern the address type without further processing
//
// This is part of the ManagedAddress interface implementation.
func (a *scriptAddress) AddrType() AddressType {
return Script
}
// Address returns the btcutil.Address which represents the managed address.
// This will be a pay-to-script-hash address.
//
// This is part of the ManagedAddress interface implementation.
func (a *scriptAddress) Address() btcutil.Address {
return a.address
}
// AddrHash returns the script hash for the address.
//
// This is part of the ManagedAddress interface implementation.
func (a *scriptAddress) AddrHash() []byte {
return a.address.Hash160()[:]
}
// Imported always returns true since script addresses are always imported
// addresses and not part of any chain.
//
// This is part of the ManagedAddress interface implementation.
func (a *scriptAddress) Imported() bool {
return true
}
// Internal always returns false since script addresses are always imported
// addresses and not part of any chain in order to be for internal use.
//
// This is part of the ManagedAddress interface implementation.
func (a *scriptAddress) Internal() bool {
return false
}
// Compressed returns false since script addresses are never compressed.
//
// This is part of the ManagedAddress interface implementation.
func (a *scriptAddress) Compressed() bool {
return false
}
// Used returns true if the address has been used in a transaction.
//
// This is part of the ManagedAddress interface implementation.
func (a *scriptAddress) Used(ns walletdb.ReadBucket) bool {
return a.manager.fetchUsed(ns, a.AddrHash())
}
// Script returns the script associated with the address.
//
// This implements the ScriptAddress interface.
func (a *scriptAddress) Script() ([]byte, error) {
// No script is available for a watching-only address manager.
if a.manager.rootManager.WatchOnly() {
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
}
a.manager.mtx.Lock()
defer a.manager.mtx.Unlock()
// Account manager must be unlocked to decrypt the script.
if a.manager.rootManager.IsLocked() {
return nil, managerError(ErrLocked, errLocked, nil)
}
// Decrypt the script as needed. Also, make sure it's a copy since the
// script stored in memory can be cleared at any time. Otherwise,
// the returned script could be invalidated from under the caller.
return a.unlock(a.manager.rootManager.cryptoKeyScript)
}
// newScriptAddress initializes and returns a new pay-to-script-hash address.
func newScriptAddress(m *ScopedKeyManager, account uint32, scriptHash,
scriptEncrypted []byte) (*scriptAddress, error) {
address, err := btcutil.NewAddressScriptHashFromHash(
scriptHash, m.rootManager.chainParams,
)
if err != nil {
return nil, err
}
return &scriptAddress{
manager: m,
account: account,
address: address,
scriptEncrypted: scriptEncrypted,
}, nil
}

View File

@@ -0,0 +1,17 @@
#!/bin/sh
# This script uses gocov to generate a test coverage report.
# The gocov tool my be obtained with the following command:
# go get github.com/axw/gocov/gocov
#
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
# Check for gocov.
type gocov >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo >&2 "This script requires the gocov tool."
echo >&2 "You may obtain it with the following command:"
echo >&2 "go get github.com/axw/gocov/gocov"
exit 1
fi
gocov test | gocov report

2280
vendor/github.com/btcsuite/btcwallet/waddrmgr/db.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

155
vendor/github.com/btcsuite/btcwallet/waddrmgr/doc.go generated vendored Normal file
View File

@@ -0,0 +1,155 @@
// 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 waddrmgr provides a secure hierarchical deterministic wallet address
manager.
Overview
One of the fundamental jobs of a wallet is to manage addresses, private keys,
and script data associated with them. At a high level, this package provides
the facilities to perform this task with a focus on security and also allows
recovery through the use of hierarchical deterministic keys (BIP0032) generated
from a caller provided seed. The specific structure used is as described in
BIP0044. This setup means as long as the user writes the seed down (even better
is to use a mnemonic for the seed), all their addresses and private keys can be
regenerated from the seed.
There are two master keys which are protected by two independent passphrases.
One is intended for public facing data, while the other is intended for private
data. The public password can be hardcoded for callers who don't want the
additional public data protection or the same password can be used if a single
password is desired. These choices provide a usability versus security
tradeoff. However, keep in mind that extended hd keys, as called out in BIP0032
need to be handled more carefully than normal EC public keys because they can be
used to generate all future addresses. While this is part of what makes them
attractive, it also means an attacker getting access to your extended public key
for an account will allow them to know all derived addresses you will use and
hence reduces privacy. For this reason, it is highly recommended that you do
not hard code a password which allows any attacker who gets a copy of your
address manager database to access your effectively plain text extended public
keys.
Each master key in turn protects the three real encryption keys (called crypto
keys) for public, private, and script data. Some examples include payment
addresses, extended hd keys, and scripts associated with pay-to-script-hash
addresses. This scheme makes changing passphrases more efficient since only the
crypto keys need to be re-encrypted versus every single piece of information
(which is what is needed for *rekeying*). This results in a fully encrypted
database where access to it does not compromise address, key, or script privacy.
This differs from the handling by other wallets at the time of this writing in
that they divulge your addresses, and worse, some even expose the chain code
which can be used by the attacker to know all future addresses that will be
used.
The address manager is also hardened against memory scrapers. This is
accomplished by typically having the address manager locked meaning no private
keys or scripts are in memory. Unlocking the address manager causes the crypto
private and script keys to be decrypted and loaded in memory which in turn are
used to decrypt private keys and scripts on demand. Relocking the address
manager actively zeros all private material from memory. In addition, temp
private key material used internally is zeroed as soon as it's used.
Locking and Unlocking
As previously mentioned, this package provide facilities for locking and
unlocking the address manager to protect access to private material and remove
it from memory when locked. The Lock, Unlock, and IsLocked functions are used
for this purpose.
Creating a New Address Manager
A new address manager is created via the Create function. This function accepts
a wallet database namespace, passphrases, network, and perhaps most importantly,
a cryptographically random seed which is used to generate the master node of the
hierarchical deterministic keychain which allows all addresses and private keys
to be recovered with only the seed. The GenerateSeed function in the hdkeychain
package can be used as a convenient way to create a random seed for use with
this function. The address manager is locked immediately upon being created.
Opening an Existing Address Manager
An existing address manager is opened via the Open function. This function
accepts an existing wallet database namespace, the public passphrase, and
network. The address manager is opened locked as expected since the open
function does not take the private passphrase to unlock it.
Closing the Address Manager
The Close method should be called on the address manager when the caller is done
with it. While it is not required, it is recommended because it sanely shuts
down the database and ensures all private and public key material is purged from
memory.
Managed Addresses
Each address returned by the address manager satisifies the ManagedAddress
interface as well as either the ManagedPubKeyAddress or ManagedScriptAddress
interfaces. These interfaces provide the means to obtain relevant information
about the addresses such as their private keys and scripts.
Chained Addresses
Most callers will make use of the chained addresses for normal operations.
Internal addresses are intended for internal wallet uses such as change outputs,
while external addresses are intended for uses such payment addresses that are
shared. The NextInternalAddresses and NextExternalAddresses functions provide
the means to acquire one or more of the next addresses that have not already
been provided. In addition, the LastInternalAddress and LastExternalAddress
functions can be used to get the most recently provided internal and external
address, respectively.
Requesting Existing Addresses
In addition to generating new addresses, access to old addresses is often
required. Most notably, to sign transactions in order to redeem them. The
Address function provides this capability and returns a ManagedAddress.
Importing Addresses
While the recommended approach is to use the chained addresses discussed above
because they can be deterministically regenerated to avoid losing funds as long
as the user has the master seed, there are many addresses that already exist,
and as a result, this package provides the ability to import existing private
keys in Wallet Import Format (WIF) and hence the associated public key and
address.
Importing Scripts
In order to support pay-to-script-hash transactions, the script must be securely
stored as it is needed to redeem the transaction. This can be useful for a
variety of scenarios, however the most common use is currently multi-signature
transactions.
Syncing
The address manager also supports storing and retrieving a block hash and height
which the manager is known to have all addresses synced through. The manager
itself does not have any notion of which addresses are synced or not. It only
provides the storage as a convenience for the caller.
Network
The address manager must be associated with a given network in order to provide
appropriate addresses and reject imported addresses and scripts which don't
apply to the associated network.
Errors
All errors returned from this package are of type ManagerError. This allows the
caller to programmatically ascertain the specific reasons for failure by
examining the ErrorCode field of the type asserted ManagerError. For certain
error codes, as documented by the specific error codes, the underlying error
will be contained in the Err field.
Bitcoin Improvement Proposals
This package includes concepts outlined by the following BIPs:
BIP0032 (https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
BIP0043 (https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki)
BIP0044 (https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)
*/
package waddrmgr

219
vendor/github.com/btcsuite/btcwallet/waddrmgr/error.go generated vendored Normal file
View File

@@ -0,0 +1,219 @@
// 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 waddrmgr
import (
"fmt"
"strconv"
"github.com/btcsuite/btcutil/hdkeychain"
)
var (
// errAlreadyExists is the common error description used for the
// ErrAlreadyExists error code.
errAlreadyExists = "the specified address manager already exists"
// errCoinTypeTooHigh is the common error description used for the
// ErrCoinTypeTooHigh error code.
errCoinTypeTooHigh = "coin type may not exceed " +
strconv.FormatUint(hdkeychain.HardenedKeyStart-1, 10)
// errAcctTooHigh is the common error description used for the
// ErrAccountNumTooHigh error code.
errAcctTooHigh = "account number may not exceed " +
strconv.FormatUint(hdkeychain.HardenedKeyStart-1, 10)
// errLocked is the common error description used for the ErrLocked
// error code.
errLocked = "address manager is locked"
// errWatchingOnly is the common error description used for the
// ErrWatchingOnly error code.
errWatchingOnly = "address manager is watching-only"
)
// ErrorCode identifies a kind of error.
type ErrorCode int
// These constants are used to identify a specific ManagerError.
const (
// ErrDatabase indicates an error with the underlying database. When
// this error code is set, the Err field of the ManagerError will be
// set to the underlying error returned from the database.
ErrDatabase ErrorCode = iota
// ErrUpgrade indicates the manager needs to be upgraded. This should
// not happen in practice unless the version number has been increased
// and there is not yet any code written to upgrade.
ErrUpgrade
// ErrKeyChain indicates an error with the key chain typically either
// due to the inability to create an extended key or deriving a child
// extended key. When this error code is set, the Err field of the
// ManagerError will be set to the underlying error.
ErrKeyChain
// ErrCrypto indicates an error with the cryptography related operations
// such as decrypting or encrypting data, parsing an EC public key,
// or deriving a secret key from a password. When this error code is
// set, the Err field of the ManagerError will be set to the underlying
// error.
ErrCrypto
// ErrInvalidKeyType indicates an error where an invalid crypto
// key type has been selected.
ErrInvalidKeyType
// ErrNoExist indicates that the specified database does not exist.
ErrNoExist
// ErrAlreadyExists indicates that the specified database already exists.
ErrAlreadyExists
// ErrCoinTypeTooHigh indicates that the coin type specified in the provided
// network parameters is higher than the max allowed value as defined
// by the maxCoinType constant.
ErrCoinTypeTooHigh
// ErrAccountNumTooHigh indicates that the specified account number is higher
// than the max allowed value as defined by the MaxAccountNum constant.
ErrAccountNumTooHigh
// ErrLocked indicates that an operation, which requires the account
// manager to be unlocked, was requested on a locked account manager.
ErrLocked
// ErrWatchingOnly indicates that an operation, which requires the
// account manager to have access to private data, was requested on
// a watching-only account manager.
ErrWatchingOnly
// ErrInvalidAccount indicates that the requested account is not valid.
ErrInvalidAccount
// ErrAddressNotFound indicates that the requested address is not known to
// the account manager.
ErrAddressNotFound
// ErrAccountNotFound indicates that the requested account is not known to
// the account manager.
ErrAccountNotFound
// ErrDuplicateAddress indicates an address already exists.
ErrDuplicateAddress
// ErrDuplicateAccount indicates an account already exists.
ErrDuplicateAccount
// ErrTooManyAddresses indicates that more than the maximum allowed number of
// addresses per account have been requested.
ErrTooManyAddresses
// ErrWrongPassphrase indicates that the specified passphrase is incorrect.
// This could be for either public or private master keys.
ErrWrongPassphrase
// ErrWrongNet indicates that the private key to be imported is not for the
// the same network the account manager is configured for.
ErrWrongNet
// ErrCallBackBreak is used to break from a callback function passed
// down to the manager.
ErrCallBackBreak
// ErrEmptyPassphrase indicates that the private passphrase was refused
// due to being empty.
ErrEmptyPassphrase
// ErrScopeNotFound is returned when a target scope cannot be found
// within the database.
ErrScopeNotFound
// ErrBirthdayBlockNotSet is returned when we attempt to retrieve the
// wallet's birthday but it has not been set yet.
ErrBirthdayBlockNotSet
// ErrBlockNotFound is returned when we attempt to retrieve the hash for
// a block that we do not know of.
ErrBlockNotFound
)
// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrDatabase: "ErrDatabase",
ErrUpgrade: "ErrUpgrade",
ErrKeyChain: "ErrKeyChain",
ErrCrypto: "ErrCrypto",
ErrInvalidKeyType: "ErrInvalidKeyType",
ErrNoExist: "ErrNoExist",
ErrAlreadyExists: "ErrAlreadyExists",
ErrCoinTypeTooHigh: "ErrCoinTypeTooHigh",
ErrAccountNumTooHigh: "ErrAccountNumTooHigh",
ErrLocked: "ErrLocked",
ErrWatchingOnly: "ErrWatchingOnly",
ErrInvalidAccount: "ErrInvalidAccount",
ErrAddressNotFound: "ErrAddressNotFound",
ErrAccountNotFound: "ErrAccountNotFound",
ErrDuplicateAddress: "ErrDuplicateAddress",
ErrDuplicateAccount: "ErrDuplicateAccount",
ErrTooManyAddresses: "ErrTooManyAddresses",
ErrWrongPassphrase: "ErrWrongPassphrase",
ErrWrongNet: "ErrWrongNet",
ErrCallBackBreak: "ErrCallBackBreak",
ErrEmptyPassphrase: "ErrEmptyPassphrase",
ErrScopeNotFound: "ErrScopeNotFound",
}
// String returns the ErrorCode as a human-readable name.
func (e ErrorCode) String() string {
if s := errorCodeStrings[e]; s != "" {
return s
}
return fmt.Sprintf("Unknown ErrorCode (%d)", int(e))
}
// ManagerError provides a single type for errors that can happen during address
// manager operation. It is used to indicate several types of failures
// including errors with caller requests such as invalid accounts or requesting
// private keys against a locked address manager, errors with the database
// (ErrDatabase), errors with key chain derivation (ErrKeyChain), and errors
// related to crypto (ErrCrypto).
//
// The caller can use type assertions to determine if an error is a ManagerError
// and access the ErrorCode field to ascertain the specific reason for the
// failure.
//
// The ErrDatabase, ErrKeyChain, and ErrCrypto error codes will also have the
// Err field set with the underlying error.
type ManagerError struct {
ErrorCode ErrorCode // Describes the kind of error
Description string // Human readable description of the issue
Err error // Underlying error
}
// Error satisfies the error interface and prints human-readable errors.
func (e ManagerError) Error() string {
if e.Err != nil {
return e.Description + ": " + e.Err.Error()
}
return e.Description
}
// managerError creates a ManagerError given a set of arguments.
func managerError(c ErrorCode, desc string, err error) ManagerError {
return ManagerError{ErrorCode: c, Description: desc, Err: err}
}
// Break is a global err used to signal a break from the callback
// function by returning an error with the code ErrCallBackBreak
var Break = managerError(ErrCallBackBreak, "callback break", nil)
// IsError returns whether the error is a ManagerError with a matching error
// code.
func IsError(err error, code ErrorCode) bool {
e, ok := err.(ManagerError)
return ok && e.ErrorCode == code
}

43
vendor/github.com/btcsuite/btcwallet/waddrmgr/log.go generated vendored Normal file
View File

@@ -0,0 +1,43 @@
package waddrmgr
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)
}

1822
vendor/github.com/btcsuite/btcwallet/waddrmgr/manager.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,412 @@
package waddrmgr
import (
"errors"
"fmt"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/walletdb/migration"
)
// versions is a list of the different database versions. The last entry should
// reflect the latest database state. If the database happens to be at a version
// number lower than the latest, migrations will be performed in order to catch
// it up.
var versions = []migration.Version{
{
Number: 2,
Migration: upgradeToVersion2,
},
{
Number: 5,
Migration: upgradeToVersion5,
},
{
Number: 6,
Migration: populateBirthdayBlock,
},
{
Number: 7,
Migration: resetSyncedBlockToBirthday,
},
{
Number: 8,
Migration: storeMaxReorgDepth,
},
}
// getLatestVersion returns the version number of the latest database version.
func getLatestVersion() uint32 {
return versions[len(versions)-1].Number
}
// MigrationManager is an implementation of the migration.Manager interface that
// will be used to handle migrations for the address manager. It exposes the
// necessary parameters required to successfully perform migrations.
type MigrationManager struct {
ns walletdb.ReadWriteBucket
}
// A compile-time assertion to ensure that MigrationManager implements the
// migration.Manager interface.
var _ migration.Manager = (*MigrationManager)(nil)
// NewMigrationManager creates a new migration manager for the address manager.
// The given bucket should reflect the top-level bucket in which all of the
// address manager's data is contained within.
func NewMigrationManager(ns walletdb.ReadWriteBucket) *MigrationManager {
return &MigrationManager{ns: ns}
}
// Name returns the name of the service we'll be attempting to upgrade.
//
// NOTE: This method is part of the migration.Manager interface.
func (m *MigrationManager) Name() string {
return "wallet address manager"
}
// Namespace returns the top-level bucket of the service.
//
// NOTE: This method is part of the migration.Manager interface.
func (m *MigrationManager) Namespace() walletdb.ReadWriteBucket {
return m.ns
}
// CurrentVersion returns the current version of the service's database.
//
// NOTE: This method is part of the migration.Manager interface.
func (m *MigrationManager) CurrentVersion(ns walletdb.ReadBucket) (uint32, error) {
if ns == nil {
ns = m.ns
}
return fetchManagerVersion(ns)
}
// SetVersion sets the version of the service's database.
//
// NOTE: This method is part of the migration.Manager interface.
func (m *MigrationManager) SetVersion(ns walletdb.ReadWriteBucket,
version uint32) error {
if ns == nil {
ns = m.ns
}
return putManagerVersion(m.ns, version)
}
// Versions returns all of the available database versions of the service.
//
// NOTE: This method is part of the migration.Manager interface.
func (m *MigrationManager) Versions() []migration.Version {
return versions
}
// upgradeToVersion2 upgrades the database from version 1 to version 2
// 'usedAddrBucketName' a bucket for storing addrs flagged as marked is
// initialized and it will be updated on the next rescan.
func upgradeToVersion2(ns walletdb.ReadWriteBucket) error {
currentMgrVersion := uint32(2)
_, err := ns.CreateBucketIfNotExists(usedAddrBucketName)
if err != nil {
str := "failed to create used addresses bucket"
return managerError(ErrDatabase, str, err)
}
return putManagerVersion(ns, currentMgrVersion)
}
// upgradeToVersion5 upgrades the database from version 4 to version 5. After
// this update, the new ScopedKeyManager features cannot be used. This is due
// to the fact that in version 5, we now store the encrypted master private
// keys on disk. However, using the BIP0044 key scope, users will still be able
// to create old p2pkh addresses.
func upgradeToVersion5(ns walletdb.ReadWriteBucket) error {
// First, we'll check if there are any existing segwit addresses, which
// can't be upgraded to the new version. If so, we abort and warn the
// user.
err := ns.NestedReadBucket(addrBucketName).ForEach(
func(k []byte, v []byte) error {
row, err := deserializeAddressRow(v)
if err != nil {
return err
}
if row.addrType > adtScript {
return fmt.Errorf("segwit address exists in " +
"wallet, can't upgrade from v4 to " +
"v5: well, we tried ¯\\_(ツ)_/¯")
}
return nil
})
if err != nil {
return err
}
// Next, we'll write out the new database version.
if err := putManagerVersion(ns, 5); err != nil {
return err
}
// First, we'll need to create the new buckets that are used in the new
// database version.
scopeBucket, err := ns.CreateBucket(scopeBucketName)
if err != nil {
str := "failed to create scope bucket"
return managerError(ErrDatabase, str, err)
}
scopeSchemas, err := ns.CreateBucket(scopeSchemaBucketName)
if err != nil {
str := "failed to create scope schema bucket"
return managerError(ErrDatabase, str, err)
}
// With the buckets created, we can now create the default BIP0044
// scope which will be the only scope usable in the database after this
// update.
scopeKey := scopeToBytes(&KeyScopeBIP0044)
scopeSchema := ScopeAddrMap[KeyScopeBIP0044]
schemaBytes := scopeSchemaToBytes(&scopeSchema)
if err := scopeSchemas.Put(scopeKey[:], schemaBytes); err != nil {
return err
}
if err := createScopedManagerNS(scopeBucket, &KeyScopeBIP0044); err != nil {
return err
}
bip44Bucket := scopeBucket.NestedReadWriteBucket(scopeKey[:])
// With the buckets created, we now need to port over *each* item in
// the prior main bucket, into the new default scope.
mainBucket := ns.NestedReadWriteBucket(mainBucketName)
// First, we'll move over the encrypted coin type private and public
// keys to the new sub-bucket.
encCoinPrivKeys := mainBucket.Get(coinTypePrivKeyName)
encCoinPubKeys := mainBucket.Get(coinTypePubKeyName)
err = bip44Bucket.Put(coinTypePrivKeyName, encCoinPrivKeys)
if err != nil {
return err
}
err = bip44Bucket.Put(coinTypePubKeyName, encCoinPubKeys)
if err != nil {
return err
}
if err := mainBucket.Delete(coinTypePrivKeyName); err != nil {
return err
}
if err := mainBucket.Delete(coinTypePubKeyName); err != nil {
return err
}
// Next, we'll move over everything that was in the meta bucket to the
// meta bucket within the new scope.
metaBucket := ns.NestedReadWriteBucket(metaBucketName)
lastAccount := metaBucket.Get(lastAccountName)
if err := metaBucket.Delete(lastAccountName); err != nil {
return err
}
scopedMetaBucket := bip44Bucket.NestedReadWriteBucket(metaBucketName)
err = scopedMetaBucket.Put(lastAccountName, lastAccount)
if err != nil {
return err
}
// Finally, we'll recursively move over a set of keys which were
// formerly under the main bucket, into the new scoped buckets. We'll
// do so by obtaining a slice of all the keys that we need to modify
// and then recursing through each of them, moving both nested buckets
// and key/value pairs.
keysToMigrate := [][]byte{
acctBucketName, addrBucketName, usedAddrBucketName,
addrAcctIdxBucketName, acctNameIdxBucketName, acctIDIdxBucketName,
}
// Migrate each bucket recursively.
for _, bucketKey := range keysToMigrate {
err := migrateRecursively(ns, bip44Bucket, bucketKey)
if err != nil {
return err
}
}
return nil
}
// migrateRecursively moves a nested bucket from one bucket to another,
// recursing into nested buckets as required.
func migrateRecursively(src, dst walletdb.ReadWriteBucket,
bucketKey []byte) error {
// Within this bucket key, we'll migrate over, then delete each key.
bucketToMigrate := src.NestedReadWriteBucket(bucketKey)
newBucket, err := dst.CreateBucketIfNotExists(bucketKey)
if err != nil {
return err
}
err = bucketToMigrate.ForEach(func(k, v []byte) error {
if nestedBucket := bucketToMigrate.
NestedReadBucket(k); nestedBucket != nil {
// We have a nested bucket, so recurse into it.
return migrateRecursively(bucketToMigrate, newBucket, k)
}
if err := newBucket.Put(k, v); err != nil {
return err
}
return bucketToMigrate.Delete(k)
})
if err != nil {
return err
}
// Finally, we'll delete the bucket itself.
if err := src.DeleteNestedBucket(bucketKey); err != nil {
return err
}
return nil
}
// populateBirthdayBlock is a migration that attempts to populate the birthday
// block of the wallet. This is needed so that in the event that we need to
// perform a rescan of the wallet, we can do so starting from this block, rather
// than from the genesis block.
//
// NOTE: This migration cannot guarantee the correctness of the birthday block
// being set as we do not store block timestamps, so a sanity check must be done
// upon starting the wallet to ensure we do not potentially miss any relevant
// events when rescanning.
func populateBirthdayBlock(ns walletdb.ReadWriteBucket) error {
// We'll need to jump through some hoops in order to determine the
// corresponding block height for our birthday timestamp. Since we do
// not store block timestamps, we'll need to estimate our height by
// looking at the genesis timestamp and assuming a block occurs every 10
// minutes. This can be unsafe, and cause us to actually miss on-chain
// events, so a sanity check is done before the wallet attempts to sync
// itself.
//
// We'll start by fetching our birthday timestamp.
birthdayTimestamp, err := fetchBirthday(ns)
if err != nil {
return fmt.Errorf("unable to fetch birthday timestamp: %v", err)
}
log.Infof("Setting the wallet's birthday block from timestamp=%v",
birthdayTimestamp)
// Now, we'll need to determine the timestamp of the genesis block for
// the corresponding chain.
genesisHash, err := fetchBlockHash(ns, 0)
if err != nil {
return fmt.Errorf("unable to fetch genesis block hash: %v", err)
}
var genesisTimestamp time.Time
switch *genesisHash {
case *chaincfg.MainNetParams.GenesisHash:
genesisTimestamp =
chaincfg.MainNetParams.GenesisBlock.Header.Timestamp
case *chaincfg.TestNet3Params.GenesisHash:
genesisTimestamp =
chaincfg.TestNet3Params.GenesisBlock.Header.Timestamp
case *chaincfg.RegressionNetParams.GenesisHash:
genesisTimestamp =
chaincfg.RegressionNetParams.GenesisBlock.Header.Timestamp
case *chaincfg.SimNetParams.GenesisHash:
genesisTimestamp =
chaincfg.SimNetParams.GenesisBlock.Header.Timestamp
default:
return fmt.Errorf("unknown genesis hash %v", genesisHash)
}
// With the timestamps retrieved, we can estimate a block height by
// taking the difference between them and dividing by the average block
// time (10 minutes).
birthdayHeight := int32((birthdayTimestamp.Sub(genesisTimestamp).Seconds() / 600))
// Now that we have the height estimate, we can fetch the corresponding
// block and set it as our birthday block.
birthdayHash, err := fetchBlockHash(ns, birthdayHeight)
// To ensure we record a height that is known to us from the chain,
// we'll make sure this height estimate can be found. Otherwise, we'll
// continue subtracting a day worth of blocks until we can find one.
for IsError(err, ErrBlockNotFound) {
birthdayHeight -= 144
if birthdayHeight < 0 {
birthdayHeight = 0
}
birthdayHash, err = fetchBlockHash(ns, birthdayHeight)
}
if err != nil {
return err
}
log.Infof("Estimated birthday block from timestamp=%v: height=%d, "+
"hash=%v", birthdayTimestamp, birthdayHeight, birthdayHash)
// NOTE: The timestamp of the birthday block isn't set since we do not
// store each block's timestamp.
return PutBirthdayBlock(ns, BlockStamp{
Height: birthdayHeight,
Hash: *birthdayHash,
})
}
// resetSyncedBlockToBirthday is a migration that resets the wallet's currently
// synced block to its birthday block. This essentially serves as a migration to
// force a rescan of the wallet.
func resetSyncedBlockToBirthday(ns walletdb.ReadWriteBucket) error {
syncBucket := ns.NestedReadWriteBucket(syncBucketName)
if syncBucket == nil {
return errors.New("sync bucket does not exist")
}
birthdayBlock, err := FetchBirthdayBlock(ns)
if err != nil {
return err
}
return PutSyncedTo(ns, &birthdayBlock)
}
// storeMaxReorgDepth is a migration responsible for allowing the wallet to only
// maintain MaxReorgDepth block hashes stored in order to recover from long
// reorgs.
func storeMaxReorgDepth(ns walletdb.ReadWriteBucket) error {
// Retrieve the current tip of the wallet. We'll use this to determine
// the highest stale height we currently have stored within it.
syncedTo, err := fetchSyncedTo(ns)
if err != nil {
return err
}
maxStaleHeight := staleHeight(syncedTo.Height)
// It's possible for this height to be non-sensical if we have less than
// MaxReorgDepth blocks stored, so we can end the migration now.
if maxStaleHeight < 1 {
return nil
}
log.Infof("Removing block hash entries beyond maximum reorg depth of "+
"%v from current tip %v", MaxReorgDepth, syncedTo.Height)
// Otherwise, since we currently store all block hashes of the chain
// before this migration, we'll remove all stale block hash entries
// above the genesis block. This would leave us with only MaxReorgDepth
// blocks stored.
for height := maxStaleHeight; height > 0; height-- {
if err := deleteBlockHash(ns, height); err != nil {
return err
}
}
return nil
}

File diff suppressed because it is too large Load Diff

135
vendor/github.com/btcsuite/btcwallet/waddrmgr/sync.go generated vendored Normal file
View File

@@ -0,0 +1,135 @@
// 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 waddrmgr
import (
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcwallet/walletdb"
)
// BlockStamp defines a block (by height and a unique hash) and is used to mark
// a point in the blockchain that an address manager element is
// synced to.
type BlockStamp struct {
Height int32
Hash chainhash.Hash
Timestamp time.Time
}
// syncState houses the sync state of the manager. It consists of the recently
// seen blocks as height, as well as the start and current sync block stamps.
type syncState struct {
// startBlock is the first block that can be safely used to start a
// rescan. It is either the block the manager was created with, or the
// earliest block provided with imported addresses or scripts.
startBlock BlockStamp
// syncedTo is the current block the addresses in the manager are known
// to be synced against.
syncedTo BlockStamp
}
// newSyncState returns a new sync state with the provided parameters.
func newSyncState(startBlock, syncedTo *BlockStamp) *syncState {
return &syncState{
startBlock: *startBlock,
syncedTo: *syncedTo,
}
}
// SetSyncedTo marks the address manager to be in sync with the recently-seen
// block described by the blockstamp. When the provided blockstamp is nil, the
// oldest blockstamp of the block the manager was created at and of all
// imported addresses will be used. This effectively allows the manager to be
// marked as unsynced back to the oldest known point any of the addresses have
// appeared in the block chain.
func (m *Manager) SetSyncedTo(ns walletdb.ReadWriteBucket, bs *BlockStamp) error {
m.mtx.Lock()
defer m.mtx.Unlock()
// Use the stored start blockstamp and reset recent hashes and height
// when the provided blockstamp is nil.
if bs == nil {
bs = &m.syncState.startBlock
}
// Update the database.
err := PutSyncedTo(ns, bs)
if err != nil {
return err
}
// Update memory now that the database is updated.
m.syncState.syncedTo = *bs
return nil
}
// SyncedTo returns details about the block height and hash that the address
// manager is synced through at the very least. The intention is that callers
// can use this information for intelligently initiating rescans to sync back to
// the best chain from the last known good block.
func (m *Manager) SyncedTo() BlockStamp {
m.mtx.RLock()
defer m.mtx.RUnlock()
return m.syncState.syncedTo
}
// BlockHash returns the block hash at a particular block height. This
// information is useful for comparing against the chain back-end to see if a
// reorg is taking place and how far back it goes.
func (m *Manager) BlockHash(ns walletdb.ReadBucket, height int32) (
*chainhash.Hash, error) {
return fetchBlockHash(ns, height)
}
// Birthday returns the birthday, or earliest time a key could have been used,
// for the manager.
func (m *Manager) Birthday() time.Time {
m.mtx.RLock()
defer m.mtx.RUnlock()
return m.birthday
}
// SetBirthday sets the birthday, or earliest time a key could have been used,
// for the manager.
func (m *Manager) SetBirthday(ns walletdb.ReadWriteBucket,
birthday time.Time) error {
m.mtx.Lock()
defer m.mtx.Unlock()
m.birthday = birthday
return putBirthday(ns, birthday)
}
// BirthdayBlock returns the birthday block, or earliest block a key could have
// been used, for the manager. A boolean is also returned to indicate whether
// the birthday block has been verified as correct.
func (m *Manager) BirthdayBlock(ns walletdb.ReadBucket) (BlockStamp, bool, error) {
birthdayBlock, err := FetchBirthdayBlock(ns)
if err != nil {
return BlockStamp{}, false, err
}
return birthdayBlock, fetchBirthdayBlockVerification(ns), nil
}
// SetBirthdayBlock sets the birthday block, or earliest time a key could have
// been used, for the manager. The verified boolean can be used to specify
// whether this birthday block should be sanity checked to determine if there
// exists a better candidate to prevent less block fetching.
func (m *Manager) SetBirthdayBlock(ns walletdb.ReadWriteBucket,
block BlockStamp, verified bool) error {
if err := PutBirthdayBlock(ns, block); err != nil {
return err
}
return putBirthdayBlockVerification(ns, verified)
}

View File

@@ -0,0 +1,126 @@
github.com/conformal/btcwallet/waddrmgr/db.go serializeBIP0044AccountRow 100.00% (19/19)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.lock 100.00% (12/12)
github.com/conformal/btcwallet/waddrmgr/db.go serializeScriptAddress 100.00% (10/10)
github.com/conformal/btcwallet/waddrmgr/db.go serializeImportedAddress 100.00% (10/10)
github.com/conformal/btcwallet/waddrmgr/db.go serializeAddressRow 100.00% (9/9)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.Address 100.00% (8/8)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.Lock 100.00% (8/8)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.Script 100.00% (7/7)
github.com/conformal/btcwallet/waddrmgr/db.go serializeAccountRow 100.00% (6/6)
github.com/conformal/btcwallet/waddrmgr/sync.go BlockIterator.Prev 100.00% (6/6)
github.com/conformal/btcwallet/waddrmgr/address.go zeroBigInt 100.00% (5/5)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.zeroSensitivePublicData 100.00% (5/5)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.lock 100.00% (4/4)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.lock 100.00% (4/4)
github.com/conformal/btcwallet/waddrmgr/db.go serializeChainedAddress 100.00% (4/4)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.ExportPrivKey 100.00% (4/4)
github.com/conformal/btcwallet/waddrmgr/manager.go fileExists 100.00% (4/4)
github.com/conformal/btcwallet/waddrmgr/error.go ManagerError.Error 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/error.go ErrorCode.String 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.pubKeyBytes 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/sync.go Manager.NewIterateRecentBlocks 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/sync.go BlockIterator.BlockStamp 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/db.go accountKey 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/sync.go Manager.SyncedTo 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutAccountInfo 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.IsLocked 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutImportedAddress 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.ExistsAddress 100.00% (3/3)
github.com/conformal/btcwallet/waddrmgr/address.go zero 100.00% (2/2)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.Account 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.Compressed 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/sync.go newSyncState 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.Internal 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/manager.go cryptoKey.CopyBytes 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/manager.go newManager 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.AddrHash 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/manager.go defaultNewSecretKey 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.Imported 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.AddrHash 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.Address 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.Account 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.Internal 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.Net 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.ExportPubKey 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/error.go managerError 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.PubKey 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/manager.go cryptoKey.Bytes 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.Compressed 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.Address 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.Imported 100.00% (1/1)
github.com/conformal/btcwallet/waddrmgr/sync.go Manager.SetSyncedTo 93.94% (31/33)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.PrivKey 91.67% (11/12)
github.com/conformal/btcwallet/waddrmgr/db.go deserializeBIP0044AccountRow 90.48% (19/21)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.keyToManaged 90.00% (9/10)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchCryptoKeys 88.89% (16/18)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.Close 88.89% (8/9)
github.com/conformal/btcwallet/waddrmgr/address.go newManagedAddressWithoutPrivKey 87.50% (7/8)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutRecentBlocks 85.71% (12/14)
github.com/conformal/btcwallet/waddrmgr/manager.go Open 85.71% (6/7)
github.com/conformal/btcwallet/waddrmgr/db.go deserializeScriptAddress 84.62% (11/13)
github.com/conformal/btcwallet/waddrmgr/db.go deserializeImportedAddress 84.62% (11/13)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchRecentBlocks 84.62% (11/13)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchMasterKeyParams 84.62% (11/13)
github.com/conformal/btcwallet/waddrmgr/db.go deserializeAddressRow 83.33% (10/12)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.loadAndCacheAddress 83.33% (10/12)
github.com/conformal/btcwallet/waddrmgr/address.go managedAddress.unlock 81.82% (9/11)
github.com/conformal/btcwallet/waddrmgr/address.go scriptAddress.unlock 81.82% (9/11)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.nextAddresses 80.00% (52/65)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutScriptAddress 80.00% (4/5)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.ChangePassphrase 79.10% (53/67)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutChainedAddress 78.26% (18/23)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchSyncedTo 77.78% (7/9)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutStartBlock 77.78% (7/9)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchStartBlock 77.78% (7/9)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutSyncedTo 77.78% (7/9)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.existsAddress 77.78% (7/9)
github.com/conformal/btcwallet/waddrmgr/db.go deserializeAccountRow 77.78% (7/9)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.ExportWatchingOnly 75.00% (12/16)
github.com/conformal/btcwallet/waddrmgr/address.go newManagedAddressFromExtKey 75.00% (12/16)
github.com/conformal/btcwallet/waddrmgr/address.go newManagedAddress 75.00% (9/12)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutWatchingOnly 75.00% (6/8)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutNumAccounts 75.00% (6/8)
github.com/conformal/btcwallet/waddrmgr/address.go newScriptAddress 75.00% (3/4)
github.com/conformal/btcwallet/waddrmgr/manager.go defaultNewCryptoKey 75.00% (3/4)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.chainAddressRowToManaged 75.00% (3/4)
github.com/conformal/btcwallet/waddrmgr/manager.go checkBranchKeys 75.00% (3/4)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.deriveKeyFromPath 75.00% (3/4)
github.com/conformal/btcwallet/waddrmgr/manager.go loadManager 72.55% (37/51)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.putAddress 71.43% (5/7)
github.com/conformal/btcwallet/waddrmgr/db.go deserializeChainedAddress 71.43% (5/7)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.deriveKey 69.23% (9/13)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.ImportScript 67.44% (29/43)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.Unlock 67.35% (33/49)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchAddress 66.67% (10/15)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.importedAddressRowToManaged 66.67% (10/15)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutMasterKeyParams 66.67% (8/12)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.LastInternalAddress 66.67% (6/9)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.LastExternalAddress 66.67% (6/9)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.putAccountRow 66.67% (4/6)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.rowInterfaceToManaged 66.67% (4/6)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.NextExternalAddresses 66.67% (4/6)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.NextInternalAddresses 66.67% (4/6)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchWatchingOnly 66.67% (4/6)
github.com/conformal/btcwallet/waddrmgr/sync.go syncState.iter 66.67% (2/3)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.DeletePrivateKeys 66.04% (35/53)
github.com/conformal/btcwallet/waddrmgr/db.go openOrCreateDB 66.04% (35/53)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.ImportPrivateKey 64.71% (33/51)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.PutCryptoKeys 64.71% (11/17)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.loadAccountInfo 62.96% (34/54)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchAccountInfo 61.54% (8/13)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.scriptAddressRowToManaged 60.00% (3/5)
github.com/conformal/btcwallet/waddrmgr/manager.go Create 58.59% (58/99)
github.com/conformal/btcwallet/waddrmgr/manager.go deriveAccountKey 53.85% (7/13)
github.com/conformal/btcwallet/waddrmgr/db.go managerDB.Update 50.00% (4/8)
github.com/conformal/btcwallet/waddrmgr/db.go managerDB.View 50.00% (4/8)
github.com/conformal/btcwallet/waddrmgr/db.go managerDB.Close 50.00% (2/4)
github.com/conformal/btcwallet/waddrmgr/db.go managerDB.CopyDB 45.45% (5/11)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchAllAddresses 0.00% (0/20)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.AllActiveAddresses 0.00% (0/16)
github.com/conformal/btcwallet/waddrmgr/db.go managerDB.WriteTo 0.00% (0/11)
github.com/conformal/btcwallet/waddrmgr/sync.go BlockIterator.Next 0.00% (0/6)
github.com/conformal/btcwallet/waddrmgr/db.go managerTx.FetchNumAccounts 0.00% (0/6)
github.com/conformal/btcwallet/waddrmgr/manager.go Manager.Export 0.00% (0/3)
github.com/conformal/btcwallet/waddrmgr ----------------------------------- 72.59% (1030/1419)