Integrate wtxmgr package.
This commit is contained in:
parent
ee72c81a73
commit
56039deb94
11 changed files with 968 additions and 743 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
|
* Copyright (c) 2013-2015 Conformal Systems LLC <info@conformal.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -26,8 +26,8 @@ import (
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcrpcclient"
|
"github.com/btcsuite/btcrpcclient"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client represents a persistent client connection to a bitcoin RPC server
|
// Client represents a persistent client connection to a bitcoin RPC server
|
||||||
|
@ -159,18 +159,11 @@ type (
|
||||||
// BlockStamp was reorganized out of the best chain.
|
// BlockStamp was reorganized out of the best chain.
|
||||||
BlockDisconnected waddrmgr.BlockStamp
|
BlockDisconnected waddrmgr.BlockStamp
|
||||||
|
|
||||||
// RecvTx is a notification for a transaction which pays to a wallet
|
// RelevantTx is a notification for a transaction which spends wallet
|
||||||
// address.
|
// inputs or pays to a watched address.
|
||||||
RecvTx struct {
|
RelevantTx struct {
|
||||||
Tx *btcutil.Tx // Index is guaranteed to be set.
|
TxRecord *wtxmgr.TxRecord
|
||||||
Block *txstore.Block // nil if unmined
|
Block *wtxmgr.BlockMeta // nil if unmined
|
||||||
}
|
|
||||||
|
|
||||||
// RedeemingTx is a notification for a transaction which spends an
|
|
||||||
// output controlled by the wallet.
|
|
||||||
RedeemingTx struct {
|
|
||||||
Tx *btcutil.Tx // Index is guaranteed to be set.
|
|
||||||
Block *txstore.Block // nil if unmined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RescanProgress is a notification describing the current status
|
// RescanProgress is a notification describing the current status
|
||||||
|
@ -209,23 +202,25 @@ func (c *Client) BlockStamp() (*waddrmgr.BlockStamp, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseBlock parses a btcjson definition of the block a tx is mined it to the
|
// parseBlock parses a btcws definition of the block a tx is mined it to the
|
||||||
// Block structure of the txstore package, and the block index. This is done
|
// Block structure of the wtxmgr package, and the block index. This is done
|
||||||
// here since btcrpcclient doesn't parse this nicely for us.
|
// here since btcrpcclient doesn't parse this nicely for us.
|
||||||
func parseBlock(block *btcjson.BlockDetails) (blk *txstore.Block, idx int, err error) {
|
func parseBlock(block *btcjson.BlockDetails) (*wtxmgr.BlockMeta, error) {
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, btcutil.TxIndexUnknown, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
blksha, err := wire.NewShaHashFromStr(block.Hash)
|
blksha, err := wire.NewShaHashFromStr(block.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, btcutil.TxIndexUnknown, err
|
return nil, err
|
||||||
}
|
}
|
||||||
blk = &txstore.Block{
|
blk := &wtxmgr.BlockMeta{
|
||||||
|
Block: wtxmgr.Block{
|
||||||
Height: block.Height,
|
Height: block.Height,
|
||||||
Hash: *blksha,
|
Hash: *blksha,
|
||||||
|
},
|
||||||
Time: time.Unix(block.Time, 0),
|
Time: time.Unix(block.Time, 0),
|
||||||
}
|
}
|
||||||
return blk, block.Index, nil
|
return blk, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) onClientConnect() {
|
func (c *Client) onClientConnect() {
|
||||||
|
@ -242,35 +237,25 @@ func (c *Client) onBlockDisconnected(hash *wire.ShaHash, height int32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) onRecvTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
|
func (c *Client) onRecvTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
|
||||||
var blk *txstore.Block
|
blk, err := parseBlock(block)
|
||||||
index := btcutil.TxIndexUnknown
|
|
||||||
if block != nil {
|
|
||||||
var err error
|
|
||||||
blk, index, err = parseBlock(block)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Log and drop improper notification.
|
// Log and drop improper notification.
|
||||||
log.Errorf("recvtx notification bad block: %v", err)
|
log.Errorf("recvtx notification bad block: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rec, err := wtxmgr.NewTxRecordFromMsgTx(tx.MsgTx(), time.Now())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Cannot create transaction record for relevant "+
|
||||||
|
"tx: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
tx.SetIndex(index)
|
c.enqueueNotification <- RelevantTx{rec, blk}
|
||||||
c.enqueueNotification <- RecvTx{tx, blk}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) onRedeemingTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
|
func (c *Client) onRedeemingTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
|
||||||
var blk *txstore.Block
|
// Handled exactly like recvtx notifications.
|
||||||
index := btcutil.TxIndexUnknown
|
c.onRecvTx(tx, block)
|
||||||
if block != nil {
|
|
||||||
var err error
|
|
||||||
blk, index, err = parseBlock(block)
|
|
||||||
if err != nil {
|
|
||||||
// Log and drop improper notification.
|
|
||||||
log.Errorf("redeemingtx notification bad block: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tx.SetIndex(index)
|
|
||||||
c.enqueueNotification <- RedeemingTx{tx, blk}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) onRescanProgress(hash *wire.ShaHash, height int32, blkTime time.Time) {
|
func (c *Client) onRescanProgress(hash *wire.ShaHash, height int32, blkTime time.Time) {
|
||||||
|
|
43
disksync.go
43
disksync.go
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
|
|
||||||
*
|
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
|
||||||
* copyright notice and this permission notice appear in all copies.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// checkCreateDir checks that the path exists and is a directory.
|
|
||||||
// If path does not exist, it is created.
|
|
||||||
func checkCreateDir(path string) error {
|
|
||||||
if fi, err := os.Stat(path); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// Attempt data directory creation
|
|
||||||
if err = os.MkdirAll(path, 0700); err != nil {
|
|
||||||
return fmt.Errorf("cannot create directory: %s", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("error checking directory: %s", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !fi.IsDir() {
|
|
||||||
return fmt.Errorf("path '%s' is not a directory", path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
10
log.go
10
log.go
|
@ -22,8 +22,8 @@ import (
|
||||||
|
|
||||||
"github.com/btcsuite/btclog"
|
"github.com/btcsuite/btclog"
|
||||||
"github.com/btcsuite/btcwallet/chain"
|
"github.com/btcsuite/btcwallet/chain"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/wallet"
|
"github.com/btcsuite/btcwallet/wallet"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
"github.com/btcsuite/seelog"
|
"github.com/btcsuite/seelog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ var (
|
||||||
backendLog = seelog.Disabled
|
backendLog = seelog.Disabled
|
||||||
log = btclog.Disabled
|
log = btclog.Disabled
|
||||||
walletLog = btclog.Disabled
|
walletLog = btclog.Disabled
|
||||||
txstLog = btclog.Disabled
|
txmgrLog = btclog.Disabled
|
||||||
chainLog = btclog.Disabled
|
chainLog = btclog.Disabled
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ var (
|
||||||
var subsystemLoggers = map[string]btclog.Logger{
|
var subsystemLoggers = map[string]btclog.Logger{
|
||||||
"BTCW": log,
|
"BTCW": log,
|
||||||
"WLLT": walletLog,
|
"WLLT": walletLog,
|
||||||
"TXST": txstLog,
|
"TMGR": txmgrLog,
|
||||||
"CHNS": chainLog,
|
"CHNS": chainLog,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,8 +87,8 @@ func useLogger(subsystemID string, logger btclog.Logger) {
|
||||||
walletLog = logger
|
walletLog = logger
|
||||||
wallet.UseLogger(logger)
|
wallet.UseLogger(logger)
|
||||||
case "TXST":
|
case "TXST":
|
||||||
txstLog = logger
|
txmgrLog = logger
|
||||||
txstore.UseLogger(logger)
|
wtxmgr.UseLogger(logger)
|
||||||
case "CHNS":
|
case "CHNS":
|
||||||
chainLog = logger
|
chainLog = logger
|
||||||
chain.UseLogger(logger)
|
chain.UseLogger(logger)
|
||||||
|
|
352
rpcserver.go
352
rpcserver.go
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
|
* Copyright (c) 2013-2015 Conformal Systems LLC <info@conformal.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -45,9 +45,9 @@ import (
|
||||||
"github.com/btcsuite/btcrpcclient"
|
"github.com/btcsuite/btcrpcclient"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/chain"
|
"github.com/btcsuite/btcwallet/chain"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/wallet"
|
"github.com/btcsuite/btcwallet/wallet"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
"github.com/btcsuite/websocket"
|
"github.com/btcsuite/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -142,6 +142,24 @@ func checkDefaultAccount(account string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// confirmed checks whether a transaction at height txHeight has met minconf
|
||||||
|
// confirmations for a blockchain at height curHeight.
|
||||||
|
func confirmed(minconf, txHeight, curHeight int32) bool {
|
||||||
|
return confirms(txHeight, curHeight) >= minconf
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirms returns the number of confirmations for a transaction in a block at
|
||||||
|
// height txHeight (or -1 for an unconfirmed tx) given the chain height
|
||||||
|
// curHeight.
|
||||||
|
func confirms(txHeight, curHeight int32) int32 {
|
||||||
|
switch {
|
||||||
|
case txHeight == -1, txHeight > curHeight:
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return curHeight - txHeight + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type websocketClient struct {
|
type websocketClient struct {
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
authenticated bool
|
authenticated bool
|
||||||
|
@ -309,10 +327,7 @@ type rpcServer struct {
|
||||||
// created.
|
// created.
|
||||||
connectedBlocks <-chan waddrmgr.BlockStamp
|
connectedBlocks <-chan waddrmgr.BlockStamp
|
||||||
disconnectedBlocks <-chan waddrmgr.BlockStamp
|
disconnectedBlocks <-chan waddrmgr.BlockStamp
|
||||||
newCredits <-chan txstore.Credit
|
relevantTxs <-chan chain.RelevantTx
|
||||||
newDebits <-chan txstore.Debits
|
|
||||||
minedCredits <-chan txstore.Credit
|
|
||||||
minedDebits <-chan txstore.Debits
|
|
||||||
managerLocked <-chan bool
|
managerLocked <-chan bool
|
||||||
confirmedBalance <-chan btcutil.Amount
|
confirmedBalance <-chan btcutil.Amount
|
||||||
unconfirmedBalance <-chan btcutil.Amount
|
unconfirmedBalance <-chan btcutil.Amount
|
||||||
|
@ -1038,8 +1053,7 @@ type (
|
||||||
blockConnected waddrmgr.BlockStamp
|
blockConnected waddrmgr.BlockStamp
|
||||||
blockDisconnected waddrmgr.BlockStamp
|
blockDisconnected waddrmgr.BlockStamp
|
||||||
|
|
||||||
txCredit txstore.Credit
|
relevantTx chain.RelevantTx
|
||||||
txDebit txstore.Debits
|
|
||||||
|
|
||||||
managerLocked bool
|
managerLocked bool
|
||||||
|
|
||||||
|
@ -1059,36 +1073,30 @@ func (b blockDisconnected) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
||||||
return []btcjson.Cmd{n}
|
return []btcjson.Cmd{n}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c txCredit) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
func (t relevantTx) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
||||||
blk := w.Manager.SyncedTo()
|
syncBlock := w.Manager.SyncedTo()
|
||||||
acctName := waddrmgr.DefaultAccountName
|
|
||||||
if creditAccount, err := w.CreditAccount(txstore.Credit(c)); err == nil {
|
var block *wtxmgr.Block
|
||||||
// acctName is defaulted to DefaultAccountName in case of an error
|
if t.Block != nil {
|
||||||
acctName, _ = w.Manager.AccountName(creditAccount)
|
block = &t.Block.Block
|
||||||
}
|
}
|
||||||
ltr, err := txstore.Credit(c).ToJSON(acctName, blk.Height, activeNet.Params)
|
details, err := w.TxStore.UniqueTxDetails(&t.TxRecord.Hash, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Cannot create notification for transaction "+
|
log.Errorf("Cannot fetch transaction details for "+
|
||||||
"credit: %v", err)
|
"client notification: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
n := btcws.NewTxNtfn(acctName, <r)
|
if details == nil {
|
||||||
return []btcjson.Cmd{n}
|
log.Errorf("No details found for client transaction notification")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d txDebit) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
ltr := wallet.ListTransactions(details, syncBlock.Height, activeNet.Params)
|
||||||
blk := w.Manager.SyncedTo()
|
ntfns := make([]btcjson.Cmd, len(ltr))
|
||||||
ltrs, err := txstore.Debits(d).ToJSON("", blk.Height, activeNet.Params)
|
for i := range ntfns {
|
||||||
if err != nil {
|
ntfns[i] = btcws.NewTxNtfn(ltr[i].Account, <r[i])
|
||||||
log.Errorf("Cannot create notification for transaction "+
|
|
||||||
"debits: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
ns := make([]btcjson.Cmd, len(ltrs))
|
return ntfns
|
||||||
for i := range ns {
|
|
||||||
ns[i] = btcws.NewTxNtfn("", <rs[i])
|
|
||||||
}
|
|
||||||
return ns
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l managerLocked) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
func (l managerLocked) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
||||||
|
@ -1121,14 +1129,8 @@ out:
|
||||||
s.enqueueNotification <- blockConnected(n)
|
s.enqueueNotification <- blockConnected(n)
|
||||||
case n := <-s.disconnectedBlocks:
|
case n := <-s.disconnectedBlocks:
|
||||||
s.enqueueNotification <- blockDisconnected(n)
|
s.enqueueNotification <- blockDisconnected(n)
|
||||||
case n := <-s.newCredits:
|
case n := <-s.relevantTxs:
|
||||||
s.enqueueNotification <- txCredit(n)
|
s.enqueueNotification <- relevantTx(n)
|
||||||
case n := <-s.newDebits:
|
|
||||||
s.enqueueNotification <- txDebit(n)
|
|
||||||
case n := <-s.minedCredits:
|
|
||||||
s.enqueueNotification <- txCredit(n)
|
|
||||||
case n := <-s.minedDebits:
|
|
||||||
s.enqueueNotification <- txDebit(n)
|
|
||||||
case n := <-s.managerLocked:
|
case n := <-s.managerLocked:
|
||||||
s.enqueueNotification <- managerLocked(n)
|
s.enqueueNotification <- managerLocked(n)
|
||||||
case n := <-s.confirmedBalance:
|
case n := <-s.confirmedBalance:
|
||||||
|
@ -1153,28 +1155,10 @@ out:
|
||||||
err)
|
err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newCredits, err := s.wallet.TxStore.ListenNewCredits()
|
relevantTxs, err := s.wallet.ListenRelevantTxs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Could not register for new "+
|
log.Errorf("Could not register for new relevant "+
|
||||||
"credit notifications: %v", err)
|
"transaction notifications: %v", err)
|
||||||
continue
|
|
||||||
}
|
|
||||||
newDebits, err := s.wallet.TxStore.ListenNewDebits()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Could not register for new "+
|
|
||||||
"debit notifications: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
minedCredits, err := s.wallet.TxStore.ListenMinedCredits()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Could not register for mined "+
|
|
||||||
"credit notifications: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
minedDebits, err := s.wallet.TxStore.ListenMinedDebits()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Could not register for mined "+
|
|
||||||
"debit notifications: %v", err)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
managerLocked, err := s.wallet.ListenLockStatus()
|
managerLocked, err := s.wallet.ListenLockStatus()
|
||||||
|
@ -1197,10 +1181,7 @@ out:
|
||||||
}
|
}
|
||||||
s.connectedBlocks = connectedBlocks
|
s.connectedBlocks = connectedBlocks
|
||||||
s.disconnectedBlocks = disconnectedBlocks
|
s.disconnectedBlocks = disconnectedBlocks
|
||||||
s.newCredits = newCredits
|
s.relevantTxs = relevantTxs
|
||||||
s.newDebits = newDebits
|
|
||||||
s.minedCredits = minedCredits
|
|
||||||
s.minedDebits = minedDebits
|
|
||||||
s.managerLocked = managerLocked
|
s.managerLocked = managerLocked
|
||||||
s.confirmedBalance = confirmedBalance
|
s.confirmedBalance = confirmedBalance
|
||||||
s.unconfirmedBalance = unconfirmedBalance
|
s.unconfirmedBalance = unconfirmedBalance
|
||||||
|
@ -1219,10 +1200,8 @@ func (s *rpcServer) drainNotifications() {
|
||||||
select {
|
select {
|
||||||
case <-s.connectedBlocks:
|
case <-s.connectedBlocks:
|
||||||
case <-s.disconnectedBlocks:
|
case <-s.disconnectedBlocks:
|
||||||
case <-s.newCredits:
|
case <-s.relevantTxs:
|
||||||
case <-s.newDebits:
|
case <-s.managerLocked:
|
||||||
case <-s.minedCredits:
|
|
||||||
case <-s.minedDebits:
|
|
||||||
case <-s.confirmedBalance:
|
case <-s.confirmedBalance:
|
||||||
case <-s.unconfirmedBalance:
|
case <-s.unconfirmedBalance:
|
||||||
case <-s.registerWalletNtfns:
|
case <-s.registerWalletNtfns:
|
||||||
|
@ -1694,13 +1673,13 @@ func GetBalance(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (int
|
||||||
var account uint32
|
var account uint32
|
||||||
var err error
|
var err error
|
||||||
if cmd.Account == nil || *cmd.Account == "*" {
|
if cmd.Account == nil || *cmd.Account == "*" {
|
||||||
balance, err = w.CalculateBalance(cmd.MinConf)
|
balance, err = w.CalculateBalance(int32(cmd.MinConf))
|
||||||
} else {
|
} else {
|
||||||
account, err = w.Manager.LookupAccount(*cmd.Account)
|
account, err = w.Manager.LookupAccount(*cmd.Account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
balance, err = w.CalculateAccountBalance(account, cmd.MinConf)
|
balance, err = w.CalculateAccountBalance(account, int32(cmd.MinConf))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1964,7 +1943,7 @@ func GetReceivedByAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bal, _, err := w.TotalReceivedForAccount(account, cmd.MinConf)
|
bal, _, err := w.TotalReceivedForAccount(account, int32(cmd.MinConf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1981,7 +1960,7 @@ func GetReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, InvalidAddressOrKeyError{err}
|
return nil, InvalidAddressOrKeyError{err}
|
||||||
}
|
}
|
||||||
total, err := w.TotalReceivedForAddr(addr, cmd.MinConf)
|
total, err := w.TotalReceivedForAddr(addr, int32(cmd.MinConf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1999,96 +1978,105 @@ func GetTransaction(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
|
||||||
return nil, btcjson.ErrDecodeHexString
|
return nil, btcjson.ErrDecodeHexString
|
||||||
}
|
}
|
||||||
|
|
||||||
record, ok := w.TxRecord(txSha)
|
details, err := w.TxStore.TxDetails(txSha)
|
||||||
if !ok {
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if details == nil {
|
||||||
return nil, btcjson.ErrNoTxInfo
|
return nil, btcjson.ErrNoTxInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
blk := w.Manager.SyncedTo()
|
syncBlock := w.Manager.SyncedTo()
|
||||||
|
|
||||||
|
// TODO: The serialized transaction is already in the DB, so
|
||||||
|
// reserializing can be avoided here.
|
||||||
var txBuf bytes.Buffer
|
var txBuf bytes.Buffer
|
||||||
txBuf.Grow(record.Tx().MsgTx().SerializeSize())
|
txBuf.Grow(details.MsgTx.SerializeSize())
|
||||||
err = record.Tx().MsgTx().Serialize(&txBuf)
|
err = details.MsgTx.Serialize(&txBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jrick) set "generate" to true if this is the coinbase (if
|
// TODO: Add a "generated" field to this result type. "generated":true
|
||||||
// record.Tx().Index() == 0).
|
// is only added if the transaction is a coinbase.
|
||||||
ret := btcjson.GetTransactionResult{
|
ret := btcjson.GetTransactionResult{
|
||||||
TxID: txSha.String(),
|
TxID: cmd.Txid,
|
||||||
Hex: hex.EncodeToString(txBuf.Bytes()),
|
Hex: hex.EncodeToString(txBuf.Bytes()),
|
||||||
Time: record.Received().Unix(),
|
Time: details.Received.Unix(),
|
||||||
TimeReceived: record.Received().Unix(),
|
TimeReceived: details.Received.Unix(),
|
||||||
WalletConflicts: []string{},
|
WalletConflicts: []string{}, // Not saved
|
||||||
|
//Generated: blockchain.IsCoinBaseTx(&details.MsgTx),
|
||||||
}
|
}
|
||||||
|
|
||||||
if record.BlockHeight != -1 {
|
if details.Block.Height != -1 {
|
||||||
txBlock, err := record.Block()
|
ret.BlockHash = details.Block.Hash.String()
|
||||||
if err != nil {
|
ret.BlockTime = details.Block.Time.Unix()
|
||||||
return nil, err
|
ret.Confirmations = int64(confirms(details.Block.Height, syncBlock.Height))
|
||||||
}
|
|
||||||
ret.BlockIndex = int64(record.Tx().Index())
|
|
||||||
ret.BlockHash = txBlock.Hash.String()
|
|
||||||
ret.BlockTime = txBlock.Time.Unix()
|
|
||||||
ret.Confirmations = int64(record.Confirmations(blk.Height))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
credits := record.Credits()
|
var (
|
||||||
debits, err := record.Debits()
|
debitTotal btcutil.Amount
|
||||||
var targetAddr *string
|
creditTotal btcutil.Amount // Excludes change
|
||||||
var creditAmount btcutil.Amount
|
outputTotal btcutil.Amount
|
||||||
if err != nil {
|
fee btcutil.Amount
|
||||||
|
feeF64 float64
|
||||||
|
)
|
||||||
|
for _, deb := range details.Debits {
|
||||||
|
debitTotal += deb.Amount
|
||||||
|
}
|
||||||
|
for _, cred := range details.Credits {
|
||||||
|
if !cred.Change {
|
||||||
|
creditTotal += cred.Amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, output := range details.MsgTx.TxOut {
|
||||||
|
outputTotal -= btcutil.Amount(output.Value)
|
||||||
|
}
|
||||||
|
// Fee can only be determined if every input is a debit.
|
||||||
|
if len(details.Debits) == len(details.MsgTx.TxIn) {
|
||||||
|
fee = debitTotal - outputTotal
|
||||||
|
feeF64 = fee.ToBTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(details.Debits) == 0 {
|
||||||
// Credits must be set later, but since we know the full length
|
// Credits must be set later, but since we know the full length
|
||||||
// of the details slice, allocate it with the correct cap.
|
// of the details slice, allocate it with the correct cap.
|
||||||
ret.Details = make([]btcjson.GetTransactionDetailsResult, 0, len(credits))
|
ret.Details = make([]btcjson.GetTransactionDetailsResult, 0, len(details.Credits))
|
||||||
} else {
|
} else {
|
||||||
ret.Details = make([]btcjson.GetTransactionDetailsResult, 1, len(credits)+1)
|
ret.Details = make([]btcjson.GetTransactionDetailsResult, 1, len(details.Credits)+1)
|
||||||
|
|
||||||
details := btcjson.GetTransactionDetailsResult{
|
ret.Details[0] = btcjson.GetTransactionDetailsResult{
|
||||||
Account: waddrmgr.DefaultAccountName,
|
Account: waddrmgr.DefaultAccountName,
|
||||||
Category: "send",
|
Category: "send",
|
||||||
// negative since it is a send
|
Amount: (-debitTotal).ToBTC(), // negative since it is a send
|
||||||
Amount: (-debits.OutputAmount(true)).ToBTC(),
|
Fee: feeF64,
|
||||||
Fee: debits.Fee().ToBTC(),
|
|
||||||
}
|
}
|
||||||
targetAddr = &details.Address
|
ret.Fee = feeF64
|
||||||
ret.Details[0] = details
|
|
||||||
ret.Fee = details.Fee
|
|
||||||
|
|
||||||
creditAmount = -debits.InputAmount()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cred := range record.Credits() {
|
credCat := wallet.RecvCategory(details, syncBlock.Height).String()
|
||||||
|
for _, cred := range details.Credits {
|
||||||
// Change is ignored.
|
// Change is ignored.
|
||||||
if cred.Change() {
|
if cred.Change {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
creditAmount += cred.Amount()
|
|
||||||
|
|
||||||
var addr string
|
var addr string
|
||||||
// Errors don't matter here, as we only consider the
|
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
|
||||||
// case where len(addrs) == 1.
|
details.MsgTx.TxOut[cred.Index].PkScript, activeNet.Params)
|
||||||
_, addrs, _, _ := cred.Addresses(activeNet.Params)
|
if err == nil && len(addrs) == 1 {
|
||||||
if len(addrs) == 1 {
|
|
||||||
addr = addrs[0].EncodeAddress()
|
addr = addrs[0].EncodeAddress()
|
||||||
// The first non-change output address is considered the
|
|
||||||
// target for sent transactions.
|
|
||||||
if targetAddr != nil && *targetAddr == "" {
|
|
||||||
*targetAddr = addr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Details = append(ret.Details, btcjson.GetTransactionDetailsResult{
|
ret.Details = append(ret.Details, btcjson.GetTransactionDetailsResult{
|
||||||
Account: waddrmgr.DefaultAccountName,
|
Account: waddrmgr.DefaultAccountName,
|
||||||
Category: cred.Category(blk.Height).String(),
|
Category: credCat,
|
||||||
Amount: cred.Amount().ToBTC(),
|
Amount: cred.Amount.ToBTC(),
|
||||||
Address: addr,
|
Address: addr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Amount = creditAmount.ToBTC()
|
ret.Amount = creditTotal.ToBTC()
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2107,7 +2095,7 @@ func ListAccounts(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (i
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrAccountNameNotFound
|
return nil, ErrAccountNameNotFound
|
||||||
}
|
}
|
||||||
bal, err := w.CalculateAccountBalance(account, cmd.MinConf)
|
bal, err := w.CalculateAccountBalance(account, int32(cmd.MinConf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2147,7 +2135,8 @@ func ListReceivedByAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjso
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ErrAccountNameNotFound
|
return nil, ErrAccountNameNotFound
|
||||||
}
|
}
|
||||||
bal, confirmations, err := w.TotalReceivedForAccount(account, cmd.MinConf)
|
bal, confirmations, err := w.TotalReceivedForAccount(account,
|
||||||
|
int32(cmd.MinConf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2186,7 +2175,7 @@ func ListReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjso
|
||||||
account string
|
account string
|
||||||
}
|
}
|
||||||
|
|
||||||
blk := w.Manager.SyncedTo()
|
syncBlock := w.Manager.SyncedTo()
|
||||||
|
|
||||||
// Intermediate data for all addresses.
|
// Intermediate data for all addresses.
|
||||||
allAddrData := make(map[string]AddrData)
|
allAddrData := make(map[string]AddrData)
|
||||||
|
@ -2202,36 +2191,47 @@ func ListReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjso
|
||||||
allAddrData[address] = AddrData{}
|
allAddrData[address] = AddrData{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, record := range w.TxStore.Records() {
|
|
||||||
for _, credit := range record.Credits() {
|
var endHeight int32
|
||||||
confirmations := credit.Confirmations(blk.Height)
|
if cmd.MinConf == -1 {
|
||||||
if !credit.Confirmed(cmd.MinConf, blk.Height) {
|
endHeight = -1
|
||||||
// Not enough confirmations, skip the current block.
|
} else {
|
||||||
continue
|
endHeight = syncBlock.Height - int32(cmd.MinConf) + 1
|
||||||
}
|
}
|
||||||
_, addresses, _, err := credit.Addresses(activeNet.Params)
|
err := w.TxStore.RangeTransactions(0, endHeight, func(details []wtxmgr.TxDetails) (bool, error) {
|
||||||
|
confirmations := confirms(details[0].Block.Height, syncBlock.Height)
|
||||||
|
for _, tx := range details {
|
||||||
|
for _, cred := range tx.Credits {
|
||||||
|
pkScript := tx.MsgTx.TxOut[cred.Index].PkScript
|
||||||
|
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
|
||||||
|
pkScript, activeNet.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Unusable address, skip it.
|
// Non standard script, skip.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, address := range addresses {
|
for _, addr := range addrs {
|
||||||
addrStr := address.EncodeAddress()
|
addrStr := addr.EncodeAddress()
|
||||||
addrData, ok := allAddrData[addrStr]
|
addrData, ok := allAddrData[addrStr]
|
||||||
if ok {
|
if ok {
|
||||||
addrData.amount += credit.Amount()
|
addrData.amount += cred.Amount
|
||||||
// Always overwrite confirmations with newer ones.
|
// Always overwrite confirmations with newer ones.
|
||||||
addrData.confirmations = confirmations
|
addrData.confirmations = confirmations
|
||||||
} else {
|
} else {
|
||||||
addrData = AddrData{
|
addrData = AddrData{
|
||||||
amount: credit.Amount(),
|
amount: cred.Amount,
|
||||||
confirmations: confirmations,
|
confirmations: confirmations,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addrData.tx = append(addrData.tx, credit.Tx().Sha().String())
|
addrData.tx = append(addrData.tx, tx.Hash.String())
|
||||||
allAddrData[addrStr] = addrData
|
allAddrData[addrStr] = addrData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Massage address data into output format.
|
// Massage address data into output format.
|
||||||
numAddresses := len(allAddrData)
|
numAddresses := len(allAddrData)
|
||||||
|
@ -2255,7 +2255,14 @@ func ListReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjso
|
||||||
func ListSinceBlock(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
|
func ListSinceBlock(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (interface{}, error) {
|
||||||
cmd := icmd.(*btcjson.ListSinceBlockCmd)
|
cmd := icmd.(*btcjson.ListSinceBlockCmd)
|
||||||
|
|
||||||
height := int32(-1)
|
syncBlock := w.Manager.SyncedTo()
|
||||||
|
|
||||||
|
// For the result we need the block hash for the last block counted
|
||||||
|
// in the blockchain due to confirmations. We send this off now so that
|
||||||
|
// it can arrive asynchronously while we figure out the rest.
|
||||||
|
gbh := chainSvr.GetBlockHashAsync(int64(syncBlock.Height) + 1 - int64(cmd.TargetConfirmations))
|
||||||
|
|
||||||
|
var start int32
|
||||||
if cmd.BlockHash != "" {
|
if cmd.BlockHash != "" {
|
||||||
hash, err := wire.NewShaHashFromStr(cmd.BlockHash)
|
hash, err := wire.NewShaHashFromStr(cmd.BlockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2265,18 +2272,11 @@ func ListSinceBlock(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
height = int32(block.Height())
|
start = int32(block.Height()) + 1
|
||||||
}
|
}
|
||||||
|
end := syncBlock.Height - int32(cmd.TargetConfirmations) + 1
|
||||||
|
|
||||||
blk := w.Manager.SyncedTo()
|
txInfoList, err := w.ListSinceBlock(start, end, syncBlock.Height)
|
||||||
|
|
||||||
// For the result we need the block hash for the last block counted
|
|
||||||
// in the blockchain due to confirmations. We send this off now so that
|
|
||||||
// it can arrive asynchronously while we figure out the rest.
|
|
||||||
gbh := chainSvr.GetBlockHashAsync(int64(blk.Height) + 1 - int64(cmd.TargetConfirmations))
|
|
||||||
|
|
||||||
txInfoList, err := w.ListSinceBlock(height, blk.Height,
|
|
||||||
cmd.TargetConfirmations)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2377,7 +2377,7 @@ func ListUnspent(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (in
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.ListUnspent(cmd.MinConf, cmd.MaxConf, addresses)
|
return w.ListUnspent(int32(cmd.MinConf), int32(cmd.MaxConf), addresses)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LockUnspent handles the lockunspent command.
|
// LockUnspent handles the lockunspent command.
|
||||||
|
@ -2405,9 +2405,10 @@ func LockUnspent(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (in
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendPairs is a helper routine to reduce duplicated code when creating and
|
// sendPairs is a helper routine to reduce duplicated code when creating and
|
||||||
// sending payment transactions.
|
// sending payment transactions. It returns the transaction hash in string
|
||||||
|
// format upon success.
|
||||||
func sendPairs(w *wallet.Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
|
func sendPairs(w *wallet.Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
|
||||||
amounts map[string]btcutil.Amount, account uint32, minconf int) (interface{}, error) {
|
amounts map[string]btcutil.Amount, account uint32, minconf int32) (string, error) {
|
||||||
|
|
||||||
// Create transaction, replying with an error if the creation
|
// Create transaction, replying with an error if the creation
|
||||||
// was not successful.
|
// was not successful.
|
||||||
|
@ -2415,41 +2416,44 @@ func sendPairs(w *wallet.Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch {
|
switch {
|
||||||
case err == wallet.ErrNonPositiveAmount:
|
case err == wallet.ErrNonPositiveAmount:
|
||||||
return nil, ErrNeedPositiveAmount
|
return "", ErrNeedPositiveAmount
|
||||||
case isManagerLockedError(err):
|
case isManagerLockedError(err):
|
||||||
return nil, btcjson.ErrWalletUnlockNeeded
|
return "", btcjson.ErrWalletUnlockNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to transaction store.
|
// Create transaction record and insert into the db.
|
||||||
txr, err := w.TxStore.InsertTx(createdTx.Tx, nil)
|
rec, err := wtxmgr.NewTxRecordFromMsgTx(createdTx.MsgTx, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Cannot create record for created transaction: %v", err)
|
||||||
|
return "", btcjson.ErrInternal
|
||||||
|
}
|
||||||
|
err = w.TxStore.InsertTx(rec, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error adding sent tx history: %v", err)
|
log.Errorf("Error adding sent tx history: %v", err)
|
||||||
return nil, btcjson.ErrInternal
|
return "", btcjson.ErrInternal
|
||||||
}
|
|
||||||
_, err = txr.AddDebits()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error adding sent tx history: %v", err)
|
|
||||||
return nil, btcjson.ErrInternal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if createdTx.ChangeIndex >= 0 {
|
if createdTx.ChangeIndex >= 0 {
|
||||||
_, err = txr.AddCredit(uint32(createdTx.ChangeIndex), true)
|
err = w.TxStore.AddCredit(rec, nil, uint32(createdTx.ChangeIndex), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error adding change address for sent "+
|
log.Errorf("Error adding change address for sent "+
|
||||||
"tx: %v", err)
|
"tx: %v", err)
|
||||||
return nil, btcjson.ErrInternal
|
return "", btcjson.ErrInternal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.TxStore.MarkDirty()
|
|
||||||
|
|
||||||
txSha, err := chainSvr.SendRawTransaction(createdTx.Tx.MsgTx(), false)
|
// TODO: The record already has the serialized tx, so no need to
|
||||||
|
// serialize it again.
|
||||||
|
txSha, err := chainSvr.SendRawTransaction(&rec.MsgTx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
log.Infof("Successfully sent transaction %v", txSha)
|
txShaStr := txSha.String()
|
||||||
return txSha.String(), nil
|
log.Infof("Successfully sent transaction %v", txShaStr)
|
||||||
|
return txShaStr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendFrom handles a sendfrom RPC request by creating a new transaction
|
// SendFrom handles a sendfrom RPC request by creating a new transaction
|
||||||
|
@ -2477,7 +2481,7 @@ func SendFrom(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inter
|
||||||
cmd.ToAddress: btcutil.Amount(cmd.Amount),
|
cmd.ToAddress: btcutil.Amount(cmd.Amount),
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendPairs(w, chainSvr, cmd, pairs, account, cmd.MinConf)
|
return sendPairs(w, chainSvr, cmd, pairs, account, int32(cmd.MinConf))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMany handles a sendmany RPC request by creating a new transaction
|
// SendMany handles a sendmany RPC request by creating a new transaction
|
||||||
|
@ -2504,7 +2508,7 @@ func SendMany(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inter
|
||||||
pairs[k] = btcutil.Amount(v)
|
pairs[k] = btcutil.Amount(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendPairs(w, chainSvr, cmd, pairs, account, cmd.MinConf)
|
return sendPairs(w, chainSvr, cmd, pairs, account, int32(cmd.MinConf))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendToAddress handles a sendtoaddress RPC request by creating a new
|
// SendToAddress handles a sendtoaddress RPC request by creating a new
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
|
* Copyright (c) 2013-2015 Conformal Systems LLC <info@conformal.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -18,10 +18,9 @@ package wallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcutil"
|
|
||||||
"github.com/btcsuite/btcwallet/chain"
|
"github.com/btcsuite/btcwallet/chain"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (w *Wallet) handleChainNotifications() {
|
func (w *Wallet) handleChainNotifications() {
|
||||||
|
@ -45,10 +44,8 @@ func (w *Wallet) handleChainNotifications() {
|
||||||
w.connectBlock(waddrmgr.BlockStamp(n))
|
w.connectBlock(waddrmgr.BlockStamp(n))
|
||||||
case chain.BlockDisconnected:
|
case chain.BlockDisconnected:
|
||||||
err = w.disconnectBlock(waddrmgr.BlockStamp(n))
|
err = w.disconnectBlock(waddrmgr.BlockStamp(n))
|
||||||
case chain.RecvTx:
|
case chain.RelevantTx:
|
||||||
err = w.addReceivedTx(n.Tx, n.Block)
|
err = w.addRelevantTx(n.TxRecord, n.Block)
|
||||||
case chain.RedeemingTx:
|
|
||||||
err = w.addRedeemingTx(n.Tx, n.Block)
|
|
||||||
|
|
||||||
// The following are handled by the wallet's rescan
|
// The following are handled by the wallet's rescan
|
||||||
// goroutines, so just pass them there.
|
// goroutines, so just pass them there.
|
||||||
|
@ -116,69 +113,87 @@ func (w *Wallet) disconnectBlock(bs waddrmgr.BlockStamp) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *txstore.Block) error {
|
func (w *Wallet) addRelevantTx(rec *wtxmgr.TxRecord, block *wtxmgr.BlockMeta) error {
|
||||||
// For every output, if it pays to a wallet address, insert the
|
// TODO: The transaction store and address manager need to be updated
|
||||||
// transaction into the store (possibly moving it from unconfirmed to
|
// together, but each operate under different namespaces and are changed
|
||||||
// confirmed), and add a credit record if one does not already exist.
|
// under new transactions. This is not error safe as we lose
|
||||||
var txr *txstore.TxRecord
|
// transaction semantics.
|
||||||
txInserted := false
|
//
|
||||||
for txOutIdx, txOut := range tx.MsgTx().TxOut {
|
// I'm unsure of the best way to solve this. Some possible solutions
|
||||||
// Errors don't matter here. If addrs is nil, the range below
|
// and drawbacks:
|
||||||
// does nothing.
|
//
|
||||||
_, addrs, _, _ := txscript.ExtractPkScriptAddrs(txOut.PkScript,
|
// 1. Open write transactions here and pass the handle to every
|
||||||
w.chainParams)
|
// waddrmr and wtxmgr method. This complicates the caller code
|
||||||
insert := false
|
// everywhere, however.
|
||||||
for _, addr := range addrs {
|
//
|
||||||
_, err := w.Manager.Address(addr)
|
// 2. Move the wtxmgr namespace into the waddrmgr namespace, likely
|
||||||
if err == nil {
|
// under its own bucket. This entire function can then be moved
|
||||||
insert = true
|
// into the waddrmgr package, which updates the nested wtxmgr.
|
||||||
break
|
// This removes some of separation between the components.
|
||||||
}
|
//
|
||||||
}
|
// 3. Use multiple wtxmgrs, one for each account, nested in the
|
||||||
if insert {
|
// waddrmgr namespace. This still provides some sort of logical
|
||||||
if !txInserted {
|
// separation (transaction handling remains in another package, and
|
||||||
var err error
|
// is simply used by waddrmgr), but may result in duplicate
|
||||||
txr, err = w.TxStore.InsertTx(tx, block)
|
// transactions being saved if they are relevant to multiple
|
||||||
|
// accounts.
|
||||||
|
//
|
||||||
|
// 4. Store wtxmgr-related details under the waddrmgr namespace, but
|
||||||
|
// solve the drawback of #3 by splitting wtxmgr to save entire
|
||||||
|
// transaction records globally for all accounts, with
|
||||||
|
// credit/debit/balance tracking per account. Each account would
|
||||||
|
// also save the relevant transaction hashes and block incidence so
|
||||||
|
// the full transaction can be loaded from the waddrmgr
|
||||||
|
// transactions bucket. This currently seems like the best
|
||||||
|
// solution.
|
||||||
|
|
||||||
|
// At the moment all notified transactions are assumed to actually be
|
||||||
|
// relevant. This assumption will not hold true when SPV support is
|
||||||
|
// added, but until then, simply insert the transaction because there
|
||||||
|
// should either be one or more relevant inputs or outputs.
|
||||||
|
err := w.TxStore.InsertTx(rec, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// InsertTx may have moved a previous unmined
|
|
||||||
// tx, so mark the entire store as dirty.
|
// Check every output to determine whether it is controlled by a wallet
|
||||||
w.TxStore.MarkDirty()
|
// key. If so, mark the output as a credit.
|
||||||
txInserted = true
|
for i, output := range rec.MsgTx.TxOut {
|
||||||
}
|
_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript,
|
||||||
if txr.HasCredit(txOutIdx) {
|
w.chainParams)
|
||||||
|
if err != nil {
|
||||||
|
// Non-standard outputs are skipped.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, err := txr.AddCredit(uint32(txOutIdx), false)
|
for _, addr := range addrs {
|
||||||
if err != nil {
|
ma, err := w.Manager.Address(addr)
|
||||||
return err
|
|
||||||
}
|
|
||||||
w.TxStore.MarkDirty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bs, err := w.chainSvr.BlockStamp()
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
w.notifyBalances(bs.Height)
|
// TODO: Credits should be added with the
|
||||||
}
|
// account they belong to, so wtxmgr is able to
|
||||||
|
// track per-account balances.
|
||||||
return nil
|
err = w.TxStore.AddCredit(rec, block, uint32(i),
|
||||||
}
|
ma.Internal())
|
||||||
|
|
||||||
// addRedeemingTx inserts the notified spending transaction as a debit and
|
|
||||||
// schedules the transaction store for a future file write.
|
|
||||||
func (w *Wallet) addRedeemingTx(tx *btcutil.Tx, block *txstore.Block) error {
|
|
||||||
txr, err := w.TxStore.InsertTx(tx, block)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := txr.AddDebits(); err != nil {
|
err = w.Manager.MarkUsed(addr)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := w.markAddrsUsed(txr); err != nil {
|
log.Debugf("Marked address %v used", addr)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Missing addresses are skipped. Other errors should
|
||||||
|
// be propagated.
|
||||||
|
code := err.(waddrmgr.ManagerError).ErrorCode
|
||||||
|
if code != waddrmgr.ErrAddressNotFound {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Notify connected clients of the added transaction.
|
||||||
|
|
||||||
bs, err := w.chainSvr.BlockStamp()
|
bs, err := w.chainSvr.BlockStamp()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -18,9 +18,9 @@ package wallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config is a structure used to initialize a Wallet
|
// Config is a structure used to initialize a Wallet
|
||||||
|
@ -28,6 +28,6 @@ import (
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ChainParams *chaincfg.Params
|
ChainParams *chaincfg.Params
|
||||||
Db *walletdb.DB
|
Db *walletdb.DB
|
||||||
TxStore *txstore.Store
|
TxStore *wtxmgr.Store
|
||||||
Waddrmgr *waddrmgr.Manager
|
Waddrmgr *waddrmgr.Manager
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2014 Conformal Systems LLC <info@conformal.com>
|
* Copyright (c) 2013-2015 Conformal Systems LLC <info@conformal.com>
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -28,8 +28,8 @@ import (
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -109,17 +109,17 @@ const defaultFeeIncrement = 1e3
|
||||||
// CreatedTx holds the state of a newly-created transaction and the change
|
// CreatedTx holds the state of a newly-created transaction and the change
|
||||||
// output (if one was added).
|
// output (if one was added).
|
||||||
type CreatedTx struct {
|
type CreatedTx struct {
|
||||||
Tx *btcutil.Tx
|
MsgTx *wire.MsgTx
|
||||||
ChangeAddr btcutil.Address
|
ChangeAddr btcutil.Address
|
||||||
ChangeIndex int // negative if no change
|
ChangeIndex int // negative if no change
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByAmount defines the methods needed to satisify sort.Interface to
|
// ByAmount defines the methods needed to satisify sort.Interface to
|
||||||
// sort a slice of Utxos by their amount.
|
// sort a slice of Utxos by their amount.
|
||||||
type ByAmount []txstore.Credit
|
type ByAmount []wtxmgr.Credit
|
||||||
|
|
||||||
func (u ByAmount) Len() int { return len(u) }
|
func (u ByAmount) Len() int { return len(u) }
|
||||||
func (u ByAmount) Less(i, j int) bool { return u[i].Amount() < u[j].Amount() }
|
func (u ByAmount) Less(i, j int) bool { return u[i].Amount < u[j].Amount }
|
||||||
func (u ByAmount) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
|
func (u ByAmount) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
|
||||||
|
|
||||||
// txToPairs creates a raw transaction sending the amounts for each
|
// txToPairs creates a raw transaction sending the amounts for each
|
||||||
|
@ -129,7 +129,7 @@ func (u ByAmount) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
|
||||||
// to addr or as a fee for the miner are sent to a newly generated
|
// to addr or as a fee for the miner are sent to a newly generated
|
||||||
// address. InsufficientFundsError is returned if there are not enough
|
// address. InsufficientFundsError is returned if there are not enough
|
||||||
// eligible unspent outputs to create the transaction.
|
// eligible unspent outputs to create the transaction.
|
||||||
func (w *Wallet) txToPairs(pairs map[string]btcutil.Amount, account uint32, minconf int) (*CreatedTx, error) {
|
func (w *Wallet) txToPairs(pairs map[string]btcutil.Amount, account uint32, minconf int32) (*CreatedTx, error) {
|
||||||
|
|
||||||
// Address manager must be unlocked to compose transaction. Grab
|
// Address manager must be unlocked to compose transaction. Grab
|
||||||
// the unlock if possible (to prevent future unlocks), or return the
|
// the unlock if possible (to prevent future unlocks), or return the
|
||||||
|
@ -159,7 +159,7 @@ func (w *Wallet) txToPairs(pairs map[string]btcutil.Amount, account uint32, minc
|
||||||
// the mining fee. It then creates and returns a CreatedTx containing
|
// the mining fee. It then creates and returns a CreatedTx containing
|
||||||
// the selected inputs and the given outputs, validating it (using
|
// the selected inputs and the given outputs, validating it (using
|
||||||
// validateMsgTx) as well.
|
// validateMsgTx) as well.
|
||||||
func createTx(eligible []txstore.Credit,
|
func createTx(eligible []wtxmgr.Credit,
|
||||||
outputs map[string]btcutil.Amount, bs *waddrmgr.BlockStamp,
|
outputs map[string]btcutil.Amount, bs *waddrmgr.BlockStamp,
|
||||||
feeIncrement btcutil.Amount, mgr *waddrmgr.Manager, account uint32,
|
feeIncrement btcutil.Amount, mgr *waddrmgr.Manager, account uint32,
|
||||||
changeAddress func(account uint32) (btcutil.Address, error),
|
changeAddress func(account uint32) (btcutil.Address, error),
|
||||||
|
@ -177,8 +177,8 @@ func createTx(eligible []txstore.Credit,
|
||||||
|
|
||||||
// Start by adding enough inputs to cover for the total amount of all
|
// Start by adding enough inputs to cover for the total amount of all
|
||||||
// desired outputs.
|
// desired outputs.
|
||||||
var input txstore.Credit
|
var input wtxmgr.Credit
|
||||||
var inputs []txstore.Credit
|
var inputs []wtxmgr.Credit
|
||||||
totalAdded := btcutil.Amount(0)
|
totalAdded := btcutil.Amount(0)
|
||||||
for totalAdded < minAmount {
|
for totalAdded < minAmount {
|
||||||
if len(eligible) == 0 {
|
if len(eligible) == 0 {
|
||||||
|
@ -186,8 +186,8 @@ func createTx(eligible []txstore.Credit,
|
||||||
}
|
}
|
||||||
input, eligible = eligible[0], eligible[1:]
|
input, eligible = eligible[0], eligible[1:]
|
||||||
inputs = append(inputs, input)
|
inputs = append(inputs, input)
|
||||||
msgtx.AddTxIn(wire.NewTxIn(input.OutPoint(), nil))
|
msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
|
||||||
totalAdded += input.Amount()
|
totalAdded += input.Amount
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an initial fee estimate based on the number of selected inputs
|
// Get an initial fee estimate based on the number of selected inputs
|
||||||
|
@ -204,9 +204,9 @@ func createTx(eligible []txstore.Credit,
|
||||||
}
|
}
|
||||||
input, eligible = eligible[0], eligible[1:]
|
input, eligible = eligible[0], eligible[1:]
|
||||||
inputs = append(inputs, input)
|
inputs = append(inputs, input)
|
||||||
msgtx.AddTxIn(wire.NewTxIn(input.OutPoint(), nil))
|
msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
|
||||||
szEst += txInEstimate
|
szEst += txInEstimate
|
||||||
totalAdded += input.Amount()
|
totalAdded += input.Amount
|
||||||
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
|
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,9 +255,9 @@ func createTx(eligible []txstore.Credit,
|
||||||
}
|
}
|
||||||
input, eligible = eligible[0], eligible[1:]
|
input, eligible = eligible[0], eligible[1:]
|
||||||
inputs = append(inputs, input)
|
inputs = append(inputs, input)
|
||||||
msgtx.AddTxIn(wire.NewTxIn(input.OutPoint(), nil))
|
msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
|
||||||
szEst += txInEstimate
|
szEst += txInEstimate
|
||||||
totalAdded += input.Amount()
|
totalAdded += input.Amount
|
||||||
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
|
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ func createTx(eligible []txstore.Credit,
|
||||||
}
|
}
|
||||||
|
|
||||||
info := &CreatedTx{
|
info := &CreatedTx{
|
||||||
Tx: btcutil.NewTx(msgtx),
|
MsgTx: msgtx,
|
||||||
ChangeAddr: changeAddr,
|
ChangeAddr: changeAddr,
|
||||||
ChangeIndex: changeIdx,
|
ChangeIndex: changeIdx,
|
||||||
}
|
}
|
||||||
|
@ -316,44 +316,59 @@ func addOutputs(msgtx *wire.MsgTx, pairs map[string]btcutil.Amount, chainParams
|
||||||
return minAmount, nil
|
return minAmount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wallet) findEligibleOutputs(account uint32, minconf int, bs *waddrmgr.BlockStamp) ([]txstore.Credit, error) {
|
func (w *Wallet) findEligibleOutputs(account uint32, minconf int32, bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) {
|
||||||
unspent, err := w.TxStore.UnspentOutputs()
|
unspent, err := w.TxStore.UnspentOutputs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Filter out unspendable outputs, that is, remove those that (at this
|
|
||||||
// time) are not P2PKH outputs. Other inputs must be manually included
|
// TODO: Eventually all of these filters (except perhaps output locking)
|
||||||
// in transactions and sent (for example, using createrawtransaction,
|
// should be handled by the call to UnspentOutputs (or similar).
|
||||||
// signrawtransaction, and sendrawtransaction).
|
// Because one of these filters requires matching the output script to
|
||||||
eligible := make([]txstore.Credit, 0, len(unspent))
|
// the desired account, this change depends on making wtxmgr a waddrmgr
|
||||||
|
// dependancy and requesting unspent outputs for a single account.
|
||||||
|
eligible := make([]wtxmgr.Credit, 0, len(unspent))
|
||||||
for i := range unspent {
|
for i := range unspent {
|
||||||
switch txscript.GetScriptClass(unspent[i].TxOut().PkScript) {
|
output := &unspent[i]
|
||||||
case txscript.PubKeyHashTy:
|
|
||||||
if !unspent[i].Confirmed(minconf, bs.Height) {
|
// Only include this output if it meets the required number of
|
||||||
|
// confirmations. Coinbase transactions must have have reached
|
||||||
|
// maturity before their outputs may be spent.
|
||||||
|
if !confirmed(minconf, output.Height, bs.Height) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Coinbase transactions must have have reached maturity
|
if output.FromCoinBase {
|
||||||
// before their outputs may be spent.
|
const target = blockchain.CoinbaseMaturity
|
||||||
if unspent[i].IsCoinbase() {
|
if !confirmed(target, output.Height, bs.Height) {
|
||||||
target := blockchain.CoinbaseMaturity
|
|
||||||
if !unspent[i].Confirmed(target, bs.Height) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locked unspent outputs are skipped.
|
// Locked unspent outputs are skipped.
|
||||||
if w.LockedOutpoint(*unspent[i].OutPoint()) {
|
if w.LockedOutpoint(output.OutPoint) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
creditAccount, err := w.CreditAccount(unspent[i])
|
// Filter out unspendable outputs, that is, remove those that
|
||||||
if err != nil {
|
// (at this time) are not P2PKH outputs. Other inputs must be
|
||||||
|
// manually included in transactions and sent (for example,
|
||||||
|
// using createrawtransaction, signrawtransaction, and
|
||||||
|
// sendrawtransaction).
|
||||||
|
class, addrs, _, err := txscript.ExtractPkScriptAddrs(
|
||||||
|
output.PkScript, w.chainParams)
|
||||||
|
if err != nil || class != txscript.PubKeyHashTy {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if creditAccount == account {
|
|
||||||
eligible = append(eligible, unspent[i])
|
// Only include the output if it is associated with the passed
|
||||||
}
|
// account. There should only be one address since this is a
|
||||||
|
// P2PKH script.
|
||||||
|
addrAcct, err := w.Manager.AddrAccount(addrs[0])
|
||||||
|
if err != nil || addrAcct != account {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eligible = append(eligible, *output)
|
||||||
}
|
}
|
||||||
return eligible, nil
|
return eligible, nil
|
||||||
}
|
}
|
||||||
|
@ -361,7 +376,7 @@ func (w *Wallet) findEligibleOutputs(account uint32, minconf int, bs *waddrmgr.B
|
||||||
// signMsgTx sets the SignatureScript for every item in msgtx.TxIn.
|
// signMsgTx sets the SignatureScript for every item in msgtx.TxIn.
|
||||||
// It must be called every time a msgtx is changed.
|
// It must be called every time a msgtx is changed.
|
||||||
// Only P2PKH outputs are supported at this point.
|
// Only P2PKH outputs are supported at this point.
|
||||||
func signMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit, mgr *waddrmgr.Manager, chainParams *chaincfg.Params) error {
|
func signMsgTx(msgtx *wire.MsgTx, prevOutputs []wtxmgr.Credit, mgr *waddrmgr.Manager, chainParams *chaincfg.Params) error {
|
||||||
if len(prevOutputs) != len(msgtx.TxIn) {
|
if len(prevOutputs) != len(msgtx.TxIn) {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Number of prevOutputs (%d) does not match number of tx inputs (%d)",
|
"Number of prevOutputs (%d) does not match number of tx inputs (%d)",
|
||||||
|
@ -370,7 +385,8 @@ func signMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit, mgr *waddrmgr.Ma
|
||||||
for i, output := range prevOutputs {
|
for i, output := range prevOutputs {
|
||||||
// Errors don't matter here, as we only consider the
|
// Errors don't matter here, as we only consider the
|
||||||
// case where len(addrs) == 1.
|
// case where len(addrs) == 1.
|
||||||
_, addrs, _, _ := output.Addresses(chainParams)
|
_, addrs, _, _ := txscript.ExtractPkScriptAddrs(output.PkScript,
|
||||||
|
chainParams)
|
||||||
if len(addrs) != 1 {
|
if len(addrs) != 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -391,7 +407,7 @@ func signMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit, mgr *waddrmgr.Ma
|
||||||
}
|
}
|
||||||
|
|
||||||
sigscript, err := txscript.SignatureScript(msgtx, i,
|
sigscript, err := txscript.SignatureScript(msgtx, i,
|
||||||
output.TxOut().PkScript, txscript.SigHashAll, privkey,
|
output.PkScript, txscript.SigHashAll, privkey,
|
||||||
ai.Compressed())
|
ai.Compressed())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create sigscript: %s", err)
|
return fmt.Errorf("cannot create sigscript: %s", err)
|
||||||
|
@ -402,9 +418,9 @@ func signMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit, mgr *waddrmgr.Ma
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit) error {
|
func validateMsgTx(msgtx *wire.MsgTx, prevOutputs []wtxmgr.Credit) error {
|
||||||
for i := range msgtx.TxIn {
|
for i := range msgtx.TxIn {
|
||||||
vm, err := txscript.NewEngine(prevOutputs[i].TxOut().PkScript,
|
vm, err := txscript.NewEngine(prevOutputs[i].PkScript,
|
||||||
msgtx, i, txscript.StandardVerifyFlags)
|
msgtx, i, txscript.StandardVerifyFlags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create script engine: %s", err)
|
return fmt.Errorf("cannot create script engine: %s", err)
|
||||||
|
@ -421,7 +437,7 @@ func validateMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit) error {
|
||||||
// s less than 1 kilobyte and none of the outputs contain a value
|
// s less than 1 kilobyte and none of the outputs contain a value
|
||||||
// less than 1 bitcent. Otherwise, the fee will be calculated using
|
// less than 1 bitcent. Otherwise, the fee will be calculated using
|
||||||
// incr, incrementing the fee for each kilobyte of transaction.
|
// incr, incrementing the fee for each kilobyte of transaction.
|
||||||
func minimumFee(incr btcutil.Amount, txLen int, outputs []*wire.TxOut, prevOutputs []txstore.Credit, height int32, disallowFree bool) btcutil.Amount {
|
func minimumFee(incr btcutil.Amount, txLen int, outputs []*wire.TxOut, prevOutputs []wtxmgr.Credit, height int32, disallowFree bool) btcutil.Amount {
|
||||||
allowFree := false
|
allowFree := false
|
||||||
if !disallowFree {
|
if !disallowFree {
|
||||||
allowFree = allowNoFeeTx(height, prevOutputs, txLen)
|
allowFree = allowNoFeeTx(height, prevOutputs, txLen)
|
||||||
|
@ -451,15 +467,15 @@ func minimumFee(incr btcutil.Amount, txLen int, outputs []*wire.TxOut, prevOutpu
|
||||||
// allowNoFeeTx calculates the transaction priority and checks that the
|
// allowNoFeeTx calculates the transaction priority and checks that the
|
||||||
// priority reaches a certain threshold. If the threshhold is
|
// priority reaches a certain threshold. If the threshhold is
|
||||||
// reached, a free transaction fee is allowed.
|
// reached, a free transaction fee is allowed.
|
||||||
func allowNoFeeTx(curHeight int32, txouts []txstore.Credit, txSize int) bool {
|
func allowNoFeeTx(curHeight int32, txouts []wtxmgr.Credit, txSize int) bool {
|
||||||
const blocksPerDayEstimate = 144.0
|
const blocksPerDayEstimate = 144.0
|
||||||
const txSizeEstimate = 250.0
|
const txSizeEstimate = 250.0
|
||||||
const threshold = btcutil.SatoshiPerBitcoin * blocksPerDayEstimate / txSizeEstimate
|
const threshold = btcutil.SatoshiPerBitcoin * blocksPerDayEstimate / txSizeEstimate
|
||||||
|
|
||||||
var weightedSum int64
|
var weightedSum int64
|
||||||
for _, txout := range txouts {
|
for _, txout := range txouts {
|
||||||
depth := chainDepth(txout.BlockHeight, curHeight)
|
depth := chainDepth(txout.Height, curHeight)
|
||||||
weightedSum += int64(txout.Amount()) * int64(depth)
|
weightedSum += int64(txout.Amount) * int64(depth)
|
||||||
}
|
}
|
||||||
priority := float64(weightedSum) / float64(txSize)
|
priority := float64(weightedSum) / float64(txSize)
|
||||||
return priority > threshold
|
return priority > threshold
|
||||||
|
|
|
@ -7,16 +7,18 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/chaincfg"
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
"github.com/btcsuite/btcutil/hdkeychain"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is a tx that transfers funds (0.371 BTC) to addresses of known privKeys.
|
// This is a tx that transfers funds (0.371 BTC) to addresses of known privKeys.
|
||||||
|
@ -80,7 +82,7 @@ func TestCreateTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick all utxos from txInfo as eligible input.
|
// Pick all utxos from txInfo as eligible input.
|
||||||
eligible := eligibleInputsFromTx(t, txInfo.hex, []uint32{1, 2, 3, 4, 5})
|
eligible := mockCredits(t, txInfo.hex, []uint32{1, 2, 3, 4, 5})
|
||||||
// Now create a new TX sending 25e6 satoshis to the following addresses:
|
// Now create a new TX sending 25e6 satoshis to the following addresses:
|
||||||
outputs := map[string]btcutil.Amount{outAddr1: 15e6, outAddr2: 10e6}
|
outputs := map[string]btcutil.Amount{outAddr1: 15e6, outAddr2: 10e6}
|
||||||
tx, err := createTx(eligible, outputs, bs, defaultFeeIncrement, mgr, account, tstChangeAddress, &chaincfg.TestNet3Params, false)
|
tx, err := createTx(eligible, outputs, bs, defaultFeeIncrement, mgr, account, tstChangeAddress, &chaincfg.TestNet3Params, false)
|
||||||
|
@ -93,7 +95,7 @@ func TestCreateTx(t *testing.T) {
|
||||||
tx.ChangeAddr.String(), changeAddr.String())
|
tx.ChangeAddr.String(), changeAddr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
msgTx := tx.Tx.MsgTx()
|
msgTx := tx.MsgTx
|
||||||
if len(msgTx.TxOut) != 3 {
|
if len(msgTx.TxOut) != 3 {
|
||||||
t.Fatalf("Unexpected number of outputs; got %d, want 3", len(msgTx.TxOut))
|
t.Fatalf("Unexpected number of outputs; got %d, want 3", len(msgTx.TxOut))
|
||||||
}
|
}
|
||||||
|
@ -122,7 +124,7 @@ func TestCreateTx(t *testing.T) {
|
||||||
|
|
||||||
func TestCreateTxInsufficientFundsError(t *testing.T) {
|
func TestCreateTxInsufficientFundsError(t *testing.T) {
|
||||||
outputs := map[string]btcutil.Amount{outAddr1: 10, outAddr2: 1e9}
|
outputs := map[string]btcutil.Amount{outAddr1: 10, outAddr2: 1e9}
|
||||||
eligible := eligibleInputsFromTx(t, txInfo.hex, []uint32{1})
|
eligible := mockCredits(t, txInfo.hex, []uint32{1})
|
||||||
bs := &waddrmgr.BlockStamp{Height: 11111}
|
bs := &waddrmgr.BlockStamp{Height: 11111}
|
||||||
account := uint32(0)
|
account := uint32(0)
|
||||||
changeAddr, _ := btcutil.DecodeAddress("muqW4gcixv58tVbSKRC5q6CRKy8RmyLgZ5", &chaincfg.TestNet3Params)
|
changeAddr, _ := btcutil.DecodeAddress("muqW4gcixv58tVbSKRC5q6CRKy8RmyLgZ5", &chaincfg.TestNet3Params)
|
||||||
|
@ -207,29 +209,36 @@ func newManager(t *testing.T, privKeys []string, bs *waddrmgr.BlockStamp) *waddr
|
||||||
return mgr
|
return mgr
|
||||||
}
|
}
|
||||||
|
|
||||||
// eligibleInputsFromTx decodes the given txHex and returns the outputs with
|
// mockCredits decodes the given txHex and returns the outputs with
|
||||||
// the given indices as eligible inputs.
|
// the given indices as eligible inputs.
|
||||||
func eligibleInputsFromTx(t *testing.T, txHex string, indices []uint32) []txstore.Credit {
|
func mockCredits(t *testing.T, txHex string, indices []uint32) []wtxmgr.Credit {
|
||||||
serialized, err := hex.DecodeString(txHex)
|
serialized, err := hex.DecodeString(txHex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
tx, err := btcutil.NewTxFromBytes(serialized)
|
utx, err := btcutil.NewTxFromBytes(serialized)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
s := txstore.New("/tmp/tx.bin")
|
tx := utx.MsgTx()
|
||||||
r, err := s.InsertTx(tx, nil)
|
|
||||||
if err != nil {
|
isCB := blockchain.IsCoinBaseTx(tx)
|
||||||
t.Fatal(err)
|
now := time.Now()
|
||||||
|
|
||||||
|
eligible := make([]wtxmgr.Credit, len(indices))
|
||||||
|
c := wtxmgr.Credit{
|
||||||
|
OutPoint: wire.OutPoint{Hash: *utx.Sha()},
|
||||||
|
BlockMeta: wtxmgr.BlockMeta{
|
||||||
|
Block: wtxmgr.Block{Height: -1},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
eligible := make([]txstore.Credit, len(indices))
|
|
||||||
for i, idx := range indices {
|
for i, idx := range indices {
|
||||||
credit, err := r.AddCredit(idx, false)
|
c.OutPoint.Index = idx
|
||||||
if err != nil {
|
c.Amount = btcutil.Amount(tx.TxOut[idx].Value)
|
||||||
t.Fatal(err)
|
c.PkScript = tx.TxOut[idx].PkScript
|
||||||
}
|
c.Received = now
|
||||||
eligible[i] = credit
|
c.FromCoinBase = isCB
|
||||||
|
eligible[i] = c
|
||||||
}
|
}
|
||||||
return eligible
|
return eligible
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,8 @@ import (
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcwallet/chain"
|
"github.com/btcsuite/btcwallet/chain"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RescanProgressMsg reports the current progress made by a rescan for a
|
// RescanProgressMsg reports the current progress made by a rescan for a
|
||||||
|
@ -252,10 +252,10 @@ func (w *Wallet) rescanRPCHandler() {
|
||||||
// a wallet. This is intended to be used to sync a wallet back up to the
|
// a wallet. This is intended to be used to sync a wallet back up to the
|
||||||
// current best block in the main chain, and is considered an initial sync
|
// current best block in the main chain, and is considered an initial sync
|
||||||
// rescan.
|
// rescan.
|
||||||
func (w *Wallet) Rescan(addrs []btcutil.Address, unspent []txstore.Credit) error {
|
func (w *Wallet) Rescan(addrs []btcutil.Address, unspent []wtxmgr.Credit) error {
|
||||||
outpoints := make([]*wire.OutPoint, len(unspent))
|
outpoints := make([]*wire.OutPoint, len(unspent))
|
||||||
for i, output := range unspent {
|
for i, output := range unspent {
|
||||||
outpoints[i] = output.OutPoint()
|
outpoints[i] = &output.OutPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
job := &RescanJob{
|
job := &RescanJob{
|
||||||
|
|
723
wallet/wallet.go
723
wallet/wallet.go
File diff suppressed because it is too large
Load diff
108
walletsetup.go
108
walletsetup.go
|
@ -31,17 +31,18 @@ import (
|
||||||
"github.com/btcsuite/btcutil"
|
"github.com/btcsuite/btcutil"
|
||||||
"github.com/btcsuite/btcutil/hdkeychain"
|
"github.com/btcsuite/btcutil/hdkeychain"
|
||||||
"github.com/btcsuite/btcwallet/legacy/keystore"
|
"github.com/btcsuite/btcwallet/legacy/keystore"
|
||||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
"github.com/btcsuite/btcwallet/wallet"
|
"github.com/btcsuite/btcwallet/wallet"
|
||||||
"github.com/btcsuite/btcwallet/walletdb"
|
"github.com/btcsuite/btcwallet/walletdb"
|
||||||
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
||||||
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||||
"github.com/btcsuite/golangcrypto/ssh/terminal"
|
"github.com/btcsuite/golangcrypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Namespace keys
|
||||||
var (
|
var (
|
||||||
// waddrmgrNamespaceKey is the namespace key for the waddrmgr package.
|
|
||||||
waddrmgrNamespaceKey = []byte("waddrmgr")
|
waddrmgrNamespaceKey = []byte("waddrmgr")
|
||||||
|
wtxmgrNamespaceKey = []byte("wtxmgr")
|
||||||
)
|
)
|
||||||
|
|
||||||
// networkDir returns the directory name of a network directory to hold wallet
|
// networkDir returns the directory name of a network directory to hold wallet
|
||||||
|
@ -558,9 +559,30 @@ func createSimulationWallet(cfg *config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// openDb opens and returns a *walletdb.DB (boltdb here) given the
|
// checkCreateDir checks that the path exists and is a directory.
|
||||||
// directory and dbname
|
// If path does not exist, it is created.
|
||||||
func openDb(directory string, dbname string) (*walletdb.DB, error) {
|
func checkCreateDir(path string) error {
|
||||||
|
if fi, err := os.Stat(path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// Attempt data directory creation
|
||||||
|
if err = os.MkdirAll(path, 0700); err != nil {
|
||||||
|
return fmt.Errorf("cannot create directory: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("error checking directory: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !fi.IsDir() {
|
||||||
|
return fmt.Errorf("path '%s' is not a directory", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// openDb opens and returns a walletdb.DB (boltdb here) given the directory and
|
||||||
|
// dbname
|
||||||
|
func openDb(directory string, dbname string) (walletdb.DB, error) {
|
||||||
dbPath := filepath.Join(directory, dbname)
|
dbPath := filepath.Join(directory, dbname)
|
||||||
|
|
||||||
// Ensure that the network directory exists.
|
// Ensure that the network directory exists.
|
||||||
|
@ -569,33 +591,50 @@ func openDb(directory string, dbname string) (*walletdb.DB, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the database using the boltdb backend.
|
// Open the database using the boltdb backend.
|
||||||
db, err := walletdb.Open("bdb", dbPath)
|
return walletdb.Open("bdb", dbPath)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &db, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// openWaddrmgr returns an address manager given a database, namespace,
|
// openManagers opens and returns the wallet address and transaction managers.
|
||||||
// public pass and the chain params
|
// If the transaction store does not already exist, the manager is marked
|
||||||
|
// unsynced so the wallet will sync with a full rescan.
|
||||||
|
//
|
||||||
// It prompts for seed and private passphrase required in case of upgrades
|
// It prompts for seed and private passphrase required in case of upgrades
|
||||||
func openWaddrmgr(db *walletdb.DB, namespaceKey []byte, pass string,
|
func openManagers(db walletdb.DB, pass string) (*waddrmgr.Manager, *wtxmgr.Store, error) {
|
||||||
chainParams *chaincfg.Params) (*waddrmgr.Manager, error) {
|
|
||||||
|
|
||||||
// Get the namespace for the address manager.
|
// Get the namespace for the address manager.
|
||||||
namespace, err := (*db).Namespace(namespaceKey)
|
namespace, err := db.Namespace(waddrmgrNamespaceKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &waddrmgr.Options{
|
config := &waddrmgr.Options{
|
||||||
ObtainSeed: promptSeed,
|
ObtainSeed: promptSeed,
|
||||||
ObtainPrivatePass: promptPrivPassPhrase,
|
ObtainPrivatePass: promptPrivPassPhrase,
|
||||||
}
|
}
|
||||||
// Open address manager and transaction store.
|
addrMgr, err := waddrmgr.Open(namespace, []byte(pass), activeNet.Params, config)
|
||||||
// var txs *txstore.Store
|
if err != nil {
|
||||||
return waddrmgr.Open(namespace, []byte(pass),
|
return nil, nil, err
|
||||||
chainParams, config)
|
}
|
||||||
|
|
||||||
|
namespace, err = db.Namespace(wtxmgrNamespaceKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
txMgr, err := wtxmgr.Open(namespace)
|
||||||
|
if err != nil {
|
||||||
|
if !wtxmgr.IsNoExists(err) {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
log.Info("No recorded transaction history -- needs full rescan")
|
||||||
|
err = addrMgr.SetSyncedTo(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
txMgr, err = wtxmgr.Create(namespace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addrMgr, txMgr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// openWallet returns a wallet. The function handles opening an existing wallet
|
// openWallet returns a wallet. The function handles opening an existing wallet
|
||||||
|
@ -610,35 +649,12 @@ func openWallet() (*wallet.Wallet, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var txs *txstore.Store
|
mgr, txs, err := openManagers(db, cfg.WalletPass)
|
||||||
mgr, err := openWaddrmgr(db, waddrmgrNamespaceKey, cfg.WalletPass,
|
|
||||||
activeNet.Params)
|
|
||||||
if err == nil {
|
|
||||||
txs, err = txstore.OpenDir(netdir)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Special case: if the address manager was successfully read
|
|
||||||
// (mgr != nil) but the transaction store was not, create a
|
|
||||||
// new txstore and write it out to disk. Write an unsynced
|
|
||||||
// manager back to disk so on future opens, the empty txstore
|
|
||||||
// is not considered fully synced.
|
|
||||||
if mgr == nil {
|
|
||||||
log.Errorf("%v", err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
txs = txstore.New(netdir)
|
|
||||||
txs.MarkDirty()
|
|
||||||
err = txs.WriteIfDirty()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mgr.SetSyncedTo(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
walletConfig := &wallet.Config{
|
walletConfig := &wallet.Config{
|
||||||
Db: db,
|
Db: &db, // TODO: Remove the pointer
|
||||||
TxStore: txs,
|
TxStore: txs,
|
||||||
Waddrmgr: mgr,
|
Waddrmgr: mgr,
|
||||||
ChainParams: activeNet.Params,
|
ChainParams: activeNet.Params,
|
||||||
|
|
Loading…
Reference in a new issue