rework the account manager somewhat.

- Move the MarkAddresForAccount and LookupAccountByAddress functionality
into account maanger.

- Move the wallet opeing logic into account manager (the only place that calls
it) and unexport.

- Move accountHandler to using a single channel for commands. Many of
the commands have ordering restraints (add account, list all accounts,
remove account, access account, mark account for address) which are very
much undefined with the multi-channel model.

- Rework all callers of LookupAccountByAddress to get the account structure
directly.
This commit is contained in:
Owain G. Ainsworth 2014-04-03 16:00:46 +01:00
parent 391b269d06
commit 35bd7ef6d9
5 changed files with 359 additions and 363 deletions

View file

@ -26,38 +26,10 @@ import (
"github.com/conformal/btcwallet/wallet"
"github.com/conformal/btcwire"
"path/filepath"
"sync"
)
// addressAccountMap holds a map of addresses to names of the
// accounts that hold each address.
//
// TODO: move this to AccountManager
var addressAccountMap = struct {
sync.RWMutex
m map[string]string
}{
m: make(map[string]string),
}
// MarkAddressForAccount marks an address as belonging to an account.
func MarkAddressForAccount(address, account string) {
addressAccountMap.Lock()
addressAccountMap.m[address] = account
addressAccountMap.Unlock()
}
// LookupAccountByAddress returns the account name for address. error
// will be set to ErrNotFound if the address has not been marked as
// associated with any account.
func LookupAccountByAddress(address string) (string, error) {
addressAccountMap.RLock()
defer addressAccountMap.RUnlock()
account, ok := addressAccountMap.m[address]
if !ok {
return "", ErrNotFound
}
return account, nil
}
// Account is a structure containing all the components for a
@ -159,7 +131,7 @@ func (a *Account) CalculateBalance(confirms int) float64 {
// 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
// include a UTXO.
func (a *Account) CalculateAddressBalance(addr *btcutil.AddressPubKeyHash, confirms int) float64 {
func (a *Account) CalculateAddressBalance(addr btcutil.Address, confirms int) float64 {
bs, err := GetCurBlock()
if bs.Height == int32(btcutil.BlockHeightUnknown) || err != nil {
return 0.
@ -174,11 +146,7 @@ func (a *Account) CalculateAddressBalance(addr *btcutil.AddressPubKeyHash, confi
if len(addrs) != 1 {
continue
}
apkh, ok := addrs[0].(*btcutil.AddressPubKeyHash)
if !ok {
continue
}
if *addr == *apkh {
if addrs[0].EncodeAddress() == addr.EncodeAddress() {
bal += txout.Value()
}
}
@ -401,9 +369,9 @@ func (a *Account) ImportPrivateKey(pk []byte, compressed bool,
}
// Associate the imported address with this account.
MarkAddressForAccount(addrStr, a.Name())
AcctMgr.MarkAddressForAccount(addr, a)
log.Infof("Imported payment address %v", addrStr)
log.Infof("Imported payment address %s", addrStr)
// Return the payment address string of the imported private key.
return addrStr, nil
@ -582,7 +550,7 @@ func (a *Account) NewAddress() (btcutil.Address, error) {
}
// Mark this new address as belonging to this account.
MarkAddressForAccount(addr.EncodeAddress(), a.Name())
AcctMgr.MarkAddressForAccount(addr, a)
// Request updates from btcd for new transactions sent to this address.
a.ReqNewTxsForAddress(addr)
@ -611,7 +579,7 @@ func (a *Account) NewChangeAddress() (btcutil.Address, error) {
}
// Mark this new address as belonging to this account.
MarkAddressForAccount(addr.EncodeAddress(), a.Name())
AcctMgr.MarkAddressForAccount(addr, a)
// Request updates from btcd for new transactions sent to this address.
a.ReqNewTxsForAddress(addr)

View file

@ -17,7 +17,6 @@
package main
import (
"container/list"
"encoding/hex"
"errors"
"fmt"
@ -25,6 +24,8 @@ import (
"github.com/conformal/btcwallet/tx"
"github.com/conformal/btcwallet/wallet"
"github.com/conformal/btcwire"
"os"
"strings"
"time"
)
@ -38,19 +39,43 @@ var (
// AcctMgr is the global account manager for all opened accounts.
var AcctMgr = NewAccountManager()
type openAccountsCmd struct{}
type accessAccountRequest struct {
name string
resp chan *Account
}
type accessAllRequest struct {
resp chan []*Account
}
type accessAccountByAddressRequest struct {
address string
resp chan *Account
}
type markAddressForAccountCmd struct {
address string
account *Account
}
type addAccountCmd struct {
a *Account
}
type removeAccountCmd struct {
a *Account
}
// AccountManager manages a collection of accounts.
type AccountManager struct {
// The accounts accessed through the account manager are not safe for
// concurrent access. The account manager therefore contains a
// binary semaphore channel to prevent incorrect access.
bsem chan struct{}
openAccounts chan struct{}
accessAccount chan *accessAccountRequest
accessAll chan *accessAllRequest
add chan *Account
remove chan *Account
rescanMsgs chan RescanMsg
bsem chan struct{}
cmdChan chan interface{}
rescanMsgs chan RescanMsg
ds *DiskSyncer
rm *RescanManager
@ -59,13 +84,9 @@ type AccountManager struct {
// NewAccountManager returns a new AccountManager.
func NewAccountManager() *AccountManager {
am := &AccountManager{
bsem: make(chan struct{}, 1),
openAccounts: make(chan struct{}, 1),
accessAccount: make(chan *accessAccountRequest),
accessAll: make(chan *accessAllRequest),
add: make(chan *Account),
remove: make(chan *Account),
rescanMsgs: make(chan RescanMsg, 1),
bsem: make(chan struct{}, 1),
cmdChan: make(chan interface{}),
rescanMsgs: make(chan RescanMsg, 1),
}
am.ds = NewDiskSyncer(am)
am.rm = NewRescanManager(am.rescanMsgs)
@ -83,63 +104,263 @@ func (am *AccountManager) Start() {
go am.rm.Start()
}
// accountHandler maintains accounts and structures for quick lookups for
// account information. Access to these structures must be done through
// with the channels in the AccountManger struct fields. This function
// never returns and should be called as a new goroutine.
func (am *AccountManager) accountHandler() {
// List and map of all accounts.
l := list.New()
m := make(map[string]*Account)
// accountData is a helper structure to let us centralise logic for adding
// and removing accounts.
type accountData struct {
// maps name to account struct. We could keep a list here for iteration
// but iteration over the small amounts we have is likely not worth
// the extra complexity.
nameToAccount map[string]*Account
addressToAccount map[string]*Account
}
for {
select {
case <-am.openAccounts:
func newAccountData() *accountData {
return &accountData{
nameToAccount: make(map[string]*Account),
addressToAccount: make(map[string]*Account),
}
}
func (ad *accountData) addAccount(a *Account) {
if _, ok := ad.nameToAccount[a.name]; ok {
return
}
ad.nameToAccount[a.name] = a
for addr := range a.ActivePaymentAddresses() {
ad.addressToAccount[addr] = a
}
}
func (ad *accountData) removeAccount(a *Account) {
a, ok := ad.nameToAccount[a.name]
if !ok {
return
}
delete(ad.nameToAccount, a.name)
for addr := range a.ActivePaymentAddresses() {
delete(ad.addressToAccount, addr)
}
}
// 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
}
var (
// errNoWallet describes an error where a wallet does not exist and
// must be created first.
errNoWallet = &walletOpenError{
Err: "wallet file does not exist",
}
// errNoTxs describes an error where the wallet and UTXO files were
// successfully read, but the TX history file was not. It is up to
// the caller whether this necessitates a rescan or not.
errNoTxs = errors.New("tx file cannot be read")
)
// openSavedAccount opens a named account from disk. If the wallet does not
// exist, errNoWallet is returned as an error.
func openSavedAccount(name string, cfg *config) (*Account, error) {
netdir := networkDir(cfg.Net())
if err := checkCreateDir(netdir); err != nil {
return nil, &walletOpenError{
Err: err.Error(),
}
}
wlt := new(wallet.Wallet)
txs := tx.NewStore()
a := &Account{
name: name,
Wallet: wlt,
TxStore: txs,
}
wfilepath := accountFilename("wallet.bin", name, netdir)
txfilepath := accountFilename("tx.bin", name, netdir)
var wfile, 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 nil, errNoWallet
}
msg := fmt.Sprintf("cannot open wallet file: %s", err)
return nil, &walletOpenError{msg}
}
defer wfile.Close()
if _, err = wlt.ReadFrom(wfile); err != nil {
msg := fmt.Sprintf("cannot read wallet: %s", err)
return nil, &walletOpenError{msg}
}
// Read tx file. If this fails, return a errNoTxs error and let
// the caller decide if a rescan is necessary.
var finalErr error
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
a.fullRescan = true
} else {
defer txfile.Close()
if _, err = txs.ReadFrom(txfile); err != nil {
log.Errorf("cannot read tx file: %s", err)
a.fullRescan = true
finalErr = errNoTxs
}
}
// Mark all active payment addresses as belonging to this account.
for addr := range a.ActivePaymentAddresses() {
MarkAddressForAccount(addr, name)
}
return a, finalErr
}
// openAccounts attempts to open all saved accounts.
func openAccounts() *accountData {
ad := newAccountData()
// If the network (account) directory is missing, but the temporary
// directory exists, move it. This is unlikely to happen, but possible,
// if writing out every account file at once to a tmp directory (as is
// done for changing a wallet passphrase) and btcwallet closes after
// removing the network directory but before renaming the temporary
// directory.
netDir := networkDir(cfg.Net())
tmpNetDir := tmpNetworkDir(cfg.Net())
if !fileExists(netDir) && fileExists(tmpNetDir) {
if err := Rename(tmpNetDir, netDir); err != nil {
log.Errorf("Cannot move temporary network dir: %v", err)
return ad
}
}
// The default account must exist, or btcwallet acts as if no
// wallets/accounts have been created yet.
a, err := openSavedAccount("", cfg)
if err != nil {
switch err.(type) {
case *walletOpenError:
log.Errorf("Default account wallet file unreadable: %v", err)
return ad
default:
log.Warnf("Non-critical problem opening an account file: %v", err)
}
}
ad.addAccount(a)
// Read all filenames in the account directory, and look for any
// filenames matching '*-wallet.bin'. These are wallets for
// additional saved accounts.
accountDir, err := os.Open(netDir)
if err != nil {
// Can't continue.
log.Errorf("Unable to open account directory: %v", err)
return ad
}
defer accountDir.Close()
fileNames, err := accountDir.Readdirnames(0)
if err != nil {
// fileNames might be partially set, so log an error and
// at least try to open some accounts.
log.Errorf("Unable to read all account files: %v", err)
}
var accountNames []string
for _, file := range fileNames {
if strings.HasSuffix(file, "-wallet.bin") {
name := strings.TrimSuffix(file, "-wallet.bin")
accountNames = append(accountNames, name)
}
}
// Open all additional accounts.
for _, acctName := range accountNames {
// Log txstore/utxostore errors as these will be recovered
// from with a rescan, but wallet errors must be returned
// to the caller.
a, err := openSavedAccount(acctName, cfg)
if err != nil {
switch err.(type) {
case *walletOpenError:
log.Errorf("Error opening account's wallet: %v", err)
default:
log.Warnf("Non-critical error opening an account file: %v", err)
}
} else {
ad.addAccount(a)
}
}
return ad
}
// accountHandler maintains accounts and structures for quick lookups for
// account information. Access to these structures must be requested via
// cmdChan. cmdChan is a single channel for multiple command types since there
// is ordering inherent in the commands and ordering between multipl goroutine
// reads via select{} is very much undefined. This function never returns and
// should be called as a new goroutine.
func (am *AccountManager) accountHandler() {
ad := openAccounts()
for c := range am.cmdChan {
switch cmd := c.(type) {
case *openAccountsCmd:
// Write all old accounts before proceeding.
for e := l.Front(); e != nil; e = e.Next() {
a := e.Value.(*Account)
for _, a := range ad.nameToAccount {
am.ds.FlushAccount(a)
}
m = OpenAccounts()
l.Init()
for _, a := range m {
l.PushBack(a)
ad = openAccounts()
case *accessAccountRequest:
a, ok := ad.nameToAccount[cmd.name]
if !ok {
a = nil
}
cmd.resp <- a
case access := <-am.accessAccount:
a, ok := m[access.name]
access.resp <- &accessAccountResponse{
a: a,
ok: ok,
case *accessAccountByAddressRequest:
a, ok := ad.addressToAccount[cmd.address]
if !ok {
a = nil
}
cmd.resp <- a
case access := <-am.accessAll:
s := make([]*Account, 0, l.Len())
for e := l.Front(); e != nil; e = e.Next() {
s = append(s, e.Value.(*Account))
case *accessAllRequest:
s := make([]*Account, 0, len(ad.nameToAccount))
for _, a := range ad.nameToAccount {
s = append(s, a)
}
access.resp <- s
cmd.resp <- s
case a := <-am.add:
if _, ok := m[a.name]; ok {
break
}
m[a.name] = a
l.PushBack(a)
case *addAccountCmd:
ad.addAccount(cmd.a)
case *removeAccountCmd:
ad.removeAccount(cmd.a)
case *markAddressForAccountCmd:
// TODO(oga) make sure we own account
ad.addressToAccount[cmd.address] = cmd.account
case a := <-am.remove:
if _, ok := m[a.name]; ok {
delete(m, a.name)
for e := l.Front(); e != nil; e = e.Next() {
v := e.Value.(*Account)
if v == a {
l.Remove(e)
break
}
}
}
}
}
}
@ -219,57 +440,77 @@ func (am *AccountManager) Release() {
am.bsem <- struct{}{}
}
// OpenAccounts triggers the manager to reopen all known accounts.
func (am *AccountManager) OpenAccounts() {
am.openAccounts <- struct{}{}
}
type accessAccountRequest struct {
name string
resp chan *accessAccountResponse
}
type accessAccountResponse struct {
a *Account
ok bool
am.cmdChan <- &openAccountsCmd{}
}
// Account returns the account specified by name, or ErrNotFound
// as an error if the account is not found.
func (am *AccountManager) Account(name string) (*Account, error) {
req := &accessAccountRequest{
respChan := make(chan *Account)
am.cmdChan <- &accessAccountRequest{
name: name,
resp: make(chan *accessAccountResponse),
resp: respChan,
}
am.accessAccount <- req
resp := <-req.resp
if !resp.ok {
resp := <-respChan
if resp == nil {
return nil, ErrNotFound
}
return resp.a, nil
return resp, nil
}
type accessAllRequest struct {
resp chan []*Account
// AccountByAddress returns the account specified by address, or
// ErrNotFound as an error if the account is not found.
func (am *AccountManager) AccountByAddress(addr btcutil.Address) (*Account,
error) {
respChan := make(chan *Account)
am.cmdChan <- &accessAccountByAddressRequest{
address: addr.EncodeAddress(),
resp: respChan,
}
resp := <-respChan
if resp == nil {
return nil, ErrNotFound
}
return resp, nil
}
// MarkAddressForAccount labels the given account as containing the provided
// address.
func (am *AccountManager) MarkAddressForAccount(address btcutil.Address,
account *Account) {
// TODO(oga) really this entire dance should be carried out implicitly
// instead of requiring explicit messaging from the account to the
// manager.
am.cmdChan <- &markAddressForAccountCmd{
address: address.EncodeAddress(),
account: account,
}
}
// AllAccounts returns a slice of all managed accounts.
func (am *AccountManager) AllAccounts() []*Account {
req := &accessAllRequest{
resp: make(chan []*Account),
respChan := make(chan []*Account)
am.cmdChan <- &accessAllRequest{
resp: respChan,
}
am.accessAll <- req
return <-req.resp
return <-respChan
}
// AddAccount adds an account to the collection managed by an AccountManager.
func (am *AccountManager) AddAccount(a *Account) {
am.add <- a
am.cmdChan <- &addAccountCmd{
a: a,
}
}
// RemoveAccount removes an account to the collection managed by an
// AccountManager.
func (am *AccountManager) RemoveAccount(a *Account) {
am.remove <- a
am.cmdChan <- &removeAccountCmd{
a: a,
}
}
// RegisterNewAccount adds a new memory account to the account manager,
@ -386,13 +627,6 @@ func (am *AccountManager) CreateEncryptedWallet(passphrase []byte) error {
return err
}
// Mark all active payment addresses as belonging to this account.
//
// TODO(jrick) move this to the account manager
for addr := range a.ActivePaymentAddresses() {
MarkAddressForAccount(addr, "")
}
// Begin tracking account against a connected btcd.
a.Track()

172
cmd.go
View file

@ -18,10 +18,8 @@ package main
import (
"errors"
"fmt"
"github.com/conformal/btcjson"
"github.com/conformal/btcutil"
"github.com/conformal/btcwallet/tx"
"github.com/conformal/btcwallet/wallet"
"github.com/conformal/btcwire"
"io/ioutil"
@ -29,23 +27,11 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"strings"
"sync"
"time"
)
var (
// ErrNoWallet describes an error where a wallet does not exist and
// must be created first.
ErrNoWallet = &WalletOpenError{
Err: "wallet file does not exist",
}
// ErrNoTxs describes an error where the wallet and UTXO files were
// successfully read, but the TX history file was not. It is up to
// the caller whether this necessitates a rescan or not.
ErrNoTxs = errors.New("tx file cannot be read")
cfg *config
curBlock = struct {
@ -148,7 +134,6 @@ func main() {
// Start account manager and open accounts.
AcctMgr.Start()
AcctMgr.OpenAccounts()
// Read CA file to verify a btcd TLS connection.
cafile, err := ioutil.ReadFile(cfg.CAFile)
@ -244,163 +229,6 @@ func main() {
}
}
// 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
}
// OpenSavedAccount opens a named account from disk. If the wallet does not
// exist, ErrNoWallet is returned as an error.
func OpenSavedAccount(name string, cfg *config) (*Account, error) {
netdir := networkDir(cfg.Net())
if err := checkCreateDir(netdir); err != nil {
return nil, &WalletOpenError{
Err: err.Error(),
}
}
wlt := new(wallet.Wallet)
txs := tx.NewStore()
a := &Account{
name: name,
Wallet: wlt,
TxStore: txs,
}
wfilepath := accountFilename("wallet.bin", name, netdir)
txfilepath := accountFilename("tx.bin", name, netdir)
var wfile, 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 nil, ErrNoWallet
}
msg := fmt.Sprintf("cannot open wallet file: %s", err)
return nil, &WalletOpenError{msg}
}
defer wfile.Close()
if _, err = wlt.ReadFrom(wfile); err != nil {
msg := fmt.Sprintf("cannot read wallet: %s", err)
return nil, &WalletOpenError{msg}
}
// Read tx file. If this fails, return a ErrNoTxs error and let
// the caller decide if a rescan is necessary.
var finalErr error
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
a.fullRescan = true
} else {
defer txfile.Close()
if _, err = txs.ReadFrom(txfile); err != nil {
log.Errorf("cannot read tx file: %s", err)
a.fullRescan = true
finalErr = ErrNoTxs
}
}
// Mark all active payment addresses as belonging to this account.
for addr := range a.ActivePaymentAddresses() {
MarkAddressForAccount(addr, name)
}
return a, finalErr
}
// OpenAccounts attempts to open all saved accounts.
func OpenAccounts() map[string]*Account {
accounts := make(map[string]*Account)
// If the network (account) directory is missing, but the temporary
// directory exists, move it. This is unlikely to happen, but possible,
// if writing out every account file at once to a tmp directory (as is
// done for changing a wallet passphrase) and btcwallet closes after
// removing the network directory but before renaming the temporary
// directory.
netDir := networkDir(cfg.Net())
tmpNetDir := tmpNetworkDir(cfg.Net())
if !fileExists(netDir) && fileExists(tmpNetDir) {
if err := Rename(tmpNetDir, netDir); err != nil {
log.Errorf("Cannot move temporary network dir: %v", err)
return accounts
}
}
// The default account must exist, or btcwallet acts as if no
// wallets/accounts have been created yet.
a, err := OpenSavedAccount("", cfg)
if err != nil {
switch err.(type) {
case *WalletOpenError:
log.Errorf("Default account wallet file unreadable: %v", err)
return accounts
default:
log.Warnf("Non-critical problem opening an account file: %v", err)
}
}
accounts[""] = a
// Read all filenames in the account directory, and look for any
// filenames matching '*-wallet.bin'. These are wallets for
// additional saved accounts.
accountDir, err := os.Open(netDir)
if err != nil {
// Can't continue.
log.Errorf("Unable to open account directory: %v", err)
return accounts
}
defer accountDir.Close()
fileNames, err := accountDir.Readdirnames(0)
if err != nil {
// fileNames might be partially set, so log an error and
// at least try to open some accounts.
log.Errorf("Unable to read all account files: %v", err)
}
var accountNames []string
for _, file := range fileNames {
if strings.HasSuffix(file, "-wallet.bin") {
name := strings.TrimSuffix(file, "-wallet.bin")
accountNames = append(accountNames, name)
}
}
// Open all additional accounts.
for _, acctName := range accountNames {
// Log txstore/utxostore errors as these will be recovered
// from with a rescan, but wallet errors must be returned
// to the caller.
a, err := OpenSavedAccount(acctName, cfg)
if err != nil {
switch err.(type) {
case *WalletOpenError:
log.Errorf("Error opening account's wallet: %v", err)
default:
log.Warnf("Non-critical error opening an account file: %v", err)
}
} else {
accounts[acctName] = a
}
}
return accounts
}
var accessServer = make(chan *AccessCurrentServerConn)
// AccessCurrentServerConn is used to access the current RPC connection

View file

@ -120,12 +120,10 @@ func NtfnRecvTx(n btcjson.Cmd) error {
var accounts []*Account
_, addrs, _, _ := btcscript.ExtractPkScriptAddrs(txout.PkScript, cfg.Net())
for _, addr := range addrs {
aname, err := LookupAccountByAddress(addr.EncodeAddress())
if err == ErrNotFound {
a, err := AcctMgr.AccountByAddress(addr)
if err != nil {
continue
}
// This cannot reasonably fail if the above succeeded.
a, _ := AcctMgr.Account(aname)
accounts = append(accounts, a)
}

View file

@ -276,15 +276,7 @@ func makeMultiSigScript(keys []string, nRequired int) ([]byte, *btcjson.Error) {
case *btcutil.AddressPubKey:
keysesPrecious[i] = addr
case *btcutil.AddressPubKeyHash:
actname, err :=
LookupAccountByAddress(addr.EncodeAddress())
if err != nil {
return nil, &btcjson.Error{
Code: btcjson.ErrParse.Code,
Message: err.Error(),
}
}
act, err := AcctMgr.Account(actname)
act, err := AcctMgr.AccountByAddress(addr)
if err != nil {
return nil, &btcjson.Error{
Code: btcjson.ErrParse.Code,
@ -620,7 +612,7 @@ func GetAccount(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
}
// Look up account which holds this address.
aname, err := LookupAccountByAddress(cmd.Address)
acct, err := AcctMgr.AccountByAddress(addr)
if err == ErrNotFound {
e := btcjson.Error{
Code: btcjson.ErrInvalidAddressOrKey.Code,
@ -629,7 +621,7 @@ func GetAccount(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
return nil, &e
}
return aname, nil
return acct.Name(), nil
}
// GetAccountAddress handles a getaccountaddress by returning the most
@ -693,14 +685,12 @@ func GetAddressBalance(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
if err != nil {
return nil, &btcjson.ErrInvalidAddressOrKey
}
apkh, ok := addr.(*btcutil.AddressPubKeyHash)
if !ok || !apkh.IsForNet(cfg.Net()) {
return nil, &btcjson.ErrInvalidAddressOrKey
}
// Look up account which holds this address.
aname, err := LookupAccountByAddress(cmd.Address)
if err == ErrNotFound {
// Get the account which holds the address in the request.
// This should not fail, so if it does, return an internal
// error to the frontend.
a, err := AcctMgr.AccountByAddress(addr)
if err != nil {
e := btcjson.Error{
Code: btcjson.ErrInvalidAddressOrKey.Code,
Message: "Address not found in wallet",
@ -708,15 +698,7 @@ func GetAddressBalance(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
return nil, &e
}
// Get the account which holds the address in the request.
// This should not fail, so if it does, return an internal
// error to the frontend.
a, err := AcctMgr.Account(aname)
if err != nil {
return nil, &btcjson.ErrInternal
}
bal := a.CalculateAddressBalance(apkh, int(cmd.Minconf))
bal := a.CalculateAddressBalance(addr, int(cmd.Minconf))
return bal, nil
}
@ -1597,26 +1579,19 @@ func SignMessage(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
return nil, &btcjson.ErrInternal
}
acctStr, err := LookupAccountByAddress(cmd.Address)
if err != nil {
return nil, &btcjson.ErrInvalidAddressOrKey
}
// look up address.
a, err := AcctMgr.Account(acctStr)
if err != nil {
return nil, &btcjson.ErrWalletInvalidAccountName
}
// This really should work when the above found something valid.
addr, err := btcutil.DecodeAddress(cmd.Address, cfg.Net())
if err != nil {
return nil, &btcjson.Error{
Code: btcjson.ErrWallet.Code,
Code: btcjson.ErrParse.Code,
Message: err.Error(),
}
}
a, err := AcctMgr.AccountByAddress(addr)
if err != nil {
return nil, &btcjson.ErrInvalidAddressOrKey
}
privkey, err := a.AddressKey(addr)
if err != nil {
return nil, &btcjson.Error{
@ -1733,12 +1708,11 @@ func ValidateAddress(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
// implementation only puts that information if the script is
// "ismine", and we follow that behaviour.
}
account, err := LookupAccountByAddress(addr.EncodeAddress())
account, err := AcctMgr.AccountByAddress(addr)
if err == nil {
// we ignore these errors because if this call passes this can't
// realistically fail.
a, _ := AcctMgr.Account(account)
ainfo, _ := a.AddressInfo(addr)
ainfo, _ := account.AddressInfo(addr)
result["ismine"] = true
result["account"] = account
@ -1784,26 +1758,20 @@ func VerifyMessage(icmd btcjson.Cmd) (interface{}, *btcjson.Error) {
return nil, &btcjson.ErrInternal
}
// First check we know about the address and get the keys.
acctStr, err := LookupAccountByAddress(cmd.Address)
if err != nil {
return nil, &btcjson.ErrInvalidAddressOrKey
}
a, err := AcctMgr.Account(acctStr)
if err != nil {
return nil, &btcjson.ErrWalletInvalidAccountName
}
// This really should work when the above found something valid.
addr, err := btcutil.DecodeAddress(cmd.Address, cfg.Net())
if err != nil {
return nil, &btcjson.Error{
Code: btcjson.ErrWallet.Code,
Code: btcjson.ErrParse.Code,
Message: err.Error(),
}
}
// First check we know about the address and get the keys.
a, err := AcctMgr.AccountByAddress(addr)
if err != nil {
return nil, &btcjson.ErrInvalidAddressOrKey
}
privkey, err := a.AddressKey(addr)
if err != nil {
return nil, &btcjson.Error{