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:
parent
98f65ac943
commit
fbe82c3531
2 changed files with 22 additions and 3 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue