Commit graph

9 commits

Author SHA1 Message Date
Josh Rickmar
e9bdf2a094 Another day, another tx store implementation.
The last transaction store was a great example of how not to write
scalable software.  For a variety of reasons, it was very slow at
processing transaction inserts.  Among them:

1) Every single transaction record being saved in a linked list
   (container/list), and inserting into this list would be an O(n)
   operation so that records could be ordered by receive date.

2) Every single transaction in the above mentioned list was iterated
   over in order to find double spends which must be removed.  It is
   silly to do this check for mined transactions, which already have
   been checked for this by btcd.  Worse yet, if double spends were
   found, the list would be iterated a second (or third, or fourth)
   time for each removed transaction.

3) All spend tracking for signed-by-wallet transactions was found on
   each transaction insert, even if the now spent previous transaction
   outputs were known by the caller.

This list could keep going on, but you get the idea.  It was bad.

To resolve these issues a new transaction store had to be implemented.
The new implementation:

1) Tracks mined and unmined transactions in different data structures.
   Mined transactions are cheap to track because the required double
   spend checks have already been performed by the chain server, and
   double spend checks are only required to be performed on
   newly-inserted mined transactions which may conflict with previous
   unmined transactions.

2) Saves mined transactions grouped by block first, and then by their
   transaction index.  Lookup keys for mined transactions are simply
   the block height (in the best chain, that's all we save) and index
   of the transaction in the block.  This makes looking up any
   arbitrary transaction almost an O(1) operation (almost, because
   block height and block indexes are mapped to their slice indexes
   with a Go map).

3) Saves records in each transaction for whether the outputs are
   wallet credits (spendable by wallet) and for whether inputs debit
   from previous credits.  Both structures point back to the source
   or spender (credits point to the transaction that spends them, or
   nil for unspent credits, and debits include keys to lookup the
   transaction credits they spent.  While complicated to keep track
   of, this greatly simplifies the spent tracking for transactions
   across rollbacks and transaction removals.

4) Implements double spend checking as an almost O(1) operation.  A
   Go map is used to map each previous outpoint for all unconfirmed
   transactions to the unconfirmed tx record itself.  Checking for
   double spends on confirmed transaction inserts only involves
   looking up each previous outpoint of the inserted tx in this map.
   If a double spend is found, removal is simplified by only
   removing the transaction and its spend chain from store maps,
   rather than iterating a linked list several times over to remove
   each dead transaction in the spend chain.

5) Allows the caller to specify the previous credits which are spent
   by a debiting transaction.  When a transaction is created by
   wallet, the previous outputs are already known, and by passing
   their record types to the AddDebits method, lookups for each
   previously unspent credit are omitted.

6) Bookkeeps all blocks with transactions with unspent credits, and
   bookkeeps the transaction indexes of all transactions with unspent
   outputs for a single block.  For the case where the caller adding a
   debit record does not know what credits a transaction debits from,
   these bookkeeping structures allow the store to only consider known
   unspent transactions, rather than searching through both spent and
   unspents.

7) Saves amount deltas for the entire balance as a result of each
   block, due to transactions within that block.  This improves the
   performance of calculating the full balance by not needing to
   iterate over every transaction, and then every credit, to determine
   if a credit is spent or unspent.  When transactions are moved from
   unconfirmed to a block structure, the amount deltas are incremented
   by the amount of all transaction credits (both spent and unspent)
   and debited by the total amount the transaction spends from
   previous wallet credits.  For the common case of calculating a
   balance with just one confirmation, the only involves iterating
   over each block structure and adding the (possibly negative)
   amount delta.  Coinbase rewards are saved similarly, but with a
   different amount variable so they can be seperatly included or
   excluded.

Due to all of the changes in how the store internally works, the
serialization format has changed.  To simplify the serialization
logic, support for reading the last store file version has been
removed.  Past this change, a rescan (run automatically) will be
required to rebuild the transaction history.
2014-05-05 16:12:05 -05:00
Owain G. Ainsworth
35bd7ef6d9 rework the account manager somewhat.
- Move the MarkAddresForAccount and LookupAccountByAddress functionality
into account maanger.

- Move the wallet opeing logic into account manager (the only place that calls
it) and unexport.

- Move accountHandler to using a single channel for commands. Many of
the commands have ordering restraints (add account, list all accounts,
remove account, access account, mark account for address) which are very
much undefined with the multi-channel model.

- Rework all callers of LookupAccountByAddress to get the account structure
directly.
2014-04-03 17:52:16 +01:00
Josh Rickmar
189df5c535 Mark partially synced addresses.
This change adds a notification handler for the new rescanprogress
notification and takes advantage of the recent rescan manager and
partial syncing support to mark addresses as partially synced.  If the
network connection to btcd is lost or wallet is restarted during a
rescan, a new rescan will start at the earliest block height for any
wallet address, taking partial syncs into consideration.
2014-03-28 11:28:59 -05:00
Josh Rickmar
d179af8ecf Use min of now and blocktime for tx recv time. 2014-03-18 15:31:16 -05:00
Josh Rickmar
089fa9de18 Rescan and track sync status of imported addresses.
The private key import codepath (called when handling the
importprivkey RPC method) was not triggering rescans for the imported
address.  This change begins a new rescan for each import and adds
additional logic to the wallet file to keep track of unsynced imported
addresses.  After a rescan on an imported address completes, the
address is marked as in sync with the rest of wallet and future
handshake rescans will start from the last seen block, rather than the
import height of the unsynced address.

While here, improve the logging for not just import rescans, but
rescanning on btcd connect (part of the handshake) as well.

Fixes #74.
2014-03-17 13:46:42 -05:00
Josh Rickmar
2e76bcd159 Handle out-of-order notifications from btcd.
Notifications ariving from btcd were being reordered (each handled by
its own goroutine, rather then being always sent in the order they
originated).  This was breaking the new transaction store by inserting
transaction records in an 'impossible' manner, that is, inserting txs
without block info after the store already held records of the same tx
with block info, without first performing a rollback.

This is handled by the transaction store insert methods by checking
for identical transactions (double spends with the same tx sha), but
where the block heights mismatch and the new record does not have a
block set.  The error is returned all the way up to the goroutine
running each rpc request/notification handler, and if hit, the btcd
connection is closed and all accounts are reopened from disk.  This is
not optimal, but it allows us to use the connect logic to correctly
catch us up to the best chain with the last good state of all accounts
while only rescanning a few blocks.

Fixes #72.
2014-02-28 15:43:50 -05:00
Josh Rickmar
fc2e313a39 Introduce new transaction store.
This change replaces the old transaction store file format and
implementation.  The most important change is how the full backing
transactions for any received or sent transaction are now saved,
rather than simply saving parsed-out details of the tx (tx shas, block
height/hash, pkScripts, etc.).

To support the change, notifications for received transaction outputs
and txs spending watched outpoints have been updated to use the new
redeemingtx and recvtx notifications as these contain the full tx,
which is deserializead and inserted into the store.

The old transaction store serialization code is completely removed, as
updating to the new format automatically cannot be done.  Old wallets
first running past this change will error reading the file and start a
full rescan to rebuild the data.  Unlike previous rescan code,
transactions spending outpoint managed by wallet are also included.
This results in recovering not just received history, but history for
sent transactions as well.
2014-02-24 14:35:30 -05:00
Josh Rickmar
e837ca5b64 Cleanly remove disconnected clients.
Also fixes a bug where responses for a single client would be sent to
every connected client.
2014-02-18 12:39:53 -05:00
Josh Rickmar
6a08c7de07 Redo account locking and RPC request processing.
This change removes the three separate mutexes which used to lock an
account's wallet, tx store, and utxo store.  Accounts no longer
contain any locking mechanism and rely on go's other synchronization
constructs (goroutines and channels) for correct access.

All accounts are now managed as a collection through the new
AccountManager, rather than the old AccountStore.  AccountManager runs
as its own goroutine to provide access to accounts.

RPC requests are now queued for handling, being denied if the queue
buffer is exhausted.  Notifications are also queued (instead of being
sent from their own goroutine after being received, in which order is
undefined), however, notifications are never dropped and will
potentially grow a queue of infinite size if unhandled.
2014-02-01 13:18:34 -05:00
Renamed from btcdrpc.go (Browse further)