From ac731b8e5216de0eb037b4c77cab6abf05977ab9 Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 30 Oct 2019 16:32:46 -0700 Subject: [PATCH 1/2] wtxmgr: add TestInsertMempoolTxAndConfirm This test ensures that there aren't any lingering unconfirmed records for a transaction that existed within the store as unconfirmed before becoming confirmed. At the moment, this is currently failing due to a gap when moving a transaction from unconfirmed to confirmed within the store. This will be resolved in a subsequent commit. --- wtxmgr/tx_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/wtxmgr/tx_test.go b/wtxmgr/tx_test.go index 8914e1a..6f8f72e 100644 --- a/wtxmgr/tx_test.go +++ b/wtxmgr/tx_test.go @@ -2119,3 +2119,71 @@ func TestAddDuplicateCreditAfterConfirm(t *testing.T) { } }) } + +// TestInsertMempoolTxAndConfirm ensures that there aren't any lingering +// unconfirmed records for a transaction that existed within the store as +// unconfirmed before becoming confirmed. +func TestInsertMempoolTxAndConfirm(t *testing.T) { + t.Parallel() + + store, db, teardown, err := testStore() + if err != nil { + t.Fatal(err) + } + defer teardown() + + // Create a transaction which we'll insert into the store as + // unconfirmed. + tx := newCoinBase(1e8) + txRec, err := NewTxRecordFromMsgTx(tx, time.Now()) + if err != nil { + t.Fatal(err) + } + commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) { + if err := store.InsertTx(ns, txRec, nil); err != nil { + t.Fatal(err) + } + err := store.AddCredit(ns, txRec, nil, 0, false) + if err != nil { + t.Fatal(err) + } + }) + + // Then, proceed to confirm it. + commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) { + block := &BlockMeta{ + Block: Block{Height: 1337}, + Time: time.Now(), + } + if err := store.InsertTx(ns, txRec, block); err != nil { + t.Fatal(err) + } + err := store.AddCredit(ns, txRec, block, 0, false) + if err != nil { + t.Fatal(err) + } + }) + + // We should not see any lingering unconfirmed records for it once it's + // been confirmed. + commitDBTx(t, store, db, func(ns walletdb.ReadWriteBucket) { + for _, input := range tx.TxIn { + prevOut := input.PreviousOutPoint + k := canonicalOutPoint(&prevOut.Hash, prevOut.Index) + if existsRawUnminedInput(ns, k) != nil { + t.Fatalf("found transaction input %v as "+ + "unconfirmed", prevOut) + } + } + if existsRawUnmined(ns, txRec.Hash[:]) != nil { + t.Fatal("found transaction as unconfirmed") + } + for i := range tx.TxOut { + k := canonicalOutPoint(&txRec.Hash, uint32(i)) + if existsRawUnminedCredit(ns, k) != nil { + t.Fatalf("found transaction output %v as "+ + "unconfirmed", i) + } + } + }) +} From 66e09f252d447ebb976bae16439671bcbeca7fdd Mon Sep 17 00:00:00 2001 From: Wilmer Paulino Date: Wed, 30 Oct 2019 16:43:46 -0700 Subject: [PATCH 2/2] wtxmgr: remove unconfirmed input reference for confirmed transcation Previously, inserting a transaction as unconfirmed into the store and later confirming it would leave a lingering unconfirmed input record. This was discovered as part of https://github.com/btcsuite/btcwallet/pull/655. This issue would only affect the wallet if it tracked spent transaction outputs, which it doesn't. We aim to resolve it in any case for the sake of internal consistency. --- wtxmgr/tx.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wtxmgr/tx.go b/wtxmgr/tx.go index b69142f..6394858 100644 --- a/wtxmgr/tx.go +++ b/wtxmgr/tx.go @@ -279,6 +279,13 @@ func (s *Store) updateMinedBalance(ns walletdb.ReadWriteBucket, rec *TxRecord, // // NOTE: This should only be used once the transaction has been mined. func (s *Store) deleteUnminedTx(ns walletdb.ReadWriteBucket, rec *TxRecord) error { + for _, input := range rec.MsgTx.TxIn { + prevOut := input.PreviousOutPoint + k := canonicalOutPoint(&prevOut.Hash, prevOut.Index) + if err := deleteRawUnminedInput(ns, k, rec.Hash); err != nil { + return err + } + } for i := range rec.MsgTx.TxOut { k := canonicalOutPoint(&rec.Hash, uint32(i)) if err := deleteRawUnminedCredit(ns, k); err != nil {