more wallet -> account renames

This commit is contained in:
Josh Rickmar 2013-11-15 11:44:24 -05:00
parent 9c827a824f
commit ef49eca365
5 changed files with 190 additions and 190 deletions

View file

@ -28,6 +28,8 @@ import (
"sync" "sync"
) )
var accounts = NewAccountStore()
// Account is a structure containing all the components for a // Account is a structure containing all the components for a
// complete wallet. It contains the Armory-style wallet (to store // complete wallet. It contains the Armory-style wallet (to store
// addresses and keys), and tx and utxo data stores, along with locks // addresses and keys), and tx and utxo data stores, along with locks
@ -82,16 +84,16 @@ func (s *AccountStore) Rollback(height int32, hash *btcwire.ShaHash) {
// with the passed chainheight and block hash was connected to the main // with the passed chainheight and block hash was connected to the main
// chain. This is used to remove transactions and utxos for each wallet // chain. This is used to remove transactions and utxos for each wallet
// that occured on a chain no longer considered to be the main chain. // that occured on a chain no longer considered to be the main chain.
func (w *Account) Rollback(height int32, hash *btcwire.ShaHash) { func (a *Account) Rollback(height int32, hash *btcwire.ShaHash) {
w.UtxoStore.Lock() a.UtxoStore.Lock()
w.UtxoStore.dirty = w.UtxoStore.dirty || w.UtxoStore.s.Rollback(height, hash) a.UtxoStore.dirty = a.UtxoStore.dirty || a.UtxoStore.s.Rollback(height, hash)
w.UtxoStore.Unlock() a.UtxoStore.Unlock()
w.TxStore.Lock() a.TxStore.Lock()
w.TxStore.dirty = w.TxStore.dirty || w.TxStore.s.Rollback(height, hash) a.TxStore.dirty = a.TxStore.dirty || a.TxStore.s.Rollback(height, hash)
w.TxStore.Unlock() a.TxStore.Unlock()
if err := w.writeDirtyToDisk(); err != nil { if err := a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err) log.Errorf("cannot sync dirty wallet: %v", err)
} }
} }
@ -105,7 +107,7 @@ func (w *Account) Rollback(height int32, hash *btcwire.ShaHash) {
// a UTXO must be in a block. If confirmations is 1 or greater, // a UTXO must be in a block. If confirmations is 1 or greater,
// the balance will be calculated based on how many how many blocks // the balance will be calculated based on how many how many blocks
// include a UTXO. // include a UTXO.
func (w *Account) CalculateBalance(confirms int) float64 { func (a *Account) CalculateBalance(confirms int) float64 {
var bal uint64 // Measured in satoshi var bal uint64 // Measured in satoshi
bs, err := GetCurBlock() bs, err := GetCurBlock()
@ -113,47 +115,47 @@ func (w *Account) CalculateBalance(confirms int) float64 {
return 0. return 0.
} }
w.UtxoStore.RLock() a.UtxoStore.RLock()
for _, u := range w.UtxoStore.s { for _, u := range a.UtxoStore.s {
// Utxos not yet in blocks (height -1) should only be // Utxos not yet in blocks (height -1) should only be
// added if confirmations is 0. // added if confirmations is 0.
if confirms == 0 || (u.Height != -1 && int(bs.Height-u.Height+1) >= confirms) { if confirms == 0 || (u.Height != -1 && int(bs.Height-u.Height+1) >= confirms) {
bal += u.Amt bal += u.Amt
} }
} }
w.UtxoStore.RUnlock() a.UtxoStore.RUnlock()
return float64(bal) / float64(btcutil.SatoshiPerBitcoin) return float64(bal) / float64(btcutil.SatoshiPerBitcoin)
} }
// Track requests btcd to send notifications of new transactions for // Track requests btcd to send notifications of new transactions for
// each address stored in a wallet and sets up a new reply handler for // each address stored in a wallet and sets up a new reply handler for
// these notifications. // these notifications.
func (w *Account) Track() { func (a *Account) Track() {
n := <-NewJSONID n := <-NewJSONID
w.mtx.Lock() a.mtx.Lock()
w.NewBlockTxSeqN = n a.NewBlockTxSeqN = n
w.mtx.Unlock() a.mtx.Unlock()
replyHandlers.Lock() replyHandlers.Lock()
replyHandlers.m[n] = w.newBlockTxOutHandler replyHandlers.m[n] = a.newBlockTxOutHandler
replyHandlers.Unlock() replyHandlers.Unlock()
for _, addr := range w.GetActiveAddresses() { for _, addr := range a.GetActiveAddresses() {
w.ReqNewTxsForAddress(addr.Address) a.ReqNewTxsForAddress(addr.Address)
} }
n = <-NewJSONID n = <-NewJSONID
w.mtx.Lock() a.mtx.Lock()
w.SpentOutpointSeqN = n a.SpentOutpointSeqN = n
w.mtx.Unlock() a.mtx.Unlock()
replyHandlers.Lock() replyHandlers.Lock()
replyHandlers.m[n] = w.spentUtxoHandler replyHandlers.m[n] = a.spentUtxoHandler
replyHandlers.Unlock() replyHandlers.Unlock()
w.UtxoStore.RLock() a.UtxoStore.RLock()
for _, utxo := range w.UtxoStore.s { for _, utxo := range a.UtxoStore.s {
w.ReqSpentUtxoNtfn(utxo) a.ReqSpentUtxoNtfn(utxo)
} }
w.UtxoStore.RUnlock() a.UtxoStore.RUnlock()
} }
// RescanToBestBlock requests btcd to rescan the blockchain for new // RescanToBestBlock requests btcd to rescan the blockchain for new
@ -161,22 +163,22 @@ func (w *Account) Track() {
// btcwallet catch up to a long-running btcd process, as otherwise // btcwallet catch up to a long-running btcd process, as otherwise
// it would have missed notifications as blocks are attached to the // it would have missed notifications as blocks are attached to the
// main chain. // main chain.
func (w *Account) RescanToBestBlock() { func (a *Account) RescanToBestBlock() {
beginBlock := int32(0) beginBlock := int32(0)
if w.fullRescan { if a.fullRescan {
// Need to perform a complete rescan since the wallet creation // Need to perform a complete rescan since the wallet creation
// block. // block.
beginBlock = w.CreatedAt() beginBlock = a.CreatedAt()
log.Debugf("Rescanning account '%v' for new transactions since block height %v", log.Debugf("Rescanning account '%v' for new transactions since block height %v",
w.name, beginBlock) a.name, beginBlock)
} else { } else {
// The last synced block height should be used the starting // The last synced block height should be used the starting
// point for block rescanning. Grab the block stamp here. // point for block rescanning. Grab the block stamp here.
bs := w.SyncedWith() bs := a.SyncedWith()
log.Debugf("Rescanning account '%v' for new transactions since block height %v hash %v", log.Debugf("Rescanning account '%v' for new transactions since block height %v hash %v",
w.name, bs.Height, bs.Hash) a.name, bs.Height, bs.Hash)
// If we're synced with block x, must scan the blocks x+1 to best block. // If we're synced with block x, must scan the blocks x+1 to best block.
beginBlock = bs.Height + 1 beginBlock = bs.Height + 1
@ -184,7 +186,7 @@ func (w *Account) RescanToBestBlock() {
n := <-NewJSONID n := <-NewJSONID
cmd, err := btcws.NewRescanCmd(fmt.Sprintf("btcwallet(%v)", n), cmd, err := btcws.NewRescanCmd(fmt.Sprintf("btcwallet(%v)", n),
beginBlock, w.ActivePaymentAddresses()) beginBlock, a.ActivePaymentAddresses())
if err != nil { if err != nil {
log.Errorf("cannot create rescan request: %v", err) log.Errorf("cannot create rescan request: %v", err)
return return
@ -199,21 +201,21 @@ func (w *Account) RescanToBestBlock() {
replyHandlers.m[n] = func(result interface{}, e *btcjson.Error) bool { replyHandlers.m[n] = func(result interface{}, e *btcjson.Error) bool {
// Rescan is compatible with new txs from connected block // Rescan is compatible with new txs from connected block
// notifications, so use that handler. // notifications, so use that handler.
_ = w.newBlockTxOutHandler(result, e) _ = a.newBlockTxOutHandler(result, e)
if result != nil { if result != nil {
// Notify frontends of new account balance. // Notify frontends of new account balance.
confirmed := w.CalculateBalance(1) confirmed := a.CalculateBalance(1)
unconfirmed := w.CalculateBalance(0) - confirmed unconfirmed := a.CalculateBalance(0) - confirmed
NotifyWalletBalance(frontendNotificationMaster, w.name, confirmed) NotifyWalletBalance(frontendNotificationMaster, a.name, confirmed)
NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, w.name, unconfirmed) NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, a.name, unconfirmed)
return false return false
} }
if bs, err := GetCurBlock(); err == nil { if bs, err := GetCurBlock(); err == nil {
w.SetSyncedWith(&bs) a.SetSyncedWith(&bs)
w.dirty = true a.dirty = true
if err = w.writeDirtyToDisk(); err != nil { if err = a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", log.Errorf("cannot sync dirty wallet: %v",
err) err)
} }
@ -229,11 +231,11 @@ func (w *Account) RescanToBestBlock() {
// SortedActivePaymentAddresses returns a slice of all active payment // SortedActivePaymentAddresses returns a slice of all active payment
// addresses in an account. // addresses in an account.
func (w *Account) SortedActivePaymentAddresses() []string { func (a *Account) SortedActivePaymentAddresses() []string {
w.mtx.RLock() a.mtx.RLock()
defer w.mtx.RUnlock() defer a.mtx.RUnlock()
infos := w.GetSortedActiveAddresses() infos := a.GetSortedActiveAddresses()
addrs := make([]string, len(infos)) addrs := make([]string, len(infos))
for i, addr := range infos { for i, addr := range infos {
@ -245,11 +247,11 @@ func (w *Account) SortedActivePaymentAddresses() []string {
// ActivePaymentAddresses returns a set of all active pubkey hashes // ActivePaymentAddresses returns a set of all active pubkey hashes
// in an account. // in an account.
func (w *Account) ActivePaymentAddresses() map[string]struct{} { func (a *Account) ActivePaymentAddresses() map[string]struct{} {
w.mtx.RLock() a.mtx.RLock()
defer w.mtx.RUnlock() defer a.mtx.RUnlock()
infos := w.GetActiveAddresses() infos := a.GetActiveAddresses()
addrs := make(map[string]struct{}, len(infos)) addrs := make(map[string]struct{}, len(infos))
for _, info := range infos { for _, info := range infos {
@ -261,12 +263,12 @@ func (w *Account) ActivePaymentAddresses() map[string]struct{} {
// ReqNewTxsForAddress sends a message to btcd to request tx updates // ReqNewTxsForAddress sends a message to btcd to request tx updates
// for addr for each new block that is added to the blockchain. // for addr for each new block that is added to the blockchain.
func (w *Account) ReqNewTxsForAddress(addr string) { func (a *Account) ReqNewTxsForAddress(addr string) {
log.Debugf("Requesting notifications of TXs sending to address %v", addr) log.Debugf("Requesting notifications of TXs sending to address %v", addr)
w.mtx.RLock() a.mtx.RLock()
n := w.NewBlockTxSeqN n := a.NewBlockTxSeqN
w.mtx.RUnlock() a.mtx.RUnlock()
cmd := btcws.NewNotifyNewTXsCmd(fmt.Sprintf("btcwallet(%d)", n), cmd := btcws.NewNotifyNewTXsCmd(fmt.Sprintf("btcwallet(%d)", n),
[]string{addr}) []string{addr})
@ -280,13 +282,13 @@ func (w *Account) ReqNewTxsForAddress(addr string) {
// ReqSpentUtxoNtfn sends a message to btcd to request updates for when // ReqSpentUtxoNtfn sends a message to btcd to request updates for when
// a stored UTXO has been spent. // a stored UTXO has been spent.
func (w *Account) ReqSpentUtxoNtfn(u *tx.Utxo) { func (a *Account) ReqSpentUtxoNtfn(u *tx.Utxo) {
log.Debugf("Requesting spent UTXO notifications for Outpoint hash %s index %d", log.Debugf("Requesting spent UTXO notifications for Outpoint hash %s index %d",
u.Out.Hash, u.Out.Index) u.Out.Hash, u.Out.Index)
w.mtx.RLock() a.mtx.RLock()
n := w.SpentOutpointSeqN n := a.SpentOutpointSeqN
w.mtx.RUnlock() a.mtx.RUnlock()
cmd := btcws.NewNotifySpentCmd(fmt.Sprintf("btcwallet(%d)", n), cmd := btcws.NewNotifySpentCmd(fmt.Sprintf("btcwallet(%d)", n),
(*btcwire.OutPoint)(&u.Out)) (*btcwire.OutPoint)(&u.Out))
@ -301,7 +303,7 @@ func (w *Account) ReqSpentUtxoNtfn(u *tx.Utxo) {
// spentUtxoHandler is the handler function for btcd spent UTXO notifications // spentUtxoHandler is the handler function for btcd spent UTXO notifications
// resulting from transactions in newly-attached blocks. // resulting from transactions in newly-attached blocks.
func (w *Account) spentUtxoHandler(result interface{}, e *btcjson.Error) bool { func (a *Account) spentUtxoHandler(result interface{}, e *btcjson.Error) bool {
if e != nil { if e != nil {
log.Errorf("Spent UTXO Handler: Error %d received from btcd: %s", log.Errorf("Spent UTXO Handler: Error %d received from btcd: %s",
e.Code, e.Message) e.Code, e.Message)
@ -334,7 +336,7 @@ func (w *Account) spentUtxoHandler(result interface{}, e *btcjson.Error) bool {
// newBlockTxOutHandler is the handler function for btcd transaction // newBlockTxOutHandler is the handler function for btcd transaction
// notifications resulting from newly-attached blocks. // notifications resulting from newly-attached blocks.
func (w *Account) newBlockTxOutHandler(result interface{}, e *btcjson.Error) bool { func (a *Account) newBlockTxOutHandler(result interface{}, e *btcjson.Error) bool {
if e != nil { if e != nil {
log.Errorf("Tx Handler: Error %d received from btcd: %s", log.Errorf("Tx Handler: Error %d received from btcd: %s",
e.Code, e.Message) e.Code, e.Message)
@ -425,37 +427,37 @@ func (w *Account) newBlockTxOutHandler(result interface{}, e *btcjson.Error) boo
copy(t.SenderAddr[:], senderHash) copy(t.SenderAddr[:], senderHash)
copy(t.ReceiverAddr[:], receiverHash) copy(t.ReceiverAddr[:], receiverHash)
w.TxStore.Lock() a.TxStore.Lock()
txs := w.TxStore.s txs := a.TxStore.s
w.TxStore.s = append(txs, t) a.TxStore.s = append(txs, t)
w.TxStore.dirty = true a.TxStore.dirty = true
w.TxStore.Unlock() a.TxStore.Unlock()
// Add to UtxoStore if unspent. // Add to UtxoStore if unspent.
if !spent { if !spent {
// First, iterate through all stored utxos. If an unconfirmed utxo // First, iterate through all stored utxos. If an unconfirmed utxo
// (not present in a block) has the same outpoint as this utxo, // (not present in a block) has the same outpoint as this utxo,
// update the block height and hash. // update the block height and hash.
w.UtxoStore.RLock() a.UtxoStore.RLock()
for _, u := range w.UtxoStore.s { for _, u := range a.UtxoStore.s {
if bytes.Equal(u.Out.Hash[:], txhash[:]) && u.Out.Index == uint32(index) { if bytes.Equal(u.Out.Hash[:], txhash[:]) && u.Out.Index == uint32(index) {
// Found a either a duplicate, or a change UTXO. If not change, // Found a either a duplicate, or a change UTXO. If not change,
// ignore it. // ignore it.
w.UtxoStore.RUnlock() a.UtxoStore.RUnlock()
if u.Height != -1 { if u.Height != -1 {
return false return false
} }
w.UtxoStore.Lock() a.UtxoStore.Lock()
copy(u.BlockHash[:], blockhash[:]) copy(u.BlockHash[:], blockhash[:])
u.Height = int32(height) u.Height = int32(height)
w.UtxoStore.dirty = true a.UtxoStore.dirty = true
w.UtxoStore.Unlock() a.UtxoStore.Unlock()
return false return false
} }
} }
w.UtxoStore.RUnlock() a.UtxoStore.RUnlock()
// After iterating through all UTXOs, it was not a duplicate or // After iterating through all UTXOs, it was not a duplicate or
// change UTXO appearing in a block. Append a new Utxo to the end. // change UTXO appearing in a block. Append a new Utxo to the end.
@ -469,10 +471,10 @@ func (w *Account) newBlockTxOutHandler(result interface{}, e *btcjson.Error) boo
u.Out.Index = uint32(index) u.Out.Index = uint32(index)
copy(u.AddrHash[:], receiverHash) copy(u.AddrHash[:], receiverHash)
copy(u.BlockHash[:], blockhash[:]) copy(u.BlockHash[:], blockhash[:])
w.UtxoStore.Lock() a.UtxoStore.Lock()
w.UtxoStore.s = append(w.UtxoStore.s, u) a.UtxoStore.s = append(a.UtxoStore.s, u)
w.UtxoStore.dirty = true a.UtxoStore.dirty = true
w.UtxoStore.Unlock() a.UtxoStore.Unlock()
// If this notification came from mempool (TODO: currently // If this notification came from mempool (TODO: currently
// unimplemented) notify the new unconfirmed balance immediately. // unimplemented) notify the new unconfirmed balance immediately.

50
cmd.go
View file

@ -56,8 +56,6 @@ var (
Height: int32(btcutil.BlockHeightUnknown), Height: int32(btcutil.BlockHeightUnknown),
}, },
} }
wallets = NewAccountStore()
) )
// walletdir returns the directory path which holds the wallet, utxo, // walletdir returns the directory path which holds the wallet, utxo,
@ -73,13 +71,13 @@ func walletdir(cfg *config, account string) string {
return filepath.Join(cfg.DataDir, wname) return filepath.Join(cfg.DataDir, wname)
} }
// OpenWallet opens a wallet described by account in the data // OpenAccount opens an account described by account in the data
// directory specified by cfg. If the wallet does not exist, ErrNoWallet // directory specified by cfg. If the wallet does not exist, ErrNoWallet
// is returned as an error. // is returned as an error.
// //
// Wallets opened from this function are not set to track against a // Wallets opened from this function are not set to track against a
// btcd connection. // btcd connection.
func OpenWallet(cfg *config, account string) (*Account, error) { func OpenAccount(cfg *config, account string) (*Account, error) {
wdir := walletdir(cfg, account) wdir := walletdir(cfg, account)
fi, err := os.Stat(wdir) fi, err := os.Stat(wdir)
if err != nil { if err != nil {
@ -117,7 +115,7 @@ func OpenWallet(cfg *config, account string) (*Account, error) {
return nil, fmt.Errorf("cannot read wallet: %s", err) return nil, fmt.Errorf("cannot read wallet: %s", err)
} }
w := &Account{ a := &Account{
Wallet: wlt, Wallet: wlt,
name: account, name: account,
} }
@ -127,30 +125,30 @@ func OpenWallet(cfg *config, account string) (*Account, error) {
var utxos tx.UtxoStore var utxos tx.UtxoStore
if utxofile, err = os.Open(utxofilepath); err != nil { if utxofile, err = os.Open(utxofilepath); err != nil {
log.Errorf("cannot open utxo file: %s", err) log.Errorf("cannot open utxo file: %s", err)
return w, ErrNoUtxos return a, ErrNoUtxos
} }
defer utxofile.Close() defer utxofile.Close()
if _, err = utxos.ReadFrom(utxofile); err != nil { if _, err = utxos.ReadFrom(utxofile); err != nil {
log.Errorf("cannot read utxo file: %s", err) log.Errorf("cannot read utxo file: %s", err)
return w, ErrNoUtxos return a, ErrNoUtxos
} }
w.UtxoStore.s = utxos a.UtxoStore.s = utxos
// Read tx file. If this fails, return a ErrNoTxs error and let // Read tx file. If this fails, return a ErrNoTxs error and let
// the caller decide if a rescan is necessary. // the caller decide if a rescan is necessary.
if txfile, err = os.Open(txfilepath); err != nil { if txfile, err = os.Open(txfilepath); err != nil {
log.Errorf("cannot open tx file: %s", err) log.Errorf("cannot open tx file: %s", err)
return w, ErrNoTxs return a, ErrNoTxs
} }
defer txfile.Close() defer txfile.Close()
var txs tx.TxStore var txs tx.TxStore
if _, err = txs.ReadFrom(txfile); err != nil { if _, err = txs.ReadFrom(txfile); err != nil {
log.Errorf("cannot read tx file: %s", err) log.Errorf("cannot read tx file: %s", err)
return w, ErrNoTxs return a, ErrNoTxs
} }
w.TxStore.s = txs a.TxStore.s = txs
return w, nil return a, nil
} }
// GetCurBlock returns the blockchain height and SHA hash of the most // GetCurBlock returns the blockchain height and SHA hash of the most
@ -286,36 +284,36 @@ func main() {
loggers = setLogLevel(cfg.DebugLevel) loggers = setLogLevel(cfg.DebugLevel)
} }
// Open default wallet // Open default account
w, err := OpenWallet(cfg, "") a, err := OpenAccount(cfg, "")
switch err { switch err {
case ErrNoTxs: case ErrNoTxs:
// Do nothing special for now. This will be implemented when // Do nothing special for now. This will be implemented when
// the tx history file is properly written. // the tx history file is properly written.
wallets.Lock() accounts.Lock()
wallets.m[""] = w accounts.m[""] = a
wallets.Unlock() accounts.Unlock()
case ErrNoUtxos: case ErrNoUtxos:
// Add wallet, but mark wallet as needing a full rescan since // Add wallet, but mark wallet as needing a full rescan since
// the wallet creation block. This will take place when btcd // the wallet creation block. This will take place when btcd
// connects. // connects.
wallets.Lock() accounts.Lock()
wallets.m[""] = w accounts.m[""] = a
wallets.Unlock() accounts.Unlock()
w.fullRescan = true a.fullRescan = true
case nil: case nil:
wallets.Lock() accounts.Lock()
wallets.m[""] = w accounts.m[""] = a
wallets.Unlock() accounts.Unlock()
default: default:
log.Errorf("cannot open wallet: %v", err) log.Errorf("cannot open wallet: %v", err)
} }
// Start wallet disk syncer goroutine. // Start account disk syncer goroutine.
go DirtyWalletSyncer() go DirtyAccountSyncer()
go func() { go func() {
// Start HTTP server to listen and send messages to frontend and btcd // Start HTTP server to listen and send messages to frontend and btcd

114
cmdmgr.go
View file

@ -168,7 +168,7 @@ func GetAddressesByAccount(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Check that the account specified in the request exists. // Check that the account specified in the request exists.
w, ok := wallets.m[cmd.Account] a, ok := accounts.m[cmd.Account]
if !ok { if !ok {
ReplyError(frontend, cmd.Id(), ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletInvalidAccountName) &btcjson.ErrWalletInvalidAccountName)
@ -176,7 +176,7 @@ func GetAddressesByAccount(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Reply with sorted active payment addresses. // Reply with sorted active payment addresses.
ReplySuccess(frontend, cmd.Id(), w.SortedActivePaymentAddresses()) ReplySuccess(frontend, cmd.Id(), a.SortedActivePaymentAddresses())
} }
// GetBalance replies to a getbalance request with the balance for an // GetBalance replies to a getbalance request with the balance for an
@ -191,7 +191,7 @@ func GetBalance(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Check that the account specified in the request exists. // Check that the account specified in the request exists.
w, ok := wallets.m[cmd.Account] a, ok := accounts.m[cmd.Account]
if !ok { if !ok {
ReplyError(frontend, cmd.Id(), ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletInvalidAccountName) &btcjson.ErrWalletInvalidAccountName)
@ -199,7 +199,7 @@ func GetBalance(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Reply with calculated balance. // Reply with calculated balance.
ReplySuccess(frontend, cmd.Id(), w.CalculateBalance(cmd.MinConf)) ReplySuccess(frontend, cmd.Id(), a.CalculateBalance(cmd.MinConf))
} }
// GetBalances replies to a getbalances extension request by notifying // GetBalances replies to a getbalances extension request by notifying
@ -208,17 +208,17 @@ func GetBalances(frontend chan []byte, cmd btcjson.Cmd) {
NotifyBalances(frontend) NotifyBalances(frontend)
} }
// NotifyBalances notifies an attached wallet of the current confirmed // NotifyBalances notifies an attached frontend of the current confirmed
// and unconfirmed account balances. // and unconfirmed account balances.
// //
// TODO(jrick): Switch this to return a JSON object (map) of all accounts // TODO(jrick): Switch this to return a JSON object (map) of all accounts
// and their balances, instead of separate notifications for each account. // and their balances, instead of separate notifications for each account.
func NotifyBalances(reply chan []byte) { func NotifyBalances(frontend chan []byte) {
for _, w := range wallets.m { for _, a := range accounts.m {
balance := w.CalculateBalance(1) balance := a.CalculateBalance(1)
unconfirmed := w.CalculateBalance(0) - balance unconfirmed := a.CalculateBalance(0) - balance
NotifyWalletBalance(reply, w.name, balance) NotifyWalletBalance(frontend, a.name, balance)
NotifyWalletBalanceUnconfirmed(reply, w.name, unconfirmed) NotifyWalletBalanceUnconfirmed(frontend, a.name, unconfirmed)
} }
} }
@ -234,7 +234,7 @@ func GetNewAddress(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Check that the account specified in the request exists. // Check that the account specified in the request exists.
w, ok := wallets.m[cmd.Account] a, ok := accounts.m[cmd.Account]
if !ok { if !ok {
ReplyError(frontend, cmd.Id(), ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletInvalidAccountName) &btcjson.ErrWalletInvalidAccountName)
@ -242,7 +242,7 @@ func GetNewAddress(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Get next address from wallet. // Get next address from wallet.
addr, err := w.NextUnusedAddress() addr, err := a.NextUnusedAddress()
if err != nil { if err != nil {
// TODO(jrick): generate new addresses if the address pool is // TODO(jrick): generate new addresses if the address pool is
// empty. // empty.
@ -253,13 +253,13 @@ func GetNewAddress(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Write updated wallet to disk. // Write updated wallet to disk.
w.dirty = true a.dirty = true
if err = w.writeDirtyToDisk(); err != nil { if err = a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err) log.Errorf("cannot sync dirty wallet: %v", err)
} }
// Request updates from btcd for new transactions sent to this address. // Request updates from btcd for new transactions sent to this address.
w.ReqNewTxsForAddress(addr) a.ReqNewTxsForAddress(addr)
// Reply with the new payment address string. // Reply with the new payment address string.
ReplySuccess(frontend, cmd.Id(), addr) ReplySuccess(frontend, cmd.Id(), addr)
@ -277,8 +277,8 @@ func ListAccounts(frontend chan []byte, icmd btcjson.Cmd) {
// Create and fill a map of account names and their balances. // Create and fill a map of account names and their balances.
pairs := make(map[string]float64) pairs := make(map[string]float64)
for account, w := range wallets.m { for aname, a := range accounts.m {
pairs[account] = w.CalculateBalance(cmd.MinConf) pairs[aname] = a.CalculateBalance(cmd.MinConf)
} }
// Reply with the map. This will be marshaled into a JSON object. // Reply with the map. This will be marshaled into a JSON object.
@ -317,7 +317,7 @@ func SendFrom(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Check that the account specified in the request exists. // Check that the account specified in the request exists.
w, ok := wallets.m[cmd.FromAccount] a, ok := accounts.m[cmd.FromAccount]
if !ok { if !ok {
ReplyError(frontend, cmd.Id(), ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletInvalidAccountName) &btcjson.ErrWalletInvalidAccountName)
@ -337,7 +337,7 @@ func SendFrom(frontend chan []byte, icmd btcjson.Cmd) {
// Create transaction, replying with an error if the creation // Create transaction, replying with an error if the creation
// was not successful. // was not successful.
createdTx, err := w.txToPairs(pairs, fee, cmd.MinConf) createdTx, err := a.txToPairs(pairs, fee, cmd.MinConf)
switch { switch {
case err == ErrNonPositiveAmount: case err == ErrNonPositiveAmount:
e := &btcjson.Error{ e := &btcjson.Error{
@ -363,11 +363,11 @@ func SendFrom(frontend chan []byte, icmd btcjson.Cmd) {
// If a change address was added, mark wallet as dirty, sync to disk, // If a change address was added, mark wallet as dirty, sync to disk,
// and Request updates for change address. // and Request updates for change address.
if len(createdTx.changeAddr) != 0 { if len(createdTx.changeAddr) != 0 {
w.dirty = true a.dirty = true
if err := w.writeDirtyToDisk(); err != nil { if err := a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot write dirty wallet: %v", err) log.Errorf("cannot write dirty wallet: %v", err)
} }
w.ReqNewTxsForAddress(createdTx.changeAddr) a.ReqNewTxsForAddress(createdTx.changeAddr)
} }
// Create sendrawtransaction request with hexstring of the raw tx. // Create sendrawtransaction request with hexstring of the raw tx.
@ -387,7 +387,7 @@ func SendFrom(frontend chan []byte, icmd btcjson.Cmd) {
// Set up a reply handler to respond to the btcd reply. // Set up a reply handler to respond to the btcd reply.
replyHandlers.Lock() replyHandlers.Lock()
replyHandlers.m[n] = func(result interface{}, err *btcjson.Error) bool { replyHandlers.m[n] = func(result interface{}, err *btcjson.Error) bool {
return handleSendRawTxReply(frontend, cmd, result, err, w, return handleSendRawTxReply(frontend, cmd, result, err, a,
createdTx) createdTx)
} }
replyHandlers.Unlock() replyHandlers.Unlock()
@ -420,7 +420,7 @@ func SendMany(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Check that the account specified in the request exists. // Check that the account specified in the request exists.
w, ok := wallets.m[cmd.FromAccount] a, ok := accounts.m[cmd.FromAccount]
if !ok { if !ok {
ReplyError(frontend, cmd.Id(), ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletInvalidAccountName) &btcjson.ErrWalletInvalidAccountName)
@ -435,7 +435,7 @@ func SendMany(frontend chan []byte, icmd btcjson.Cmd) {
// Create transaction, replying with an error if the creation // Create transaction, replying with an error if the creation
// was not successful. // was not successful.
createdTx, err := w.txToPairs(cmd.Amounts, fee, cmd.MinConf) createdTx, err := a.txToPairs(cmd.Amounts, fee, cmd.MinConf)
switch { switch {
case err == ErrNonPositiveAmount: case err == ErrNonPositiveAmount:
e := &btcjson.Error{ e := &btcjson.Error{
@ -461,11 +461,11 @@ func SendMany(frontend chan []byte, icmd btcjson.Cmd) {
// If a change address was added, mark wallet as dirty, sync to disk, // If a change address was added, mark wallet as dirty, sync to disk,
// and request updates for change address. // and request updates for change address.
if len(createdTx.changeAddr) != 0 { if len(createdTx.changeAddr) != 0 {
w.dirty = true a.dirty = true
if err := w.writeDirtyToDisk(); err != nil { if err := a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot write dirty wallet: %v", err) log.Errorf("cannot write dirty wallet: %v", err)
} }
w.ReqNewTxsForAddress(createdTx.changeAddr) a.ReqNewTxsForAddress(createdTx.changeAddr)
} }
// Create sendrawtransaction request with hexstring of the raw tx. // Create sendrawtransaction request with hexstring of the raw tx.
@ -485,7 +485,7 @@ func SendMany(frontend chan []byte, icmd btcjson.Cmd) {
// Set up a reply handler to respond to the btcd reply. // Set up a reply handler to respond to the btcd reply.
replyHandlers.Lock() replyHandlers.Lock()
replyHandlers.m[n] = func(result interface{}, err *btcjson.Error) bool { replyHandlers.m[n] = func(result interface{}, err *btcjson.Error) bool {
return handleSendRawTxReply(frontend, cmd, result, err, w, return handleSendRawTxReply(frontend, cmd, result, err, a,
createdTx) createdTx)
} }
replyHandlers.Unlock() replyHandlers.Unlock()
@ -495,7 +495,7 @@ func SendMany(frontend chan []byte, icmd btcjson.Cmd) {
} }
func handleSendRawTxReply(frontend chan []byte, icmd btcjson.Cmd, func handleSendRawTxReply(frontend chan []byte, icmd btcjson.Cmd,
result interface{}, err *btcjson.Error, w *Account, result interface{}, err *btcjson.Error, a *Account,
txInfo *CreatedTx) bool { txInfo *CreatedTx) bool {
if err != nil { if err != nil {
@ -504,31 +504,31 @@ func handleSendRawTxReply(frontend chan []byte, icmd btcjson.Cmd,
} }
// Remove previous unspent outputs now spent by the tx. // Remove previous unspent outputs now spent by the tx.
w.UtxoStore.Lock() a.UtxoStore.Lock()
modified := w.UtxoStore.s.Remove(txInfo.inputs) modified := a.UtxoStore.s.Remove(txInfo.inputs)
// Add unconfirmed change utxo (if any) to UtxoStore. // Add unconfirmed change utxo (if any) to UtxoStore.
if txInfo.changeUtxo != nil { if txInfo.changeUtxo != nil {
w.UtxoStore.s = append(w.UtxoStore.s, txInfo.changeUtxo) a.UtxoStore.s = append(a.UtxoStore.s, txInfo.changeUtxo)
w.ReqSpentUtxoNtfn(txInfo.changeUtxo) a.ReqSpentUtxoNtfn(txInfo.changeUtxo)
modified = true modified = true
} }
if modified { if modified {
w.UtxoStore.dirty = true a.UtxoStore.dirty = true
w.UtxoStore.Unlock() a.UtxoStore.Unlock()
if err := w.writeDirtyToDisk(); err != nil { if err := a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err) log.Errorf("cannot sync dirty wallet: %v", err)
} }
// Notify all frontends of account's new unconfirmed and // Notify all frontends of account's new unconfirmed and
// confirmed balance. // confirmed balance.
confirmed := w.CalculateBalance(1) confirmed := a.CalculateBalance(1)
unconfirmed := w.CalculateBalance(0) - confirmed unconfirmed := a.CalculateBalance(0) - confirmed
NotifyWalletBalance(frontendNotificationMaster, w.name, confirmed) NotifyWalletBalance(frontendNotificationMaster, a.name, confirmed)
NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, w.name, unconfirmed) NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, a.name, unconfirmed)
} else { } else {
w.UtxoStore.Unlock() a.UtxoStore.Unlock()
} }
// btcd cannot be trusted to successfully relay the tx to the // btcd cannot be trusted to successfully relay the tx to the
@ -613,11 +613,11 @@ func CreateEncryptedWallet(frontend chan []byte, icmd btcjson.Cmd) {
// Grab the account map lock and defer the unlock. If an // Grab the account map lock and defer the unlock. If an
// account is successfully created, it will be added to the // account is successfully created, it will be added to the
// map while the lock is held. // map while the lock is held.
wallets.Lock() accounts.Lock()
defer wallets.Unlock() defer accounts.Unlock()
// Does this wallet already exist? // Does this wallet already exist?
if _, ok = wallets.m[cmd.Account]; ok { if _, ok = accounts.m[cmd.Account]; ok {
ReplyError(frontend, cmd.Id(), ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletInvalidAccountName) &btcjson.ErrWalletInvalidAccountName)
return return
@ -653,7 +653,7 @@ func CreateEncryptedWallet(frontend chan []byte, icmd btcjson.Cmd) {
// Create new account with the wallet. A new JSON ID is set for // Create new account with the wallet. A new JSON ID is set for
// transaction notifications. // transaction notifications.
bw := &Account{ a := &Account{
Wallet: wlt, Wallet: wlt,
name: cmd.Account, name: cmd.Account,
dirty: true, dirty: true,
@ -663,15 +663,15 @@ func CreateEncryptedWallet(frontend chan []byte, icmd btcjson.Cmd) {
// Begin tracking account against a connected btcd. // Begin tracking account against a connected btcd.
// //
// TODO(jrick): this should *only* happen if btcd is connected. // TODO(jrick): this should *only* happen if btcd is connected.
bw.Track() a.Track()
// Save the account in the global account map. The mutex is // Save the account in the global account map. The mutex is
// already held at this point, and will be unlocked when this // already held at this point, and will be unlocked when this
// func returns. // func returns.
wallets.m[cmd.Account] = bw accounts.m[cmd.Account] = a
// Write new wallet to disk. // Write new wallet to disk.
if err := bw.writeDirtyToDisk(); err != nil { if err := a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", err) log.Errorf("cannot sync dirty wallet: %v", err)
} }
@ -695,7 +695,7 @@ func WalletIsLocked(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Check that the account specified in the request exists. // Check that the account specified in the request exists.
w, ok := wallets.m[cmd.Account] a, ok := accounts.m[cmd.Account]
if !ok { if !ok {
ReplyError(frontend, cmd.Id(), ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletInvalidAccountName) &btcjson.ErrWalletInvalidAccountName)
@ -703,7 +703,7 @@ func WalletIsLocked(frontend chan []byte, icmd btcjson.Cmd) {
} }
// Reply with true for a locked wallet, and false for unlocked. // Reply with true for a locked wallet, and false for unlocked.
ReplySuccess(frontend, cmd.Id(), w.IsLocked()) ReplySuccess(frontend, cmd.Id(), a.IsLocked())
} }
// WalletLock responds to walletlock request by locking the wallet, // WalletLock responds to walletlock request by locking the wallet,
@ -713,8 +713,8 @@ func WalletIsLocked(frontend chan []byte, icmd btcjson.Cmd) {
// with this. Lock all the wallets, like if all accounts are locked // with this. Lock all the wallets, like if all accounts are locked
// for one bitcoind wallet? // for one bitcoind wallet?
func WalletLock(frontend chan []byte, icmd btcjson.Cmd) { func WalletLock(frontend chan []byte, icmd btcjson.Cmd) {
if w, ok := wallets.m[""]; ok { if a, ok := accounts.m[""]; ok {
if err := w.Lock(); err != nil { if err := a.Lock(); err != nil {
ReplyError(frontend, icmd.Id(), ReplyError(frontend, icmd.Id(),
&btcjson.ErrWalletWrongEncState) &btcjson.ErrWalletWrongEncState)
return return
@ -737,8 +737,8 @@ func WalletPassphrase(frontend chan []byte, icmd btcjson.Cmd) {
return return
} }
if w, ok := wallets.m[""]; ok { if a, ok := accounts.m[""]; ok {
if err := w.Unlock([]byte(cmd.Passphrase)); err != nil { if err := a.Unlock([]byte(cmd.Passphrase)); err != nil {
ReplyError(frontend, cmd.Id(), ReplyError(frontend, cmd.Id(),
&btcjson.ErrWalletPassphraseIncorrect) &btcjson.ErrWalletPassphraseIncorrect)
return return
@ -747,7 +747,7 @@ func WalletPassphrase(frontend chan []byte, icmd btcjson.Cmd) {
NotifyWalletLockStateChange("", false) NotifyWalletLockStateChange("", false)
go func() { go func() {
time.Sleep(time.Second * time.Duration(int64(cmd.Timeout))) time.Sleep(time.Second * time.Duration(int64(cmd.Timeout)))
w.Lock() a.Lock()
NotifyWalletLockStateChange("", true) NotifyWalletLockStateChange("", true)
}() }()
} }

View file

@ -25,8 +25,8 @@ import (
) )
var ( var (
// dirtyWallets holds a set of wallets that include dirty components. // dirtyAccounts holds a set of accounts that include dirty components.
dirtyWallets = struct { dirtyAccounts = struct {
sync.Mutex sync.Mutex
m map[*Account]bool m map[*Account]bool
}{ }{
@ -34,29 +34,29 @@ var (
} }
) )
// DirtyWalletSyncer synces dirty wallets for cases where the updated // DirtyAccountSyncer synces dirty accounts for cases where the updated
// information was not required to be immediately written to disk. Wallets // information was not required to be immediately written to disk. Accounts
// may be added to dirtyWallets and will be checked and processed every 10 // may be added to dirtyAccounts and will be checked and processed every 10
// seconds by this function. // seconds by this function.
// //
// This never returns and is meant to be called from a goroutine. // This never returns and is meant to be called from a goroutine.
func DirtyWalletSyncer() { func DirtyAccountSyncer() {
ticker := time.Tick(10 * time.Second) ticker := time.Tick(10 * time.Second)
for { for {
select { select {
case <-ticker: case <-ticker:
dirtyWallets.Lock() dirtyAccounts.Lock()
for w := range dirtyWallets.m { for a := range dirtyAccounts.m {
log.Debugf("Syncing wallet '%v' to disk", log.Debugf("Syncing account '%v' to disk",
w.Wallet.Name()) a.Wallet.Name())
if err := w.writeDirtyToDisk(); err != nil { if err := a.writeDirtyToDisk(); err != nil {
log.Errorf("cannot sync dirty wallet: %v", log.Errorf("cannot sync dirty wallet: %v",
err) err)
} else { } else {
delete(dirtyWallets.m, w) delete(dirtyAccounts.m, a)
} }
} }
dirtyWallets.Unlock() dirtyAccounts.Unlock()
} }
} }
} }

View file

@ -397,23 +397,23 @@ func NtfnBlockConnected(n btcws.Notification) {
// //
// TODO(jrick): send frontend tx notifications once that's // TODO(jrick): send frontend tx notifications once that's
// implemented. // implemented.
for _, w := range wallets.m { for _, a := range accounts.m {
// Mark wallet as being synced with the new blockstamp. // Mark wallet as being synced with the new blockstamp.
w.mtx.Lock() a.mtx.Lock()
w.Wallet.SetSyncedWith(bs) a.Wallet.SetSyncedWith(bs)
w.mtx.Unlock() a.mtx.Unlock()
// The UTXO store will be dirty if it was modified // The UTXO store will be dirty if it was modified
// from a tx notification. // from a tx notification.
if w.UtxoStore.dirty { if a.UtxoStore.dirty {
// Notify all frontends of account's new unconfirmed // Notify all frontends of account's new unconfirmed
// and confirmed balance. // and confirmed balance.
confirmed := w.CalculateBalance(1) confirmed := a.CalculateBalance(1)
unconfirmed := w.CalculateBalance(0) - confirmed unconfirmed := a.CalculateBalance(0) - confirmed
NotifyWalletBalance(frontendNotificationMaster, NotifyWalletBalance(frontendNotificationMaster,
w.name, confirmed) a.name, confirmed)
NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, NotifyWalletBalanceUnconfirmed(frontendNotificationMaster,
w.name, unconfirmed) a.name, unconfirmed)
} }
// The account is intentionaly not immediately synced to disk. // The account is intentionaly not immediately synced to disk.
@ -426,10 +426,10 @@ func NtfnBlockConnected(n btcws.Notification) {
// //
// Instead, the wallet is queued to be written to disk at the // Instead, the wallet is queued to be written to disk at the
// next scheduled disk sync. // next scheduled disk sync.
w.dirty = true a.dirty = true
dirtyWallets.Lock() dirtyAccounts.Lock()
dirtyWallets.m[w] = true dirtyAccounts.m[a] = true
dirtyWallets.Unlock() dirtyAccounts.Unlock()
} }
// Notify frontends of new blockchain height. // Notify frontends of new blockchain height.
@ -455,7 +455,7 @@ func NtfnBlockDisconnected(n btcws.Notification) {
// Rollback Utxo and Tx data stores. // Rollback Utxo and Tx data stores.
go func() { go func() {
wallets.Rollback(bdn.Height, hash) accounts.Rollback(bdn.Height, hash)
}() }()
// Notify frontends of new blockchain height. // Notify frontends of new blockchain height.
@ -619,13 +619,13 @@ func BtcdHandshake(ws *websocket.Conn) {
// since last connection. If so, rollback and rescan to // since last connection. If so, rollback and rescan to
// catch up. // catch up.
for _, w := range wallets.m { for _, a := range accounts.m {
w.RescanToBestBlock() a.RescanToBestBlock()
} }
// Begin tracking wallets against this btcd instance. // Begin tracking wallets against this btcd instance.
for _, w := range wallets.m { for _, a := range accounts.m {
w.Track() a.Track()
} }
// (Re)send any unmined transactions to btcd in case of a btcd restart. // (Re)send any unmined transactions to btcd in case of a btcd restart.