wtxmgr: check existing unspent outputs before adding the credit

In this commit, we resolve a lingering bug within the wallet where it's
possible that an output is added as unconfirmed credit after the fact
that it has already confirmed. This would lead to duplicate entries for
the same output within the wallet causing double spend transactions to
be crafted.
This commit is contained in:
Wilmer Paulino 2018-08-29 15:28:40 -07:00
parent 98f65ac943
commit fbe82c3531
No known key found for this signature in database
GPG key ID: 6DF57B9F9514972F
2 changed files with 22 additions and 3 deletions

View file

@ -405,10 +405,16 @@ func (s *Store) AddCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *Blo
// duplicate (false).
func (s *Store) addCredit(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) (bool, error) {
if block == nil {
// If the outpoint that we should mark as credit already exists
// within the store, either as unconfirmed or confirmed, then we
// have nothing left to do and can exit.
k := canonicalOutPoint(&rec.Hash, index)
if existsRawUnminedCredit(ns, k) != nil {
return false, nil
}
if existsRawUnspent(ns, k) != nil {
return false, nil
}
v := valueUnminedCredit(btcutil.Amount(rec.MsgTx.TxOut[index].Value), change)
return true, putRawUnminedCredit(ns, k, v)
}

View file

@ -14,12 +14,25 @@ import (
// insertMemPoolTx inserts the unmined transaction record. It also marks
// previous outputs referenced by the inputs as spent.
func (s *Store) insertMemPoolTx(ns walletdb.ReadWriteBucket, rec *TxRecord) error {
v := existsRawUnmined(ns, rec.Hash[:])
if v != nil {
// TODO: compare serialized txs to ensure this isn't a hash collision?
// Check whether the transaction has already been added to the
// unconfirmed bucket.
if existsRawUnmined(ns, rec.Hash[:]) != nil {
// TODO: compare serialized txs to ensure this isn't a hash
// collision?
return nil
}
// Since transaction records within the store are keyed by their
// transaction _and_ block confirmation, we'll iterate through the
// transaction's outputs to determine if we've already seen them to
// prevent from adding this transaction to the unconfirmed bucket.
for i := range rec.MsgTx.TxOut {
k := canonicalOutPoint(&rec.Hash, uint32(i))
if existsRawUnspent(ns, k) != nil {
return nil
}
}
log.Infof("Inserting unconfirmed transaction %v", rec.Hash)
v, err := valueTxRecord(rec)
if err != nil {