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.
- Instead of returning a special constructed type whenever queries for an
address. Return the internal object with an immutable external
interface.
- Make the private key gettable from PubKeyAddress to prevent having to look up
multiple times to get information from the same structure
- Enforce addresses always have public keys.
Shortly we will add new types of address, so make AddressInfo an
interface, with concrete types providing address-specific information.
Adapt existing code to this new status quo.
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.
Use the new confirmed function to test whether unspent transaction
outputs are confirmed enough to be used as a possible transaction
inputs instead of the old check (which was incorrect and required an
extra confirmation).
Modified the test to require one confirmation instead of none so the
confirmed enough check actually occurs.
Previous to this commit, all change addresses were indistinguishable
from manually requested addresses. This adds a new function to return
the new address, setting a new change flag to true, and return the
change status with the AddressInfo.
This is needed as part of resolving #41 (getrawchangeaddress).
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.
Boolean options cannot be unset from a default true value on the
command line, so invert the allowfree option, renaming it
disallowfree, so attaching fees may always be forced by specifying
disallowfree = true in the configuration file, or --disallowfree on
the command line.
It may be desirable to never allow free transactions, even if the
calculated priority is high enough that a fee would not be required,
so this change adds a global configuration option to remove this check
and always attach a fee.
This change better organizes account handling by creating a new
AccountStore type and accountstore global variable, with receiver
funcs for all operations that require all accounts. More Account
funcs are also added to clean up account handling in the RPC code.
Intial work on this done by dhill.
This change adds support for the listtransactions RPC command. To
properly reply to this command, additonal information about received
transactions was added, and is now saved in an account's tx.bin file.
Additionally, when sending a transaction, a *tx.SendTx is now saved to
the Tx store, and is included in listtransactions replies under the
"send" category.
WARNING: All account's tx.bin and utxo.bin files should be removed
before running with this change, or else the files may not be read
correctly. Removing tx.bin is not an issue as it was not being used
before, and was being saved with incorrect data. Removing utxo.bin is
not an issue as it will just trigger a rescan on next start. File
format versions are now included in both files, so automatic updates
from previous file formats will be possible with future changes.
Fixes#12.
This change moves the handlers to a map (instead of falling through a
switch statement), and updates each handler to use a btcjson.Cmd
instead of passing parameters in a btcjson.Message manually.
Plenty of comments were also added, which should also make the code
much more understandable.
This removes the enforced check for the spent field for tx-to-me
notifications, as this is no longer sent, and should be calculated by
wallet (not done yet). Additionally, the full CreatedTx information
is saved with the unmined tx map, so when a tx is mined, information
about which inputs and ouputs it creates that are relevant to the
wallet can be used.
Wallets that include compressed pubkeys are no longer compatible with
armory, however, imported wallets from armory (using uncompressed
pubkeys) are still valid.
When a wallet is opened, a rescan request will be sent to btcd with
all active addresses from the wallet, to rescan from the last synced
block (now saved to the wallet file) and the current best block.
As multi-account support is further explored, rescan requests should
be batched together to send a single request for all addresses from
all wallets.
This change introduces several changes to the wallet, tx, and utxo
files. Wallet files are still compatible, however, a rescan will try
to start at the genesis block since no correct "last synced to" or
"created at block X" was saved. The tx and utxo files, however, are
not compatible and should be deleted (or an error will occur on read).
If any errors occur opening the utxo file, a rescan will start
beginning at the creation block saved in the wallet.
bitcoin-qt uses "Balance" to mean all spendable outputs that have been
mined into a block, while "Unconfirmed" only refers to outputs that
have not yet been mined into a block. This change copies that
behavior, instead of the previous required 6 blocks to be confirmed.
This change fixes many issues with the tracking of unspent transaction
outputs. First, notifications for when UTXOs arse spent are now
requested from btcd, and when spent, will be removed from the
UtxoStore.
Second, when transactions are created, the unconfirmed (not yet in a
block) Utxo (with block height -1 and zeroed block hash) is added to
the wallet's UtxoStore. Notifications for when this UTXO is spent are
also requested from btcd. After the tx appears in a block, because
the UTXO has a pkScript to be spent by another owned wallet address, a
notification with the UTXO will be sent to btcwallet. We already
store the unconfirmed UTXO, so at this point the actual block height
and hash are filled in.
Finally, when calculating the balance, if confirmations is zero,
unconfirmed UTXOs (block height -1) will be included in the balance.
Otherwise, they are ignored.
This is a big change that also many general fixes to problems found
when creating transactions. In particular the Utxo and Tx formats and
serialization functions were updated with additional information that
would be necessary for rolling back old utxo and tx data data after
btcd chain switches. This change also implements the json methods
'sendfrom' and 'sendmany' to create a new transaction based on a
frontend request.
Transactions are currently not sent to btcd since the tx relay code is
not finished yet, so a temporary error is returned back to frontends
who try to send new transactions.