create wallet package

This a refactor of the btcwallet main package to create a new wallet
package.
The main feature of this package is the integration of all the other
wallet components (waddrmgr, txstore, and chain) and the Wallet type is
'runnable', so it will be continuously updating itself against changes
notified by the remote btcd instance.

It also includes several methods which provide access to information
necessary to run a wallet RPC server.
This commit is contained in:
Manan Patel 2015-04-02 11:13:38 -07:00
parent 2181f4859d
commit dfe617e05d
16 changed files with 539 additions and 278 deletions

View file

@ -28,8 +28,7 @@ import (
)
var (
cfg *config
shutdownChan = make(chan struct{})
cfg *config
)
func main() {
@ -75,7 +74,7 @@ func walletMain() error {
log.Errorf("%v", err)
return err
}
defer wallet.db.Close()
defer wallet.Db().Close()
// Create and start HTTP server to serve wallet client connections.
// This will be updated with the wallet and chain server RPC client

View file

@ -24,24 +24,33 @@ import (
"sort"
"strings"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/legacy/keystore"
flags "github.com/btcsuite/go-flags"
)
const (
defaultCAFilename = "btcd.cert"
defaultConfigFilename = "btcwallet.conf"
defaultBtcNet = wire.TestNet3
defaultLogLevel = "info"
defaultLogDirname = "logs"
defaultLogFilename = "btcwallet.log"
defaultDisallowFree = false
defaultRPCMaxClients = 10
defaultRPCMaxWebsockets = 25
walletDbName = "wallet.db"
walletDbWatchingOnlyName = "wowallet.db"
defaultCAFilename = "btcd.cert"
defaultConfigFilename = "btcwallet.conf"
defaultLogLevel = "info"
defaultLogDirname = "logs"
defaultLogFilename = "btcwallet.log"
defaultDisallowFree = false
defaultRPCMaxClients = 10
defaultRPCMaxWebsockets = 25
// defaultPubPassphrase is the default public wallet passphrase which is
// used when the user indicates they do not want additional protection
// provided by having all public data in the wallet encrypted by a
// passphrase only known to them.
defaultPubPassphrase = "public"
// maxEmptyAccounts is the number of accounts to scan even if they have no
// transaction history. This is a deviation from BIP044 to make account
// creation easier by allowing a limited number of empty accounts.
maxEmptyAccounts = 100
walletDbName = "wallet.db"
)
var (

6
log.go
View file

@ -23,6 +23,7 @@ import (
"github.com/btcsuite/btclog"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/txstore"
"github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/seelog"
)
@ -42,6 +43,7 @@ const (
var (
backendLog = seelog.Disabled
log = btclog.Disabled
walletLog = btclog.Disabled
txstLog = btclog.Disabled
chainLog = btclog.Disabled
)
@ -49,6 +51,7 @@ var (
// subsystemLoggers maps each subsystem identifier to its associated logger.
var subsystemLoggers = map[string]btclog.Logger{
"BTCW": log,
"WLLT": walletLog,
"TXST": txstLog,
"CHNS": chainLog,
}
@ -80,6 +83,9 @@ func useLogger(subsystemID string, logger btclog.Logger) {
switch subsystemID {
case "BTCW":
log = logger
case "WLLT":
walletLog = logger
wallet.UseLogger(logger)
case "TXST":
txstLog = logger
txstore.UseLogger(logger)

View file

@ -47,6 +47,7 @@ import (
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/txstore"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/websocket"
)
@ -286,7 +287,7 @@ func genCertPair(certFile, keyFile string) error {
// rpcServer holds the items the RPC server may need to access (auth,
// config, shutdown, etc.)
type rpcServer struct {
wallet *Wallet
wallet *wallet.Wallet
chainSvr *chain.Client
createOK bool
handlerLookup func(string) (requestHandler, bool)
@ -557,7 +558,7 @@ func (noopLocker) Unlock() {}
// functional bitcoin wallet RPC server. If wallet is nil, this informs the
// server that the createencryptedwallet RPC method is valid and must be called
// by a client before any other wallet methods are allowed.
func (s *rpcServer) SetWallet(wallet *Wallet) {
func (s *rpcServer) SetWallet(wallet *wallet.Wallet) {
s.handlerLock.Lock()
defer s.handlerLock.Unlock()
@ -1031,7 +1032,7 @@ type (
wsClientNotification interface {
// This returns a slice only because some of these types result
// in multpile client notifications.
notificationCmds(w *Wallet) []btcjson.Cmd
notificationCmds(w *wallet.Wallet) []btcjson.Cmd
}
blockConnected waddrmgr.BlockStamp
@ -1048,17 +1049,17 @@ type (
btcdConnected bool
)
func (b blockConnected) notificationCmds(w *Wallet) []btcjson.Cmd {
func (b blockConnected) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
n := btcws.NewBlockConnectedNtfn(b.Hash.String(), b.Height)
return []btcjson.Cmd{n}
}
func (b blockDisconnected) notificationCmds(w *Wallet) []btcjson.Cmd {
func (b blockDisconnected) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
n := btcws.NewBlockDisconnectedNtfn(b.Hash.String(), b.Height)
return []btcjson.Cmd{n}
}
func (c txCredit) notificationCmds(w *Wallet) []btcjson.Cmd {
func (c txCredit) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
blk := w.Manager.SyncedTo()
acctName := waddrmgr.DefaultAccountName
if creditAccount, err := w.CreditAccount(txstore.Credit(c)); err == nil {
@ -1075,7 +1076,7 @@ func (c txCredit) notificationCmds(w *Wallet) []btcjson.Cmd {
return []btcjson.Cmd{n}
}
func (d txDebit) notificationCmds(w *Wallet) []btcjson.Cmd {
func (d txDebit) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
blk := w.Manager.SyncedTo()
ltrs, err := txstore.Debits(d).ToJSON("", blk.Height, activeNet.Params)
if err != nil {
@ -1090,24 +1091,24 @@ func (d txDebit) notificationCmds(w *Wallet) []btcjson.Cmd {
return ns
}
func (l managerLocked) notificationCmds(w *Wallet) []btcjson.Cmd {
func (l managerLocked) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
n := btcws.NewWalletLockStateNtfn("", bool(l))
return []btcjson.Cmd{n}
}
func (b confirmedBalance) notificationCmds(w *Wallet) []btcjson.Cmd {
func (b confirmedBalance) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
n := btcws.NewAccountBalanceNtfn("",
btcutil.Amount(b).ToBTC(), true)
return []btcjson.Cmd{n}
}
func (b unconfirmedBalance) notificationCmds(w *Wallet) []btcjson.Cmd {
func (b unconfirmedBalance) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
n := btcws.NewAccountBalanceNtfn("",
btcutil.Amount(b).ToBTC(), false)
return []btcjson.Cmd{n}
}
func (b btcdConnected) notificationCmds(w *Wallet) []btcjson.Cmd {
func (b btcdConnected) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
n := btcws.NewBtcdConnectedNtfn(bool(b))
return []btcjson.Cmd{n}
}
@ -1325,7 +1326,7 @@ out:
// or any of the above special error classes, the server will respond with
// the JSON-RPC appropiate error code. All other errors use the wallet
// catch-all error code, btcjson.ErrWallet.Code.
type requestHandler func(*Wallet, *chain.Client, btcjson.Cmd) (interface{}, error)
type requestHandler func(*wallet.Wallet, *chain.Client, btcjson.Cmd) (interface{}, error)
var rpcHandlers = map[string]requestHandler{
// Reference implementation wallet methods (implemented)
@ -1396,13 +1397,13 @@ var rpcHandlers = map[string]requestHandler{
// Unimplemented handles an unimplemented RPC request with the
// appropiate error.
func Unimplemented(*Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
func Unimplemented(*wallet.Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
return nil, btcjson.ErrUnimplemented
}
// Unsupported handles a standard bitcoind RPC request which is
// unsupported by btcwallet due to design differences.
func Unsupported(*Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
func Unsupported(*wallet.Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
return nil, btcjson.Error{
Code: -1,
Message: "Request unsupported by btcwallet",
@ -1411,14 +1412,14 @@ func Unsupported(*Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
// UnloadedWallet is the handler func that is run when a wallet has not been
// loaded yet when trying to execute a wallet RPC.
func UnloadedWallet(*Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
func UnloadedWallet(*wallet.Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
return nil, ErrUnloadedWallet
}
// NoEncryptedWallet is the handler func that is run when no wallet has been
// created by the user yet.
// loaded yet when trying to execute a wallet RPC.
func NoEncryptedWallet(*Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
func NoEncryptedWallet(*wallet.Wallet, *chain.Client, btcjson.Cmd) (interface{}, error) {
return nil, btcjson.Error{
Code: btcjson.ErrWallet.Code,
Message: "Request requires a wallet but no wallet has been " +
@ -1519,7 +1520,7 @@ func jsonError(err error) *btcjson.Error {
// AddMultiSig and CreateMultiSig.
// all error codes are rpc parse error here to match bitcoind which just throws
// a runtime exception. *sigh*.
func makeMultiSigScript(w *Wallet, keys []string, nRequired int) ([]byte, error) {
func makeMultiSigScript(w *wallet.Wallet, keys []string, nRequired int) ([]byte, error) {
keysesPrecious := make([]*btcutil.AddressPubKey, len(keys))
// The address list will made up either of addreseses (pubkey hash), for
@ -1562,7 +1563,7 @@ func makeMultiSigScript(w *Wallet, keys []string, nRequired int) ([]byte, error)
// AddMultiSigAddress handles an addmultisigaddress request by adding a
// multisig address to the given wallet.
func AddMultiSigAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func AddMultiSigAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.AddMultisigAddressCmd)
err := checkDefaultAccount(cmd.Account)
@ -1591,7 +1592,7 @@ func AddMultiSigAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (in
// CreateMultiSig handles an createmultisig request by returning a
// multisig address for the given inputs.
func CreateMultiSig(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func CreateMultiSig(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.CreateMultisigCmd)
script, err := makeMultiSigScript(w, cmd.Keys, cmd.NRequired)
@ -1614,7 +1615,7 @@ func CreateMultiSig(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
// DumpPrivKey handles a dumpprivkey request with the private key
// for a single address, or an appropiate error if the wallet
// is locked.
func DumpPrivKey(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func DumpPrivKey(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.DumpPrivKeyCmd)
addr, err := btcutil.DecodeAddress(cmd.Address, activeNet.Params)
@ -1634,7 +1635,7 @@ func DumpPrivKey(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface
// DumpWallet handles a dumpwallet request by returning all private
// keys in a wallet, or an appropiate error if the wallet is locked.
// TODO: finish this to match bitcoind by writing the dump to a file.
func DumpWallet(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func DumpWallet(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
keys, err := w.DumpPrivKeys()
if isManagerLockedError(err) {
return nil, btcjson.ErrWalletUnlockNeeded
@ -1648,7 +1649,7 @@ func DumpWallet(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{
// returning base64-encoding of serialized account files.
//
// TODO: remove Download from the command, this always assumes download now.
func ExportWatchingWallet(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ExportWatchingWallet(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcws.ExportWatchingWalletCmd)
err := checkAccountName(cmd.Account)
@ -1656,13 +1657,13 @@ func ExportWatchingWallet(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (
return nil, err
}
return w.ExportWatchingWallet()
return w.ExportWatchingWallet(cfg.WalletPass)
}
// GetAddressesByAccount handles a getaddressesbyaccount request by returning
// all addresses for an account, or an error if the requested account does
// not exist.
func GetAddressesByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetAddressesByAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetAddressesByAccountCmd)
account, err := w.Manager.LookupAccount(cmd.Account)
@ -1686,7 +1687,7 @@ func GetAddressesByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
// GetBalance handles a getbalance request by returning the balance for an
// account (wallet), or an error if the requested account does not
// exist.
func GetBalance(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetBalance(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetBalanceCmd)
var balance btcutil.Amount
@ -1709,7 +1710,7 @@ func GetBalance(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{
// GetBestBlock handles a getbestblock request by returning a JSON object
// with the height and hash of the most recently processed block.
func GetBestBlock(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetBestBlock(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
blk := w.Manager.SyncedTo()
result := &btcws.GetBestBlockResult{
Hash: blk.Hash.String(),
@ -1720,14 +1721,14 @@ func GetBestBlock(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interfac
// GetBestBlockHash handles a getbestblockhash request by returning the hash
// of the most recently processed block.
func GetBestBlockHash(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetBestBlockHash(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
blk := w.Manager.SyncedTo()
return blk.Hash.String(), nil
}
// GetBlockCount handles a getblockcount request by returning the chain height
// of the most recently processed block.
func GetBlockCount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetBlockCount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
blk := w.Manager.SyncedTo()
return blk.Height, nil
}
@ -1735,7 +1736,7 @@ func GetBlockCount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interfa
// GetInfo handles a getinfo request by returning the a structure containing
// information about the current state of btcwallet.
// exist.
func GetInfo(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetInfo(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
// Call down to btcd for all of the information in this command known
// by them.
info, err := chainSvr.GetInfo()
@ -1766,7 +1767,7 @@ func GetInfo(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{},
// GetAccount handles a getaccount request by returning the account name
// associated with a single address.
func GetAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetAccountCmd)
// Is address valid?
@ -1794,7 +1795,7 @@ func GetAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{
// If the most recently-requested address has been used, a new address (the
// next chained address in the keypool) is used. This can fail if the keypool
// runs out (and will return btcjson.ErrWalletKeypoolRanOut if that happens).
func GetAccountAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetAccountAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetAccountAddressCmd)
account, err := w.Manager.LookupAccount(cmd.Account)
@ -1811,7 +1812,7 @@ func GetAccountAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (int
// GetUnconfirmedBalance handles a getunconfirmedbalance extension request
// by returning the current unconfirmed balance of an account.
func GetUnconfirmedBalance(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetUnconfirmedBalance(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcws.GetUnconfirmedBalanceCmd)
account, err := w.Manager.LookupAccount(cmd.Account)
@ -1833,7 +1834,7 @@ func GetUnconfirmedBalance(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
// ImportPrivKey handles an importprivkey request by parsing
// a WIF-encoded private key and adding it to an account.
func ImportPrivKey(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ImportPrivKey(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ImportPrivKeyCmd)
// Yes, Label is the account name...
@ -1862,14 +1863,14 @@ func ImportPrivKey(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interfa
// KeypoolRefill handles the keypoolrefill command. Since we handle the keypool
// automatically this does nothing since refilling is never manually required.
func KeypoolRefill(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func KeypoolRefill(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
return nil, nil
}
// CreateNewAccount handles a createnewaccount request by creating and
// returning a new account. If the last account has no transaction history
// as per BIP 0044 a new account cannot be created so an error will be returned.
func CreateNewAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func CreateNewAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcws.CreateNewAccountCmd)
// Check that we are within the maximum allowed non-empty accounts limit.
@ -1897,7 +1898,7 @@ func CreateNewAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inte
// RenameAccount handles a renameaccount request by renaming an account.
// If the account does not exist an appropiate error will be returned.
func RenameAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func RenameAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcws.RenameAccountCmd)
// Check that given account exists
account, err := w.Manager.LookupAccount(cmd.OldAccount)
@ -1912,7 +1913,7 @@ func RenameAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interfa
// error is returned.
// TODO: Follow BIP 0044 and warn if number of unused addresses exceeds
// the gap limit.
func GetNewAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetNewAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetNewAddressCmd)
account, err := w.Manager.LookupAccount(cmd.Account)
@ -1934,7 +1935,7 @@ func GetNewAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interfa
//
// Note: bitcoind allows specifying the account as an optional parameter,
// but ignores the parameter.
func GetRawChangeAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetRawChangeAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetRawChangeAddressCmd)
account, err := w.Manager.LookupAccount(cmd.Account)
if err != nil {
@ -1951,7 +1952,7 @@ func GetRawChangeAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (i
// GetReceivedByAccount handles a getreceivedbyaccount request by returning
// the total amount received by addresses of an account.
func GetReceivedByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetReceivedByAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetReceivedByAccountCmd)
account, err := w.Manager.LookupAccount(cmd.Account)
@ -1969,7 +1970,7 @@ func GetReceivedByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (
// GetReceivedByAddress handles a getreceivedbyaddress request by returning
// the total amount received by a single address.
func GetReceivedByAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetReceivedByAddressCmd)
addr, err := btcutil.DecodeAddress(cmd.Address, activeNet.Params)
@ -1986,7 +1987,7 @@ func GetReceivedByAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (
// GetTransaction handles a gettransaction request by returning details about
// a single transaction saved by wallet.
func GetTransaction(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func GetTransaction(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.GetTransactionCmd)
txSha, err := wire.NewShaHashFromStr(cmd.Txid)
@ -2089,7 +2090,7 @@ func GetTransaction(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
// ListAccounts handles a listaccounts request by returning a map of account
// names to their balances.
func ListAccounts(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListAccounts(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ListAccountsCmd)
accountBalances := map[string]float64{}
@ -2114,7 +2115,7 @@ func ListAccounts(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interfac
// ListLockUnspent handles a listlockunspent request by returning an slice of
// all locked outpoints.
func ListLockUnspent(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListLockUnspent(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
return w.LockedOutpoints(), nil
}
@ -2128,7 +2129,7 @@ func ListLockUnspent(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inter
// default: one;
// "includeempty": whether or not to include addresses that have no transactions -
// default: false.
func ListReceivedByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListReceivedByAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ListReceivedByAccountCmd)
accounts, err := w.Manager.AllAccounts()
@ -2166,7 +2167,7 @@ func ListReceivedByAccount(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
// default: one;
// "includeempty": whether or not to include addresses that have no transactions -
// default: false.
func ListReceivedByAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ListReceivedByAddressCmd)
// Intermediate data for each address.
@ -2247,7 +2248,7 @@ func ListReceivedByAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
// ListSinceBlock handles a listsinceblock request by returning an array of maps
// with details of sent and received wallet transactions since the given block.
func ListSinceBlock(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListSinceBlock(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ListSinceBlockCmd)
height := int32(-1)
@ -2291,7 +2292,7 @@ func ListSinceBlock(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interf
// ListTransactions handles a listtransactions request by returning an
// array of maps with details of sent and recevied wallet transactions.
func ListTransactions(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListTransactions(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ListTransactionsCmd)
if cmd.Account != nil {
@ -2309,7 +2310,7 @@ func ListTransactions(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inte
// transactions. The form of the reply is identical to listtransactions,
// but the array elements are limited to transaction details which are
// about the addresess included in the request.
func ListAddressTransactions(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListAddressTransactions(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcws.ListAddressTransactionsCmd)
err := checkAccountName(cmd.Account)
@ -2338,7 +2339,7 @@ func ListAddressTransactions(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd
// a map with details of sent and recevied wallet transactions. This is
// similar to ListTransactions, except it takes only a single optional
// argument for the account name and replies with all transactions.
func ListAllTransactions(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListAllTransactions(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcws.ListAllTransactionsCmd)
if cmd.Account != nil {
@ -2352,7 +2353,7 @@ func ListAllTransactions(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (i
}
// ListUnspent handles the listunspent command.
func ListUnspent(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ListUnspent(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ListUnspentCmd)
addresses := make(map[string]bool)
@ -2376,7 +2377,7 @@ func ListUnspent(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface
}
// LockUnspent handles the lockunspent command.
func LockUnspent(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func LockUnspent(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.LockUnspentCmd)
switch {
@ -2401,7 +2402,7 @@ func LockUnspent(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface
// sendPairs is a helper routine to reduce duplicated code when creating and
// sending payment transactions.
func sendPairs(w *Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
func sendPairs(w *wallet.Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
amounts map[string]btcutil.Amount, account uint32, minconf int) (interface{}, error) {
// Create transaction, replying with an error if the creation
@ -2409,7 +2410,7 @@ func sendPairs(w *Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
createdTx, err := w.CreateSimpleTx(account, amounts, minconf)
if err != nil {
switch {
case err == ErrNonPositiveAmount:
case err == wallet.ErrNonPositiveAmount:
return nil, ErrNeedPositiveAmount
case isManagerLockedError(err):
return nil, btcjson.ErrWalletUnlockNeeded
@ -2419,7 +2420,7 @@ func sendPairs(w *Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
}
// Add to transaction store.
txr, err := w.TxStore.InsertTx(createdTx.tx, nil)
txr, err := w.TxStore.InsertTx(createdTx.Tx, nil)
if err != nil {
log.Errorf("Error adding sent tx history: %v", err)
return nil, btcjson.ErrInternal
@ -2429,8 +2430,8 @@ func sendPairs(w *Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
log.Errorf("Error adding sent tx history: %v", err)
return nil, btcjson.ErrInternal
}
if createdTx.changeIndex >= 0 {
_, err = txr.AddCredit(uint32(createdTx.changeIndex), true)
if createdTx.ChangeIndex >= 0 {
_, err = txr.AddCredit(uint32(createdTx.ChangeIndex), true)
if err != nil {
log.Errorf("Error adding change address for sent "+
"tx: %v", err)
@ -2439,7 +2440,7 @@ func sendPairs(w *Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
}
w.TxStore.MarkDirty()
txSha, err := chainSvr.SendRawTransaction(createdTx.tx.MsgTx(), false)
txSha, err := chainSvr.SendRawTransaction(createdTx.Tx.MsgTx(), false)
if err != nil {
return nil, err
}
@ -2452,7 +2453,7 @@ func sendPairs(w *Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
// address. Leftover inputs not sent to the payment address or a fee for
// the miner are sent back to a new address in the wallet. Upon success,
// the TxID for the created transaction is returned.
func SendFrom(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func SendFrom(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.SendFromCmd)
account, err := w.Manager.LookupAccount(cmd.FromAccount)
@ -2480,7 +2481,7 @@ func SendFrom(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{},
// payment addresses. Leftover inputs not sent to the payment address
// or a fee for the miner are sent back to a new address in the wallet.
// Upon success, the TxID for the created transaction is returned.
func SendMany(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func SendMany(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.SendManyCmd)
account, err := w.Manager.LookupAccount(cmd.FromAccount)
@ -2507,7 +2508,7 @@ func SendMany(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{},
// payment address. Leftover inputs not sent to the payment address or a fee
// for the miner are sent back to a new address in the wallet. Upon success,
// the TxID for the created transaction is returned.
func SendToAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func SendToAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.SendToAddressCmd)
// Check that signed integer parameters are positive.
@ -2525,7 +2526,7 @@ func SendToAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interfa
}
// SetTxFee sets the transaction fee per kilobyte added to transactions.
func SetTxFee(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func SetTxFee(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.SetTxFeeCmd)
// Check that amount is not negative.
@ -2541,7 +2542,7 @@ func SetTxFee(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{},
// SignMessage signs the given message with the private key for the given
// address
func SignMessage(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func SignMessage(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.SignMessageCmd)
addr, err := btcutil.DecodeAddress(cmd.Address, activeNet.Params)
@ -2578,7 +2579,7 @@ type pendingTx struct {
}
// SignRawTransaction handles the signrawtransaction command.
func SignRawTransaction(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func SignRawTransaction(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.SignRawTransactionCmd)
serializedTx, err := decodeHexStr(cmd.RawTx)
@ -2852,7 +2853,7 @@ func SignRawTransaction(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (in
}
// ValidateAddress handles the validateaddress command.
func ValidateAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func ValidateAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.ValidateAddressCmd)
result := btcjson.ValidateAddressResult{}
@ -2933,7 +2934,7 @@ func ValidateAddress(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inter
// VerifyMessage handles the verifymessage command by verifying the provided
// compact signature for the given address and message.
func VerifyMessage(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func VerifyMessage(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.VerifyMessageCmd)
addr, err := btcutil.DecodeAddress(cmd.Address, activeNet.Params)
@ -2976,14 +2977,14 @@ func VerifyMessage(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interfa
// WalletIsLocked handles the walletislocked extension request by
// returning the current lock state (false for unlocked, true for locked)
// of an account.
func WalletIsLocked(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func WalletIsLocked(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
return w.Locked(), nil
}
// WalletLock handles a walletlock request by locking the all account
// wallets, returning an error if any wallet is not encrypted (for example,
// a watching-only wallet).
func WalletLock(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func WalletLock(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
w.Lock()
return nil, nil
}
@ -2991,7 +2992,7 @@ func WalletLock(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{
// WalletPassphrase responds to the walletpassphrase request by unlocking
// the wallet. The decryption key is saved in the wallet until timeout
// seconds expires, after which the wallet is locked.
func WalletPassphrase(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func WalletPassphrase(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.WalletPassphraseCmd)
timeout := time.Second * time.Duration(cmd.Timeout)
@ -3006,7 +3007,7 @@ func WalletPassphrase(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inte
//
// If the old passphrase is correct and the passphrase is changed, all
// wallets will be immediately locked.
func WalletPassphraseChange(w *Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
func WalletPassphraseChange(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
cmd := icmd.(*btcjson.WalletPassphraseChangeCmd)
err := w.ChangePassphrase([]byte(cmd.OldPassphrase),

31
wallet/README.md Normal file
View file

@ -0,0 +1,31 @@
wallet
======
[![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)]
(https://travis-ci.org/btcsuite/btcwallet)
## Feature Overview
TODO: Flesh out this section
## Documentation
[![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/wallet?status.png)]
(http://godoc.org/github.com/btcsuite/btcwallet/wallet)
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/wallet
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/wallet
## Installation
```bash
$ go get github.com/btcsuite/btcwallet/wallet
```
Package wallet is licensed under the [copyfree](http://copyfree.org) ISC
License.

View file

@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package main
package wallet
import (
"github.com/btcsuite/btcd/txscript"
@ -120,7 +120,7 @@ func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *txstore.Block) error {
// Errors don't matter here. If addrs is nil, the range below
// does nothing.
_, addrs, _, _ := txscript.ExtractPkScriptAddrs(txOut.PkScript,
activeNet.Params)
w.chainParams)
insert := false
for _, addr := range addrs {
_, err := w.Manager.Address(addr)

33
wallet/config.go Normal file
View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2015 Conformal Systems LLC <info@conformal.com>
*
* 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.
*/
package wallet
import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcwallet/txstore"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
)
// Config is a structure used to initialize a Wallet
// All values are required for successfully opening a Wallet
type Config struct {
ChainParams *chaincfg.Params
Db *walletdb.DB
TxStore *txstore.Store
Waddrmgr *waddrmgr.Manager
}

View file

@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package main
package wallet
import (
"errors"
@ -24,6 +24,7 @@ import (
"time"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
@ -108,9 +109,9 @@ const defaultFeeIncrement = 1e3
// CreatedTx holds the state of a newly-created transaction and the change
// output (if one was added).
type CreatedTx struct {
tx *btcutil.Tx
changeAddr btcutil.Address
changeIndex int // negative if no change
Tx *btcutil.Tx
ChangeAddr btcutil.Address
ChangeIndex int // negative if no change
}
// ByAmount defines the methods needed to satisify sort.Interface to
@ -150,7 +151,7 @@ func (w *Wallet) txToPairs(pairs map[string]btcutil.Amount, account uint32, minc
return nil, err
}
return createTx(eligible, pairs, bs, w.FeeIncrement, w.Manager, account, w.NewChangeAddress)
return createTx(eligible, pairs, bs, w.FeeIncrement, w.Manager, account, w.NewChangeAddress, w.chainParams, w.DisallowFree)
}
// createTx selects inputs (from the given slice of eligible utxos)
@ -158,18 +159,14 @@ func (w *Wallet) txToPairs(pairs map[string]btcutil.Amount, account uint32, minc
// the mining fee. It then creates and returns a CreatedTx containing
// the selected inputs and the given outputs, validating it (using
// validateMsgTx) as well.
func createTx(
eligible []txstore.Credit,
outputs map[string]btcutil.Amount,
bs *waddrmgr.BlockStamp,
feeIncrement btcutil.Amount,
mgr *waddrmgr.Manager,
account uint32,
changeAddress func(account uint32) (btcutil.Address, error)) (
*CreatedTx, error) {
func createTx(eligible []txstore.Credit,
outputs map[string]btcutil.Amount, bs *waddrmgr.BlockStamp,
feeIncrement btcutil.Amount, mgr *waddrmgr.Manager, account uint32,
changeAddress func(account uint32) (btcutil.Address, error),
chainParams *chaincfg.Params, disallowFree bool) (*CreatedTx, error) {
msgtx := wire.NewMsgTx()
minAmount, err := addOutputs(msgtx, outputs)
minAmount, err := addOutputs(msgtx, outputs, chainParams)
if err != nil {
return nil, err
}
@ -196,7 +193,7 @@ func createTx(
// Get an initial fee estimate based on the number of selected inputs
// and added outputs, with no change.
szEst := estimateTxSize(len(inputs), len(msgtx.TxOut))
feeEst := minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height)
feeEst := minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
// Now make sure the sum amount of all our inputs is enough for the
// sum amount of all outputs plus the fee. If necessary we add more,
@ -210,7 +207,7 @@ func createTx(
msgtx.AddTxIn(wire.NewTxIn(input.OutPoint(), nil))
szEst += txInEstimate
totalAdded += input.Amount()
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height)
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
}
var changeAddr btcutil.Address
@ -233,7 +230,7 @@ func createTx(
}
}
if err = signMsgTx(msgtx, inputs, mgr); err != nil {
if err = signMsgTx(msgtx, inputs, mgr, chainParams); err != nil {
return nil, err
}
@ -261,7 +258,7 @@ func createTx(
msgtx.AddTxIn(wire.NewTxIn(input.OutPoint(), nil))
szEst += txInEstimate
totalAdded += input.Amount()
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height)
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
}
}
@ -270,9 +267,9 @@ func createTx(
}
info := &CreatedTx{
tx: btcutil.NewTx(msgtx),
changeAddr: changeAddr,
changeIndex: changeIdx,
Tx: btcutil.NewTx(msgtx),
ChangeAddr: changeAddr,
ChangeIndex: changeIdx,
}
return info, nil
}
@ -296,14 +293,14 @@ func addChange(msgtx *wire.MsgTx, change btcutil.Amount, changeAddr btcutil.Addr
// addOutputs adds the given address/amount pairs as outputs to msgtx,
// returning their total amount.
func addOutputs(msgtx *wire.MsgTx, pairs map[string]btcutil.Amount) (btcutil.Amount, error) {
func addOutputs(msgtx *wire.MsgTx, pairs map[string]btcutil.Amount, chainParams *chaincfg.Params) (btcutil.Amount, error) {
var minAmount btcutil.Amount
for addrStr, amt := range pairs {
if amt <= 0 {
return minAmount, ErrNonPositiveAmount
}
minAmount += amt
addr, err := btcutil.DecodeAddress(addrStr, activeNet.Params)
addr, err := btcutil.DecodeAddress(addrStr, chainParams)
if err != nil {
return minAmount, fmt.Errorf("cannot decode address: %s", err)
}
@ -364,7 +361,7 @@ func (w *Wallet) findEligibleOutputs(account uint32, minconf int, bs *waddrmgr.B
// signMsgTx sets the SignatureScript for every item in msgtx.TxIn.
// It must be called every time a msgtx is changed.
// Only P2PKH outputs are supported at this point.
func signMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit, mgr *waddrmgr.Manager) error {
func signMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit, mgr *waddrmgr.Manager, chainParams *chaincfg.Params) error {
if len(prevOutputs) != len(msgtx.TxIn) {
return fmt.Errorf(
"Number of prevOutputs (%d) does not match number of tx inputs (%d)",
@ -373,7 +370,7 @@ func signMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit, mgr *waddrmgr.Ma
for i, output := range prevOutputs {
// Errors don't matter here, as we only consider the
// case where len(addrs) == 1.
_, addrs, _, _ := output.Addresses(activeNet.Params)
_, addrs, _, _ := output.Addresses(chainParams)
if len(addrs) != 1 {
continue
}
@ -425,9 +422,9 @@ func validateMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit) error {
// s less than 1 kilobyte and none of the outputs contain a value
// less than 1 bitcent. Otherwise, the fee will be calculated using
// incr, incrementing the fee for each kilobyte of transaction.
func minimumFee(incr btcutil.Amount, txLen int, outputs []*wire.TxOut, prevOutputs []txstore.Credit, height int32) btcutil.Amount {
func minimumFee(incr btcutil.Amount, txLen int, outputs []*wire.TxOut, prevOutputs []txstore.Credit, height int32, disallowFree bool) btcutil.Amount {
allowFree := false
if !cfg.DisallowFree {
if !disallowFree {
allowFree = allowNoFeeTx(height, prevOutputs, txLen)
}
fee := feeForSize(incr, txLen)

View file

@ -1,4 +1,4 @@
package main
package wallet
import (
"encoding/hex"
@ -8,6 +8,7 @@ import (
"sort"
"testing"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
@ -56,7 +57,7 @@ var fastScrypt = &waddrmgr.Options{
func Test_addOutputs(t *testing.T) {
msgtx := wire.NewMsgTx()
pairs := map[string]btcutil.Amount{outAddr1: 10, outAddr2: 1}
if _, err := addOutputs(msgtx, pairs); err != nil {
if _, err := addOutputs(msgtx, pairs, &chaincfg.TestNet3Params); err != nil {
t.Fatal(err)
}
if len(msgtx.TxOut) != 2 {
@ -70,11 +71,10 @@ func Test_addOutputs(t *testing.T) {
}
func TestCreateTx(t *testing.T) {
cfg = &config{DisallowFree: false}
bs := &waddrmgr.BlockStamp{Height: 11111}
mgr := newManager(t, txInfo.privKeys, bs)
account := uint32(0)
changeAddr, _ := btcutil.DecodeAddress("muqW4gcixv58tVbSKRC5q6CRKy8RmyLgZ5", activeNet.Params)
changeAddr, _ := btcutil.DecodeAddress("muqW4gcixv58tVbSKRC5q6CRKy8RmyLgZ5", &chaincfg.TestNet3Params)
var tstChangeAddress = func(account uint32) (btcutil.Address, error) {
return changeAddr, nil
}
@ -83,17 +83,17 @@ func TestCreateTx(t *testing.T) {
eligible := eligibleInputsFromTx(t, txInfo.hex, []uint32{1, 2, 3, 4, 5})
// Now create a new TX sending 25e6 satoshis to the following addresses:
outputs := map[string]btcutil.Amount{outAddr1: 15e6, outAddr2: 10e6}
tx, err := createTx(eligible, outputs, bs, defaultFeeIncrement, mgr, account, tstChangeAddress)
tx, err := createTx(eligible, outputs, bs, defaultFeeIncrement, mgr, account, tstChangeAddress, &chaincfg.TestNet3Params, false)
if err != nil {
t.Fatal(err)
}
if tx.changeAddr.String() != changeAddr.String() {
if tx.ChangeAddr.String() != changeAddr.String() {
t.Fatalf("Unexpected change address; got %v, want %v",
tx.changeAddr.String(), changeAddr.String())
tx.ChangeAddr.String(), changeAddr.String())
}
msgTx := tx.tx.MsgTx()
msgTx := tx.Tx.MsgTx()
if len(msgTx.TxOut) != 3 {
t.Fatalf("Unexpected number of outputs; got %d, want 3", len(msgTx.TxOut))
}
@ -121,17 +121,16 @@ func TestCreateTx(t *testing.T) {
}
func TestCreateTxInsufficientFundsError(t *testing.T) {
cfg = &config{DisallowFree: false}
outputs := map[string]btcutil.Amount{outAddr1: 10, outAddr2: 1e9}
eligible := eligibleInputsFromTx(t, txInfo.hex, []uint32{1})
bs := &waddrmgr.BlockStamp{Height: 11111}
account := uint32(0)
changeAddr, _ := btcutil.DecodeAddress("muqW4gcixv58tVbSKRC5q6CRKy8RmyLgZ5", activeNet.Params)
changeAddr, _ := btcutil.DecodeAddress("muqW4gcixv58tVbSKRC5q6CRKy8RmyLgZ5", &chaincfg.TestNet3Params)
var tstChangeAddress = func(account uint32) (btcutil.Address, error) {
return changeAddr, nil
}
_, err := createTx(eligible, outputs, bs, defaultFeeIncrement, nil, account, tstChangeAddress)
_, err := createTx(eligible, outputs, bs, defaultFeeIncrement, nil, account, tstChangeAddress, &chaincfg.TestNet3Params, false)
if err == nil {
t.Error("Expected InsufficientFundsError, got no error")
@ -144,7 +143,7 @@ func TestCreateTxInsufficientFundsError(t *testing.T) {
func checkOutputsMatch(t *testing.T, msgtx *wire.MsgTx, expected map[string]btcutil.Amount) {
// This is a bit convoluted because the index of the change output is randomized.
for addrStr, v := range expected {
addr, err := btcutil.DecodeAddress(addrStr, activeNet.Params)
addr, err := btcutil.DecodeAddress(addrStr, &chaincfg.TestNet3Params)
if err != nil {
t.Fatalf("Cannot decode address: %v", err)
}
@ -187,7 +186,7 @@ func newManager(t *testing.T, privKeys []string, bs *waddrmgr.BlockStamp) *waddr
pubPassphrase := []byte("pub")
privPassphrase := []byte("priv")
mgr, err := waddrmgr.Create(namespace, seed, pubPassphrase,
privPassphrase, activeNet.Params, fastScrypt)
privPassphrase, &chaincfg.TestNet3Params, fastScrypt)
if err != nil {
t.Fatal(err)
}

View file

@ -7,7 +7,7 @@
//
// +build ignore
package main
package wallet
import (
"testing"
@ -19,7 +19,7 @@ import (
)
func init() {
cfg = &config{
cfg = &Config{
KeypoolSize: 100,
}
}

43
wallet/disksync.go Normal file
View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2015 Conformal Systems LLC <info@conformal.com>
*
* 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.
*/
package wallet
import (
"fmt"
"os"
)
// checkCreateDir checks that the path exists and is a directory.
// If path does not exist, it is created.
func checkCreateDir(path string) error {
if fi, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
// Attempt data directory creation
if err = os.MkdirAll(path, 0700); err != nil {
return fmt.Errorf("cannot create directory: %s", err)
}
} else {
return fmt.Errorf("error checking directory: %s", err)
}
} else {
if !fi.IsDir() {
return fmt.Errorf("path '%s' is not a directory", path)
}
}
return nil
}

24
wallet/doc.go Normal file
View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2015 Conformal Systems LLC <info@conformal.com>
*
* 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.
*/
/*
Package wallet provides ...
TODO: Flesh out this section
Overview
*/
package wallet

68
wallet/log.go Normal file
View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2015 Conformal Systems LLC <info@conformal.com>
*
* 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.
*/
package wallet
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() {
log = 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)
}
// pickNoun returns the singular or plural form of a noun depending
// on the count n.
func pickNoun(n int, singular, plural string) string {
if n == 1 {
return singular
}
return plural
}

View file

@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package main
package wallet
import (
"github.com/btcsuite/btcd/wire"

View file

@ -14,10 +14,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package main
package wallet
import (
"bufio"
"bytes"
"encoding/base64"
"encoding/hex"
@ -27,7 +26,6 @@ import (
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
@ -36,12 +34,14 @@ import (
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/txstore"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/golangcrypto/ssh/terminal"
)
const (
walletDbWatchingOnlyName = "wowallet.db"
)
// ErrNotSynced describes an error where an operation cannot complete
@ -54,82 +54,10 @@ var (
waddrmgrNamespaceKey = []byte("waddrmgr")
)
const (
// defaultPubPassphrase is the default public wallet passphrase which is
// used when the user indicates they do not want additional protection
// provided by having all public data in the wallet encrypted by a
// passphrase only known to them.
defaultPubPassphrase = "public"
type noopLocker struct{}
// maxEmptyAccounts is the number of accounts to scan even if they have no
// transaction history. This is a deviation from BIP044 to make account
// creation more easier by allowing a limited number of empty accounts.
maxEmptyAccounts = 100
)
// promptSeed is used to prompt for the wallet seed which maybe required during
// upgrades.
func promptSeed() ([]byte, error) {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("Enter existing wallet seed: ")
seedStr, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
seedStr = strings.TrimSpace(strings.ToLower(seedStr))
seed, err := hex.DecodeString(seedStr)
if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
len(seed) > hdkeychain.MaxSeedBytes {
fmt.Printf("Invalid seed specified. Must be a "+
"hexadecimal value that is at least %d bits and "+
"at most %d bits\n", hdkeychain.MinSeedBytes*8,
hdkeychain.MaxSeedBytes*8)
continue
}
return seed, nil
}
}
// promptPrivPassPhrase is used to prompt for the private passphrase which maybe
// required during upgrades.
func promptPrivPassPhrase() ([]byte, error) {
prompt := "Enter the private passphrase of your wallet: "
for {
fmt.Print(prompt)
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
fmt.Print("\n")
pass = bytes.TrimSpace(pass)
if len(pass) == 0 {
continue
}
return pass, nil
}
}
// networkDir returns the directory name of a network directory to hold wallet
// files.
func networkDir(dataDir string, chainParams *chaincfg.Params) string {
netname := chainParams.Name
// For now, we must always name the testnet data directory as "testnet"
// and not "testnet3" or any other version, as the chaincfg testnet3
// paramaters will likely be switched to being named "testnet3" in the
// future. This is done to future proof that change, and an upgrade
// plan to move the testnet3 data directory can be worked out later.
if chainParams.Net == wire.TestNet3 {
netname = "testnet"
}
return filepath.Join(dataDir, netname)
}
func (noopLocker) Lock() {}
func (noopLocker) Unlock() {}
// Wallet is a structure containing all the components for a
// complete wallet. It contains the Armory-style key store
@ -147,6 +75,7 @@ type Wallet struct {
lockedOutpoints map[wire.OutPoint]struct{}
FeeIncrement btcutil.Amount
DisallowFree bool
// Channels for rescan processing. Requests are added and merged with
// any waiting requests, before being sent to another goroutine to
@ -177,14 +106,17 @@ type Wallet struct {
unconfirmedBalance chan btcutil.Amount
notificationLock sync.Locker
wg sync.WaitGroup
quit chan struct{}
chainParams *chaincfg.Params
Config *Config
wg sync.WaitGroup
quit chan struct{}
}
// newWallet creates a new Wallet structure with the provided address manager
// and transaction store.
func newWallet(mgr *waddrmgr.Manager, txs *txstore.Store) *Wallet {
func newWallet(mgr *waddrmgr.Manager, txs *txstore.Store, db *walletdb.DB) *Wallet {
return &Wallet{
db: *db,
Manager: mgr,
TxStore: txs,
chainSvrLock: new(sync.Mutex),
@ -231,7 +163,7 @@ func (w *Wallet) updateNotificationLock() {
// with the given credit.
// If no account is found, ErrAccountNotFound is returned.
func (w *Wallet) CreditAccount(c txstore.Credit) (uint32, error) {
_, addrs, _, _ := c.Addresses(activeNet.Params)
_, addrs, _, _ := c.Addresses(w.chainParams)
addr := addrs[0]
return w.Manager.AddrAccount(addr)
}
@ -328,7 +260,7 @@ func (w *Wallet) markAddrsUsed(t *txstore.TxRecord) error {
for _, c := range t.Credits() {
// Errors don't matter here. If addrs is nil, the
// range below does nothing.
_, addrs, _, _ := c.Addresses(activeNet.Params)
_, addrs, _, _ := c.Addresses(w.chainParams)
for _, addr := range addrs {
addressID := addr.ScriptAddress()
if err := w.Manager.MarkUsed(addressID); err != nil {
@ -942,7 +874,7 @@ func (w *Wallet) ListAddressTransactions(pkHashes map[string]struct{}) (
for _, c := range r.Credits() {
// We only care about the case where len(addrs) == 1,
// and err will never be non-nil in that case.
_, addrs, _, _ := c.Addresses(activeNet.Params)
_, addrs, _, _ := c.Addresses(w.chainParams)
if len(addrs) != 1 {
continue
}
@ -1032,7 +964,7 @@ func (w *Wallet) ListUnspent(minconf, maxconf int,
return nil, err
}
_, addrs, _, _ := credit.Addresses(activeNet.Params)
_, addrs, _, _ := credit.Addresses(w.chainParams)
if filter {
for _, addr := range addrs {
_, ok := addresses[addr.EncodeAddress()]
@ -1131,7 +1063,7 @@ func (w *Wallet) ImportPrivateKey(wif *btcutil.WIF, bs *waddrmgr.BlockStamp,
// specified.
if bs == nil {
bs = &waddrmgr.BlockStamp{
Hash: *activeNet.Params.GenesisHash,
Hash: *w.chainParams.GenesisHash,
Height: 0,
}
}
@ -1167,7 +1099,7 @@ func (w *Wallet) ImportPrivateKey(wif *btcutil.WIF, bs *waddrmgr.BlockStamp,
// ExportWatchingWallet returns a watching-only version of the wallet serialized
// in a map.
func (w *Wallet) ExportWatchingWallet() (map[string]string, error) {
func (w *Wallet) ExportWatchingWallet(pubPass string) (map[string]string, error) {
tmpDir, err := ioutil.TempDir("", "btcwallet")
if err != nil {
return nil, err
@ -1200,8 +1132,8 @@ func (w *Wallet) ExportWatchingWallet() (map[string]string, error) {
if err != nil {
return nil, err
}
woMgr, err := waddrmgr.Open(namespace, []byte(cfg.WalletPass),
activeNet.Params, nil)
woMgr, err := waddrmgr.Open(namespace, []byte(pubPass),
w.chainParams, nil)
if err != nil {
return nil, err
}
@ -1408,7 +1340,7 @@ func (w *Wallet) TotalReceivedForAddr(addr btcutil.Address, confirms int) (btcut
continue
}
_, addrs, _, err := c.Addresses(activeNet.Params)
_, addrs, _, err := c.Addresses(w.chainParams)
// An error creating addresses from the output script only
// indicates a non-standard script, so ignore this credit.
if err != nil {
@ -1437,60 +1369,15 @@ func (w *Wallet) TxRecord(txSha *wire.ShaHash) (r *txstore.TxRecord, ok bool) {
return nil, false
}
// openWallet opens a wallet from disk.
func openWallet() (*Wallet, error) {
netdir := networkDir(cfg.DataDir, activeNet.Params)
dbPath := filepath.Join(netdir, walletDbName)
// Ensure that the network directory exists.
if err := checkCreateDir(netdir); err != nil {
return nil, err
}
// Open the database using the boltdb backend.
db, err := walletdb.Open("bdb", dbPath)
if err != nil {
return nil, err
}
// Get the namespace for the address manager.
namespace, err := db.Namespace(waddrmgrNamespaceKey)
if err != nil {
return nil, err
}
// Open address manager and transaction store.
var txs *txstore.Store
config := &waddrmgr.Options{
ObtainSeed: promptSeed,
ObtainPrivatePass: promptPrivPassPhrase,
}
mgr, err := waddrmgr.Open(namespace, []byte(cfg.WalletPass),
activeNet.Params, config)
if err == nil {
txs, err = txstore.OpenDir(netdir)
}
if err != nil {
// Special case: if the address manager was successfully read
// (mgr != nil) but the transaction store was not, create a
// new txstore and write it out to disk. Write an unsynced
// manager back to disk so on future opens, the empty txstore
// is not considered fully synced.
if mgr == nil {
return nil, err
}
txs = txstore.New(netdir)
txs.MarkDirty()
err = txs.WriteIfDirty()
if err != nil {
return nil, err
}
mgr.SetSyncedTo(nil)
}
log.Infof("Opened wallet files") // TODO: log balance? last sync height?
wallet := newWallet(mgr, txs)
wallet.db = db
return wallet, nil
// Db returns wallet db being used by a wallet
func (w *Wallet) Db() walletdb.DB {
return w.db
}
// Open opens a wallet from disk.
func Open(config *Config) *Wallet {
wallet := newWallet(config.Waddrmgr, config.TxStore, config.Db)
wallet.chainParams = config.ChainParams
return wallet
}

View file

@ -26,15 +26,88 @@ import (
"strings"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/btcsuite/btcwallet/legacy/keystore"
"github.com/btcsuite/btcwallet/txstore"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet"
"github.com/btcsuite/btcwallet/walletdb"
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
"github.com/btcsuite/golangcrypto/ssh/terminal"
)
var (
// waddrmgrNamespaceKey is the namespace key for the waddrmgr package.
waddrmgrNamespaceKey = []byte("waddrmgr")
)
// networkDir returns the directory name of a network directory to hold wallet
// files.
func networkDir(dataDir string, chainParams *chaincfg.Params) string {
netname := chainParams.Name
// For now, we must always name the testnet data directory as "testnet"
// and not "testnet3" or any other version, as the chaincfg testnet3
// paramaters will likely be switched to being named "testnet3" in the
// future. This is done to future proof that change, and an upgrade
// plan to move the testnet3 data directory can be worked out later.
if chainParams.Net == wire.TestNet3 {
netname = "testnet"
}
return filepath.Join(dataDir, netname)
}
// promptSeed is used to prompt for the wallet seed which maybe required during
// upgrades.
func promptSeed() ([]byte, error) {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("Enter existing wallet seed: ")
seedStr, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
seedStr = strings.TrimSpace(strings.ToLower(seedStr))
seed, err := hex.DecodeString(seedStr)
if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
len(seed) > hdkeychain.MaxSeedBytes {
fmt.Printf("Invalid seed specified. Must be a "+
"hexadecimal value that is at least %d bits and "+
"at most %d bits\n", hdkeychain.MinSeedBytes*8,
hdkeychain.MaxSeedBytes*8)
continue
}
return seed, nil
}
}
// promptPrivPassPhrase is used to prompt for the private passphrase which maybe
// required during upgrades.
func promptPrivPassPhrase() ([]byte, error) {
prompt := "Enter the private passphrase of your wallet: "
for {
fmt.Print(prompt)
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
fmt.Print("\n")
pass = bytes.TrimSpace(pass)
if len(pass) == 0 {
continue
}
return pass, nil
}
}
// promptConsoleList prompts the user with the given prefix, list of valid
// responses, and default list entry to use. The function will repeat the
// prompt to the user until they enter a valid response.
@ -484,3 +557,94 @@ func createSimulationWallet(cfg *config) error {
fmt.Println("The wallet has been created successfully.")
return nil
}
// openDb opens and returns a *walletdb.DB (boltdb here) given the
// directory and dbname
func openDb(directory string, dbname string) (*walletdb.DB, error) {
dbPath := filepath.Join(directory, dbname)
// Ensure that the network directory exists.
if err := checkCreateDir(directory); err != nil {
return nil, err
}
// Open the database using the boltdb backend.
db, err := walletdb.Open("bdb", dbPath)
if err != nil {
return nil, err
}
return &db, nil
}
// openWaddrmgr returns an address manager given a database, namespace,
// public pass and the chain params
// It prompts for seed and private passphrase required in case of upgrades
func openWaddrmgr(db *walletdb.DB, namespaceKey []byte, pass string,
chainParams *chaincfg.Params) (*waddrmgr.Manager, error) {
// Get the namespace for the address manager.
namespace, err := (*db).Namespace(namespaceKey)
if err != nil {
return nil, err
}
config := &waddrmgr.Options{
ObtainSeed: promptSeed,
ObtainPrivatePass: promptPrivPassPhrase,
}
// Open address manager and transaction store.
// var txs *txstore.Store
return waddrmgr.Open(namespace, []byte(pass),
chainParams, config)
}
// openWallet returns a wallet. The function handles opening an existing wallet
// database, the address manager and the transaction store and uses the values
// to open a wallet.Wallet
func openWallet() (*wallet.Wallet, error) {
netdir := networkDir(cfg.DataDir, activeNet.Params)
db, err := openDb(netdir, walletDbName)
if err != nil {
log.Errorf("%v", err)
return nil, err
}
var txs *txstore.Store
mgr, err := openWaddrmgr(db, waddrmgrNamespaceKey, cfg.WalletPass,
activeNet.Params)
if err == nil {
txs, err = txstore.OpenDir(netdir)
}
if err != nil {
// Special case: if the address manager was successfully read
// (mgr != nil) but the transaction store was not, create a
// new txstore and write it out to disk. Write an unsynced
// manager back to disk so on future opens, the empty txstore
// is not considered fully synced.
if mgr == nil {
log.Errorf("%v", err)
return nil, err
}
txs = txstore.New(netdir)
txs.MarkDirty()
err = txs.WriteIfDirty()
if err != nil {
log.Errorf("%v", err)
return nil, err
}
mgr.SetSyncedTo(nil)
}
walletConfig := &wallet.Config{
Db: db,
TxStore: txs,
Waddrmgr: mgr,
ChainParams: activeNet.Params,
}
log.Infof("Opened wallet files") // TODO: log balance? last sync height?
w := wallet.Open(walletConfig)
return w, nil
}