Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add package wtxmgr to use walletdb interface #190

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
e47cdf0
Move keystore to legacy directory.
davecgh Oct 13, 2014
50dbda0
Switch to new waddrmgr package
davecgh Oct 29, 2014
7371fcf
Add conversion from legacy txstore if it exists.
davecgh Oct 1, 2014
f02f766
Turn echo off when reading passwords
tuxcanfly Oct 20, 2014
247033b
Return the memory after scrypt to the OS.
davecgh Dec 16, 2014
4040027
Prevent deadlock when changing passphrases.
jrick Jan 31, 2015
3cd3bb4
Merge branch 'master' into intwaddrmgr
jrick Feb 6, 2015
fc3c1bb
Merge branch 'master' into intwaddrmgr
jrick Feb 6, 2015
1a6694d
Implement several btcd RPCs in wallet as well.
jrick Dec 31, 2014
7a3b770
Use the correct synced block when calculating confs.
jrick Dec 31, 2014
9a24a96
Merge branch 'master' into intwaddrmgr
jrick Feb 17, 2015
dfc9d13
Merge branch 'master' into intwaddrmgr
jrick Feb 20, 2015
7cdb89d
Merge branch 'master' into intwaddrmgr
jrick Feb 24, 2015
ed46efc
Merge branch 'master' into intwaddrmgr
jrick Feb 24, 2015
87df9ab
Move rename to legacy
tuxcanfly Feb 10, 2015
9303ce1
Move txstore package to legacy directory
tuxcanfly Jan 16, 2015
5b6225c
Add pkg wtxmgr - new txstore with walletdb backend
tuxcanfly Jan 16, 2015
3408c8c
Updated wallet to use wtxmgr
tuxcanfly Jan 16, 2015
ed45d59
Address review items:
tuxcanfly Feb 12, 2015
b40cdd2
Address review items:
tuxcanfly Feb 16, 2015
e24db1a
Use a debits bucket for storing tx debits
tuxcanfly Feb 18, 2015
e33fd02
Use debit bucket for both confirmed/unconfirmed tx
tuxcanfly Feb 19, 2015
53e3f39
Use credit bucket for both unconfirmed/confirmed tx
tuxcanfly Feb 19, 2015
e9f305c
Update only credits/debits bucket on AddCredit/AddDebit
tuxcanfly Feb 19, 2015
1394f57
Use single bucket for all tx credits
tuxcanfly Feb 23, 2015
8f0fcee
Use TxStoreError for all errors returned by wtxmgr
tuxcanfly Feb 26, 2015
5a316ad
Update single credit at a time
tuxcanfly Feb 26, 2015
66c329b
Address review items:
tuxcanfly Feb 26, 2015
deb7030
Address review items:
tuxcanfly Feb 26, 2015
5735b15
Address review items:
tuxcanfly Feb 26, 2015
b473c75
Use meta bucket for maintaining block tx count
tuxcanfly Feb 26, 2015
97888ff
Address review items:
tuxcanfly Feb 27, 2015
4ef88df
Address review items:
tuxcanfly Feb 27, 2015
18898d4
Use fixed size buf for serializing credit
tuxcanfly Feb 27, 2015
a18a7f6
Added block, txrecord serialization benchmarks
tuxcanfly Feb 27, 2015
f5532e4
Updated doc and style for db funcs
tuxcanfly Feb 27, 2015
1054b45
Utilize buffer reuse in serialization
tuxcanfly Feb 27, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 11 additions & 34 deletions btcwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ func walletMain() error {
}()
}

// Load the wallet database. It must have been created with the
// --create option already or this will return an appropriate error.
wallet, err := openWallet()
if err != nil {
log.Errorf("%v", err)
return err
}
defer wallet.db.Close()

// Create and start HTTP server to serve wallet client connections.
// This will be updated with the wallet and chain server RPC client
// created below after each is created.
Expand All @@ -78,6 +87,7 @@ func walletMain() error {
return err
}
server.Start()
server.SetWallet(wallet)

// Shutdown the server if an interrupt signal is received.
addInterruptHandler(server.Stop)
Expand Down Expand Up @@ -121,49 +131,16 @@ func walletMain() error {
chainSvrChan <- rpcc
}()

// Create a channel to report unrecoverable errors during the loading of
// the wallet files. These may include OS file handling errors or
// issues deserializing the wallet files, but does not include missing
// wallet files (as that must be handled by creating a new wallet).
walletOpenErrors := make(chan error)

go func() {
defer close(walletOpenErrors)

// Open wallet structures from disk.
w, err := openWallet()
if err != nil {
if os.IsNotExist(err) {
// If the keystore file is missing, notify the server
// that generating new wallets is ok.
server.SetWallet(nil)
return
}
// If the keystore file exists but another error was
// encountered, we cannot continue.
log.Errorf("Cannot load wallet files: %v", err)
walletOpenErrors <- err
return
}

server.SetWallet(w)

// Start wallet goroutines and handle RPC client notifications
// if the chain server connection was opened.
select {
case chainSvr := <-chainSvrChan:
w.Start(chainSvr)
wallet.Start(chainSvr)
case <-server.quit:
}
}()

// Check for unrecoverable errors during the wallet startup, and return
// the error, if any.
err, ok := <-walletOpenErrors
if ok {
return err
}

// Wait for the server to shutdown either due to a stop RPC request
// or an interrupt.
server.WaitForShutdown()
Expand Down
41 changes: 20 additions & 21 deletions chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcrpcclient"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/keystore"
"github.com/btcsuite/btcwallet/txstore"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wtxmgr"
)

// Client represents a persistent client connection to a bitcoin RPC server
Expand All @@ -38,7 +38,7 @@ type Client struct {

enqueueNotification chan interface{}
dequeueNotification chan interface{}
currentBlock chan *keystore.BlockStamp
currentBlock chan *waddrmgr.BlockStamp

// Notification channels regarding the state of the client. These exist
// so other components can listen in on chain activity. These are
Expand All @@ -64,7 +64,7 @@ func NewClient(chainParams *chaincfg.Params, connect, user, pass string, certs [
chainParams: chainParams,
enqueueNotification: make(chan interface{}),
dequeueNotification: make(chan interface{}),
currentBlock: make(chan *keystore.BlockStamp),
currentBlock: make(chan *waddrmgr.BlockStamp),
notificationLock: new(sync.Mutex),
quit: make(chan struct{}),
}
Expand Down Expand Up @@ -157,24 +157,23 @@ func (c *Client) WaitForShutdown() {
type (
// BlockConnected is a notification for a newly-attached block to the
// best chain.
BlockConnected keystore.BlockStamp
BlockConnected waddrmgr.BlockStamp

// BlockDisconnected is a notifcation that the block described by the
// BlockStamp was reorganized out of the best chain.
BlockDisconnected keystore.BlockStamp

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
Tx *btcutil.Tx // Index is guaranteed to be set.
Block *wtxmgr.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
Tx *btcutil.Tx // Index is guaranteed to be set.
Block *wtxmgr.Block // nil if unmined
}

// RescanProgress is a notification describing the current status
Expand Down Expand Up @@ -204,7 +203,7 @@ func (c *Client) Notifications() <-chan interface{} {

// BlockStamp returns the latest block notified by the client, or an error
// if the client has been shut down.
func (c *Client) BlockStamp() (*keystore.BlockStamp, error) {
func (c *Client) BlockStamp() (*waddrmgr.BlockStamp, error) {
select {
case bs := <-c.currentBlock:
return bs, nil
Expand All @@ -214,17 +213,17 @@ func (c *Client) BlockStamp() (*keystore.BlockStamp, error) {
}

// 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.
func parseBlock(block *btcws.BlockDetails) (blk *txstore.Block, idx int, err error) {
func parseBlock(block *btcws.BlockDetails) (blk *wtxmgr.Block, idx int, err error) {
if block == nil {
return nil, btcutil.TxIndexUnknown, nil
}
blksha, err := wire.NewShaHashFromStr(block.Hash)
if err != nil {
return nil, btcutil.TxIndexUnknown, err
}
blk = &txstore.Block{
blk = &wtxmgr.Block{
Height: block.Height,
Hash: *blksha,
Time: time.Unix(block.Time, 0),
Expand All @@ -238,15 +237,15 @@ func (c *Client) onClientConnect() {
}

func (c *Client) onBlockConnected(hash *wire.ShaHash, height int32) {
c.enqueueNotification <- BlockConnected{Hash: hash, Height: height}
c.enqueueNotification <- BlockConnected{Hash: *hash, Height: height}
}

func (c *Client) onBlockDisconnected(hash *wire.ShaHash, height int32) {
c.enqueueNotification <- BlockDisconnected{Hash: hash, Height: height}
c.enqueueNotification <- BlockDisconnected{Hash: *hash, Height: height}
}

func (c *Client) onRecvTx(tx *btcutil.Tx, block *btcws.BlockDetails) {
var blk *txstore.Block
var blk *wtxmgr.Block
index := btcutil.TxIndexUnknown
if block != nil {
var err error
Expand All @@ -262,7 +261,7 @@ func (c *Client) onRecvTx(tx *btcutil.Tx, block *btcws.BlockDetails) {
}

func (c *Client) onRedeemingTx(tx *btcutil.Tx, block *btcws.BlockDetails) {
var blk *txstore.Block
var blk *wtxmgr.Block
index := btcutil.TxIndexUnknown
if block != nil {
var err error
Expand Down Expand Up @@ -294,7 +293,7 @@ func (c *Client) handler() {
c.wg.Done()
}

bs := &keystore.BlockStamp{Hash: hash, Height: height}
bs := &waddrmgr.BlockStamp{Hash: *hash, Height: height}

// TODO: Rather than leaving this as an unbounded queue for all types of
// notifications, try dropping ones where a later enqueued notification
Expand Down Expand Up @@ -329,7 +328,7 @@ out:

case dequeue <- next:
if n, ok := next.(BlockConnected); ok {
bs = (*keystore.BlockStamp)(&n)
bs = (*waddrmgr.BlockStamp)(&n)
}

notifications[0] = nil
Expand Down
50 changes: 26 additions & 24 deletions chainntfns.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/keystore"
"github.com/btcsuite/btcwallet/txstore"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wtxmgr"
)

func (w *Wallet) handleChainNotifications() {
for n := range w.chainSvr.Notifications() {
var err error
switch n := n.(type) {
case chain.BlockConnected:
w.connectBlock(keystore.BlockStamp(n))
w.connectBlock(waddrmgr.BlockStamp(n))
case chain.BlockDisconnected:
w.disconnectBlock(keystore.BlockStamp(n))
w.disconnectBlock(waddrmgr.BlockStamp(n))
case chain.RecvTx:
err = w.addReceivedTx(n.Tx, n.Block)
case chain.RedeemingTx:
Expand All @@ -53,13 +53,16 @@ func (w *Wallet) handleChainNotifications() {
// connectBlock handles a chain server notification by marking a wallet
// that's currently in-sync with the chain server as being synced up to
// the passed block.
func (w *Wallet) connectBlock(bs keystore.BlockStamp) {
func (w *Wallet) connectBlock(bs waddrmgr.BlockStamp) {
if !w.ChainSynced() {
return
}

w.KeyStore.SetSyncedWith(&bs)
w.KeyStore.MarkDirty()
if err := w.Manager.SetSyncedTo(&bs); err != nil {
log.Errorf("failed to update address manager sync state in "+
"connect block for hash %v (height %d): %v", bs.Hash,
bs.Height, err)
}
w.notifyConnectedBlock(bs)

w.notifyBalances(bs.Height)
Expand All @@ -68,33 +71,37 @@ func (w *Wallet) connectBlock(bs keystore.BlockStamp) {
// disconnectBlock handles a chain server reorganize by rolling back all
// block history from the reorged block for a wallet in-sync with the chain
// server.
func (w *Wallet) disconnectBlock(bs keystore.BlockStamp) {
func (w *Wallet) disconnectBlock(bs waddrmgr.BlockStamp) {
if !w.ChainSynced() {
return
}

// Disconnect the last seen block from the keystore if it
// matches the removed block.
iter := w.KeyStore.NewIterateRecentBlocks()
if iter != nil && *iter.BlockStamp().Hash == *bs.Hash {
// Disconnect the last seen block from the manager if it matches the
// removed block.
iter := w.Manager.NewIterateRecentBlocks()
if iter != nil && iter.BlockStamp().Hash == bs.Hash {
if iter.Prev() {
prev := iter.BlockStamp()
w.KeyStore.SetSyncedWith(&prev)
w.Manager.SetSyncedTo(&prev)
} else {
w.KeyStore.SetSyncedWith(nil)
// The reorg is farther back than the recently-seen list
// of blocks has recorded, so set it to unsynced which
// will in turn lead to a rescan from either the
// earliest blockstamp the addresses in the manager are
// known to have been created.
w.Manager.SetSyncedTo(nil)
}
w.KeyStore.MarkDirty()
}
w.notifyDisconnectedBlock(bs)

w.notifyBalances(bs.Height - 1)
}

func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *txstore.Block) error {
func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *wtxmgr.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
var txr *wtxmgr.TxRecord
txInserted := false
for txOutIdx, txOut := range tx.MsgTx().TxOut {
// Errors don't matter here. If addrs is nil, the range below
Expand All @@ -103,7 +110,7 @@ func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *txstore.Block) error {
activeNet.Params)
insert := false
for _, addr := range addrs {
_, err := w.KeyStore.Address(addr)
_, err := w.Manager.Address(addr)
if err == nil {
insert = true
break
Expand All @@ -116,9 +123,6 @@ func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *txstore.Block) error {
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) {
Expand All @@ -128,7 +132,6 @@ func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *txstore.Block) error {
if err != nil {
return err
}
w.TxStore.MarkDirty()
}
}

Expand All @@ -142,15 +145,14 @@ func (w *Wallet) addReceivedTx(tx *btcutil.Tx, block *txstore.Block) error {

// 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 {
func (w *Wallet) addRedeemingTx(tx *btcutil.Tx, block *wtxmgr.Block) error {
txr, err := w.TxStore.InsertTx(tx, block)
if err != nil {
return err
}
if _, err := txr.AddDebits(); err != nil {
return err
}
w.KeyStore.MarkDirty()

bs, err := w.chainSvr.BlockStamp()
if err == nil {
Expand Down
Loading