Reply with correct account balance for getbalance requests.

This commit is contained in:
Josh Rickmar 2013-09-05 15:31:39 -04:00
parent 290fdb5427
commit 24d6168709
3 changed files with 119 additions and 10 deletions

106
cmd.go
View file

@ -32,6 +32,10 @@ import (
"time" "time"
) )
const (
satoshiPerBTC = 100000000
)
var ( var (
ErrNoWallet = errors.New("Wallet file does not exist.") ErrNoWallet = errors.New("Wallet file does not exist.")
) )
@ -39,6 +43,12 @@ var (
var ( var (
log seelog.LoggerInterface = seelog.Default log seelog.LoggerInterface = seelog.Default
cfg *config cfg *config
curHeight = struct {
sync.RWMutex
h int64
}{
h: btcutil.BlockHeightUnknown,
}
wallets = struct { wallets = struct {
sync.RWMutex sync.RWMutex
m map[string]*BtcWallet m map[string]*BtcWallet
@ -185,6 +195,82 @@ func OpenWallet(cfg *config, account string) (*BtcWallet, error) {
return w, nil return w, nil
} }
func getCurHeight() (height int64) {
curHeight.RLock()
height = curHeight.h
curHeight.RUnlock()
if height != btcutil.BlockHeightUnknown {
return height
} else {
seq.Lock()
n := seq.n
seq.n++
seq.Unlock()
m, err := btcjson.CreateMessageWithId("getblockcount",
fmt.Sprintf("btcwallet(%v)", n))
if err != nil {
// Can't continue.
return btcutil.BlockHeightUnknown
}
c := make(chan int64)
replyHandlers.Lock()
replyHandlers.m[n] = func(result, e interface{}) bool {
if e != nil {
c <- btcutil.BlockHeightUnknown
return true
}
if balance, ok := result.(float64); ok {
c <- int64(balance)
} else {
c <- btcutil.BlockHeightUnknown
}
return true
}
replyHandlers.Unlock()
// send message
btcdMsgs <- m
// Block until reply is ready.
height = <-c
curHeight.Lock()
if height > curHeight.h {
curHeight.h = height
} else {
height = curHeight.h
}
curHeight.Unlock()
return height
}
}
func (w *BtcWallet) CalculateBalance(confirmations int) float64 {
var bal int64 // Measured in satoshi
height := getCurHeight()
if height == btcutil.BlockHeightUnknown {
return 0.
}
w.UtxoStore.RLock()
for _, u := range w.UtxoStore.s.Confirmed {
if int(height-u.Height) >= confirmations {
bal += u.Amt
}
}
for _, u := range w.UtxoStore.s.Unconfirmed {
if int(height-u.Height) >= confirmations {
bal += u.Amt
}
}
w.UtxoStore.RUnlock()
return float64(bal) / satoshiPerBTC
}
func (w *BtcWallet) Track() { func (w *BtcWallet) Track() {
seq.Lock() seq.Lock()
n := seq.n n := seq.n
@ -225,7 +311,7 @@ func (w *BtcWallet) RescanForAddress(addr string, blocks ...int) {
msg, _ := json.Marshal(m) msg, _ := json.Marshal(m)
replyHandlers.Lock() replyHandlers.Lock()
replyHandlers.m[n] = func(result interface{}) bool { replyHandlers.m[n] = func(result, e interface{}) bool {
// TODO(jrick) // TODO(jrick)
// btcd returns a nil result when the rescan is complete. // btcd returns a nil result when the rescan is complete.
@ -254,12 +340,26 @@ func (w *BtcWallet) ReqNewTxsForAddress(addr string) {
btcdMsgs <- msg btcdMsgs <- msg
} }
func (w *BtcWallet) NewBlockTxHandler(result interface{}) bool { func (w *BtcWallet) NewBlockTxHandler(result, e interface{}) bool {
if e != nil {
if v, ok := e.(map[string]interface{}); ok {
if msg, ok := v["message"]; ok {
log.Errorf("Tx Handler: Error received from btcd: %s", msg)
return false
}
}
log.Errorf("Tx Handler: Error is non-nil but cannot be parsed.")
}
// TODO(jrick): btcd also sends the block hash in the reply. // TODO(jrick): btcd also sends the block hash in the reply.
// Do we want it saved as well? // Do we want it saved as well?
v, ok := result.(map[string]interface{}) v, ok := result.(map[string]interface{})
if !ok { if !ok {
log.Error("Tx Handler: Unexpected result type.") // The first result sent from btcd is nil. This could be used to
// indicate that the request for notifications succeeded.
if result != nil {
log.Errorf("Tx Handler: Unexpected result type %T.", result)
}
return false return false
} }
sender58, ok := v["sender"].(string) sender58, ok := v["sender"].(string)

View file

@ -245,6 +245,7 @@ func GetBalance(reply chan []byte, msg []byte) {
json.Unmarshal(msg, &v) json.Unmarshal(msg, &v)
params := v["params"].([]interface{}) params := v["params"].([]interface{})
var wname string var wname string
conf := 1
if len(params) > 0 { if len(params) > 0 {
if s, ok := params[0].(string); ok { if s, ok := params[0].(string); ok {
wname = s wname = s
@ -252,13 +253,20 @@ func GetBalance(reply chan []byte, msg []byte) {
ReplyError(reply, v["id"], &InvalidParams) ReplyError(reply, v["id"], &InvalidParams)
} }
} }
if len(params) > 1 {
if f, ok := params[1].(float64); ok {
conf = int(f)
} else {
ReplyError(reply, v["id"], &InvalidParams)
}
}
wallets.RLock() wallets.RLock()
w := wallets.m[wname] w := wallets.m[wname]
wallets.RUnlock() wallets.RUnlock()
var result interface{} var result interface{}
if w != nil { if w != nil {
result = 0 // TODO(jrick) result = w.CalculateBalance(conf)
ReplySuccess(reply, v["id"], result) ReplySuccess(reply, v["id"], result)
} else { } else {
e := WalletInvalidAccountName e := WalletInvalidAccountName

View file

@ -33,7 +33,8 @@ var (
// Channel to close to notify that connection to btcd has been lost. // Channel to close to notify that connection to btcd has been lost.
btcdDisconnected = make(chan int) btcdDisconnected = make(chan int)
// Channel to send messages btcwallet does not understand to btcd. // Channel to send messages btcwallet does not understand and requests
// from btcwallet to btcd.
btcdMsgs = make(chan []byte, 100) btcdMsgs = make(chan []byte, 100)
// Adds a frontend listener channel // Adds a frontend listener channel
@ -51,9 +52,9 @@ var (
// handler function to route the reply to. // handler function to route the reply to.
replyHandlers = struct { replyHandlers = struct {
sync.Mutex sync.Mutex
m map[uint64]func(interface{}) bool m map[uint64]func(interface{}, interface{}) bool
}{ }{
m: make(map[uint64]func(interface{}) bool), m: make(map[uint64]func(interface{}, interface{}) bool),
} }
) )
@ -231,7 +232,7 @@ func ProcessBtcdNotificationReply(b []byte) {
replyHandlers.Unlock() replyHandlers.Unlock()
if f != nil { if f != nil {
go func() { go func() {
if f(m["result"]) { if f(m["result"], m["error"]) {
replyHandlers.Lock() replyHandlers.Lock()
delete(replyHandlers.m, routeId) delete(replyHandlers.m, routeId)
replyHandlers.Unlock() replyHandlers.Unlock()