2013-12-02 20:56:06 +01:00
|
|
|
/*
|
2014-01-03 19:34:37 +01:00
|
|
|
* Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
|
2013-12-02 20:56:06 +01:00
|
|
|
*
|
|
|
|
* 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 main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2014-01-06 18:24:29 +01:00
|
|
|
"github.com/conformal/btcutil"
|
2013-12-02 20:56:06 +01:00
|
|
|
"github.com/conformal/btcwallet/tx"
|
|
|
|
"github.com/conformal/btcwallet/wallet"
|
|
|
|
"github.com/conformal/btcwire"
|
|
|
|
"os"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Errors relating to accounts.
|
|
|
|
var (
|
|
|
|
ErrAcctExists = errors.New("account already exists")
|
|
|
|
ErrAcctNotExist = errors.New("account does not exist")
|
|
|
|
)
|
|
|
|
|
|
|
|
var accountstore = NewAccountStore()
|
|
|
|
|
|
|
|
// AccountStore stores all wallets currently being handled by
|
|
|
|
// btcwallet. Wallet are stored in a map with the account name as the
|
|
|
|
// key. A RWMutex is used to protect against incorrect concurrent
|
|
|
|
// access.
|
|
|
|
type AccountStore struct {
|
2014-01-03 19:34:37 +01:00
|
|
|
sync.RWMutex
|
2013-12-02 20:56:06 +01:00
|
|
|
accounts map[string]*Account
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewAccountStore returns an initialized and empty AccountStore.
|
|
|
|
func NewAccountStore() *AccountStore {
|
|
|
|
return &AccountStore{
|
|
|
|
accounts: make(map[string]*Account),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Account returns the account specified by name, or ErrAcctNotExist
|
|
|
|
// as an error if the account is not found.
|
|
|
|
func (store *AccountStore) Account(name string) (*Account, error) {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
account, ok := store.accounts[name]
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrAcctNotExist
|
|
|
|
}
|
|
|
|
return account, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rollback rolls back each Account saved in the store.
|
|
|
|
func (store *AccountStore) Rollback(height int32, hash *btcwire.ShaHash) {
|
2013-12-06 21:37:07 +01:00
|
|
|
log.Debugf("Rolling back tx history since block height %v hash %v",
|
|
|
|
height, hash)
|
|
|
|
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
for _, account := range store.accounts {
|
|
|
|
account.Rollback(height, hash)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// BlockNotify runs after btcwallet is notified of a new block connected to
|
|
|
|
// the best chain. It notifies all frontends of any changes from the new
|
|
|
|
// block, including changed balances. Each account is then set to be synced
|
|
|
|
// with the latest block.
|
|
|
|
func (store *AccountStore) BlockNotify(bs *wallet.BlockStamp) {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
// The UTXO store will be dirty if it was modified
|
|
|
|
// from a tx notification.
|
|
|
|
if a.UtxoStore.dirty {
|
|
|
|
// Notify all frontends of account's new unconfirmed
|
|
|
|
// and confirmed balance.
|
|
|
|
confirmed := a.CalculateBalance(1)
|
|
|
|
unconfirmed := a.CalculateBalance(0) - confirmed
|
|
|
|
NotifyWalletBalance(frontendNotificationMaster,
|
|
|
|
a.name, confirmed)
|
|
|
|
NotifyWalletBalanceUnconfirmed(frontendNotificationMaster,
|
|
|
|
a.name, unconfirmed)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The account is intentionaly not immediately synced to disk.
|
|
|
|
// If btcd is performing an IBD, writing the wallet file for
|
|
|
|
// each newly-connected block would result in too many
|
|
|
|
// unnecessary disk writes. The UTXO and transaction stores
|
|
|
|
// could be written, but in the case of btcwallet closing
|
|
|
|
// before writing the dirty wallet, both would have to be
|
|
|
|
// pruned anyways.
|
|
|
|
//
|
|
|
|
// Instead, the wallet is queued to be written to disk at the
|
|
|
|
// next scheduled disk sync.
|
|
|
|
a.mtx.Lock()
|
|
|
|
a.Wallet.SetSyncedWith(bs)
|
2014-01-23 18:34:41 +01:00
|
|
|
a.MarkDirtyWallet()
|
2013-12-02 20:56:06 +01:00
|
|
|
a.mtx.Unlock()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RecordMinedTx searches through each account's TxStore, searching for a
|
|
|
|
// sent transaction with the same txid as from a txmined notification. If
|
|
|
|
// the transaction IDs match, the record in the TxStore is updated with
|
|
|
|
// the full information about the newly-mined tx, and the TxStore is
|
|
|
|
// marked as dirty.
|
|
|
|
func (store *AccountStore) RecordMinedTx(txid *btcwire.ShaHash,
|
|
|
|
blkhash *btcwire.ShaHash, blkheight int32, blkindex int,
|
|
|
|
blktime int64) error {
|
|
|
|
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
for _, account := range store.accounts {
|
|
|
|
account.TxStore.Lock()
|
|
|
|
|
|
|
|
// Search in reverse order. Since more recently-created
|
|
|
|
// transactions are appended to the end of the store, it's
|
|
|
|
// more likely to find it when searching from the end.
|
|
|
|
for i := len(account.TxStore.s) - 1; i >= 0; i-- {
|
|
|
|
sendtx, ok := account.TxStore.s[i].(*tx.SendTx)
|
|
|
|
if ok {
|
|
|
|
if bytes.Equal(txid.Bytes(), sendtx.TxID[:]) {
|
|
|
|
copy(sendtx.BlockHash[:], blkhash.Bytes())
|
|
|
|
sendtx.BlockHeight = blkheight
|
|
|
|
sendtx.BlockIndex = int32(blkindex)
|
|
|
|
sendtx.BlockTime = blktime
|
2014-01-23 18:34:41 +01:00
|
|
|
account.MarkDirtyTxStore()
|
2013-12-02 20:56:06 +01:00
|
|
|
account.TxStore.Unlock()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
account.TxStore.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.New("txid does not match any recorded sent transaction")
|
|
|
|
}
|
|
|
|
|
|
|
|
// CalculateBalance returns the balance, calculated using minconf
|
|
|
|
// block confirmations, of an account.
|
|
|
|
func (store *AccountStore) CalculateBalance(account string,
|
|
|
|
minconf int) (float64, error) {
|
|
|
|
|
|
|
|
a, err := store.Account(account)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a.CalculateBalance(minconf), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateEncryptedWallet creates a new account with a wallet file
|
|
|
|
// encrypted with passphrase.
|
|
|
|
func (store *AccountStore) CreateEncryptedWallet(name, desc string, passphrase []byte) error {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
2013-12-02 20:56:06 +01:00
|
|
|
_, ok := store.accounts[name]
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
if ok {
|
|
|
|
return ErrAcctExists
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get current block's height and hash.
|
|
|
|
bs, err := GetCurBlock()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new wallet in memory.
|
2014-01-15 23:29:01 +01:00
|
|
|
wlt, err := wallet.NewWallet(name, desc, passphrase, cfg.Net(), &bs, cfg.KeypoolSize)
|
2013-12-02 20:56:06 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new account with the wallet. A new JSON ID is set for
|
|
|
|
// transaction notifications.
|
|
|
|
account := &Account{
|
2014-01-03 19:34:37 +01:00
|
|
|
Wallet: wlt,
|
|
|
|
name: name,
|
|
|
|
dirty: true,
|
2013-12-02 20:56:06 +01:00
|
|
|
}
|
2014-01-15 20:25:37 +01:00
|
|
|
account.UtxoStore.dirty = true
|
|
|
|
account.TxStore.dirty = true
|
2013-12-02 20:56:06 +01:00
|
|
|
|
2014-01-23 15:51:31 +01:00
|
|
|
// Mark all active payment addresses as belonging to this account.
|
|
|
|
for addr := range account.ActivePaymentAddresses() {
|
|
|
|
MarkAddressForAccount(addr, name)
|
|
|
|
}
|
|
|
|
|
2013-12-02 20:56:06 +01:00
|
|
|
// Save the account in the global account map. The mutex is
|
|
|
|
// already held at this point, and will be unlocked when this
|
|
|
|
// func returns.
|
2014-01-03 19:34:37 +01:00
|
|
|
store.Lock()
|
2013-12-02 20:56:06 +01:00
|
|
|
store.accounts[name] = account
|
2014-01-03 19:34:37 +01:00
|
|
|
store.Unlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
// Begin tracking account against a connected btcd.
|
|
|
|
//
|
|
|
|
// TODO(jrick): this should *only* happen if btcd is connected.
|
|
|
|
account.Track()
|
|
|
|
|
|
|
|
// Write new wallet to disk.
|
|
|
|
if err := account.writeDirtyToDisk(); err != nil {
|
2014-01-23 15:51:31 +01:00
|
|
|
return err
|
2013-12-02 20:56:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-01-27 15:30:42 +01:00
|
|
|
// ChangePassphrase unlocks all account wallets with the old
|
|
|
|
// passphrase, and re-encrypts each using the new passphrase.
|
|
|
|
func (store *AccountStore) ChangePassphrase(old, new []byte) error {
|
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
|
|
|
|
|
|
|
// Check that each account can be unlocked with the old passphrase.
|
|
|
|
// Each's account's wallet mutex is unlocked with a defer so they
|
|
|
|
// will be held for the duration of this function. This prevents
|
|
|
|
// a wallet from being locked after some timeout after a RPC call
|
|
|
|
// to walletpassphrase.
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
a.mtx.Lock()
|
|
|
|
defer a.mtx.Unlock()
|
|
|
|
|
|
|
|
if locked := a.Wallet.IsLocked(); !locked {
|
|
|
|
if err := a.Wallet.Lock(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := a.Wallet.Unlock(old); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer a.Wallet.Lock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change passphrase for each unlocked wallet.
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
if err := a.Wallet.ChangePassphrase(new); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
a.dirty = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Immediately write out to disk. Create a new temporary network
|
|
|
|
// directory to write to, write all account files there, then move
|
|
|
|
// to the real network directory. This provides an safe
|
|
|
|
// replacement of all account files and ensures that all wallets
|
|
|
|
// are using either the old or new passphrase, but never two wallets
|
|
|
|
// with different passphrases.
|
|
|
|
netDir := networkDir(cfg.Net())
|
|
|
|
tmpNetDir := tmpNetworkDir(cfg.Net())
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
// Writer locks must be held for the tx and utxo stores as well,
|
|
|
|
// to unset the dirty flag.
|
|
|
|
a.UtxoStore.Lock()
|
|
|
|
defer a.UtxoStore.Unlock()
|
|
|
|
a.TxStore.Lock()
|
|
|
|
defer a.TxStore.Unlock()
|
|
|
|
|
|
|
|
if err := a.writeAllToFreshDir(tmpNetDir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is technically NOT an atomic operation, but at startup, if the
|
|
|
|
// network directory is missing but the temporary network directory
|
|
|
|
// exists, the temporary is moved before accounts are opened.
|
|
|
|
if err := os.RemoveAll(netDir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := Rename(tmpNetDir, netDir); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-01-27 21:48:12 +01:00
|
|
|
// LockWallets locks all account's wallets in the store.
|
|
|
|
func (store *AccountStore) LockWallets() error {
|
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
|
|
|
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
if err := a.Lock(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnlockWallets unlocks all account's wallets in the store with the provided
|
|
|
|
// passphrase. If any wallet unlocks fail, all successfully unlocked wallets
|
|
|
|
// are locked again.
|
|
|
|
func (store *AccountStore) UnlockWallets(passphrase string) error {
|
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
|
|
|
|
|
|
|
unlockedAccts := make([]*Account, 0, len(store.accounts))
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
if err := a.Unlock([]byte(passphrase)); err != nil {
|
|
|
|
for _, ua := range unlockedAccts {
|
|
|
|
ua.Lock()
|
|
|
|
}
|
|
|
|
return fmt.Errorf("cannot unlock account %v: %v",
|
|
|
|
a.name, err)
|
|
|
|
}
|
|
|
|
unlockedAccts = append(unlockedAccts, a)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-12-02 20:56:06 +01:00
|
|
|
// DumpKeys returns all WIF-encoded private keys associated with all
|
|
|
|
// accounts. All wallets must be unlocked for this operation to succeed.
|
|
|
|
func (store *AccountStore) DumpKeys() ([]string, error) {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
var keys []string
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
switch walletKeys, err := a.DumpPrivKeys(); err {
|
|
|
|
case wallet.ErrWalletLocked:
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
case nil:
|
|
|
|
keys = append(keys, walletKeys...)
|
|
|
|
|
|
|
|
default: // any other non-nil error
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return keys, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DumpWIFPrivateKey searches through all accounts for the bitcoin
|
|
|
|
// payment address addr and returns the WIF-encdoded private key.
|
2014-01-06 18:24:29 +01:00
|
|
|
func (store *AccountStore) DumpWIFPrivateKey(addr btcutil.Address) (string, error) {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
switch wif, err := a.DumpWIFPrivateKey(addr); err {
|
|
|
|
case wallet.ErrAddressNotFound:
|
|
|
|
// Move on to the next account.
|
|
|
|
continue
|
|
|
|
|
|
|
|
case nil:
|
|
|
|
return wif, nil
|
|
|
|
|
|
|
|
default: // all other non-nil errors
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", errors.New("address does not refer to a key")
|
|
|
|
}
|
|
|
|
|
|
|
|
// NotifyBalances notifies a wallet frontend of all confirmed and unconfirmed
|
|
|
|
// account balances.
|
|
|
|
func (store *AccountStore) NotifyBalances(frontend chan []byte) {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
for _, account := range store.accounts {
|
|
|
|
balance := account.CalculateBalance(1)
|
|
|
|
unconfirmed := account.CalculateBalance(0) - balance
|
|
|
|
NotifyWalletBalance(frontend, account.name, balance)
|
|
|
|
NotifyWalletBalanceUnconfirmed(frontend, account.name, unconfirmed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListAccounts returns a map of account names to their current account
|
|
|
|
// balances. The balances are calculated using minconf confirmations.
|
|
|
|
func (store *AccountStore) ListAccounts(minconf int) map[string]float64 {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
// Create and fill a map of account names and their balances.
|
|
|
|
pairs := make(map[string]float64)
|
|
|
|
for name, a := range store.accounts {
|
|
|
|
pairs[name] = a.CalculateBalance(minconf)
|
|
|
|
}
|
|
|
|
return pairs
|
|
|
|
}
|
|
|
|
|
2014-01-27 18:53:32 +01:00
|
|
|
// ListSinceBlock returns a slice of maps of strings to interface containing
|
|
|
|
// structures defining all transactions in the wallets since the given block.
|
|
|
|
// To be used for the listsinceblock command.
|
|
|
|
func (store *AccountStore) ListSinceBlock(since, curBlockHeight int32, minconf int) ([]map[string]interface{}, error) {
|
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
|
|
|
|
|
|
|
// Create and fill a map of account names and their balances.
|
|
|
|
txInfoList := []map[string]interface{}{}
|
|
|
|
for _, a := range store.accounts {
|
|
|
|
txTmp, err := a.ListSinceBlock(since, curBlockHeight, minconf)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
txInfoList = append(txInfoList, txTmp...)
|
|
|
|
}
|
|
|
|
return txInfoList, nil
|
|
|
|
}
|
|
|
|
|
2013-12-02 20:56:06 +01:00
|
|
|
// RescanActiveAddresses begins a rescan for all active addresses for
|
|
|
|
// each account.
|
|
|
|
//
|
|
|
|
// TODO(jrick): batch addresses for all accounts together so multiple
|
|
|
|
// rescan commands can be avoided.
|
|
|
|
func (store *AccountStore) RescanActiveAddresses() {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
for _, account := range store.accounts {
|
|
|
|
account.RescanActiveAddresses()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Track begins tracking all addresses in all accounts for updates from
|
|
|
|
// btcd.
|
|
|
|
func (store *AccountStore) Track() {
|
2014-01-03 19:34:37 +01:00
|
|
|
store.RLock()
|
|
|
|
defer store.RUnlock()
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
for _, account := range store.accounts {
|
|
|
|
account.Track()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-23 23:12:55 +01:00
|
|
|
// WalletOpenError is a special error type so problems opening wallet
|
|
|
|
// files can be differentiated (by a type assertion) from other errors.
|
|
|
|
type WalletOpenError struct {
|
|
|
|
Err string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error satisifies the builtin error interface.
|
|
|
|
func (e *WalletOpenError) Error() string {
|
|
|
|
return e.Err
|
|
|
|
}
|
|
|
|
|
2013-12-02 20:56:06 +01:00
|
|
|
// OpenAccount opens an account described by account in the data
|
|
|
|
// directory specified by cfg. If the wallet does not exist, ErrNoWallet
|
|
|
|
// is returned as an error.
|
|
|
|
//
|
|
|
|
// Wallets opened from this function are not set to track against a
|
|
|
|
// btcd connection.
|
|
|
|
func (store *AccountStore) OpenAccount(name string, cfg *config) error {
|
|
|
|
wlt := new(wallet.Wallet)
|
|
|
|
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
a := &Account{
|
2013-12-02 20:56:06 +01:00
|
|
|
Wallet: wlt,
|
|
|
|
name: name,
|
|
|
|
}
|
|
|
|
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
netdir := networkDir(cfg.Net())
|
|
|
|
if err := checkCreateDir(netdir); err != nil {
|
2013-12-02 20:56:06 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
wfilepath := accountFilename("wallet.bin", name, netdir)
|
|
|
|
utxofilepath := accountFilename("utxo.bin", name, netdir)
|
|
|
|
txfilepath := accountFilename("tx.bin", name, netdir)
|
2013-12-02 20:56:06 +01:00
|
|
|
var wfile, utxofile, txfile *os.File
|
|
|
|
|
|
|
|
// Read wallet file.
|
|
|
|
wfile, err := os.Open(wfilepath)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
// Must create and save wallet first.
|
|
|
|
return ErrNoWallet
|
|
|
|
}
|
2014-01-23 23:12:55 +01:00
|
|
|
msg := fmt.Sprintf("cannot open wallet file: %s", err)
|
|
|
|
return &WalletOpenError{msg}
|
2013-12-02 20:56:06 +01:00
|
|
|
}
|
|
|
|
defer wfile.Close()
|
|
|
|
|
|
|
|
if _, err = wlt.ReadFrom(wfile); err != nil {
|
2014-01-23 23:12:55 +01:00
|
|
|
msg := fmt.Sprintf("cannot read wallet: %s", err)
|
|
|
|
return &WalletOpenError{msg}
|
2013-12-02 20:56:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read tx file. If this fails, return a ErrNoTxs error and let
|
|
|
|
// the caller decide if a rescan is necessary.
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
var finalErr error
|
2013-12-02 20:56:06 +01:00
|
|
|
if txfile, err = os.Open(txfilepath); err != nil {
|
|
|
|
log.Errorf("cannot open tx file: %s", err)
|
|
|
|
// This is not a error we should immediately return with,
|
|
|
|
// but other errors can be more important, so only return
|
|
|
|
// this if none of the others are hit.
|
|
|
|
finalErr = ErrNoTxs
|
|
|
|
} else {
|
|
|
|
defer txfile.Close()
|
|
|
|
var txs tx.TxStore
|
|
|
|
if _, err = txs.ReadFrom(txfile); err != nil {
|
|
|
|
log.Errorf("cannot read tx file: %s", err)
|
|
|
|
finalErr = ErrNoTxs
|
|
|
|
} else {
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
a.TxStore.s = txs
|
2013-12-02 20:56:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read utxo file. If this fails, return a ErrNoUtxos error so a
|
|
|
|
// rescan can be done since the wallet creation block.
|
|
|
|
var utxos tx.UtxoStore
|
|
|
|
utxofile, err = os.Open(utxofilepath)
|
|
|
|
if err != nil {
|
|
|
|
log.Errorf("cannot open utxo file: %s", err)
|
|
|
|
finalErr = ErrNoUtxos
|
|
|
|
} else {
|
|
|
|
defer utxofile.Close()
|
|
|
|
if _, err = utxos.ReadFrom(utxofile); err != nil {
|
|
|
|
log.Errorf("cannot read utxo file: %s", err)
|
|
|
|
finalErr = ErrNoUtxos
|
|
|
|
} else {
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
a.UtxoStore.s = utxos
|
2013-12-02 20:56:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-03 19:34:37 +01:00
|
|
|
store.Lock()
|
2013-12-02 20:56:06 +01:00
|
|
|
switch finalErr {
|
|
|
|
case ErrNoTxs:
|
|
|
|
// Do nothing special for now. This will be implemented when
|
|
|
|
// the tx history file is properly written.
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
store.accounts[name] = a
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
case ErrNoUtxos:
|
|
|
|
// Add wallet, but mark wallet as needing a full rescan since
|
|
|
|
// the wallet creation block. This will take place when btcd
|
|
|
|
// connects.
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
a.fullRescan = true
|
|
|
|
store.accounts[name] = a
|
2013-12-02 20:56:06 +01:00
|
|
|
case nil:
|
Introduce new account file structure.
This changes the locations that account files (wallet.bin, utxo.bin,
and tx.bin) are searched for when opening or disk syncing accounts.
Previously, files were saved in the following layout:
~/.btcwallet/
- btcwallet/
- wallet.bin
- tx.bin
- utxo.bin
- btcwallet-AccountA/
- wallet.bin
- tx.bin
- utxo.bin
This format had two issues. First, each account would require its own
directory, causing a scalability issue on unix (and perhaps other)
platforms. Second, there was no distinction between testnet and
mainnet wallets, and if mainnet support was enabled, btcwallet would
attempt to open accounts with testnet wallets.
Instead, the following file structure is now used:
~/.btcwallet/
- testnet/
- wallet.bin
- tx.bin
- utxo.bin
- AccountA-wallet.bin
- AccountA-tx.bin
- AccountA-utxo.bin
This solves both of the previously-mentioned issues by requiring only
two subdirectories (one each for the testnet and mainnet bitcoin
networks), and by separating the locations to open and save testnet
and mainnet account files.
At startup, a check for the old account file structure is performed.
If found, files are moved to the new locations, and the old account
directories are removed. Account files are moved to the testnet
directory, as only testnet support is currently enabled.
The version has been bumped to 0.1.1 to reflect this change.
Fixes #16.
2013-12-05 02:16:50 +01:00
|
|
|
store.accounts[name] = a
|
2013-12-02 20:56:06 +01:00
|
|
|
|
|
|
|
default:
|
|
|
|
log.Warnf("cannot open wallet: %v", err)
|
|
|
|
}
|
2014-01-03 19:34:37 +01:00
|
|
|
store.Unlock()
|
2013-12-10 22:15:25 +01:00
|
|
|
|
|
|
|
// Mark all active payment addresses as belonging to this account.
|
|
|
|
for addr := range a.ActivePaymentAddresses() {
|
|
|
|
MarkAddressForAccount(addr, name)
|
|
|
|
}
|
|
|
|
|
2013-12-02 20:56:06 +01:00
|
|
|
return nil
|
|
|
|
}
|