fix wtxmgr tests

While making these tests compile and pass, we ended up tripping on the
broken bolt cursor usage painfully discovered in dcrwallet, so i've
ported that fix over as well.  Would have learned about that a whole
lot sooner if those tests were never disabled..
This commit is contained in:
Josh Rickmar 2017-01-21 11:29:06 -05:00 committed by Olaoluwa Osuntokun
parent 4e58d61556
commit 2bb45752e1
5 changed files with 477 additions and 326 deletions

View file

@ -341,13 +341,23 @@ func (it *blockIterator) prev() bool {
return true return true
} }
func (it *blockIterator) delete() error { // unavailable until https://github.com/boltdb/bolt/issues/620 is fixed.
err := it.c.Delete() // func (it *blockIterator) delete() error {
if err != nil { // err := it.c.Delete()
str := "failed to delete block record" // if err != nil {
storeError(ErrDatabase, str, err) // str := "failed to delete block record"
} // storeError(ErrDatabase, str, err)
return nil // }
// return nil
// }
func (it *blockIterator) reposition(height int32) {
it.c.Seek(keyBlockRecord(height))
}
func deleteBlockRecord(ns walletdb.ReadWriteBucket, height int32) error {
k := keyBlockRecord(height)
return ns.NestedReadWriteBucket(bucketBlocks).Delete(k)
} }
// Transaction records are keyed as such: // Transaction records are keyed as such:
@ -1177,13 +1187,18 @@ func (it *unminedCreditIterator) next() bool {
return true return true
} }
func (it *unminedCreditIterator) delete() error { // unavailable until https://github.com/boltdb/bolt/issues/620 is fixed.
err := it.c.Delete() // func (it *unminedCreditIterator) delete() error {
if err != nil { // err := it.c.Delete()
str := "failed to delete unmined credit" // if err != nil {
return storeError(ErrDatabase, str, err) // str := "failed to delete unmined credit"
} // return storeError(ErrDatabase, str, err)
return nil // }
// return nil
// }
func (it *unminedCreditIterator) reposition(txHash *chainhash.Hash, index uint32) {
it.c.Seek(canonicalOutPoint(txHash, index))
} }
// Outpoints spent by unmined transactions are saved in the unmined inputs // Outpoints spent by unmined transactions are saved in the unmined inputs

View file

@ -10,6 +10,7 @@ import (
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/wtxmgr" "github.com/btcsuite/btcwallet/wtxmgr"
) )
@ -44,7 +45,7 @@ var exampleBlock100 = makeBlockMeta(100)
// This example demonstrates reporting the Store balance given an unmined and // This example demonstrates reporting the Store balance given an unmined and
// mined transaction given 0, 1, and 6 block confirmations. // mined transaction given 0, 1, and 6 block confirmations.
func ExampleStore_Balance() { func ExampleStore_Balance() {
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -54,17 +55,24 @@ func ExampleStore_Balance() {
// Prints balances for 0 block confirmations, 1 confirmation, and 6 // Prints balances for 0 block confirmations, 1 confirmation, and 6
// confirmations. // confirmations.
printBalances := func(syncHeight int32) { printBalances := func(syncHeight int32) {
zeroConfBal, err := s.Balance(0, syncHeight) dbtx, err := db.BeginReadTx()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
oneConfBal, err := s.Balance(1, syncHeight) defer dbtx.Rollback()
ns := dbtx.ReadBucket(namespaceKey)
zeroConfBal, err := s.Balance(ns, 0, syncHeight)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
sixConfBal, err := s.Balance(6, syncHeight) oneConfBal, err := s.Balance(ns, 1, syncHeight)
if err != nil {
fmt.Println(err)
return
}
sixConfBal, err := s.Balance(ns, 6, syncHeight)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -74,12 +82,14 @@ func ExampleStore_Balance() {
// Insert a transaction which outputs 10 BTC unmined and mark the output // Insert a transaction which outputs 10 BTC unmined and mark the output
// as a credit. // as a credit.
err = s.InsertTx(exampleTxRecordA, nil) err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
if err != nil { ns := tx.ReadWriteBucket(namespaceKey)
fmt.Println(err) err := s.InsertTx(ns, exampleTxRecordA, nil)
return if err != nil {
} return err
err = s.AddCredit(exampleTxRecordA, nil, 0, false) }
return s.AddCredit(ns, exampleTxRecordA, nil, 0, false)
})
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -88,7 +98,10 @@ func ExampleStore_Balance() {
// Mine the transaction in block 100 and print balances again with a // Mine the transaction in block 100 and print balances again with a
// sync height of 100 and 105 blocks. // sync height of 100 and 105 blocks.
err = s.InsertTx(exampleTxRecordA, &exampleBlock100) err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(namespaceKey)
return s.InsertTx(ns, exampleTxRecordA, &exampleBlock100)
})
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -103,38 +116,43 @@ func ExampleStore_Balance() {
} }
func ExampleStore_Rollback() { func ExampleStore_Rollback() {
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
// Insert a transaction which outputs 10 BTC in a block at height 100. err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
err = s.InsertTx(exampleTxRecordA, &exampleBlock100) ns := tx.ReadWriteBucket(namespaceKey)
if err != nil {
fmt.Println(err)
return
}
// Rollback everything from block 100 onwards. // Insert a transaction which outputs 10 BTC in a block at height 100.
err = s.Rollback(100) err := s.InsertTx(ns, exampleTxRecordA, &exampleBlock100)
if err != nil { if err != nil {
fmt.Println(err) return err
return }
}
// Assert that the transaction is now unmined. // Rollback everything from block 100 onwards.
details, err := s.TxDetails(&exampleTxRecordA.Hash) err = s.Rollback(ns, 100)
if err != nil {
return err
}
// Assert that the transaction is now unmined.
details, err := s.TxDetails(ns, &exampleTxRecordA.Hash)
if err != nil {
return err
}
if details == nil {
return fmt.Errorf("no details found")
}
fmt.Println(details.Block.Height)
return nil
})
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
if details == nil {
fmt.Println("No details found")
return
}
fmt.Println(details.Block.Height)
// Output: // Output:
// -1 // -1
@ -149,20 +167,28 @@ func Example_basicUsage() {
return return
} }
// Create or open a db namespace for the transaction store. // Open a read-write transaction to operate on the database.
ns, err := db.Namespace([]byte("txstore")) dbtx, err := db.BeginReadWriteTx()
if err != nil {
fmt.Println(err)
return
}
defer dbtx.Commit()
// Create a bucket for the transaction store.
b, err := dbtx.CreateTopLevelBucket([]byte("txstore"))
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
// Create (or open) the transaction store in the provided namespace. // Create and open the transaction store in the provided namespace.
err = wtxmgr.Create(ns) err = wtxmgr.Create(b)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
s, err := wtxmgr.Open(ns, &chaincfg.TestNet3Params) s, err := wtxmgr.Open(b, &chaincfg.TestNet3Params)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -170,12 +196,12 @@ func Example_basicUsage() {
// Insert an unmined transaction that outputs 10 BTC to a wallet address // Insert an unmined transaction that outputs 10 BTC to a wallet address
// at output 0. // at output 0.
err = s.InsertTx(exampleTxRecordA, nil) err = s.InsertTx(b, exampleTxRecordA, nil)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
err = s.AddCredit(exampleTxRecordA, nil, 0, false) err = s.AddCredit(b, exampleTxRecordA, nil, 0, false)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -183,31 +209,31 @@ func Example_basicUsage() {
// Insert a second transaction which spends the output, and creates two // Insert a second transaction which spends the output, and creates two
// outputs. Mark the second one (5 BTC) as wallet change. // outputs. Mark the second one (5 BTC) as wallet change.
err = s.InsertTx(exampleTxRecordB, nil) err = s.InsertTx(b, exampleTxRecordB, nil)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
err = s.AddCredit(exampleTxRecordB, nil, 1, true) err = s.AddCredit(b, exampleTxRecordB, nil, 1, true)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
// Mine each transaction in a block at height 100. // Mine each transaction in a block at height 100.
err = s.InsertTx(exampleTxRecordA, &exampleBlock100) err = s.InsertTx(b, exampleTxRecordA, &exampleBlock100)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
err = s.InsertTx(exampleTxRecordB, &exampleBlock100) err = s.InsertTx(b, exampleTxRecordB, &exampleBlock100)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
// Print the one confirmation balance. // Print the one confirmation balance.
bal, err := s.Balance(1, 100) bal, err := s.Balance(b, 1, 100)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -215,7 +241,7 @@ func Example_basicUsage() {
fmt.Println(bal) fmt.Println(bal)
// Fetch unspent outputs. // Fetch unspent outputs.
utxos, err := s.UnspentOutputs() utxos, err := s.UnspentOutputs(b)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
} }

View file

@ -14,6 +14,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/walletdb"
. "github.com/btcsuite/btcwallet/wtxmgr" . "github.com/btcsuite/btcwallet/wtxmgr"
) )
@ -63,7 +64,7 @@ func deepCopyTxDetails(d *TxDetails) *TxDetails {
return &cpy return &cpy
} }
func (q *queryState) compare(t *testing.T, s *Store, changeDesc string) { func (q *queryState) compare(t *testing.T, s *Store, ns walletdb.ReadBucket, changeDesc string) {
defer func() { defer func() {
if t.Failed() { if t.Failed() {
t.Fatalf("Store state queries failed after '%s'", changeDesc) t.Fatalf("Store state queries failed after '%s'", changeDesc)
@ -95,11 +96,11 @@ func (q *queryState) compare(t *testing.T, s *Store, changeDesc string) {
return false, nil return false, nil
} }
} }
err := s.RangeTransactions(0, -1, checkBlock(fwdBlocks)) err := s.RangeTransactions(ns, 0, -1, checkBlock(fwdBlocks))
if err != nil { if err != nil {
t.Fatalf("Failed in RangeTransactions (forwards iteration): %v", err) t.Fatalf("Failed in RangeTransactions (forwards iteration): %v", err)
} }
err = s.RangeTransactions(-1, 0, checkBlock(revBlocks)) err = s.RangeTransactions(ns, -1, 0, checkBlock(revBlocks))
if err != nil { if err != nil {
t.Fatalf("Failed in RangeTransactions (reverse iteration): %v", err) t.Fatalf("Failed in RangeTransactions (reverse iteration): %v", err)
} }
@ -110,7 +111,7 @@ func (q *queryState) compare(t *testing.T, s *Store, changeDesc string) {
if blk.Height == -1 { if blk.Height == -1 {
blk = nil blk = nil
} }
d, err := s.UniqueTxDetails(&txHash, blk) d, err := s.UniqueTxDetails(ns, &txHash, blk)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -128,7 +129,7 @@ func (q *queryState) compare(t *testing.T, s *Store, changeDesc string) {
// TxDetails (not looking up a tx at any particular // TxDetails (not looking up a tx at any particular
// height) matches the last. // height) matches the last.
detail := &details[len(details)-1] detail := &details[len(details)-1]
d, err := s.TxDetails(&txHash) d, err := s.TxDetails(ns, &txHash)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -237,13 +238,13 @@ func TestStoreQueries(t *testing.T) {
type queryTest struct { type queryTest struct {
desc string desc string
updates func() // Unwinds from t.Fatal if the update errors. updates func(ns walletdb.ReadWriteBucket) // Unwinds from t.Fatal if the update errors.
state *queryState state *queryState
} }
var tests []queryTest var tests []queryTest
// Create the store and test initial state. // Create the store and test initial state.
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -251,19 +252,19 @@ func TestStoreQueries(t *testing.T) {
lastState := newQueryState() lastState := newQueryState()
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "initial store", desc: "initial store",
updates: func() {}, updates: func(walletdb.ReadWriteBucket) {},
state: lastState, state: lastState,
}) })
// simplify error handling // simplify error handling
insertTx := func(rec *TxRecord, block *BlockMeta) { insertTx := func(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta) {
err := s.InsertTx(rec, block) err := s.InsertTx(ns, rec, block)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
addCredit := func(s *Store, rec *TxRecord, block *BlockMeta, index uint32, change bool) { addCredit := func(s *Store, ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) {
err := s.AddCredit(rec, block, index, change) err := s.AddCredit(ns, rec, block, index, change)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -275,8 +276,8 @@ func TestStoreQueries(t *testing.T) {
} }
return rec return rec
} }
rollback := func(height int32) { rollback := func(ns walletdb.ReadWriteBucket, height int32) {
err := s.Rollback(height) err := s.Rollback(ns, height)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -300,7 +301,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "insert tx A unmined", desc: "insert tx A unmined",
updates: func() { insertTx(recA, nil) }, updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recA, nil) },
state: newState, state: newState,
}) })
@ -318,7 +319,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "mark unconfirmed txA:0 as credit", desc: "mark unconfirmed txA:0 as credit",
updates: func() { addCredit(s, recA, nil, 0, true) }, updates: func(ns walletdb.ReadWriteBucket) { addCredit(s, ns, recA, nil, 0, true) },
state: newState, state: newState,
}) })
@ -343,7 +344,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "insert tx B unmined", desc: "insert tx B unmined",
updates: func() { insertTx(recB, nil) }, updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recB, nil) },
state: newState, state: newState,
}) })
newState = lastState.deepCopy() newState = lastState.deepCopy()
@ -359,7 +360,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "mark txB:0 as non-change credit", desc: "mark txB:0 as non-change credit",
updates: func() { addCredit(s, recB, nil, 0, false) }, updates: func(ns walletdb.ReadWriteBucket) { addCredit(s, ns, recB, nil, 0, false) },
state: newState, state: newState,
}) })
@ -373,7 +374,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "mine tx A", desc: "mine tx A",
updates: func() { insertTx(recA, &b100) }, updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recA, &b100) },
state: newState, state: newState,
}) })
@ -385,13 +386,20 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "mine tx B", desc: "mine tx B",
updates: func() { insertTx(recB, &b101) }, updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recB, &b101) },
state: newState, state: newState,
}) })
for _, tst := range tests { for _, tst := range tests {
tst.updates() err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
tst.state.compare(t, s, tst.desc) ns := tx.ReadWriteBucket(namespaceKey)
tst.updates(ns)
tst.state.compare(t, s, ns, tst.desc)
return nil
})
if err != nil {
t.Fatal(err)
}
} }
// Run some additional query tests with the current store's state: // Run some additional query tests with the current store's state:
@ -402,62 +410,70 @@ func TestStoreQueries(t *testing.T) {
// - Verify that breaking early on RangeTransactions stops further // - Verify that breaking early on RangeTransactions stops further
// iteration. // iteration.
missingTx := spendOutput(&recB.Hash, 0, 40e8) err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
missingRec := newTxRecordFromMsgTx(missingTx, timeNow()) ns := tx.ReadWriteBucket(namespaceKey)
missingBlock := makeBlockMeta(102)
missingDetails, err := s.TxDetails(&missingRec.Hash) missingTx := spendOutput(&recB.Hash, 0, 40e8)
if err != nil { missingRec := newTxRecordFromMsgTx(missingTx, timeNow())
t.Fatal(err) missingBlock := makeBlockMeta(102)
} missingDetails, err := s.TxDetails(ns, &missingRec.Hash)
if missingDetails != nil {
t.Errorf("Expected no details, found details for tx %v", missingDetails.Hash)
}
missingUniqueTests := []struct {
hash *chainhash.Hash
block *Block
}{
{&missingRec.Hash, &b100.Block},
{&missingRec.Hash, &missingBlock.Block},
{&missingRec.Hash, nil},
{&recB.Hash, &b100.Block},
{&recB.Hash, &missingBlock.Block},
{&recB.Hash, nil},
}
for _, tst := range missingUniqueTests {
missingDetails, err = s.UniqueTxDetails(tst.hash, tst.block)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if missingDetails != nil { if missingDetails != nil {
t.Errorf("Expected no details, found details for tx %v", missingDetails.Hash) t.Errorf("Expected no details, found details for tx %v", missingDetails.Hash)
} }
} missingUniqueTests := []struct {
hash *chainhash.Hash
block *Block
}{
{&missingRec.Hash, &b100.Block},
{&missingRec.Hash, &missingBlock.Block},
{&missingRec.Hash, nil},
{&recB.Hash, &b100.Block},
{&recB.Hash, &missingBlock.Block},
{&recB.Hash, nil},
}
for _, tst := range missingUniqueTests {
missingDetails, err = s.UniqueTxDetails(ns, tst.hash, tst.block)
if err != nil {
t.Fatal(err)
}
if missingDetails != nil {
t.Errorf("Expected no details, found details for tx %v", missingDetails.Hash)
}
}
iterations := 0 iterations := 0
err = s.RangeTransactions(0, -1, func([]TxDetails) (bool, error) { err = s.RangeTransactions(ns, 0, -1, func([]TxDetails) (bool, error) {
iterations++ iterations++
return true, nil return true, nil
})
if iterations != 1 {
t.Errorf("RangeTransactions (forwards) ran func %d times", iterations)
}
iterations = 0
err = s.RangeTransactions(ns, -1, 0, func([]TxDetails) (bool, error) {
iterations++
return true, nil
})
if iterations != 1 {
t.Errorf("RangeTransactions (reverse) ran func %d times", iterations)
}
// Make sure it also breaks early after one iteration through unmined transactions.
rollback(ns, b101.Height)
iterations = 0
err = s.RangeTransactions(ns, -1, 0, func([]TxDetails) (bool, error) {
iterations++
return true, nil
})
if iterations != 1 {
t.Errorf("RangeTransactions (reverse) ran func %d times", iterations)
}
return nil
}) })
if iterations != 1 { if err != nil {
t.Errorf("RangeTransactions (forwards) ran func %d times", iterations) t.Fatal(err)
}
iterations = 0
err = s.RangeTransactions(-1, 0, func([]TxDetails) (bool, error) {
iterations++
return true, nil
})
if iterations != 1 {
t.Errorf("RangeTransactions (reverse) ran func %d times", iterations)
}
// Make sure it also breaks early after one iteration through unmined transactions.
rollback(b101.Height)
iterations = 0
err = s.RangeTransactions(-1, 0, func([]TxDetails) (bool, error) {
iterations++
return true, nil
})
if iterations != 1 {
t.Errorf("RangeTransactions (reverse) ran func %d times", iterations)
} }
// None of the above tests have tested RangeTransactions with multiple // None of the above tests have tested RangeTransactions with multiple
@ -472,7 +488,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests[:0:0], queryTest{ tests = append(tests[:0:0], queryTest{
desc: "move tx B to block 100", desc: "move tx B to block 100",
updates: func() { insertTx(recB, &b100) }, updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recB, &b100) },
state: newState, state: newState,
}) })
newState = lastState.deepCopy() newState = lastState.deepCopy()
@ -483,7 +499,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "rollback block 100", desc: "rollback block 100",
updates: func() { rollback(b100.Height) }, updates: func(ns walletdb.ReadWriteBucket) { rollback(ns, b100.Height) },
state: newState, state: newState,
}) })
@ -510,7 +526,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "insert duplicate tx A", desc: "insert duplicate tx A",
updates: func() { insertTx(recA, &b100); insertTx(recA, nil) }, updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recA, &b100); insertTx(ns, recA, nil) },
state: newState, state: newState,
}) })
newState = lastState.deepCopy() newState = lastState.deepCopy()
@ -524,20 +540,27 @@ func TestStoreQueries(t *testing.T) {
lastState = newState lastState = newState
tests = append(tests, queryTest{ tests = append(tests, queryTest{
desc: "mine duplicate tx A", desc: "mine duplicate tx A",
updates: func() { insertTx(recA, &b101) }, updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recA, &b101) },
state: newState, state: newState,
}) })
for _, tst := range tests { for _, tst := range tests {
tst.updates() err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
tst.state.compare(t, s, tst.desc) ns := tx.ReadWriteBucket(namespaceKey)
tst.updates(ns)
tst.state.compare(t, s, ns, tst.desc)
return nil
})
if err != nil {
t.Fatal(err)
}
} }
} }
func TestPreviousPkScripts(t *testing.T) { func TestPreviousPkScripts(t *testing.T) {
t.Parallel() t.Parallel()
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -593,14 +616,14 @@ func TestPreviousPkScripts(t *testing.T) {
recD = newTxRecordFromMsgTx(txD) recD = newTxRecordFromMsgTx(txD)
) )
insertTx := func(rec *TxRecord, block *BlockMeta) { insertTx := func(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta) {
err := s.InsertTx(rec, block) err := s.InsertTx(ns, rec, block)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
addCredit := func(rec *TxRecord, block *BlockMeta, index uint32) { addCredit := func(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32) {
err := s.AddCredit(rec, block, index, false) err := s.AddCredit(ns, rec, block, index, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -611,8 +634,8 @@ func TestPreviousPkScripts(t *testing.T) {
block *Block block *Block
scripts [][]byte scripts [][]byte
} }
runTest := func(tst *scriptTest) { runTest := func(ns walletdb.ReadWriteBucket, tst *scriptTest) {
scripts, err := s.PreviousPkScripts(tst.rec, tst.block) scripts, err := s.PreviousPkScripts(ns, tst.rec, tst.block)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -634,12 +657,19 @@ func TestPreviousPkScripts(t *testing.T) {
} }
} }
dbtx, err := db.BeginReadWriteTx()
if err != nil {
t.Fatal(err)
}
defer dbtx.Commit()
ns := dbtx.ReadWriteBucket(namespaceKey)
// Insert transactions A-C unmined, but mark no credits yet. Until // Insert transactions A-C unmined, but mark no credits yet. Until
// these are marked as credits, PreviousPkScripts should not return // these are marked as credits, PreviousPkScripts should not return
// them. // them.
insertTx(recA, nil) insertTx(ns, recA, nil)
insertTx(recB, nil) insertTx(ns, recB, nil)
insertTx(recC, nil) insertTx(ns, recC, nil)
b100 := makeBlockMeta(100) b100 := makeBlockMeta(100)
b101 := makeBlockMeta(101) b101 := makeBlockMeta(101)
@ -653,7 +683,7 @@ func TestPreviousPkScripts(t *testing.T) {
{recC, &b100.Block, nil}, {recC, &b100.Block, nil},
} }
for _, tst := range tests { for _, tst := range tests {
runTest(&tst) runTest(ns, &tst)
} }
if t.Failed() { if t.Failed() {
t.Fatal("Failed after unmined tx inserts") t.Fatal("Failed after unmined tx inserts")
@ -662,11 +692,11 @@ func TestPreviousPkScripts(t *testing.T) {
// Mark credits. Tx C output 1 not marked as a credit: tx D will spend // Mark credits. Tx C output 1 not marked as a credit: tx D will spend
// both later but when C is mined, output 1's script should not be // both later but when C is mined, output 1's script should not be
// returned. // returned.
addCredit(recA, nil, 0) addCredit(ns, recA, nil, 0)
addCredit(recA, nil, 1) addCredit(ns, recA, nil, 1)
addCredit(recB, nil, 0) addCredit(ns, recB, nil, 0)
addCredit(recB, nil, 1) addCredit(ns, recB, nil, 1)
addCredit(recC, nil, 0) addCredit(ns, recC, nil, 0)
tests = []scriptTest{ tests = []scriptTest{
{recA, nil, nil}, {recA, nil, nil},
{recA, &b100.Block, nil}, {recA, &b100.Block, nil},
@ -676,23 +706,23 @@ func TestPreviousPkScripts(t *testing.T) {
{recC, &b100.Block, nil}, {recC, &b100.Block, nil},
} }
for _, tst := range tests { for _, tst := range tests {
runTest(&tst) runTest(ns, &tst)
} }
if t.Failed() { if t.Failed() {
t.Fatal("Failed after marking unmined credits") t.Fatal("Failed after marking unmined credits")
} }
// Mine tx A in block 100. Test results should be identical. // Mine tx A in block 100. Test results should be identical.
insertTx(recA, &b100) insertTx(ns, recA, &b100)
for _, tst := range tests { for _, tst := range tests {
runTest(&tst) runTest(ns, &tst)
} }
if t.Failed() { if t.Failed() {
t.Fatal("Failed after mining tx A") t.Fatal("Failed after mining tx A")
} }
// Mine tx B in block 101. // Mine tx B in block 101.
insertTx(recB, &b101) insertTx(ns, recB, &b101)
tests = []scriptTest{ tests = []scriptTest{
{recA, nil, nil}, {recA, nil, nil},
{recA, &b100.Block, nil}, {recA, &b100.Block, nil},
@ -702,7 +732,7 @@ func TestPreviousPkScripts(t *testing.T) {
{recC, &b101.Block, nil}, {recC, &b101.Block, nil},
} }
for _, tst := range tests { for _, tst := range tests {
runTest(&tst) runTest(ns, &tst)
} }
if t.Failed() { if t.Failed() {
t.Fatal("Failed after mining tx B") t.Fatal("Failed after mining tx B")
@ -710,7 +740,7 @@ func TestPreviousPkScripts(t *testing.T) {
// Mine tx C in block 101 (same block as tx B) to test debits from the // Mine tx C in block 101 (same block as tx B) to test debits from the
// same block. // same block.
insertTx(recC, &b101) insertTx(ns, recC, &b101)
tests = []scriptTest{ tests = []scriptTest{
{recA, nil, nil}, {recA, nil, nil},
{recA, &b100.Block, nil}, {recA, &b100.Block, nil},
@ -720,7 +750,7 @@ func TestPreviousPkScripts(t *testing.T) {
{recC, &b101.Block, [][]byte{scriptB0, scriptB1}}, {recC, &b101.Block, [][]byte{scriptB0, scriptB1}},
} }
for _, tst := range tests { for _, tst := range tests {
runTest(&tst) runTest(ns, &tst)
} }
if t.Failed() { if t.Failed() {
t.Fatal("Failed after mining tx C") t.Fatal("Failed after mining tx C")
@ -728,11 +758,11 @@ func TestPreviousPkScripts(t *testing.T) {
// Insert tx D, which spends C:0 and C:1. However, only C:0 is marked // Insert tx D, which spends C:0 and C:1. However, only C:0 is marked
// as a credit, and only that output script should be returned. // as a credit, and only that output script should be returned.
insertTx(recD, nil) insertTx(ns, recD, nil)
tests = append(tests, scriptTest{recD, nil, [][]byte{scriptC0}}) tests = append(tests, scriptTest{recD, nil, [][]byte{scriptC0}})
tests = append(tests, scriptTest{recD, &b101.Block, nil}) tests = append(tests, scriptTest{recD, &b101.Block, nil})
for _, tst := range tests { for _, tst := range tests {
runTest(&tst) runTest(ns, &tst)
} }
if t.Failed() { if t.Failed() {
t.Fatal("Failed after inserting tx D") t.Fatal("Failed after inserting tx D")

View file

@ -198,18 +198,26 @@ func (s *Store) moveMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord, recKey,
return err return err
} }
// For all mined transactions with unspent credits spent by this // For all transaction inputs, remove the previous output marker from the
// transaction, mark each spent, remove from the unspents map, and // unmined inputs bucket. For any mined transactions with unspent credits
// insert a debit record for the spent credit. // spent by this transaction, mark each spent, remove from the unspents map,
// and insert a debit record for the spent credit.
debitIncidence := indexedIncidence{ debitIncidence := indexedIncidence{
incidence: incidence{txHash: rec.Hash, block: block.Block}, incidence: incidence{txHash: rec.Hash, block: block.Block},
// index set for each rec input below. // index set for each rec input below.
} }
for i, input := range rec.MsgTx.TxIn { for i, input := range rec.MsgTx.TxIn {
unspentKey, credKey := existsUnspent(ns, &input.PreviousOutPoint) unspentKey, credKey := existsUnspent(ns, &input.PreviousOutPoint)
err = deleteRawUnminedInput(ns, unspentKey)
if err != nil {
return err
}
if credKey == nil { if credKey == nil {
continue continue
} }
debitIncidence.index = uint32(i) debitIncidence.index = uint32(i)
amt, err := spendCredit(ns, credKey, &debitIncidence) amt, err := spendCredit(ns, credKey, &debitIncidence)
if err != nil { if err != nil {
@ -225,11 +233,6 @@ func (s *Store) moveMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord, recKey,
if err != nil { if err != nil {
return err return err
} }
err = deleteRawUnminedInput(ns, unspentKey)
if err != nil {
return err
}
} }
// For each output of the record that is marked as a credit, if the // For each output of the record that is marked as a credit, if the
@ -261,10 +264,6 @@ func (s *Store) moveMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord, recKey,
cred.amount = amount cred.amount = amount
cred.change = change cred.change = change
err = it.delete()
if err != nil {
return err
}
err = putUnspentCredit(ns, &cred) err = putUnspentCredit(ns, &cred)
if err != nil { if err != nil {
return err return err
@ -273,12 +272,33 @@ func (s *Store) moveMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord, recKey,
if err != nil { if err != nil {
return err return err
} }
// reposition cursor before deleting, since the above puts have
// invalidated the cursor.
it.reposition(&rec.Hash, index)
// Avoid cursor deletion until bolt issue #620 is resolved.
// err = it.delete()
// if err != nil {
// return err
// }
minedBalance += amount minedBalance += amount
} }
if it.err != nil { if it.err != nil {
return it.err return it.err
} }
// Delete all possible credits outside of the iteration since the cursor
// deletion is broken.
for i := 0; i < len(rec.MsgTx.TxOut); i++ {
k := canonicalOutPoint(&rec.Hash, uint32(i))
err = deleteRawUnminedCredit(ns, k)
if err != nil {
return err
}
}
err = putMinedBalance(ns, minedBalance) err = putMinedBalance(ns, minedBalance)
if err != nil { if err != nil {
return err return err
@ -502,10 +522,16 @@ func (s *Store) rollback(ns walletdb.ReadWriteBucket, height int32) error {
// It is necessary to keep these in memory and fix the unmined // It is necessary to keep these in memory and fix the unmined
// transactions later since blocks are removed in increasing order. // transactions later since blocks are removed in increasing order.
var coinBaseCredits []wire.OutPoint var coinBaseCredits []wire.OutPoint
var heightsToRemove []int32
it := makeBlockIterator(ns, height) it := makeReverseBlockIterator(ns)
for it.next() { for it.prev() {
b := &it.elem b := &it.elem
if it.elem.Height < height {
break
}
heightsToRemove = append(heightsToRemove, it.elem.Height)
log.Infof("Rolling back %d transactions from block %v height %d", log.Infof("Rolling back %d transactions from block %v height %d",
len(b.transactions), b.Hash, b.Height) len(b.transactions), b.Hash, b.Height)
@ -665,15 +691,29 @@ func (s *Store) rollback(ns walletdb.ReadWriteBucket, height int32) error {
} }
} }
err = it.delete() // reposition cursor before deleting this k/v pair and advancing to the
if err != nil { // previous.
return err it.reposition(it.elem.Height)
}
// Avoid cursor deletion until bolt issue #620 is resolved.
// err = it.delete()
// if err != nil {
// return err
// }
} }
if it.err != nil { if it.err != nil {
return it.err return it.err
} }
// Delete the block records outside of the iteration since cursor deletion
// is broken.
for _, h := range heightsToRemove {
err = deleteBlockRecord(ns, h)
if err != nil {
return err
}
}
for _, op := range coinBaseCredits { for _, op := range coinBaseCredits {
opKey := canonicalOutPoint(&op.Hash, op.Index) opKey := canonicalOutPoint(&op.Hash, op.Index)
unminedKey := existsRawUnminedInput(ns, opKey) unminedKey := existsRawUnminedInput(ns, opKey)

View file

@ -56,32 +56,38 @@ func testDB() (walletdb.DB, func(), error) {
return db, func() { os.RemoveAll(tmpDir) }, err return db, func() { os.RemoveAll(tmpDir) }, err
} }
func testStore() (*Store, func(), error) { var namespaceKey = []byte("txstore")
func testStore() (*Store, walletdb.DB, func(), error) {
tmpDir, err := ioutil.TempDir("", "wtxmgr_test") tmpDir, err := ioutil.TempDir("", "wtxmgr_test")
if err != nil { if err != nil {
return nil, func() {}, err return nil, nil, func() {}, err
} }
db, err := walletdb.Create("bdb", filepath.Join(tmpDir, "db")) db, err := walletdb.Create("bdb", filepath.Join(tmpDir, "db"))
if err != nil { if err != nil {
teardown := func() { teardown := func() {
os.RemoveAll(tmpDir) os.RemoveAll(tmpDir)
} }
return nil, teardown, err return nil, nil, teardown, err
} }
teardown := func() { teardown := func() {
db.Close() db.Close()
os.RemoveAll(tmpDir) os.RemoveAll(tmpDir)
} }
ns, err := db.Namespace([]byte("txstore")) var s *Store
if err != nil { err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
return nil, teardown, err ns, err := tx.CreateTopLevelBucket(namespaceKey)
} if err != nil {
err = Create(ns) return err
if err != nil { }
return nil, teardown, err err = Create(ns)
} if err != nil {
s, err := Open(ns, &chaincfg.TestNet3Params) return err
return s, teardown, err }
s, err = Open(ns, &chaincfg.TestNet3Params)
return err
})
return s, db, teardown, err
} }
func serializeTx(tx *btcutil.Tx) []byte { func serializeTx(tx *btcutil.Tx) []byte {
@ -122,14 +128,14 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
f func(*Store) (*Store, error) f func(*Store, walletdb.ReadWriteBucket) (*Store, error)
bal, unc btcutil.Amount bal, unc btcutil.Amount
unspents map[wire.OutPoint]struct{} unspents map[wire.OutPoint]struct{}
unmined map[chainhash.Hash]struct{} unmined map[chainhash.Hash]struct{}
}{ }{
{ {
name: "new store", name: "new store",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
return s, nil return s, nil
}, },
bal: 0, bal: 0,
@ -139,17 +145,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "txout insert", name: "txout insert",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstRecvSerializedTx, time.Now()) rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, nil) err = s.InsertTx(ns, rec, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.AddCredit(rec, nil, 0, false) err = s.AddCredit(ns, rec, nil, 0, false)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -166,17 +172,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "insert duplicate unconfirmed", name: "insert duplicate unconfirmed",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstRecvSerializedTx, time.Now()) rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, nil) err = s.InsertTx(ns, rec, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.AddCredit(rec, nil, 0, false) err = s.AddCredit(ns, rec, nil, 0, false)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -193,17 +199,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "confirmed txout insert", name: "confirmed txout insert",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstRecvSerializedTx, time.Now()) rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, TstRecvTxBlockDetails) err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false) err = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
return s, err return s, err
}, },
bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value), bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
@ -218,17 +224,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "insert duplicate confirmed", name: "insert duplicate confirmed",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstRecvSerializedTx, time.Now()) rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, TstRecvTxBlockDetails) err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false) err = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
return s, err return s, err
}, },
bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value), bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
@ -243,8 +249,8 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "rollback confirmed credit", name: "rollback confirmed credit",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
err := s.Rollback(TstRecvTxBlockDetails.Height) err := s.Rollback(ns, TstRecvTxBlockDetails.Height)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -261,17 +267,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "insert confirmed double spend", name: "insert confirmed double spend",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstDoubleSpendSerializedTx, time.Now()) rec, err := NewTxRecord(TstDoubleSpendSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, TstRecvTxBlockDetails) err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false) err = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
return s, err return s, err
}, },
bal: btcutil.Amount(TstDoubleSpendTx.MsgTx().TxOut[0].Value), bal: btcutil.Amount(TstDoubleSpendTx.MsgTx().TxOut[0].Value),
@ -286,12 +292,12 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "insert unconfirmed debit", name: "insert unconfirmed debit",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now()) rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, nil) err = s.InsertTx(ns, rec, nil)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -303,12 +309,12 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "insert unconfirmed debit again", name: "insert unconfirmed debit again",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstDoubleSpendSerializedTx, time.Now()) rec, err := NewTxRecord(TstDoubleSpendSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, TstRecvTxBlockDetails) err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -320,17 +326,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "insert change (index 0)", name: "insert change (index 0)",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now()) rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, nil) err = s.InsertTx(ns, rec, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.AddCredit(rec, nil, 0, true) err = s.AddCredit(ns, rec, nil, 0, true)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -347,16 +353,16 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "insert output back to this own wallet (index 1)", name: "insert output back to this own wallet (index 1)",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now()) rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, nil) err = s.InsertTx(ns, rec, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.AddCredit(rec, nil, 1, true) err = s.AddCredit(ns, rec, nil, 1, true)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -377,12 +383,12 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "confirm signed tx", name: "confirm signed tx",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now()) rec, err := NewTxRecord(TstSpendingSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, TstSignedTxBlockDetails) err = s.InsertTx(ns, rec, TstSignedTxBlockDetails)
return s, err return s, err
}, },
bal: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value), bal: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
@ -401,8 +407,8 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "rollback after spending tx", name: "rollback after spending tx",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
err := s.Rollback(TstSignedTxBlockDetails.Height + 1) err := s.Rollback(ns, TstSignedTxBlockDetails.Height+1)
return s, err return s, err
}, },
bal: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value), bal: btcutil.Amount(TstSpendingTx.MsgTx().TxOut[0].Value + TstSpendingTx.MsgTx().TxOut[1].Value),
@ -421,8 +427,8 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "rollback spending tx block", name: "rollback spending tx block",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
err := s.Rollback(TstSignedTxBlockDetails.Height) err := s.Rollback(ns, TstSignedTxBlockDetails.Height)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -443,8 +449,8 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "rollback double spend tx block", name: "rollback double spend tx block",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
err := s.Rollback(TstRecvTxBlockDetails.Height) err := s.Rollback(ns, TstRecvTxBlockDetails.Height)
return s, err return s, err
}, },
bal: 0, bal: 0,
@ -460,16 +466,16 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
{ {
name: "insert original recv txout", name: "insert original recv txout",
f: func(s *Store) (*Store, error) { f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstRecvSerializedTx, time.Now()) rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.InsertTx(rec, TstRecvTxBlockDetails) err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false) err = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
return s, err return s, err
}, },
bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value), bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
@ -481,88 +487,101 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
}, },
} }
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for _, test := range tests { for _, test := range tests {
tmpStore, err := test.f(s) err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
if err != nil { ns := tx.ReadWriteBucket(namespaceKey)
t.Fatalf("%s: got error: %v", test.name, err) tmpStore, err := test.f(s, ns)
} if err != nil {
s = tmpStore t.Fatalf("%s: got error: %v", test.name, err)
bal, err := s.Balance(1, TstRecvCurrentHeight)
if err != nil {
t.Fatalf("%s: Confirmed Balance failed: %v", test.name, err)
}
if bal != test.bal {
t.Fatalf("%s: balance mismatch: expected: %d, got: %d", test.name, test.bal, bal)
}
unc, err := s.Balance(0, TstRecvCurrentHeight)
if err != nil {
t.Fatalf("%s: Unconfirmed Balance failed: %v", test.name, err)
}
unc -= bal
if unc != test.unc {
t.Fatalf("%s: unconfirmed balance mismatch: expected %d, got %d", test.name, test.unc, unc)
}
// Check that unspent outputs match expected.
unspent, err := s.UnspentOutputs()
if err != nil {
t.Fatalf("%s: failed to fetch unspent outputs: %v", test.name, err)
}
for _, cred := range unspent {
if _, ok := test.unspents[cred.OutPoint]; !ok {
t.Errorf("%s: unexpected unspent output: %v", test.name, cred.OutPoint)
} }
delete(test.unspents, cred.OutPoint) s = tmpStore
} bal, err := s.Balance(ns, 1, TstRecvCurrentHeight)
if len(test.unspents) != 0 { if err != nil {
t.Fatalf("%s: missing expected unspent output(s)", test.name) t.Fatalf("%s: Confirmed Balance failed: %v", test.name, err)
} }
if bal != test.bal {
// Check that unmined txs match expected. t.Fatalf("%s: balance mismatch: expected: %d, got: %d", test.name, test.bal, bal)
unmined, err := s.UnminedTxs() }
if err != nil { unc, err := s.Balance(ns, 0, TstRecvCurrentHeight)
t.Fatalf("%s: cannot load unmined transactions: %v", test.name, err) if err != nil {
} t.Fatalf("%s: Unconfirmed Balance failed: %v", test.name, err)
for _, tx := range unmined { }
txHash := tx.TxHash() unc -= bal
if _, ok := test.unmined[txHash]; !ok { if unc != test.unc {
t.Fatalf("%s: unexpected unmined tx: %v", test.name, txHash) t.Fatalf("%s: unconfirmed balance mismatch: expected %d, got %d", test.name, test.unc, unc)
} }
delete(test.unmined, txHash)
}
if len(test.unmined) != 0 {
t.Fatalf("%s: missing expected unmined tx(s)", test.name)
}
// Check that unspent outputs match expected.
unspent, err := s.UnspentOutputs(ns)
if err != nil {
t.Fatalf("%s: failed to fetch unspent outputs: %v", test.name, err)
}
for _, cred := range unspent {
if _, ok := test.unspents[cred.OutPoint]; !ok {
t.Errorf("%s: unexpected unspent output: %v", test.name, cred.OutPoint)
}
delete(test.unspents, cred.OutPoint)
}
if len(test.unspents) != 0 {
t.Fatalf("%s: missing expected unspent output(s)", test.name)
}
// Check that unmined txs match expected.
unmined, err := s.UnminedTxs(ns)
if err != nil {
t.Fatalf("%s: cannot load unmined transactions: %v", test.name, err)
}
for _, tx := range unmined {
txHash := tx.TxHash()
if _, ok := test.unmined[txHash]; !ok {
t.Fatalf("%s: unexpected unmined tx: %v", test.name, txHash)
}
delete(test.unmined, txHash)
}
if len(test.unmined) != 0 {
t.Fatalf("%s: missing expected unmined tx(s)", test.name)
}
return nil
})
if err != nil {
t.Fatal(err)
}
} }
} }
func TestFindingSpentCredits(t *testing.T) { func TestFindingSpentCredits(t *testing.T) {
t.Parallel() t.Parallel()
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
dbtx, err := db.BeginReadWriteTx()
if err != nil {
t.Fatal(err)
}
defer dbtx.Commit()
ns := dbtx.ReadWriteBucket(namespaceKey)
// Insert transaction and credit which will be spent. // Insert transaction and credit which will be spent.
recvRec, err := NewTxRecord(TstRecvSerializedTx, time.Now()) recvRec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.InsertTx(recvRec, TstRecvTxBlockDetails) err = s.InsertTx(ns, recvRec, TstRecvTxBlockDetails)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(recvRec, TstRecvTxBlockDetails, 0, false) err = s.AddCredit(ns, recvRec, TstRecvTxBlockDetails, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -573,16 +592,16 @@ func TestFindingSpentCredits(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
err = s.InsertTx(spendingRec, TstSignedTxBlockDetails) err = s.InsertTx(ns, spendingRec, TstSignedTxBlockDetails)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(spendingRec, TstSignedTxBlockDetails, 0, false) err = s.AddCredit(ns, spendingRec, TstSignedTxBlockDetails, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
bal, err := s.Balance(1, TstSignedTxBlockDetails.Height) bal, err := s.Balance(ns, 1, TstSignedTxBlockDetails.Height)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -590,7 +609,7 @@ func TestFindingSpentCredits(t *testing.T) {
if bal != expectedBal { if bal != expectedBal {
t.Fatalf("bad balance: %v != %v", bal, expectedBal) t.Fatalf("bad balance: %v != %v", bal, expectedBal)
} }
unspents, err := s.UnspentOutputs() unspents, err := s.UnspentOutputs(ns)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -634,12 +653,19 @@ func spendOutput(txHash *chainhash.Hash, index uint32, outputValues ...int64) *w
func TestCoinbases(t *testing.T) { func TestCoinbases(t *testing.T) {
t.Parallel() t.Parallel()
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
dbtx, err := db.BeginReadWriteTx()
if err != nil {
t.Fatal(err)
}
defer dbtx.Commit()
ns := dbtx.ReadWriteBucket(namespaceKey)
b100 := BlockMeta{ b100 := BlockMeta{
Block: Block{Height: 100}, Block: Block{Height: 100},
Time: time.Now(), Time: time.Now(),
@ -652,15 +678,15 @@ func TestCoinbases(t *testing.T) {
} }
// Insert coinbase and mark outputs 0 and 2 as credits. // Insert coinbase and mark outputs 0 and 2 as credits.
err = s.InsertTx(cbRec, &b100) err = s.InsertTx(ns, cbRec, &b100)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(cbRec, &b100, 0, false) err = s.AddCredit(ns, cbRec, &b100, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(cbRec, &b100, 2, false) err = s.AddCredit(ns, cbRec, &b100, 2, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -746,7 +772,7 @@ func TestCoinbases(t *testing.T) {
}, },
} }
for i, tst := range balTests { for i, tst := range balTests {
bal, err := s.Balance(tst.minConf, tst.height) bal, err := s.Balance(ns, tst.minConf, tst.height)
if err != nil { if err != nil {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err) t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
} }
@ -766,11 +792,11 @@ func TestCoinbases(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.InsertTx(spenderARec, nil) err = s.InsertTx(ns, spenderARec, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(spenderARec, nil, 0, false) err = s.AddCredit(ns, spenderARec, nil, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -827,7 +853,7 @@ func TestCoinbases(t *testing.T) {
} }
balTestsBeforeMaturity := balTests balTestsBeforeMaturity := balTests
for i, tst := range balTests { for i, tst := range balTests {
bal, err := s.Balance(tst.minConf, tst.height) bal, err := s.Balance(ns, tst.minConf, tst.height)
if err != nil { if err != nil {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err) t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
} }
@ -844,7 +870,7 @@ func TestCoinbases(t *testing.T) {
Block: Block{Height: b100.Height + coinbaseMaturity}, Block: Block{Height: b100.Height + coinbaseMaturity},
Time: time.Now(), Time: time.Now(),
} }
err = s.InsertTx(spenderARec, &bMaturity) err = s.InsertTx(ns, spenderARec, &bMaturity)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -910,7 +936,7 @@ func TestCoinbases(t *testing.T) {
}, },
} }
for i, tst := range balTests { for i, tst := range balTests {
bal, err := s.Balance(tst.minConf, tst.height) bal, err := s.Balance(ns, tst.minConf, tst.height)
if err != nil { if err != nil {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err) t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
} }
@ -934,16 +960,16 @@ func TestCoinbases(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.InsertTx(spenderBRec, &bMaturity) err = s.InsertTx(ns, spenderBRec, &bMaturity)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(spenderBRec, &bMaturity, 0, false) err = s.AddCredit(ns, spenderBRec, &bMaturity, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
for i, tst := range balTests { for i, tst := range balTests {
bal, err := s.Balance(tst.minConf, tst.height) bal, err := s.Balance(ns, tst.minConf, tst.height)
if err != nil { if err != nil {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err) t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
} }
@ -957,13 +983,13 @@ func TestCoinbases(t *testing.T) {
// Reorg out the block that matured the coinbase and check balances // Reorg out the block that matured the coinbase and check balances
// again. // again.
err = s.Rollback(bMaturity.Height) err = s.Rollback(ns, bMaturity.Height)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
balTests = balTestsBeforeMaturity balTests = balTestsBeforeMaturity
for i, tst := range balTests { for i, tst := range balTests {
bal, err := s.Balance(tst.minConf, tst.height) bal, err := s.Balance(ns, tst.minConf, tst.height)
if err != nil { if err != nil {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err) t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
} }
@ -979,7 +1005,7 @@ func TestCoinbases(t *testing.T) {
// more transactions in the store (since the previous outputs referenced // more transactions in the store (since the previous outputs referenced
// by the spending tx no longer exist), and the balance will always be // by the spending tx no longer exist), and the balance will always be
// zero. // zero.
err = s.Rollback(b100.Height) err = s.Rollback(ns, b100.Height)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1009,7 +1035,7 @@ func TestCoinbases(t *testing.T) {
}, },
} }
for i, tst := range balTests { for i, tst := range balTests {
bal, err := s.Balance(tst.minConf, tst.height) bal, err := s.Balance(ns, tst.minConf, tst.height)
if err != nil { if err != nil {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err) t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
} }
@ -1020,7 +1046,7 @@ func TestCoinbases(t *testing.T) {
if t.Failed() { if t.Failed() {
t.Fatal("Failed balance checks after reorging coinbase block") t.Fatal("Failed balance checks after reorging coinbase block")
} }
unminedTxs, err := s.UnminedTxs() unminedTxs, err := s.UnminedTxs(ns)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1033,12 +1059,19 @@ func TestCoinbases(t *testing.T) {
func TestMoveMultipleToSameBlock(t *testing.T) { func TestMoveMultipleToSameBlock(t *testing.T) {
t.Parallel() t.Parallel()
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
dbtx, err := db.BeginReadWriteTx()
if err != nil {
t.Fatal(err)
}
defer dbtx.Commit()
ns := dbtx.ReadWriteBucket(namespaceKey)
b100 := BlockMeta{ b100 := BlockMeta{
Block: Block{Height: 100}, Block: Block{Height: 100},
Time: time.Now(), Time: time.Now(),
@ -1051,15 +1084,15 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
} }
// Insert coinbase and mark both outputs as credits. // Insert coinbase and mark both outputs as credits.
err = s.InsertTx(cbRec, &b100) err = s.InsertTx(ns, cbRec, &b100)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(cbRec, &b100, 0, false) err = s.AddCredit(ns, cbRec, &b100, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(cbRec, &b100, 1, false) err = s.AddCredit(ns, cbRec, &b100, 1, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1072,15 +1105,15 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.InsertTx(spenderARec, nil) err = s.InsertTx(ns, spenderARec, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(spenderARec, nil, 0, false) err = s.AddCredit(ns, spenderARec, nil, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(spenderARec, nil, 1, false) err = s.AddCredit(ns, spenderARec, nil, 1, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1090,15 +1123,15 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.InsertTx(spenderBRec, nil) err = s.InsertTx(ns, spenderBRec, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(spenderBRec, nil, 0, false) err = s.AddCredit(ns, spenderBRec, nil, 0, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.AddCredit(spenderBRec, nil, 1, false) err = s.AddCredit(ns, spenderBRec, nil, 1, false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1110,24 +1143,24 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
Block: Block{Height: b100.Height + coinbaseMaturity}, Block: Block{Height: b100.Height + coinbaseMaturity},
Time: time.Now(), Time: time.Now(),
} }
err = s.InsertTx(spenderARec, &bMaturity) err = s.InsertTx(ns, spenderARec, &bMaturity)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.InsertTx(spenderBRec, &bMaturity) err = s.InsertTx(ns, spenderBRec, &bMaturity)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// Check that both transactions can be queried at the maturity block. // Check that both transactions can be queried at the maturity block.
detailsA, err := s.UniqueTxDetails(&spenderARec.Hash, &bMaturity.Block) detailsA, err := s.UniqueTxDetails(ns, &spenderARec.Hash, &bMaturity.Block)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if detailsA == nil { if detailsA == nil {
t.Fatal("No details found for first spender") t.Fatal("No details found for first spender")
} }
detailsB, err := s.UniqueTxDetails(&spenderBRec.Hash, &bMaturity.Block) detailsB, err := s.UniqueTxDetails(ns, &spenderBRec.Hash, &bMaturity.Block)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1177,7 +1210,7 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
}, },
} }
for i, tst := range balTests { for i, tst := range balTests {
bal, err := s.Balance(tst.minConf, tst.height) bal, err := s.Balance(ns, tst.minConf, tst.height)
if err != nil { if err != nil {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err) t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
} }
@ -1188,7 +1221,7 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
if t.Failed() { if t.Failed() {
t.Fatal("Failed balance checks after moving both coinbase spenders") t.Fatal("Failed balance checks after moving both coinbase spenders")
} }
unminedTxs, err := s.UnminedTxs() unminedTxs, err := s.UnminedTxs(ns)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1203,25 +1236,32 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
func TestInsertUnserializedTx(t *testing.T) { func TestInsertUnserializedTx(t *testing.T) {
t.Parallel() t.Parallel()
s, teardown, err := testStore() s, db, teardown, err := testStore()
defer teardown() defer teardown()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
dbtx, err := db.BeginReadWriteTx()
if err != nil {
t.Fatal(err)
}
defer dbtx.Commit()
ns := dbtx.ReadWriteBucket(namespaceKey)
tx := newCoinBase(50e8) tx := newCoinBase(50e8)
rec, err := NewTxRecordFromMsgTx(tx, timeNow()) rec, err := NewTxRecordFromMsgTx(tx, timeNow())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
b100 := makeBlockMeta(100) b100 := makeBlockMeta(100)
err = s.InsertTx(stripSerializedTx(rec), &b100) err = s.InsertTx(ns, stripSerializedTx(rec), &b100)
if err != nil { if err != nil {
t.Fatalf("Insert for stripped TxRecord failed: %v", err) t.Fatalf("Insert for stripped TxRecord failed: %v", err)
} }
// Ensure it can be retreived successfully. // Ensure it can be retreived successfully.
details, err := s.UniqueTxDetails(&rec.Hash, &b100.Block) details, err := s.UniqueTxDetails(ns, &rec.Hash, &b100.Block)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1239,11 +1279,11 @@ func TestInsertUnserializedTx(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = s.InsertTx(rec, nil) err = s.InsertTx(ns, rec, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
details, err = s.UniqueTxDetails(&rec.Hash, nil) details, err = s.UniqueTxDetails(ns, &rec.Hash, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }