Begin sending notifications based on all accounts.
We need to notify frontends of notifications for every account (wallet), not just the "current" opened account, since wallet will need to have multiple wallets open at the same time. Frontends will have to filter notifications to show only details of only one account if they need to display just one account at a time. As of this commit, account balances and lock state notifications are using this per-account notification scheme.
This commit is contained in:
parent
94d4bd28ae
commit
1ecc74c37d
3 changed files with 115 additions and 8 deletions
8
cmd.go
8
cmd.go
|
@ -60,6 +60,7 @@ var (
|
||||||
type BtcWallet struct {
|
type BtcWallet struct {
|
||||||
*wallet.Wallet
|
*wallet.Wallet
|
||||||
mtx sync.RWMutex
|
mtx sync.RWMutex
|
||||||
|
name string
|
||||||
dirty bool
|
dirty bool
|
||||||
NewBlockTxSeqN uint64
|
NewBlockTxSeqN uint64
|
||||||
UtxoStore struct {
|
UtxoStore struct {
|
||||||
|
@ -99,7 +100,6 @@ func (s *BtcWalletStore) Rollback(height int64, hash *btcwire.ShaHash) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *BtcWallet) Rollback(height int64, hash *btcwire.ShaHash) {
|
func (w *BtcWallet) Rollback(height int64, hash *btcwire.ShaHash) {
|
||||||
// TODO(jrick): set dirty=true if modified.
|
|
||||||
w.UtxoStore.Lock()
|
w.UtxoStore.Lock()
|
||||||
w.UtxoStore.dirty = w.UtxoStore.dirty || w.UtxoStore.s.Rollback(height, hash)
|
w.UtxoStore.dirty = w.UtxoStore.dirty || w.UtxoStore.s.Rollback(height, hash)
|
||||||
w.UtxoStore.Unlock()
|
w.UtxoStore.Unlock()
|
||||||
|
@ -193,6 +193,8 @@ func OpenWallet(cfg *config, account string) (*BtcWallet, error) {
|
||||||
|
|
||||||
w := &BtcWallet{
|
w := &BtcWallet{
|
||||||
Wallet: wlt,
|
Wallet: wlt,
|
||||||
|
name: account,
|
||||||
|
//NewBlockTxSeqN: // TODO(jrick): this MUST be set or notifications will be lost.
|
||||||
}
|
}
|
||||||
w.UtxoStore.s = utxos
|
w.UtxoStore.s = utxos
|
||||||
w.TxStore.s = txs
|
w.TxStore.s = txs
|
||||||
|
@ -462,6 +464,10 @@ func (w *BtcWallet) newBlockTxHandler(result interface{}, e *btcjson.Error) bool
|
||||||
w.UtxoStore.s = append(w.UtxoStore.s, u)
|
w.UtxoStore.s = append(w.UtxoStore.s, u)
|
||||||
w.UtxoStore.dirty = true
|
w.UtxoStore.dirty = true
|
||||||
w.UtxoStore.Unlock()
|
w.UtxoStore.Unlock()
|
||||||
|
confirmed := w.CalculateBalance(6)
|
||||||
|
unconfirmed := w.CalculateBalance(0) - confirmed
|
||||||
|
NotifyWalletBalance(frontendNotificationMaster, w.name, confirmed)
|
||||||
|
NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, w.name, unconfirmed)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
88
cmdmgr.go
88
cmdmgr.go
|
@ -165,6 +165,8 @@ func ProcessFrontendMsg(reply chan []byte, msg []byte) {
|
||||||
GetBalance(reply, &jsonMsg)
|
GetBalance(reply, &jsonMsg)
|
||||||
case "getnewaddress":
|
case "getnewaddress":
|
||||||
GetNewAddress(reply, &jsonMsg)
|
GetNewAddress(reply, &jsonMsg)
|
||||||
|
case "listaccounts":
|
||||||
|
ListAccounts(reply, &jsonMsg)
|
||||||
case "sendfrom":
|
case "sendfrom":
|
||||||
SendFrom(reply, &jsonMsg)
|
SendFrom(reply, &jsonMsg)
|
||||||
case "sendmany":
|
case "sendmany":
|
||||||
|
@ -179,6 +181,8 @@ func ProcessFrontendMsg(reply chan []byte, msg []byte) {
|
||||||
// btcwallet extensions
|
// btcwallet extensions
|
||||||
case "createencryptedwallet":
|
case "createencryptedwallet":
|
||||||
CreateEncryptedWallet(reply, &jsonMsg)
|
CreateEncryptedWallet(reply, &jsonMsg)
|
||||||
|
case "getbalances":
|
||||||
|
GetBalances(reply, &jsonMsg)
|
||||||
case "walletislocked":
|
case "walletislocked":
|
||||||
WalletIsLocked(reply, &jsonMsg)
|
WalletIsLocked(reply, &jsonMsg)
|
||||||
case "btcdconnected":
|
case "btcdconnected":
|
||||||
|
@ -306,6 +310,17 @@ func GetBalance(reply chan []byte, msg *btcjson.Message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBalances(reply chan []byte, msg *btcjson.Message) {
|
||||||
|
wallets.RLock()
|
||||||
|
for _, w := range wallets.m {
|
||||||
|
balance := w.CalculateBalance(6)
|
||||||
|
unconfirmed := w.CalculateBalance(0) - balance
|
||||||
|
NotifyWalletBalance(reply, w.name, balance)
|
||||||
|
NotifyWalletBalanceUnconfirmed(reply, w.name, unconfirmed)
|
||||||
|
}
|
||||||
|
wallets.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
// GetNewAddress gets or generates a new address for an account. If
|
// GetNewAddress gets or generates a new address for an account. If
|
||||||
// the requested wallet does not exist, a JSON error will be returned to
|
// the requested wallet does not exist, a JSON error will be returned to
|
||||||
// the client.
|
// the client.
|
||||||
|
@ -347,6 +362,33 @@ func GetNewAddress(reply chan []byte, msg *btcjson.Message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListAccounts returns a JSON object filled with account names as
|
||||||
|
// keys and their balances as values.
|
||||||
|
func ListAccounts(reply chan []byte, msg *btcjson.Message) {
|
||||||
|
minconf := 1
|
||||||
|
e := InvalidParams
|
||||||
|
params, ok := msg.Params.([]interface{})
|
||||||
|
if ok && len(params) != 0 {
|
||||||
|
fnum, ok := params[0].(float64)
|
||||||
|
if !ok {
|
||||||
|
e.Message = "minconf is not a number"
|
||||||
|
ReplyError(reply, msg.Id, &e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
minconf = int(fnum)
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs := make(map[string]float64)
|
||||||
|
|
||||||
|
wallets.RLock()
|
||||||
|
for account, w := range wallets.m {
|
||||||
|
pairs[account] = w.CalculateBalance(minconf)
|
||||||
|
}
|
||||||
|
wallets.RUnlock()
|
||||||
|
|
||||||
|
ReplySuccess(reply, msg.Id, pairs)
|
||||||
|
}
|
||||||
|
|
||||||
// SendFrom creates a new transaction spending unspent transaction
|
// SendFrom creates a new transaction spending unspent transaction
|
||||||
// outputs for a wallet to another payment address. Leftover inputs
|
// outputs for a wallet to another payment address. Leftover inputs
|
||||||
// not sent to the payment address or a fee for the miner are sent
|
// not sent to the payment address or a fee for the miner are sent
|
||||||
|
@ -694,6 +736,7 @@ func CreateEncryptedWallet(reply chan []byte, msg *btcjson.Message) {
|
||||||
|
|
||||||
bw := &BtcWallet{
|
bw := &BtcWallet{
|
||||||
Wallet: w,
|
Wallet: w,
|
||||||
|
name: wname,
|
||||||
NewBlockTxSeqN: n,
|
NewBlockTxSeqN: n,
|
||||||
}
|
}
|
||||||
// TODO(jrick): only begin tracking wallet if btcwallet is already
|
// TODO(jrick): only begin tracking wallet if btcwallet is already
|
||||||
|
@ -745,7 +788,7 @@ func WalletLock(reply chan []byte, msg *btcjson.Message) {
|
||||||
ReplyError(reply, msg.Id, &WalletWrongEncState)
|
ReplyError(reply, msg.Id, &WalletWrongEncState)
|
||||||
} else {
|
} else {
|
||||||
ReplySuccess(reply, msg.Id, nil)
|
ReplySuccess(reply, msg.Id, nil)
|
||||||
NotifyWalletLockStateChange(reply, true)
|
NotifyWalletLockStateChange("", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -780,11 +823,11 @@ func WalletPassphrase(reply chan []byte, msg *btcjson.Message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ReplySuccess(reply, msg.Id, nil)
|
ReplySuccess(reply, msg.Id, nil)
|
||||||
NotifyWalletLockStateChange(reply, false)
|
NotifyWalletLockStateChange("", false)
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Second * time.Duration(int64(timeout)))
|
time.Sleep(time.Second * time.Duration(int64(timeout)))
|
||||||
w.Lock()
|
w.Lock()
|
||||||
NotifyWalletLockStateChange(reply, true)
|
NotifyWalletLockStateChange("", true)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -796,14 +839,49 @@ func BtcdConnected(reply chan []byte, msg *btcjson.Message) {
|
||||||
ReplySuccess(reply, msg.Id, btcdConnected.b)
|
ReplySuccess(reply, msg.Id, btcdConnected.b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(jrick): move somewhere better so it can be shared with frontends.
|
||||||
|
type AccountNtfn struct {
|
||||||
|
Account string `json:"account"`
|
||||||
|
Notification interface{} `json:"notification"`
|
||||||
|
}
|
||||||
|
|
||||||
// NotifyWalletLockStateChange sends a notification to all frontends
|
// NotifyWalletLockStateChange sends a notification to all frontends
|
||||||
// that the wallet has just been locked or unlocked.
|
// that the wallet has just been locked or unlocked.
|
||||||
func NotifyWalletLockStateChange(reply chan []byte, locked bool) {
|
func NotifyWalletLockStateChange(account string, locked bool) {
|
||||||
var id interface{} = "btcwallet:newwalletlockstate"
|
var id interface{} = "btcwallet:newwalletlockstate"
|
||||||
m := btcjson.Reply{
|
m := btcjson.Reply{
|
||||||
Result: locked,
|
Result: &AccountNtfn{
|
||||||
|
Account: account,
|
||||||
|
Notification: locked,
|
||||||
|
},
|
||||||
Id: &id,
|
Id: &id,
|
||||||
}
|
}
|
||||||
msg, _ := json.Marshal(&m)
|
msg, _ := json.Marshal(&m)
|
||||||
frontendNotificationMaster <- msg
|
frontendNotificationMaster <- msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NotifyWalletBalance(frontend chan []byte, account string, balance float64) {
|
||||||
|
var id interface{} = "btcwallet:accountbalance"
|
||||||
|
m := btcjson.Reply{
|
||||||
|
Result: &AccountNtfn{
|
||||||
|
Account: account,
|
||||||
|
Notification: balance,
|
||||||
|
},
|
||||||
|
Id: &id,
|
||||||
|
}
|
||||||
|
msg, _ := json.Marshal(&m)
|
||||||
|
frontend <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func NotifyWalletBalanceUnconfirmed(frontend chan []byte, account string, balance float64) {
|
||||||
|
var id interface{} = "btcwallet:accountbalanceunconfirmed"
|
||||||
|
m := btcjson.Reply{
|
||||||
|
Result: &AccountNtfn{
|
||||||
|
Account: account,
|
||||||
|
Notification: balance,
|
||||||
|
},
|
||||||
|
Id: &id,
|
||||||
|
}
|
||||||
|
msg, _ := json.Marshal(&m)
|
||||||
|
frontend <- msg
|
||||||
|
}
|
||||||
|
|
25
sockets.go
25
sockets.go
|
@ -93,6 +93,7 @@ func frontendListenerDuplicator() {
|
||||||
mtx.Lock()
|
mtx.Lock()
|
||||||
frontendListeners[c] = true
|
frontendListeners[c] = true
|
||||||
mtx.Unlock()
|
mtx.Unlock()
|
||||||
|
|
||||||
case c := <-deleteFrontendListener:
|
case c := <-deleteFrontendListener:
|
||||||
mtx.Lock()
|
mtx.Lock()
|
||||||
delete(frontendListeners, c)
|
delete(frontendListeners, c)
|
||||||
|
@ -109,7 +110,7 @@ func frontendListenerDuplicator() {
|
||||||
select {
|
select {
|
||||||
case conn := <-btcdConnected.c:
|
case conn := <-btcdConnected.c:
|
||||||
btcdConnected.b = conn
|
btcdConnected.b = conn
|
||||||
var idStr interface{} = "btcwallet:btcconnected"
|
var idStr interface{} = "btcwallet:btcdconnected"
|
||||||
r := btcjson.Reply{
|
r := btcjson.Reply{
|
||||||
Result: conn,
|
Result: conn,
|
||||||
Id: &idStr,
|
Id: &idStr,
|
||||||
|
@ -333,6 +334,10 @@ func NtfnBlockConnected(r interface{}) {
|
||||||
}
|
}
|
||||||
height := int64(heightf)
|
height := int64(heightf)
|
||||||
|
|
||||||
|
curHeight.Lock()
|
||||||
|
curHeight.h = height
|
||||||
|
curHeight.Unlock()
|
||||||
|
|
||||||
// TODO(jrick): update TxStore and UtxoStore with new hash
|
// TODO(jrick): update TxStore and UtxoStore with new hash
|
||||||
_ = hash
|
_ = hash
|
||||||
var id interface{} = "btcwallet:newblockchainheight"
|
var id interface{} = "btcwallet:newblockchainheight"
|
||||||
|
@ -346,6 +351,15 @@ func NtfnBlockConnected(r interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
frontendNotificationMaster <- msg
|
frontendNotificationMaster <- msg
|
||||||
|
|
||||||
|
wallets.RLock()
|
||||||
|
for _, w := range wallets.m {
|
||||||
|
confirmed := w.CalculateBalance(6)
|
||||||
|
unconfirmed := w.CalculateBalance(0) - confirmed
|
||||||
|
NotifyWalletBalance(frontendNotificationMaster, w.name, confirmed)
|
||||||
|
NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, w.name, unconfirmed)
|
||||||
|
}
|
||||||
|
wallets.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NtfnBlockDisconnected handles btcd notifications resulting from
|
// NtfnBlockDisconnected handles btcd notifications resulting from
|
||||||
|
@ -391,6 +405,15 @@ func NtfnBlockDisconnected(r interface{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
frontendNotificationMaster <- msg
|
frontendNotificationMaster <- msg
|
||||||
|
|
||||||
|
wallets.RLock()
|
||||||
|
for _, w := range wallets.m {
|
||||||
|
confirmed := w.CalculateBalance(6)
|
||||||
|
unconfirmed := w.CalculateBalance(0) - confirmed
|
||||||
|
NotifyWalletBalance(frontendNotificationMaster, w.name, confirmed)
|
||||||
|
NotifyWalletBalanceUnconfirmed(frontendNotificationMaster, w.name, unconfirmed)
|
||||||
|
}
|
||||||
|
wallets.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
var duplicateOnce sync.Once
|
var duplicateOnce sync.Once
|
||||||
|
|
Loading…
Reference in a new issue