2016-02-28 05:30:56 +01:00
|
|
|
// Copyright (c) 2013-2016 The btcsuite developers
|
2015-12-01 19:44:58 +01:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
2013-09-09 20:14:57 +02:00
|
|
|
|
2015-04-02 20:13:38 +02:00
|
|
|
package wallet
|
2013-09-09 20:14:57 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2014-05-23 04:16:50 +02:00
|
|
|
"sort"
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
"github.com/btcsuite/btcd/btcec"
|
2015-01-30 19:31:29 +01:00
|
|
|
"github.com/btcsuite/btcd/txscript"
|
2015-02-05 22:41:38 +01:00
|
|
|
"github.com/btcsuite/btcd/wire"
|
2015-01-15 17:48:58 +01:00
|
|
|
"github.com/btcsuite/btcutil"
|
2014-10-29 07:43:29 +01:00
|
|
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
2016-02-28 05:30:56 +01:00
|
|
|
"github.com/btcsuite/btcwallet/wallet/txauthor"
|
2015-04-06 21:03:24 +02:00
|
|
|
"github.com/btcsuite/btcwallet/wtxmgr"
|
2013-09-09 20:14:57 +02:00
|
|
|
)
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
// byAmount defines the methods needed to satisify sort.Interface to
|
|
|
|
// sort credits by their output amount.
|
|
|
|
type byAmount []wtxmgr.Credit
|
2014-09-25 19:27:18 +02:00
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
func (s byAmount) Len() int { return len(s) }
|
|
|
|
func (s byAmount) Less(i, j int) bool { return s[i].Amount < s[j].Amount }
|
|
|
|
func (s byAmount) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
2014-09-25 19:27:18 +02:00
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
func makeInputSource(eligible []wtxmgr.Credit) txauthor.InputSource {
|
|
|
|
// Pick largest outputs first. This is only done for compatibility with
|
|
|
|
// previous tx creation code, not because it's a good idea.
|
|
|
|
sort.Sort(sort.Reverse(byAmount(eligible)))
|
2014-09-25 19:27:18 +02:00
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
// Current inputs and their total value. These are closed over by the
|
|
|
|
// returned input source and reused across multiple calls.
|
|
|
|
currentTotal := btcutil.Amount(0)
|
|
|
|
currentInputs := make([]*wire.TxIn, 0, len(eligible))
|
|
|
|
currentScripts := make([][]byte, 0, len(eligible))
|
2014-09-25 19:27:18 +02:00
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
return func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn, [][]byte, error) {
|
|
|
|
for currentTotal < target && len(eligible) != 0 {
|
|
|
|
nextCredit := &eligible[0]
|
|
|
|
eligible = eligible[1:]
|
2016-04-13 06:27:32 +02:00
|
|
|
nextInput := wire.NewTxIn(&nextCredit.OutPoint, nil, nil)
|
2016-02-28 05:30:56 +01:00
|
|
|
currentTotal += nextCredit.Amount
|
|
|
|
currentInputs = append(currentInputs, nextInput)
|
|
|
|
currentScripts = append(currentScripts, nextCredit.PkScript)
|
|
|
|
}
|
|
|
|
return currentTotal, currentInputs, currentScripts, nil
|
|
|
|
}
|
2014-09-25 19:27:18 +02:00
|
|
|
}
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
// secretSource is an implementation of txauthor.SecretSource for the wallet's
|
|
|
|
// address manager.
|
|
|
|
type secretSource struct {
|
|
|
|
*waddrmgr.Manager
|
2014-06-20 18:58:21 +02:00
|
|
|
}
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
func (s secretSource) GetKey(addr btcutil.Address) (*btcec.PrivateKey, bool, error) {
|
|
|
|
ma, err := s.Address(addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, err
|
2014-06-20 18:58:21 +02:00
|
|
|
}
|
2016-02-28 05:30:56 +01:00
|
|
|
mpka, ok := ma.(waddrmgr.ManagedPubKeyAddress)
|
|
|
|
if !ok {
|
|
|
|
e := fmt.Errorf("managed address type for %v is `%T` but "+
|
|
|
|
"want waddrmgr.ManagedPubKeyAddress", addr, ma)
|
|
|
|
return nil, false, e
|
|
|
|
}
|
|
|
|
privKey, err := mpka.PrivKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
return privKey, ma.Compressed(), nil
|
2014-06-20 18:58:21 +02:00
|
|
|
}
|
2013-09-09 20:14:57 +02:00
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
func (s secretSource) GetScript(addr btcutil.Address) ([]byte, error) {
|
|
|
|
ma, err := s.Address(addr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
msa, ok := ma.(waddrmgr.ManagedScriptAddress)
|
|
|
|
if !ok {
|
|
|
|
e := fmt.Errorf("managed address type for %v is `%T` but "+
|
|
|
|
"want waddrmgr.ManagedScriptAddress", addr, ma)
|
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
return msa.Script()
|
2013-10-24 00:23:20 +02:00
|
|
|
}
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
// txToOutputs creates a signed transaction which includes each output from
|
|
|
|
// outputs. Previous outputs to reedeem are chosen from the passed account's
|
|
|
|
// UTXO set and minconf policy. An additional output may be added to return
|
|
|
|
// change to the wallet. An appropriate fee is included based on the wallet's
|
|
|
|
// current relay fee. The wallet must be unlocked to create the transaction.
|
|
|
|
func (w *Wallet) txToOutputs(outputs []*wire.TxOut, account uint32, minconf int32) (*txauthor.AuthoredTx, error) {
|
2014-10-29 07:43:29 +01:00
|
|
|
// Address manager must be unlocked to compose transaction. Grab
|
|
|
|
// the unlock if possible (to prevent future unlocks), or return the
|
|
|
|
// error if already locked.
|
Remove account support, fix races on btcd connect.
This commit is the result of several big changes being made to the
wallet. In particular, the "handshake" (initial sync to the chain
server) was quite racy and required proper synchronization. To make
fixing this race easier, several other changes were made to the
internal wallet data structures and much of the RPC server ended up
being rewritten.
First, all account support has been removed. The previous Account
struct has been replaced with a Wallet structure, which includes a
keystore for saving keys, and a txstore for storing relevant
transactions. This decision has been made since it is the opinion of
myself and other developers that bitcoind accounts are fundamentally
broken (as accounts implemented by bitcoind support both arbitrary
address groupings as well as moving balances between accounts -- these
are fundamentally incompatible features), and since a BIP0032 keystore
is soon planned to be implemented (at which point, "accounts" can
return as HD extended keys). With the keystore handling the grouping
of related keys, there is no reason have many different Account
structs, and the AccountManager has been removed as well. All RPC
handlers that take an account option will only work with "" (the
default account) or "*" if the RPC allows specifying all accounts.
Second, much of the RPC server has been cleaned up. The global
variables for the RPC server and chain server client have been moved
to part of the rpcServer struct, and the handlers for each RPC method
that are looked up change depending on which components have been set.
Passthrough requests are also no longer handled specially, but when
the chain server is set, a handler to perform the passthrough will be
returned if the method is not otherwise a wallet RPC. The
notification system for websocket clients has also been rewritten so
wallet components can send notifications through channels, rather than
requiring direct access to the RPC server itself, or worse still,
sending directly to a websocket client's send channel. In the future,
this will enable proper registration of notifications, rather than
unsolicited broadcasts to every connected websocket client (see
issue #84).
Finally, and the main reason why much of this cleanup was necessary,
the races during intial sync with the chain server have been fixed.
Previously, when the 'Handshake' was run, a rescan would occur which
would perform modifications to Account data structures as
notifications were received. Synchronization was provided with a
single binary semaphore which serialized all access to wallet and
account data. However, the Handshake itself was not able to run with
this lock (or else notifications would block), and many data races
would occur as both notifications were being handled. If GOMAXPROCS
was ever increased beyond 1, btcwallet would always immediately crash
due to invalid addresses caused by the data races on startup. To fix
this, the single lock for all wallet access has been replaced with
mutexes for both the keystore and txstore. Handling of btcd
notifications and client requests may now occur simultaneously.
GOMAXPROCS has also been set to the number of logical CPUs at the
beginning of main, since with the data races fixed, there's no reason
to prevent the extra parallelism gained by increasing it.
Closes #78.
Closes #101.
Closes #110.
2014-07-09 05:17:38 +02:00
|
|
|
heldUnlock, err := w.HoldUnlock()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2014-03-20 17:21:52 +01:00
|
|
|
}
|
Remove account support, fix races on btcd connect.
This commit is the result of several big changes being made to the
wallet. In particular, the "handshake" (initial sync to the chain
server) was quite racy and required proper synchronization. To make
fixing this race easier, several other changes were made to the
internal wallet data structures and much of the RPC server ended up
being rewritten.
First, all account support has been removed. The previous Account
struct has been replaced with a Wallet structure, which includes a
keystore for saving keys, and a txstore for storing relevant
transactions. This decision has been made since it is the opinion of
myself and other developers that bitcoind accounts are fundamentally
broken (as accounts implemented by bitcoind support both arbitrary
address groupings as well as moving balances between accounts -- these
are fundamentally incompatible features), and since a BIP0032 keystore
is soon planned to be implemented (at which point, "accounts" can
return as HD extended keys). With the keystore handling the grouping
of related keys, there is no reason have many different Account
structs, and the AccountManager has been removed as well. All RPC
handlers that take an account option will only work with "" (the
default account) or "*" if the RPC allows specifying all accounts.
Second, much of the RPC server has been cleaned up. The global
variables for the RPC server and chain server client have been moved
to part of the rpcServer struct, and the handlers for each RPC method
that are looked up change depending on which components have been set.
Passthrough requests are also no longer handled specially, but when
the chain server is set, a handler to perform the passthrough will be
returned if the method is not otherwise a wallet RPC. The
notification system for websocket clients has also been rewritten so
wallet components can send notifications through channels, rather than
requiring direct access to the RPC server itself, or worse still,
sending directly to a websocket client's send channel. In the future,
this will enable proper registration of notifications, rather than
unsolicited broadcasts to every connected websocket client (see
issue #84).
Finally, and the main reason why much of this cleanup was necessary,
the races during intial sync with the chain server have been fixed.
Previously, when the 'Handshake' was run, a rescan would occur which
would perform modifications to Account data structures as
notifications were received. Synchronization was provided with a
single binary semaphore which serialized all access to wallet and
account data. However, the Handshake itself was not able to run with
this lock (or else notifications would block), and many data races
would occur as both notifications were being handled. If GOMAXPROCS
was ever increased beyond 1, btcwallet would always immediately crash
due to invalid addresses caused by the data races on startup. To fix
this, the single lock for all wallet access has been replaced with
mutexes for both the keystore and txstore. Handling of btcd
notifications and client requests may now occur simultaneously.
GOMAXPROCS has also been set to the number of logical CPUs at the
beginning of main, since with the data races fixed, there's no reason
to prevent the extra parallelism gained by increasing it.
Closes #78.
Closes #101.
Closes #110.
2014-07-09 05:17:38 +02:00
|
|
|
defer heldUnlock.Release()
|
2014-03-20 17:21:52 +01:00
|
|
|
|
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
|
|
|
chainClient, err := w.requireChainClient()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2013-12-04 01:22:47 +01:00
|
|
|
// Get current block's height and hash.
|
Modernize the RPC server.
This is a rather monolithic commit that moves the old RPC server to
its own package (rpc/legacyrpc), introduces a new RPC server using
gRPC (rpc/rpcserver), and provides the ability to defer wallet loading
until request at a later time by an RPC (--noinitialload).
The legacy RPC server remains the default for now while the new gRPC
server is not enabled by default. Enabling the new server requires
setting a listen address (--experimenalrpclisten). This experimental
flag is used to effectively feature gate the server until it is ready
to use as a default. Both RPC servers can be run at the same time,
but require binding to different listen addresses.
In theory, with the legacy RPC server now living in its own package it
should become much easier to unit test the handlers. This will be
useful for any future changes to the package, as compatibility with
Core's wallet is still desired.
Type safety has also been improved in the legacy RPC server. Multiple
handler types are now used for methods that do and do not require the
RPC client as a dependency. This can statically help prevent nil
pointer dereferences, and was very useful for catching bugs during
refactoring.
To synchronize the wallet loading process between the main package
(the default) and through the gRPC WalletLoader service (with the
--noinitialload option), as well as increasing the loose coupling of
packages, a new wallet.Loader type has been added. All creating and
loading of existing wallets is done through a single Loader instance,
and callbacks can be attached to the instance to run after the wallet
has been opened. This is how the legacy RPC server is associated with
a loaded wallet, even after the wallet is loaded by a gRPC method in a
completely unrelated package.
Documentation for the new RPC server has been added to the
rpc/documentation directory. The documentation includes a
specification for the new RPC API, addresses how to make changes to
the server implementation, and provides short example clients in
several different languages.
Some of the new RPC methods are not implementated exactly as described
by the specification. These are considered bugs with the
implementation, not the spec. Known bugs are commented as such.
2015-06-01 21:57:50 +02:00
|
|
|
bs, err := chainClient.BlockStamp()
|
2013-12-04 01:22:47 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2014-12-12 09:54:26 +01:00
|
|
|
eligible, err := w.findEligibleOutputs(account, minconf, bs)
|
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 23:12:05 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-06-13 21:52:49 +02:00
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
inputSource := makeInputSource(eligible)
|
|
|
|
changeSource := func() ([]byte, error) {
|
|
|
|
// Derive the change output script. As a hack to allow spending from
|
|
|
|
// the imported account, change addresses are created from account 0.
|
|
|
|
var changeAddr btcutil.Address
|
|
|
|
if account == waddrmgr.ImportedAddrAccount {
|
|
|
|
changeAddr, err = w.NewChangeAddress(0)
|
|
|
|
} else {
|
|
|
|
changeAddr, err = w.NewChangeAddress(account)
|
2013-11-22 19:42:25 +01:00
|
|
|
}
|
2016-02-28 05:30:56 +01:00
|
|
|
if err != nil {
|
2014-07-29 00:12:01 +02:00
|
|
|
return nil, err
|
2013-11-04 17:50:32 +01:00
|
|
|
}
|
2016-02-28 05:30:56 +01:00
|
|
|
return txscript.PayToAddrScript(changeAddr)
|
2013-09-09 20:14:57 +02:00
|
|
|
}
|
2016-03-09 15:56:32 +01:00
|
|
|
tx, err := txauthor.NewUnsignedTransaction(outputs, w.RelayFee(),
|
2016-02-28 05:30:56 +01:00
|
|
|
inputSource, changeSource)
|
|
|
|
if err != nil {
|
2014-07-29 00:12:01 +02:00
|
|
|
return nil, err
|
Perform smarter UTXO tracking.
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.
2013-10-22 15:55:53 +02:00
|
|
|
}
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
// Randomize change position, if change exists, before signing. This
|
|
|
|
// doesn't affect the serialize size, so the change amount will still be
|
|
|
|
// valid.
|
|
|
|
if tx.ChangeIndex >= 0 {
|
|
|
|
tx.RandomizeChangePosition()
|
2013-11-22 19:42:25 +01:00
|
|
|
}
|
2013-12-04 01:22:47 +01:00
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
err = tx.AddAllInputScripts(secretSource{w.Manager})
|
2014-09-25 19:27:18 +02:00
|
|
|
if err != nil {
|
2016-02-28 05:30:56 +01:00
|
|
|
return nil, err
|
2014-09-25 19:27:18 +02:00
|
|
|
}
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
err = validateMsgTx(tx.Tx, tx.PrevScripts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2014-07-29 00:12:01 +02:00
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
if tx.ChangeIndex >= 0 && account == waddrmgr.ImportedAddrAccount {
|
|
|
|
changeAmount := btcutil.Amount(tx.Tx.TxOut[tx.ChangeIndex].Value)
|
|
|
|
log.Warnf("Spend from imported account produced change: moving"+
|
|
|
|
" %v from imported account into default account.", changeAmount)
|
2014-07-29 00:12:01 +02:00
|
|
|
}
|
2016-02-28 05:30:56 +01:00
|
|
|
|
|
|
|
return tx, nil
|
2014-07-29 00:12:01 +02:00
|
|
|
}
|
|
|
|
|
2015-04-06 21:03:24 +02:00
|
|
|
func (w *Wallet) findEligibleOutputs(account uint32, minconf int32, bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) {
|
2014-07-29 00:12:01 +02:00
|
|
|
unspent, err := w.TxStore.UnspentOutputs()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-04-06 21:03:24 +02:00
|
|
|
|
|
|
|
// 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))
|
2014-07-29 00:12:01 +02:00
|
|
|
for i := range unspent {
|
2015-04-06 21:03:24 +02:00
|
|
|
output := &unspent[i]
|
2014-07-29 00:12:01 +02:00
|
|
|
|
2015-04-06 21:03:24 +02:00
|
|
|
// 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 {
|
2016-08-13 02:27:51 +02:00
|
|
|
target := int32(w.chainParams.CoinbaseMaturity)
|
2015-04-06 21:03:24 +02:00
|
|
|
if !confirmed(target, output.Height, bs.Height) {
|
2014-07-29 00:12:01 +02:00
|
|
|
continue
|
|
|
|
}
|
2015-04-06 21:03:24 +02:00
|
|
|
}
|
2014-07-29 00:12:01 +02:00
|
|
|
|
2015-04-06 21:03:24 +02:00
|
|
|
// Locked unspent outputs are skipped.
|
|
|
|
if w.LockedOutpoint(output.OutPoint) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
// Only include the output if it is associated with the passed
|
|
|
|
// account.
|
|
|
|
//
|
|
|
|
// TODO: Handle multisig outputs by determining if enough of the
|
|
|
|
// addresses are controlled.
|
|
|
|
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
|
2015-04-06 21:03:24 +02:00
|
|
|
output.PkScript, w.chainParams)
|
2016-02-28 05:30:56 +01:00
|
|
|
if err != nil || len(addrs) != 1 {
|
2015-04-06 21:03:24 +02:00
|
|
|
continue
|
2014-07-29 00:12:01 +02:00
|
|
|
}
|
2015-04-06 21:03:24 +02:00
|
|
|
addrAcct, err := w.Manager.AddrAccount(addrs[0])
|
|
|
|
if err != nil || addrAcct != account {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
eligible = append(eligible, *output)
|
2014-07-29 00:12:01 +02:00
|
|
|
}
|
|
|
|
return eligible, nil
|
|
|
|
}
|
|
|
|
|
2016-02-28 05:30:56 +01:00
|
|
|
// validateMsgTx verifies transaction input scripts for tx. All previous output
|
|
|
|
// scripts from outputs redeemed by the transaction, in the same order they are
|
|
|
|
// spent, must be passed in the prevScripts slice.
|
|
|
|
func validateMsgTx(tx *wire.MsgTx, prevScripts [][]byte) error {
|
|
|
|
for i, prevScript := range prevScripts {
|
|
|
|
vm, err := txscript.NewEngine(prevScript, tx, i,
|
2016-04-13 06:27:32 +02:00
|
|
|
txscript.StandardVerifyFlags, nil, nil, 0)
|
2014-07-29 00:12:01 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("cannot create script engine: %s", err)
|
|
|
|
}
|
2016-02-28 05:30:56 +01:00
|
|
|
err = vm.Execute()
|
|
|
|
if err != nil {
|
2014-07-29 00:12:01 +02:00
|
|
|
return fmt.Errorf("cannot validate transaction: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|