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
|
||||
* 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/btcrpcclient"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
)
|
||||
|
||||
// Client represents a persistent client connection to a bitcoin RPC server
|
||||
|
@ -159,18 +159,11 @@ type (
|
|||
// BlockStamp was reorganized out of the best chain.
|
||||
BlockDisconnected waddrmgr.BlockStamp
|
||||
|
||||
// RecvTx is a notification for a transaction which pays to a wallet
|
||||
// address.
|
||||
RecvTx struct {
|
||||
Tx *btcutil.Tx // Index is guaranteed to be set.
|
||||
Block *txstore.Block // 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
|
||||
// RelevantTx is a notification for a transaction which spends wallet
|
||||
// inputs or pays to a watched address.
|
||||
RelevantTx struct {
|
||||
TxRecord *wtxmgr.TxRecord
|
||||
Block *wtxmgr.BlockMeta // nil if unmined
|
||||
}
|
||||
|
||||
// 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
|
||||
// Block structure of the txstore package, and the block index. This is done
|
||||
// parseBlock parses a btcws definition of the block a tx is mined it to the
|
||||
// Block structure of the wtxmgr package, and the block index. This is done
|
||||
// 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 {
|
||||
return nil, btcutil.TxIndexUnknown, nil
|
||||
return nil, nil
|
||||
}
|
||||
blksha, err := wire.NewShaHashFromStr(block.Hash)
|
||||
if err != nil {
|
||||
return nil, btcutil.TxIndexUnknown, err
|
||||
return nil, err
|
||||
}
|
||||
blk = &txstore.Block{
|
||||
Height: block.Height,
|
||||
Hash: *blksha,
|
||||
Time: time.Unix(block.Time, 0),
|
||||
blk := &wtxmgr.BlockMeta{
|
||||
Block: wtxmgr.Block{
|
||||
Height: block.Height,
|
||||
Hash: *blksha,
|
||||
},
|
||||
Time: time.Unix(block.Time, 0),
|
||||
}
|
||||
return blk, block.Index, nil
|
||||
return blk, nil
|
||||
}
|
||||
|
||||
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) {
|
||||
var blk *txstore.Block
|
||||
index := btcutil.TxIndexUnknown
|
||||
if block != nil {
|
||||
var err error
|
||||
blk, index, err = parseBlock(block)
|
||||
if err != nil {
|
||||
// Log and drop improper notification.
|
||||
log.Errorf("recvtx notification bad block: %v", err)
|
||||
return
|
||||
}
|
||||
blk, err := parseBlock(block)
|
||||
if err != nil {
|
||||
// Log and drop improper notification.
|
||||
log.Errorf("recvtx notification bad block: %v", err)
|
||||
return
|
||||
}
|
||||
tx.SetIndex(index)
|
||||
c.enqueueNotification <- RecvTx{tx, blk}
|
||||
|
||||
rec, err := wtxmgr.NewTxRecordFromMsgTx(tx.MsgTx(), time.Now())
|
||||
if err != nil {
|
||||
log.Errorf("Cannot create transaction record for relevant "+
|
||||
"tx: %v", err)
|
||||
return
|
||||
}
|
||||
c.enqueueNotification <- RelevantTx{rec, blk}
|
||||
}
|
||||
|
||||
func (c *Client) onRedeemingTx(tx *btcutil.Tx, block *btcjson.BlockDetails) {
|
||||
var blk *txstore.Block
|
||||
index := btcutil.TxIndexUnknown
|
||||
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}
|
||||
// Handled exactly like recvtx notifications.
|
||||
c.onRecvTx(tx, block)
|
||||
}
|
||||
|
||||
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/btcwallet/chain"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
"github.com/btcsuite/seelog"
|
||||
)
|
||||
|
||||
|
@ -44,7 +44,7 @@ var (
|
|||
backendLog = seelog.Disabled
|
||||
log = btclog.Disabled
|
||||
walletLog = btclog.Disabled
|
||||
txstLog = btclog.Disabled
|
||||
txmgrLog = btclog.Disabled
|
||||
chainLog = btclog.Disabled
|
||||
)
|
||||
|
||||
|
@ -52,7 +52,7 @@ var (
|
|||
var subsystemLoggers = map[string]btclog.Logger{
|
||||
"BTCW": log,
|
||||
"WLLT": walletLog,
|
||||
"TXST": txstLog,
|
||||
"TMGR": txmgrLog,
|
||||
"CHNS": chainLog,
|
||||
}
|
||||
|
||||
|
@ -87,8 +87,8 @@ func useLogger(subsystemID string, logger btclog.Logger) {
|
|||
walletLog = logger
|
||||
wallet.UseLogger(logger)
|
||||
case "TXST":
|
||||
txstLog = logger
|
||||
txstore.UseLogger(logger)
|
||||
txmgrLog = logger
|
||||
wtxmgr.UseLogger(logger)
|
||||
case "CHNS":
|
||||
chainLog = logger
|
||||
chain.UseLogger(logger)
|
||||
|
|
380
rpcserver.go
380
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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -45,9 +45,9 @@ import (
|
|||
"github.com/btcsuite/btcrpcclient"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/chain"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
"github.com/btcsuite/websocket"
|
||||
)
|
||||
|
||||
|
@ -142,6 +142,24 @@ func checkDefaultAccount(account string) error {
|
|||
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 {
|
||||
conn *websocket.Conn
|
||||
authenticated bool
|
||||
|
@ -309,10 +327,7 @@ type rpcServer struct {
|
|||
// created.
|
||||
connectedBlocks <-chan waddrmgr.BlockStamp
|
||||
disconnectedBlocks <-chan waddrmgr.BlockStamp
|
||||
newCredits <-chan txstore.Credit
|
||||
newDebits <-chan txstore.Debits
|
||||
minedCredits <-chan txstore.Credit
|
||||
minedDebits <-chan txstore.Debits
|
||||
relevantTxs <-chan chain.RelevantTx
|
||||
managerLocked <-chan bool
|
||||
confirmedBalance <-chan btcutil.Amount
|
||||
unconfirmedBalance <-chan btcutil.Amount
|
||||
|
@ -1038,8 +1053,7 @@ type (
|
|||
blockConnected waddrmgr.BlockStamp
|
||||
blockDisconnected waddrmgr.BlockStamp
|
||||
|
||||
txCredit txstore.Credit
|
||||
txDebit txstore.Debits
|
||||
relevantTx chain.RelevantTx
|
||||
|
||||
managerLocked bool
|
||||
|
||||
|
@ -1059,36 +1073,30 @@ func (b blockDisconnected) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
|||
return []btcjson.Cmd{n}
|
||||
}
|
||||
|
||||
func (c txCredit) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
||||
blk := w.Manager.SyncedTo()
|
||||
acctName := waddrmgr.DefaultAccountName
|
||||
if creditAccount, err := w.CreditAccount(txstore.Credit(c)); err == nil {
|
||||
// acctName is defaulted to DefaultAccountName in case of an error
|
||||
acctName, _ = w.Manager.AccountName(creditAccount)
|
||||
}
|
||||
ltr, err := txstore.Credit(c).ToJSON(acctName, blk.Height, activeNet.Params)
|
||||
if err != nil {
|
||||
log.Errorf("Cannot create notification for transaction "+
|
||||
"credit: %v", err)
|
||||
return nil
|
||||
}
|
||||
n := btcws.NewTxNtfn(acctName, <r)
|
||||
return []btcjson.Cmd{n}
|
||||
}
|
||||
func (t relevantTx) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
||||
syncBlock := w.Manager.SyncedTo()
|
||||
|
||||
func (d txDebit) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
||||
blk := w.Manager.SyncedTo()
|
||||
ltrs, err := txstore.Debits(d).ToJSON("", blk.Height, activeNet.Params)
|
||||
var block *wtxmgr.Block
|
||||
if t.Block != nil {
|
||||
block = &t.Block.Block
|
||||
}
|
||||
details, err := w.TxStore.UniqueTxDetails(&t.TxRecord.Hash, block)
|
||||
if err != nil {
|
||||
log.Errorf("Cannot create notification for transaction "+
|
||||
"debits: %v", err)
|
||||
log.Errorf("Cannot fetch transaction details for "+
|
||||
"client notification: %v", err)
|
||||
return nil
|
||||
}
|
||||
ns := make([]btcjson.Cmd, len(ltrs))
|
||||
for i := range ns {
|
||||
ns[i] = btcws.NewTxNtfn("", <rs[i])
|
||||
if details == nil {
|
||||
log.Errorf("No details found for client transaction notification")
|
||||
return nil
|
||||
}
|
||||
return ns
|
||||
|
||||
ltr := wallet.ListTransactions(details, syncBlock.Height, activeNet.Params)
|
||||
ntfns := make([]btcjson.Cmd, len(ltr))
|
||||
for i := range ntfns {
|
||||
ntfns[i] = btcws.NewTxNtfn(ltr[i].Account, <r[i])
|
||||
}
|
||||
return ntfns
|
||||
}
|
||||
|
||||
func (l managerLocked) notificationCmds(w *wallet.Wallet) []btcjson.Cmd {
|
||||
|
@ -1121,14 +1129,8 @@ out:
|
|||
s.enqueueNotification <- blockConnected(n)
|
||||
case n := <-s.disconnectedBlocks:
|
||||
s.enqueueNotification <- blockDisconnected(n)
|
||||
case n := <-s.newCredits:
|
||||
s.enqueueNotification <- txCredit(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.relevantTxs:
|
||||
s.enqueueNotification <- relevantTx(n)
|
||||
case n := <-s.managerLocked:
|
||||
s.enqueueNotification <- managerLocked(n)
|
||||
case n := <-s.confirmedBalance:
|
||||
|
@ -1153,28 +1155,10 @@ out:
|
|||
err)
|
||||
continue
|
||||
}
|
||||
newCredits, err := s.wallet.TxStore.ListenNewCredits()
|
||||
relevantTxs, err := s.wallet.ListenRelevantTxs()
|
||||
if err != nil {
|
||||
log.Errorf("Could not register for new "+
|
||||
"credit 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)
|
||||
log.Errorf("Could not register for new relevant "+
|
||||
"transaction notifications: %v", err)
|
||||
continue
|
||||
}
|
||||
managerLocked, err := s.wallet.ListenLockStatus()
|
||||
|
@ -1197,10 +1181,7 @@ out:
|
|||
}
|
||||
s.connectedBlocks = connectedBlocks
|
||||
s.disconnectedBlocks = disconnectedBlocks
|
||||
s.newCredits = newCredits
|
||||
s.newDebits = newDebits
|
||||
s.minedCredits = minedCredits
|
||||
s.minedDebits = minedDebits
|
||||
s.relevantTxs = relevantTxs
|
||||
s.managerLocked = managerLocked
|
||||
s.confirmedBalance = confirmedBalance
|
||||
s.unconfirmedBalance = unconfirmedBalance
|
||||
|
@ -1219,10 +1200,8 @@ func (s *rpcServer) drainNotifications() {
|
|||
select {
|
||||
case <-s.connectedBlocks:
|
||||
case <-s.disconnectedBlocks:
|
||||
case <-s.newCredits:
|
||||
case <-s.newDebits:
|
||||
case <-s.minedCredits:
|
||||
case <-s.minedDebits:
|
||||
case <-s.relevantTxs:
|
||||
case <-s.managerLocked:
|
||||
case <-s.confirmedBalance:
|
||||
case <-s.unconfirmedBalance:
|
||||
case <-s.registerWalletNtfns:
|
||||
|
@ -1694,13 +1673,13 @@ func GetBalance(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (int
|
|||
var account uint32
|
||||
var err error
|
||||
if cmd.Account == nil || *cmd.Account == "*" {
|
||||
balance, err = w.CalculateBalance(cmd.MinConf)
|
||||
balance, err = w.CalculateBalance(int32(cmd.MinConf))
|
||||
} else {
|
||||
account, err = w.Manager.LookupAccount(*cmd.Account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
balance, err = w.CalculateAccountBalance(account, cmd.MinConf)
|
||||
balance, err = w.CalculateAccountBalance(account, int32(cmd.MinConf))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1964,7 +1943,7 @@ func GetReceivedByAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson
|
|||
return nil, err
|
||||
}
|
||||
|
||||
bal, _, err := w.TotalReceivedForAccount(account, cmd.MinConf)
|
||||
bal, _, err := w.TotalReceivedForAccount(account, int32(cmd.MinConf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1981,7 +1960,7 @@ func GetReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson
|
|||
if err != nil {
|
||||
return nil, InvalidAddressOrKeyError{err}
|
||||
}
|
||||
total, err := w.TotalReceivedForAddr(addr, cmd.MinConf)
|
||||
total, err := w.TotalReceivedForAddr(addr, int32(cmd.MinConf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1999,96 +1978,105 @@ func GetTransaction(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
|
|||
return nil, btcjson.ErrDecodeHexString
|
||||
}
|
||||
|
||||
record, ok := w.TxRecord(txSha)
|
||||
if !ok {
|
||||
details, err := w.TxStore.TxDetails(txSha)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if details == nil {
|
||||
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
|
||||
txBuf.Grow(record.Tx().MsgTx().SerializeSize())
|
||||
err = record.Tx().MsgTx().Serialize(&txBuf)
|
||||
txBuf.Grow(details.MsgTx.SerializeSize())
|
||||
err = details.MsgTx.Serialize(&txBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(jrick) set "generate" to true if this is the coinbase (if
|
||||
// record.Tx().Index() == 0).
|
||||
// TODO: Add a "generated" field to this result type. "generated":true
|
||||
// is only added if the transaction is a coinbase.
|
||||
ret := btcjson.GetTransactionResult{
|
||||
TxID: txSha.String(),
|
||||
TxID: cmd.Txid,
|
||||
Hex: hex.EncodeToString(txBuf.Bytes()),
|
||||
Time: record.Received().Unix(),
|
||||
TimeReceived: record.Received().Unix(),
|
||||
WalletConflicts: []string{},
|
||||
Time: details.Received.Unix(),
|
||||
TimeReceived: details.Received.Unix(),
|
||||
WalletConflicts: []string{}, // Not saved
|
||||
//Generated: blockchain.IsCoinBaseTx(&details.MsgTx),
|
||||
}
|
||||
|
||||
if record.BlockHeight != -1 {
|
||||
txBlock, err := record.Block()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if details.Block.Height != -1 {
|
||||
ret.BlockHash = details.Block.Hash.String()
|
||||
ret.BlockTime = details.Block.Time.Unix()
|
||||
ret.Confirmations = int64(confirms(details.Block.Height, syncBlock.Height))
|
||||
}
|
||||
|
||||
var (
|
||||
debitTotal btcutil.Amount
|
||||
creditTotal btcutil.Amount // Excludes change
|
||||
outputTotal btcutil.Amount
|
||||
fee btcutil.Amount
|
||||
feeF64 float64
|
||||
)
|
||||
for _, deb := range details.Debits {
|
||||
debitTotal += deb.Amount
|
||||
}
|
||||
for _, cred := range details.Credits {
|
||||
if !cred.Change {
|
||||
creditTotal += cred.Amount
|
||||
}
|
||||
ret.BlockIndex = int64(record.Tx().Index())
|
||||
ret.BlockHash = txBlock.Hash.String()
|
||||
ret.BlockTime = txBlock.Time.Unix()
|
||||
ret.Confirmations = int64(record.Confirmations(blk.Height))
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
credits := record.Credits()
|
||||
debits, err := record.Debits()
|
||||
var targetAddr *string
|
||||
var creditAmount btcutil.Amount
|
||||
if err != nil {
|
||||
if len(details.Debits) == 0 {
|
||||
// Credits must be set later, but since we know the full length
|
||||
// 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 {
|
||||
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,
|
||||
Category: "send",
|
||||
// negative since it is a send
|
||||
Amount: (-debits.OutputAmount(true)).ToBTC(),
|
||||
Fee: debits.Fee().ToBTC(),
|
||||
Amount: (-debitTotal).ToBTC(), // negative since it is a send
|
||||
Fee: feeF64,
|
||||
}
|
||||
targetAddr = &details.Address
|
||||
ret.Details[0] = details
|
||||
ret.Fee = details.Fee
|
||||
|
||||
creditAmount = -debits.InputAmount()
|
||||
ret.Fee = feeF64
|
||||
}
|
||||
|
||||
for _, cred := range record.Credits() {
|
||||
credCat := wallet.RecvCategory(details, syncBlock.Height).String()
|
||||
for _, cred := range details.Credits {
|
||||
// Change is ignored.
|
||||
if cred.Change() {
|
||||
if cred.Change {
|
||||
continue
|
||||
}
|
||||
|
||||
creditAmount += cred.Amount()
|
||||
|
||||
var addr string
|
||||
// Errors don't matter here, as we only consider the
|
||||
// case where len(addrs) == 1.
|
||||
_, addrs, _, _ := cred.Addresses(activeNet.Params)
|
||||
if len(addrs) == 1 {
|
||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
|
||||
details.MsgTx.TxOut[cred.Index].PkScript, activeNet.Params)
|
||||
if err == nil && len(addrs) == 1 {
|
||||
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{
|
||||
Account: waddrmgr.DefaultAccountName,
|
||||
Category: cred.Category(blk.Height).String(),
|
||||
Amount: cred.Amount().ToBTC(),
|
||||
Category: credCat,
|
||||
Amount: cred.Amount.ToBTC(),
|
||||
Address: addr,
|
||||
})
|
||||
}
|
||||
|
||||
ret.Amount = creditAmount.ToBTC()
|
||||
ret.Amount = creditTotal.ToBTC()
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
@ -2107,7 +2095,7 @@ func ListAccounts(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (i
|
|||
if err != nil {
|
||||
return nil, ErrAccountNameNotFound
|
||||
}
|
||||
bal, err := w.CalculateAccountBalance(account, cmd.MinConf)
|
||||
bal, err := w.CalculateAccountBalance(account, int32(cmd.MinConf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2147,7 +2135,8 @@ func ListReceivedByAccount(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjso
|
|||
if err != nil {
|
||||
return nil, ErrAccountNameNotFound
|
||||
}
|
||||
bal, confirmations, err := w.TotalReceivedForAccount(account, cmd.MinConf)
|
||||
bal, confirmations, err := w.TotalReceivedForAccount(account,
|
||||
int32(cmd.MinConf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -2186,7 +2175,7 @@ func ListReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjso
|
|||
account string
|
||||
}
|
||||
|
||||
blk := w.Manager.SyncedTo()
|
||||
syncBlock := w.Manager.SyncedTo()
|
||||
|
||||
// Intermediate data for all addresses.
|
||||
allAddrData := make(map[string]AddrData)
|
||||
|
@ -2202,35 +2191,46 @@ func ListReceivedByAddress(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjso
|
|||
allAddrData[address] = AddrData{}
|
||||
}
|
||||
}
|
||||
for _, record := range w.TxStore.Records() {
|
||||
for _, credit := range record.Credits() {
|
||||
confirmations := credit.Confirmations(blk.Height)
|
||||
if !credit.Confirmed(cmd.MinConf, blk.Height) {
|
||||
// Not enough confirmations, skip the current block.
|
||||
continue
|
||||
}
|
||||
_, addresses, _, err := credit.Addresses(activeNet.Params)
|
||||
if err != nil {
|
||||
// Unusable address, skip it.
|
||||
continue
|
||||
}
|
||||
for _, address := range addresses {
|
||||
addrStr := address.EncodeAddress()
|
||||
addrData, ok := allAddrData[addrStr]
|
||||
if ok {
|
||||
addrData.amount += credit.Amount()
|
||||
// Always overwrite confirmations with newer ones.
|
||||
addrData.confirmations = confirmations
|
||||
} else {
|
||||
addrData = AddrData{
|
||||
amount: credit.Amount(),
|
||||
confirmations: confirmations,
|
||||
}
|
||||
|
||||
var endHeight int32
|
||||
if cmd.MinConf == -1 {
|
||||
endHeight = -1
|
||||
} else {
|
||||
endHeight = syncBlock.Height - int32(cmd.MinConf) + 1
|
||||
}
|
||||
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 {
|
||||
// Non standard script, skip.
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
addrStr := addr.EncodeAddress()
|
||||
addrData, ok := allAddrData[addrStr]
|
||||
if ok {
|
||||
addrData.amount += cred.Amount
|
||||
// Always overwrite confirmations with newer ones.
|
||||
addrData.confirmations = confirmations
|
||||
} else {
|
||||
addrData = AddrData{
|
||||
amount: cred.Amount,
|
||||
confirmations: confirmations,
|
||||
}
|
||||
}
|
||||
addrData.tx = append(addrData.tx, tx.Hash.String())
|
||||
allAddrData[addrStr] = addrData
|
||||
}
|
||||
addrData.tx = append(addrData.tx, credit.Tx().Sha().String())
|
||||
allAddrData[addrStr] = addrData
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Massage address data into output format.
|
||||
|
@ -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) {
|
||||
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 != "" {
|
||||
hash, err := wire.NewShaHashFromStr(cmd.BlockHash)
|
||||
if err != nil {
|
||||
|
@ -2265,18 +2272,11 @@ func ListSinceBlock(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
height = int32(block.Height())
|
||||
start = int32(block.Height()) + 1
|
||||
}
|
||||
end := syncBlock.Height - int32(cmd.TargetConfirmations) + 1
|
||||
|
||||
blk := 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(blk.Height) + 1 - int64(cmd.TargetConfirmations))
|
||||
|
||||
txInfoList, err := w.ListSinceBlock(height, blk.Height,
|
||||
cmd.TargetConfirmations)
|
||||
txInfoList, err := w.ListSinceBlock(start, end, syncBlock.Height)
|
||||
if err != nil {
|
||||
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.
|
||||
|
@ -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
|
||||
// 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,
|
||||
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
|
||||
// was not successful.
|
||||
|
@ -2415,41 +2416,44 @@ func sendPairs(w *wallet.Wallet, chainSvr *chain.Client, cmd btcjson.Cmd,
|
|||
if err != nil {
|
||||
switch {
|
||||
case err == wallet.ErrNonPositiveAmount:
|
||||
return nil, ErrNeedPositiveAmount
|
||||
return "", ErrNeedPositiveAmount
|
||||
case isManagerLockedError(err):
|
||||
return nil, btcjson.ErrWalletUnlockNeeded
|
||||
return "", btcjson.ErrWalletUnlockNeeded
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Add to transaction store.
|
||||
txr, err := w.TxStore.InsertTx(createdTx.Tx, nil)
|
||||
// Create transaction record and insert into the db.
|
||||
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 {
|
||||
log.Errorf("Error adding sent tx history: %v", err)
|
||||
return nil, btcjson.ErrInternal
|
||||
}
|
||||
_, err = txr.AddDebits()
|
||||
if err != nil {
|
||||
log.Errorf("Error adding sent tx history: %v", err)
|
||||
return nil, btcjson.ErrInternal
|
||||
return "", btcjson.ErrInternal
|
||||
}
|
||||
|
||||
if createdTx.ChangeIndex >= 0 {
|
||||
_, err = txr.AddCredit(uint32(createdTx.ChangeIndex), true)
|
||||
err = w.TxStore.AddCredit(rec, nil, uint32(createdTx.ChangeIndex), true)
|
||||
if err != nil {
|
||||
log.Errorf("Error adding change address for sent "+
|
||||
"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 {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
log.Infof("Successfully sent transaction %v", txSha)
|
||||
return txSha.String(), nil
|
||||
txShaStr := txSha.String()
|
||||
log.Infof("Successfully sent transaction %v", txShaStr)
|
||||
return txShaStr, nil
|
||||
}
|
||||
|
||||
// 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),
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -2504,7 +2508,7 @@ func SendMany(w *wallet.Wallet, chainSvr *chain.Client, icmd btcjson.Cmd) (inter
|
|||
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
|
||||
|
|
|
@ -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
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -18,10 +18,9 @@ package wallet
|
|||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/chain"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
)
|
||||
|
||||
func (w *Wallet) handleChainNotifications() {
|
||||
|
@ -45,10 +44,8 @@ func (w *Wallet) handleChainNotifications() {
|
|||
w.connectBlock(waddrmgr.BlockStamp(n))
|
||||
case chain.BlockDisconnected:
|
||||
err = w.disconnectBlock(waddrmgr.BlockStamp(n))
|
||||
case chain.RecvTx:
|
||||
err = w.addReceivedTx(n.Tx, n.Block)
|
||||
case chain.RedeemingTx:
|
||||
err = w.addRedeemingTx(n.Tx, n.Block)
|
||||
case chain.RelevantTx:
|
||||
err = w.addRelevantTx(n.TxRecord, n.Block)
|
||||
|
||||
// The following are handled by the wallet's rescan
|
||||
// goroutines, so just pass them there.
|
||||
|
@ -116,70 +113,88 @@ func (w *Wallet) disconnectBlock(bs waddrmgr.BlockStamp) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *txstore.Block) error {
|
||||
// For every output, if it pays to a wallet address, insert the
|
||||
// transaction into the store (possibly moving it from unconfirmed to
|
||||
// confirmed), and add a credit record if one does not already exist.
|
||||
var txr *txstore.TxRecord
|
||||
txInserted := false
|
||||
for txOutIdx, txOut := range tx.MsgTx().TxOut {
|
||||
// Errors don't matter here. If addrs is nil, the range below
|
||||
// does nothing.
|
||||
_, addrs, _, _ := txscript.ExtractPkScriptAddrs(txOut.PkScript,
|
||||
w.chainParams)
|
||||
insert := false
|
||||
for _, addr := range addrs {
|
||||
_, err := w.Manager.Address(addr)
|
||||
if err == nil {
|
||||
insert = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if insert {
|
||||
if !txInserted {
|
||||
var err error
|
||||
txr, err = w.TxStore.InsertTx(tx, block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// InsertTx may have moved a previous unmined
|
||||
// tx, so mark the entire store as dirty.
|
||||
w.TxStore.MarkDirty()
|
||||
txInserted = true
|
||||
}
|
||||
if txr.HasCredit(txOutIdx) {
|
||||
continue
|
||||
}
|
||||
_, err := txr.AddCredit(uint32(txOutIdx), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.TxStore.MarkDirty()
|
||||
}
|
||||
}
|
||||
func (w *Wallet) addRelevantTx(rec *wtxmgr.TxRecord, block *wtxmgr.BlockMeta) error {
|
||||
// TODO: The transaction store and address manager need to be updated
|
||||
// together, but each operate under different namespaces and are changed
|
||||
// under new transactions. This is not error safe as we lose
|
||||
// transaction semantics.
|
||||
//
|
||||
// I'm unsure of the best way to solve this. Some possible solutions
|
||||
// and drawbacks:
|
||||
//
|
||||
// 1. Open write transactions here and pass the handle to every
|
||||
// waddrmr and wtxmgr method. This complicates the caller code
|
||||
// everywhere, however.
|
||||
//
|
||||
// 2. Move the wtxmgr namespace into the waddrmgr namespace, likely
|
||||
// under its own bucket. This entire function can then be moved
|
||||
// into the waddrmgr package, which updates the nested wtxmgr.
|
||||
// This removes some of separation between the components.
|
||||
//
|
||||
// 3. Use multiple wtxmgrs, one for each account, nested in the
|
||||
// waddrmgr namespace. This still provides some sort of logical
|
||||
// separation (transaction handling remains in another package, and
|
||||
// is simply used by waddrmgr), but may result in duplicate
|
||||
// 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.
|
||||
|
||||
bs, err := w.chainSvr.BlockStamp()
|
||||
if err == nil {
|
||||
w.notifyBalances(bs.Height)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
if _, err := txr.AddDebits(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.markAddrsUsed(txr); err != nil {
|
||||
return err
|
||||
|
||||
// Check every output to determine whether it is controlled by a wallet
|
||||
// key. If so, mark the output as a credit.
|
||||
for i, output := range rec.MsgTx.TxOut {
|
||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript,
|
||||
w.chainParams)
|
||||
if err != nil {
|
||||
// Non-standard outputs are skipped.
|
||||
continue
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
ma, err := w.Manager.Address(addr)
|
||||
if err == nil {
|
||||
// TODO: Credits should be added with the
|
||||
// account they belong to, so wtxmgr is able to
|
||||
// track per-account balances.
|
||||
err = w.TxStore.AddCredit(rec, block, uint32(i),
|
||||
ma.Internal())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.Manager.MarkUsed(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Notify connected clients of the added transaction.
|
||||
|
||||
bs, err := w.chainSvr.BlockStamp()
|
||||
if err == nil {
|
||||
w.notifyBalances(bs.Height)
|
||||
|
|
|
@ -18,9 +18,9 @@ package wallet
|
|||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/walletdb"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
)
|
||||
|
||||
// Config is a structure used to initialize a Wallet
|
||||
|
@ -28,6 +28,6 @@ import (
|
|||
type Config struct {
|
||||
ChainParams *chaincfg.Params
|
||||
Db *walletdb.DB
|
||||
TxStore *txstore.Store
|
||||
TxStore *wtxmgr.Store
|
||||
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
|
||||
* 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/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -109,17 +109,17 @@ const defaultFeeIncrement = 1e3
|
|||
// CreatedTx holds the state of a newly-created transaction and the change
|
||||
// output (if one was added).
|
||||
type CreatedTx struct {
|
||||
Tx *btcutil.Tx
|
||||
MsgTx *wire.MsgTx
|
||||
ChangeAddr btcutil.Address
|
||||
ChangeIndex int // negative if no change
|
||||
}
|
||||
|
||||
// ByAmount defines the methods needed to satisify sort.Interface to
|
||||
// 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) 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] }
|
||||
|
||||
// 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
|
||||
// address. InsufficientFundsError is returned if there are not enough
|
||||
// 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
|
||||
// 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 selected inputs and the given outputs, validating it (using
|
||||
// validateMsgTx) as well.
|
||||
func createTx(eligible []txstore.Credit,
|
||||
func createTx(eligible []wtxmgr.Credit,
|
||||
outputs map[string]btcutil.Amount, bs *waddrmgr.BlockStamp,
|
||||
feeIncrement btcutil.Amount, mgr *waddrmgr.Manager, account uint32,
|
||||
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
|
||||
// desired outputs.
|
||||
var input txstore.Credit
|
||||
var inputs []txstore.Credit
|
||||
var input wtxmgr.Credit
|
||||
var inputs []wtxmgr.Credit
|
||||
totalAdded := btcutil.Amount(0)
|
||||
for totalAdded < minAmount {
|
||||
if len(eligible) == 0 {
|
||||
|
@ -186,8 +186,8 @@ func createTx(eligible []txstore.Credit,
|
|||
}
|
||||
input, eligible = eligible[0], eligible[1:]
|
||||
inputs = append(inputs, input)
|
||||
msgtx.AddTxIn(wire.NewTxIn(input.OutPoint(), nil))
|
||||
totalAdded += input.Amount()
|
||||
msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
|
||||
totalAdded += input.Amount
|
||||
}
|
||||
|
||||
// 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:]
|
||||
inputs = append(inputs, input)
|
||||
msgtx.AddTxIn(wire.NewTxIn(input.OutPoint(), nil))
|
||||
msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
|
||||
szEst += txInEstimate
|
||||
totalAdded += input.Amount()
|
||||
totalAdded += input.Amount
|
||||
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:]
|
||||
inputs = append(inputs, input)
|
||||
msgtx.AddTxIn(wire.NewTxIn(input.OutPoint(), nil))
|
||||
msgtx.AddTxIn(wire.NewTxIn(&input.OutPoint, nil))
|
||||
szEst += txInEstimate
|
||||
totalAdded += input.Amount()
|
||||
totalAdded += input.Amount
|
||||
feeEst = minimumFee(feeIncrement, szEst, msgtx.TxOut, inputs, bs.Height, disallowFree)
|
||||
}
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ func createTx(eligible []txstore.Credit,
|
|||
}
|
||||
|
||||
info := &CreatedTx{
|
||||
Tx: btcutil.NewTx(msgtx),
|
||||
MsgTx: msgtx,
|
||||
ChangeAddr: changeAddr,
|
||||
ChangeIndex: changeIdx,
|
||||
}
|
||||
|
@ -316,44 +316,59 @@ func addOutputs(msgtx *wire.MsgTx, pairs map[string]btcutil.Amount, chainParams
|
|||
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()
|
||||
if err != nil {
|
||||
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
|
||||
// in transactions and sent (for example, using createrawtransaction,
|
||||
// signrawtransaction, and sendrawtransaction).
|
||||
eligible := make([]txstore.Credit, 0, len(unspent))
|
||||
|
||||
// TODO: Eventually all of these filters (except perhaps output locking)
|
||||
// should be handled by the call to UnspentOutputs (or similar).
|
||||
// Because one of these filters requires matching the output script to
|
||||
// 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 {
|
||||
switch txscript.GetScriptClass(unspent[i].TxOut().PkScript) {
|
||||
case txscript.PubKeyHashTy:
|
||||
if !unspent[i].Confirmed(minconf, bs.Height) {
|
||||
continue
|
||||
}
|
||||
// Coinbase transactions must have have reached maturity
|
||||
// before their outputs may be spent.
|
||||
if unspent[i].IsCoinbase() {
|
||||
target := blockchain.CoinbaseMaturity
|
||||
if !unspent[i].Confirmed(target, bs.Height) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
output := &unspent[i]
|
||||
|
||||
// Locked unspent outputs are skipped.
|
||||
if w.LockedOutpoint(*unspent[i].OutPoint()) {
|
||||
// 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
|
||||
}
|
||||
if output.FromCoinBase {
|
||||
const target = blockchain.CoinbaseMaturity
|
||||
if !confirmed(target, output.Height, bs.Height) {
|
||||
continue
|
||||
}
|
||||
|
||||
creditAccount, err := w.CreditAccount(unspent[i])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if creditAccount == account {
|
||||
eligible = append(eligible, unspent[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Locked unspent outputs are skipped.
|
||||
if w.LockedOutpoint(output.OutPoint) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Filter out unspendable outputs, that is, remove those that
|
||||
// (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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
@ -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.
|
||||
// It must be called every time a msgtx is changed.
|
||||
// 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) {
|
||||
return fmt.Errorf(
|
||||
"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 {
|
||||
// Errors don't matter here, as we only consider the
|
||||
// case where len(addrs) == 1.
|
||||
_, addrs, _, _ := output.Addresses(chainParams)
|
||||
_, addrs, _, _ := txscript.ExtractPkScriptAddrs(output.PkScript,
|
||||
chainParams)
|
||||
if len(addrs) != 1 {
|
||||
continue
|
||||
}
|
||||
|
@ -391,7 +407,7 @@ func signMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit, mgr *waddrmgr.Ma
|
|||
}
|
||||
|
||||
sigscript, err := txscript.SignatureScript(msgtx, i,
|
||||
output.TxOut().PkScript, txscript.SigHashAll, privkey,
|
||||
output.PkScript, txscript.SigHashAll, privkey,
|
||||
ai.Compressed())
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func validateMsgTx(msgtx *wire.MsgTx, prevOutputs []txstore.Credit) error {
|
||||
func validateMsgTx(msgtx *wire.MsgTx, prevOutputs []wtxmgr.Credit) error {
|
||||
for i := range msgtx.TxIn {
|
||||
vm, err := txscript.NewEngine(prevOutputs[i].TxOut().PkScript,
|
||||
vm, err := txscript.NewEngine(prevOutputs[i].PkScript,
|
||||
msgtx, i, txscript.StandardVerifyFlags)
|
||||
if err != nil {
|
||||
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
|
||||
// less than 1 bitcent. Otherwise, the fee will be calculated using
|
||||
// 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
|
||||
if !disallowFree {
|
||||
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
|
||||
// priority reaches a certain threshold. If the threshhold is
|
||||
// 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 txSizeEstimate = 250.0
|
||||
const threshold = btcutil.SatoshiPerBitcoin * blocksPerDayEstimate / txSizeEstimate
|
||||
|
||||
var weightedSum int64
|
||||
for _, txout := range txouts {
|
||||
depth := chainDepth(txout.BlockHeight, curHeight)
|
||||
weightedSum += int64(txout.Amount()) * int64(depth)
|
||||
depth := chainDepth(txout.Height, curHeight)
|
||||
weightedSum += int64(txout.Amount) * int64(depth)
|
||||
}
|
||||
priority := float64(weightedSum) / float64(txSize)
|
||||
return priority > threshold
|
||||
|
|
|
@ -7,16 +7,18 @@ import (
|
|||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/blockchain"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/walletdb"
|
||||
_ "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.
|
||||
|
@ -80,7 +82,7 @@ func TestCreateTx(t *testing.T) {
|
|||
}
|
||||
|
||||
// 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:
|
||||
outputs := map[string]btcutil.Amount{outAddr1: 15e6, outAddr2: 10e6}
|
||||
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())
|
||||
}
|
||||
|
||||
msgTx := tx.Tx.MsgTx()
|
||||
msgTx := tx.MsgTx
|
||||
if len(msgTx.TxOut) != 3 {
|
||||
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) {
|
||||
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}
|
||||
account := uint32(0)
|
||||
changeAddr, _ := btcutil.DecodeAddress("muqW4gcixv58tVbSKRC5q6CRKy8RmyLgZ5", &chaincfg.TestNet3Params)
|
||||
|
@ -207,29 +209,36 @@ func newManager(t *testing.T, privKeys []string, bs *waddrmgr.BlockStamp) *waddr
|
|||
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.
|
||||
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)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tx, err := btcutil.NewTxFromBytes(serialized)
|
||||
utx, err := btcutil.NewTxFromBytes(serialized)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := txstore.New("/tmp/tx.bin")
|
||||
r, err := s.InsertTx(tx, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
tx := utx.MsgTx()
|
||||
|
||||
isCB := blockchain.IsCoinBaseTx(tx)
|
||||
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 {
|
||||
credit, err := r.AddCredit(idx, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
eligible[i] = credit
|
||||
c.OutPoint.Index = idx
|
||||
c.Amount = btcutil.Amount(tx.TxOut[idx].Value)
|
||||
c.PkScript = tx.TxOut[idx].PkScript
|
||||
c.Received = now
|
||||
c.FromCoinBase = isCB
|
||||
eligible[i] = c
|
||||
}
|
||||
return eligible
|
||||
}
|
||||
|
|
|
@ -20,8 +20,8 @@ import (
|
|||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/chain"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
)
|
||||
|
||||
// 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
|
||||
// current best block in the main chain, and is considered an initial sync
|
||||
// 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))
|
||||
for i, output := range unspent {
|
||||
outpoints[i] = output.OutPoint()
|
||||
outpoints[i] = &output.OutPoint
|
||||
}
|
||||
|
||||
job := &RescanJob{
|
||||
|
|
763
wallet/wallet.go
763
wallet/wallet.go
File diff suppressed because it is too large
Load diff
110
walletsetup.go
110
walletsetup.go
|
@ -31,17 +31,18 @@ import (
|
|||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcutil/hdkeychain"
|
||||
"github.com/btcsuite/btcwallet/legacy/keystore"
|
||||
"github.com/btcsuite/btcwallet/legacy/txstore"
|
||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||
"github.com/btcsuite/btcwallet/wallet"
|
||||
"github.com/btcsuite/btcwallet/walletdb"
|
||||
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
||||
"github.com/btcsuite/btcwallet/wtxmgr"
|
||||
"github.com/btcsuite/golangcrypto/ssh/terminal"
|
||||
)
|
||||
|
||||
// Namespace keys
|
||||
var (
|
||||
// waddrmgrNamespaceKey is the namespace key for the waddrmgr package.
|
||||
waddrmgrNamespaceKey = []byte("waddrmgr")
|
||||
wtxmgrNamespaceKey = []byte("wtxmgr")
|
||||
)
|
||||
|
||||
// networkDir returns the directory name of a network directory to hold wallet
|
||||
|
@ -558,9 +559,30 @@ func createSimulationWallet(cfg *config) error {
|
|||
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) {
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// 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.
|
||||
db, err := walletdb.Open("bdb", dbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &db, nil
|
||||
return walletdb.Open("bdb", dbPath)
|
||||
}
|
||||
|
||||
// openWaddrmgr returns an address manager given a database, namespace,
|
||||
// public pass and the chain params
|
||||
// openManagers opens and returns the wallet address and transaction managers.
|
||||
// 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
|
||||
func openWaddrmgr(db *walletdb.DB, namespaceKey []byte, pass string,
|
||||
chainParams *chaincfg.Params) (*waddrmgr.Manager, error) {
|
||||
|
||||
func openManagers(db walletdb.DB, pass string) (*waddrmgr.Manager, *wtxmgr.Store, error) {
|
||||
// Get the namespace for the address manager.
|
||||
namespace, err := (*db).Namespace(namespaceKey)
|
||||
namespace, err := db.Namespace(waddrmgrNamespaceKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
config := &waddrmgr.Options{
|
||||
ObtainSeed: promptSeed,
|
||||
ObtainPrivatePass: promptPrivPassPhrase,
|
||||
}
|
||||
// Open address manager and transaction store.
|
||||
// var txs *txstore.Store
|
||||
return waddrmgr.Open(namespace, []byte(pass),
|
||||
chainParams, config)
|
||||
addrMgr, err := waddrmgr.Open(namespace, []byte(pass), activeNet.Params, config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -610,35 +649,12 @@ func openWallet() (*wallet.Wallet, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var txs *txstore.Store
|
||||
mgr, err := openWaddrmgr(db, waddrmgrNamespaceKey, cfg.WalletPass,
|
||||
activeNet.Params)
|
||||
if err == nil {
|
||||
txs, err = txstore.OpenDir(netdir)
|
||||
}
|
||||
mgr, txs, err := openManagers(db, cfg.WalletPass)
|
||||
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
|
||||
}
|
||||
|
||||
txs = txstore.New(netdir)
|
||||
txs.MarkDirty()
|
||||
err = txs.WriteIfDirty()
|
||||
if err != nil {
|
||||
log.Errorf("%v", err)
|
||||
return nil, err
|
||||
}
|
||||
mgr.SetSyncedTo(nil)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
walletConfig := &wallet.Config{
|
||||
Db: db,
|
||||
Db: &db, // TODO: Remove the pointer
|
||||
TxStore: txs,
|
||||
Waddrmgr: mgr,
|
||||
ChainParams: activeNet.Params,
|
||||
|
|
Loading…
Reference in a new issue