more wallet -> account renames
This commit is contained in:
parent
9c827a824f
commit
ef49eca365
5 changed files with 190 additions and 190 deletions
154
account.go
154
account.go
|
@ -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
50
cmd.go
|
@ -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
114
cmdmgr.go
|
@ -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)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
26
disksync.go
26
disksync.go
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
36
sockets.go
36
sockets.go
|
@ -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.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue