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
}
func (it *blockIterator) delete() error {
err := it.c.Delete()
if err != nil {
str := "failed to delete block record"
storeError(ErrDatabase, str, err)
}
return nil
// unavailable until https://github.com/boltdb/bolt/issues/620 is fixed.
// func (it *blockIterator) delete() error {
// err := it.c.Delete()
// if err != nil {
// str := "failed to delete block record"
// storeError(ErrDatabase, str, err)
// }
// 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:
@ -1177,13 +1187,18 @@ func (it *unminedCreditIterator) next() bool {
return true
}
func (it *unminedCreditIterator) delete() error {
err := it.c.Delete()
if err != nil {
str := "failed to delete unmined credit"
return storeError(ErrDatabase, str, err)
}
return nil
// unavailable until https://github.com/boltdb/bolt/issues/620 is fixed.
// func (it *unminedCreditIterator) delete() error {
// err := it.c.Delete()
// if err != nil {
// str := "failed to delete unmined credit"
// return storeError(ErrDatabase, str, err)
// }
// 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

View file

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

View file

@ -14,6 +14,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/walletdb"
. "github.com/btcsuite/btcwallet/wtxmgr"
)
@ -63,7 +64,7 @@ func deepCopyTxDetails(d *TxDetails) *TxDetails {
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() {
if t.Failed() {
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
}
}
err := s.RangeTransactions(0, -1, checkBlock(fwdBlocks))
err := s.RangeTransactions(ns, 0, -1, checkBlock(fwdBlocks))
if err != nil {
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 {
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 {
blk = nil
}
d, err := s.UniqueTxDetails(&txHash, blk)
d, err := s.UniqueTxDetails(ns, &txHash, blk)
if err != nil {
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
// height) matches the last.
detail := &details[len(details)-1]
d, err := s.TxDetails(&txHash)
d, err := s.TxDetails(ns, &txHash)
if err != nil {
t.Fatal(err)
}
@ -237,13 +238,13 @@ func TestStoreQueries(t *testing.T) {
type queryTest struct {
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
}
var tests []queryTest
// Create the store and test initial state.
s, teardown, err := testStore()
s, db, teardown, err := testStore()
defer teardown()
if err != nil {
t.Fatal(err)
@ -251,19 +252,19 @@ func TestStoreQueries(t *testing.T) {
lastState := newQueryState()
tests = append(tests, queryTest{
desc: "initial store",
updates: func() {},
updates: func(walletdb.ReadWriteBucket) {},
state: lastState,
})
// simplify error handling
insertTx := func(rec *TxRecord, block *BlockMeta) {
err := s.InsertTx(rec, block)
insertTx := func(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta) {
err := s.InsertTx(ns, rec, block)
if err != nil {
t.Fatal(err)
}
}
addCredit := func(s *Store, rec *TxRecord, block *BlockMeta, index uint32, change bool) {
err := s.AddCredit(rec, block, index, change)
addCredit := func(s *Store, ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32, change bool) {
err := s.AddCredit(ns, rec, block, index, change)
if err != nil {
t.Fatal(err)
}
@ -275,8 +276,8 @@ func TestStoreQueries(t *testing.T) {
}
return rec
}
rollback := func(height int32) {
err := s.Rollback(height)
rollback := func(ns walletdb.ReadWriteBucket, height int32) {
err := s.Rollback(ns, height)
if err != nil {
t.Fatal(err)
}
@ -300,7 +301,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
desc: "insert tx A unmined",
updates: func() { insertTx(recA, nil) },
updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recA, nil) },
state: newState,
})
@ -318,7 +319,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
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,
})
@ -343,7 +344,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
desc: "insert tx B unmined",
updates: func() { insertTx(recB, nil) },
updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recB, nil) },
state: newState,
})
newState = lastState.deepCopy()
@ -359,7 +360,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
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,
})
@ -373,7 +374,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
desc: "mine tx A",
updates: func() { insertTx(recA, &b100) },
updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recA, &b100) },
state: newState,
})
@ -385,13 +386,20 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
desc: "mine tx B",
updates: func() { insertTx(recB, &b101) },
updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recB, &b101) },
state: newState,
})
for _, tst := range tests {
tst.updates()
tst.state.compare(t, s, tst.desc)
err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
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:
@ -402,62 +410,70 @@ func TestStoreQueries(t *testing.T) {
// - Verify that breaking early on RangeTransactions stops further
// iteration.
missingTx := spendOutput(&recB.Hash, 0, 40e8)
missingRec := newTxRecordFromMsgTx(missingTx, timeNow())
missingBlock := makeBlockMeta(102)
missingDetails, err := s.TxDetails(&missingRec.Hash)
if err != nil {
t.Fatal(err)
}
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)
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(namespaceKey)
missingTx := spendOutput(&recB.Hash, 0, 40e8)
missingRec := newTxRecordFromMsgTx(missingTx, timeNow())
missingBlock := makeBlockMeta(102)
missingDetails, err := s.TxDetails(ns, &missingRec.Hash)
if err != nil {
t.Fatal(err)
}
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(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
err = s.RangeTransactions(0, -1, func([]TxDetails) (bool, error) {
iterations++
return true, nil
iterations := 0
err = s.RangeTransactions(ns, 0, -1, func([]TxDetails) (bool, error) {
iterations++
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 {
t.Errorf("RangeTransactions (forwards) ran func %d times", iterations)
}
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)
if err != nil {
t.Fatal(err)
}
// None of the above tests have tested RangeTransactions with multiple
@ -472,7 +488,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests[:0:0], queryTest{
desc: "move tx B to block 100",
updates: func() { insertTx(recB, &b100) },
updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recB, &b100) },
state: newState,
})
newState = lastState.deepCopy()
@ -483,7 +499,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
desc: "rollback block 100",
updates: func() { rollback(b100.Height) },
updates: func(ns walletdb.ReadWriteBucket) { rollback(ns, b100.Height) },
state: newState,
})
@ -510,7 +526,7 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
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,
})
newState = lastState.deepCopy()
@ -524,20 +540,27 @@ func TestStoreQueries(t *testing.T) {
lastState = newState
tests = append(tests, queryTest{
desc: "mine duplicate tx A",
updates: func() { insertTx(recA, &b101) },
updates: func(ns walletdb.ReadWriteBucket) { insertTx(ns, recA, &b101) },
state: newState,
})
for _, tst := range tests {
tst.updates()
tst.state.compare(t, s, tst.desc)
err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
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) {
t.Parallel()
s, teardown, err := testStore()
s, db, teardown, err := testStore()
defer teardown()
if err != nil {
t.Fatal(err)
@ -593,14 +616,14 @@ func TestPreviousPkScripts(t *testing.T) {
recD = newTxRecordFromMsgTx(txD)
)
insertTx := func(rec *TxRecord, block *BlockMeta) {
err := s.InsertTx(rec, block)
insertTx := func(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta) {
err := s.InsertTx(ns, rec, block)
if err != nil {
t.Fatal(err)
}
}
addCredit := func(rec *TxRecord, block *BlockMeta, index uint32) {
err := s.AddCredit(rec, block, index, false)
addCredit := func(ns walletdb.ReadWriteBucket, rec *TxRecord, block *BlockMeta, index uint32) {
err := s.AddCredit(ns, rec, block, index, false)
if err != nil {
t.Fatal(err)
}
@ -611,8 +634,8 @@ func TestPreviousPkScripts(t *testing.T) {
block *Block
scripts [][]byte
}
runTest := func(tst *scriptTest) {
scripts, err := s.PreviousPkScripts(tst.rec, tst.block)
runTest := func(ns walletdb.ReadWriteBucket, tst *scriptTest) {
scripts, err := s.PreviousPkScripts(ns, tst.rec, tst.block)
if err != nil {
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
// these are marked as credits, PreviousPkScripts should not return
// them.
insertTx(recA, nil)
insertTx(recB, nil)
insertTx(recC, nil)
insertTx(ns, recA, nil)
insertTx(ns, recB, nil)
insertTx(ns, recC, nil)
b100 := makeBlockMeta(100)
b101 := makeBlockMeta(101)
@ -653,7 +683,7 @@ func TestPreviousPkScripts(t *testing.T) {
{recC, &b100.Block, nil},
}
for _, tst := range tests {
runTest(&tst)
runTest(ns, &tst)
}
if t.Failed() {
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
// both later but when C is mined, output 1's script should not be
// returned.
addCredit(recA, nil, 0)
addCredit(recA, nil, 1)
addCredit(recB, nil, 0)
addCredit(recB, nil, 1)
addCredit(recC, nil, 0)
addCredit(ns, recA, nil, 0)
addCredit(ns, recA, nil, 1)
addCredit(ns, recB, nil, 0)
addCredit(ns, recB, nil, 1)
addCredit(ns, recC, nil, 0)
tests = []scriptTest{
{recA, nil, nil},
{recA, &b100.Block, nil},
@ -676,23 +706,23 @@ func TestPreviousPkScripts(t *testing.T) {
{recC, &b100.Block, nil},
}
for _, tst := range tests {
runTest(&tst)
runTest(ns, &tst)
}
if t.Failed() {
t.Fatal("Failed after marking unmined credits")
}
// Mine tx A in block 100. Test results should be identical.
insertTx(recA, &b100)
insertTx(ns, recA, &b100)
for _, tst := range tests {
runTest(&tst)
runTest(ns, &tst)
}
if t.Failed() {
t.Fatal("Failed after mining tx A")
}
// Mine tx B in block 101.
insertTx(recB, &b101)
insertTx(ns, recB, &b101)
tests = []scriptTest{
{recA, nil, nil},
{recA, &b100.Block, nil},
@ -702,7 +732,7 @@ func TestPreviousPkScripts(t *testing.T) {
{recC, &b101.Block, nil},
}
for _, tst := range tests {
runTest(&tst)
runTest(ns, &tst)
}
if t.Failed() {
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
// same block.
insertTx(recC, &b101)
insertTx(ns, recC, &b101)
tests = []scriptTest{
{recA, nil, nil},
{recA, &b100.Block, nil},
@ -720,7 +750,7 @@ func TestPreviousPkScripts(t *testing.T) {
{recC, &b101.Block, [][]byte{scriptB0, scriptB1}},
}
for _, tst := range tests {
runTest(&tst)
runTest(ns, &tst)
}
if t.Failed() {
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
// 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, &b101.Block, nil})
for _, tst := range tests {
runTest(&tst)
runTest(ns, &tst)
}
if t.Failed() {
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
}
// For all mined transactions with unspent credits spent by this
// transaction, mark each spent, remove from the unspents map, and
// insert a debit record for the spent credit.
// For all transaction inputs, remove the previous output marker from the
// unmined inputs bucket. For any mined transactions with unspent credits
// spent by this transaction, mark each spent, remove from the unspents map,
// and insert a debit record for the spent credit.
debitIncidence := indexedIncidence{
incidence: incidence{txHash: rec.Hash, block: block.Block},
// index set for each rec input below.
}
for i, input := range rec.MsgTx.TxIn {
unspentKey, credKey := existsUnspent(ns, &input.PreviousOutPoint)
err = deleteRawUnminedInput(ns, unspentKey)
if err != nil {
return err
}
if credKey == nil {
continue
}
debitIncidence.index = uint32(i)
amt, err := spendCredit(ns, credKey, &debitIncidence)
if err != nil {
@ -225,11 +233,6 @@ func (s *Store) moveMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord, recKey,
if err != nil {
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
@ -261,10 +264,6 @@ func (s *Store) moveMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord, recKey,
cred.amount = amount
cred.change = change
err = it.delete()
if err != nil {
return err
}
err = putUnspentCredit(ns, &cred)
if err != nil {
return err
@ -273,12 +272,33 @@ func (s *Store) moveMinedTx(ns walletdb.ReadWriteBucket, rec *TxRecord, recKey,
if err != nil {
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
}
if it.err != nil {
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)
if err != nil {
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
// transactions later since blocks are removed in increasing order.
var coinBaseCredits []wire.OutPoint
var heightsToRemove []int32
it := makeBlockIterator(ns, height)
for it.next() {
it := makeReverseBlockIterator(ns)
for it.prev() {
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",
len(b.transactions), b.Hash, b.Height)
@ -665,15 +691,29 @@ func (s *Store) rollback(ns walletdb.ReadWriteBucket, height int32) error {
}
}
err = it.delete()
if err != nil {
return err
}
// reposition cursor before deleting this k/v pair and advancing to the
// previous.
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 {
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 {
opKey := canonicalOutPoint(&op.Hash, op.Index)
unminedKey := existsRawUnminedInput(ns, opKey)

View file

@ -56,32 +56,38 @@ func testDB() (walletdb.DB, func(), error) {
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")
if err != nil {
return nil, func() {}, err
return nil, nil, func() {}, err
}
db, err := walletdb.Create("bdb", filepath.Join(tmpDir, "db"))
if err != nil {
teardown := func() {
os.RemoveAll(tmpDir)
}
return nil, teardown, err
return nil, nil, teardown, err
}
teardown := func() {
db.Close()
os.RemoveAll(tmpDir)
}
ns, err := db.Namespace([]byte("txstore"))
if err != nil {
return nil, teardown, err
}
err = Create(ns)
if err != nil {
return nil, teardown, err
}
s, err := Open(ns, &chaincfg.TestNet3Params)
return s, teardown, err
var s *Store
err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns, err := tx.CreateTopLevelBucket(namespaceKey)
if err != nil {
return err
}
err = Create(ns)
if err != nil {
return err
}
s, err = Open(ns, &chaincfg.TestNet3Params)
return err
})
return s, db, teardown, err
}
func serializeTx(tx *btcutil.Tx) []byte {
@ -122,14 +128,14 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
tests := []struct {
name string
f func(*Store) (*Store, error)
f func(*Store, walletdb.ReadWriteBucket) (*Store, error)
bal, unc btcutil.Amount
unspents map[wire.OutPoint]struct{}
unmined map[chainhash.Hash]struct{}
}{
{
name: "new store",
f: func(s *Store) (*Store, error) {
f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
return s, nil
},
bal: 0,
@ -139,17 +145,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
name: "txout insert",
f: func(s *Store) (*Store, error) {
f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
rec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, nil)
err = s.InsertTx(ns, rec, nil)
if err != nil {
return nil, err
}
err = s.AddCredit(rec, nil, 0, false)
err = s.AddCredit(ns, rec, nil, 0, false)
return s, err
},
bal: 0,
@ -166,17 +172,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, nil)
err = s.InsertTx(ns, rec, nil)
if err != nil {
return nil, err
}
err = s.AddCredit(rec, nil, 0, false)
err = s.AddCredit(ns, rec, nil, 0, false)
return s, err
},
bal: 0,
@ -193,17 +199,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, TstRecvTxBlockDetails)
err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
if err != nil {
return nil, err
}
err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false)
err = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
return s, err
},
bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
@ -218,17 +224,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, TstRecvTxBlockDetails)
err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
if err != nil {
return nil, err
}
err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false)
err = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
return s, err
},
bal: btcutil.Amount(TstRecvTx.MsgTx().TxOut[0].Value),
@ -243,8 +249,8 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
name: "rollback confirmed credit",
f: func(s *Store) (*Store, error) {
err := s.Rollback(TstRecvTxBlockDetails.Height)
f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
err := s.Rollback(ns, TstRecvTxBlockDetails.Height)
return s, err
},
bal: 0,
@ -261,17 +267,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, TstRecvTxBlockDetails)
err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
if err != nil {
return nil, err
}
err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false)
err = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
return s, err
},
bal: btcutil.Amount(TstDoubleSpendTx.MsgTx().TxOut[0].Value),
@ -286,12 +292,12 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, nil)
err = s.InsertTx(ns, rec, nil)
return s, err
},
bal: 0,
@ -303,12 +309,12 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, TstRecvTxBlockDetails)
err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
return s, err
},
bal: 0,
@ -320,17 +326,17 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, nil)
err = s.InsertTx(ns, rec, nil)
if err != nil {
return nil, err
}
err = s.AddCredit(rec, nil, 0, true)
err = s.AddCredit(ns, rec, nil, 0, true)
return s, err
},
bal: 0,
@ -347,16 +353,16 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, nil)
err = s.InsertTx(ns, rec, nil)
if err != nil {
return nil, err
}
err = s.AddCredit(rec, nil, 1, true)
err = s.AddCredit(ns, rec, nil, 1, true)
return s, err
},
bal: 0,
@ -377,12 +383,12 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, TstSignedTxBlockDetails)
err = s.InsertTx(ns, rec, TstSignedTxBlockDetails)
return s, err
},
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",
f: func(s *Store) (*Store, error) {
err := s.Rollback(TstSignedTxBlockDetails.Height + 1)
f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
err := s.Rollback(ns, TstSignedTxBlockDetails.Height+1)
return s, err
},
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",
f: func(s *Store) (*Store, error) {
err := s.Rollback(TstSignedTxBlockDetails.Height)
f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
err := s.Rollback(ns, TstSignedTxBlockDetails.Height)
return s, err
},
bal: 0,
@ -443,8 +449,8 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
name: "rollback double spend tx block",
f: func(s *Store) (*Store, error) {
err := s.Rollback(TstRecvTxBlockDetails.Height)
f: func(s *Store, ns walletdb.ReadWriteBucket) (*Store, error) {
err := s.Rollback(ns, TstRecvTxBlockDetails.Height)
return s, err
},
bal: 0,
@ -460,16 +466,16 @@ func TestInsertsCreditsDebitsRollbacks(t *testing.T) {
},
{
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())
if err != nil {
return nil, err
}
err = s.InsertTx(rec, TstRecvTxBlockDetails)
err = s.InsertTx(ns, rec, TstRecvTxBlockDetails)
if err != nil {
return nil, err
}
err = s.AddCredit(rec, TstRecvTxBlockDetails, 0, false)
err = s.AddCredit(ns, rec, TstRecvTxBlockDetails, 0, false)
return s, err
},
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()
if err != nil {
t.Fatal(err)
}
for _, test := range tests {
tmpStore, err := test.f(s)
if err != nil {
t.Fatalf("%s: got error: %v", test.name, err)
}
s = tmpStore
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)
err := walletdb.Update(db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(namespaceKey)
tmpStore, err := test.f(s, ns)
if err != nil {
t.Fatalf("%s: got error: %v", test.name, err)
}
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()
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)
s = tmpStore
bal, err := s.Balance(ns, 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(ns, 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)
}
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) {
t.Parallel()
s, teardown, err := testStore()
s, db, teardown, err := testStore()
defer teardown()
if err != nil {
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.
recvRec, err := NewTxRecord(TstRecvSerializedTx, time.Now())
if err != nil {
t.Fatal(err)
}
err = s.InsertTx(recvRec, TstRecvTxBlockDetails)
err = s.InsertTx(ns, recvRec, TstRecvTxBlockDetails)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(recvRec, TstRecvTxBlockDetails, 0, false)
err = s.AddCredit(ns, recvRec, TstRecvTxBlockDetails, 0, false)
if err != nil {
t.Fatal(err)
}
@ -573,16 +592,16 @@ func TestFindingSpentCredits(t *testing.T) {
t.Fatal(err)
}
err = s.InsertTx(spendingRec, TstSignedTxBlockDetails)
err = s.InsertTx(ns, spendingRec, TstSignedTxBlockDetails)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(spendingRec, TstSignedTxBlockDetails, 0, false)
err = s.AddCredit(ns, spendingRec, TstSignedTxBlockDetails, 0, false)
if err != nil {
t.Fatal(err)
}
bal, err := s.Balance(1, TstSignedTxBlockDetails.Height)
bal, err := s.Balance(ns, 1, TstSignedTxBlockDetails.Height)
if err != nil {
t.Fatal(err)
}
@ -590,7 +609,7 @@ func TestFindingSpentCredits(t *testing.T) {
if bal != expectedBal {
t.Fatalf("bad balance: %v != %v", bal, expectedBal)
}
unspents, err := s.UnspentOutputs()
unspents, err := s.UnspentOutputs(ns)
if err != nil {
t.Fatal(err)
}
@ -634,12 +653,19 @@ func spendOutput(txHash *chainhash.Hash, index uint32, outputValues ...int64) *w
func TestCoinbases(t *testing.T) {
t.Parallel()
s, teardown, err := testStore()
s, db, teardown, err := testStore()
defer teardown()
if err != nil {
t.Fatal(err)
}
dbtx, err := db.BeginReadWriteTx()
if err != nil {
t.Fatal(err)
}
defer dbtx.Commit()
ns := dbtx.ReadWriteBucket(namespaceKey)
b100 := BlockMeta{
Block: Block{Height: 100},
Time: time.Now(),
@ -652,15 +678,15 @@ func TestCoinbases(t *testing.T) {
}
// Insert coinbase and mark outputs 0 and 2 as credits.
err = s.InsertTx(cbRec, &b100)
err = s.InsertTx(ns, cbRec, &b100)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(cbRec, &b100, 0, false)
err = s.AddCredit(ns, cbRec, &b100, 0, false)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(cbRec, &b100, 2, false)
err = s.AddCredit(ns, cbRec, &b100, 2, false)
if err != nil {
t.Fatal(err)
}
@ -746,7 +772,7 @@ func TestCoinbases(t *testing.T) {
},
}
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 {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
}
@ -766,11 +792,11 @@ func TestCoinbases(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = s.InsertTx(spenderARec, nil)
err = s.InsertTx(ns, spenderARec, nil)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(spenderARec, nil, 0, false)
err = s.AddCredit(ns, spenderARec, nil, 0, false)
if err != nil {
t.Fatal(err)
}
@ -827,7 +853,7 @@ func TestCoinbases(t *testing.T) {
}
balTestsBeforeMaturity := 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 {
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},
Time: time.Now(),
}
err = s.InsertTx(spenderARec, &bMaturity)
err = s.InsertTx(ns, spenderARec, &bMaturity)
if err != nil {
t.Fatal(err)
}
@ -910,7 +936,7 @@ func TestCoinbases(t *testing.T) {
},
}
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 {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
}
@ -934,16 +960,16 @@ func TestCoinbases(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = s.InsertTx(spenderBRec, &bMaturity)
err = s.InsertTx(ns, spenderBRec, &bMaturity)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(spenderBRec, &bMaturity, 0, false)
err = s.AddCredit(ns, spenderBRec, &bMaturity, 0, false)
if err != nil {
t.Fatal(err)
}
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 {
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
// again.
err = s.Rollback(bMaturity.Height)
err = s.Rollback(ns, bMaturity.Height)
if err != nil {
t.Fatal(err)
}
balTests = balTestsBeforeMaturity
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 {
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
// by the spending tx no longer exist), and the balance will always be
// zero.
err = s.Rollback(b100.Height)
err = s.Rollback(ns, b100.Height)
if err != nil {
t.Fatal(err)
}
@ -1009,7 +1035,7 @@ func TestCoinbases(t *testing.T) {
},
}
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 {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
}
@ -1020,7 +1046,7 @@ func TestCoinbases(t *testing.T) {
if t.Failed() {
t.Fatal("Failed balance checks after reorging coinbase block")
}
unminedTxs, err := s.UnminedTxs()
unminedTxs, err := s.UnminedTxs(ns)
if err != nil {
t.Fatal(err)
}
@ -1033,12 +1059,19 @@ func TestCoinbases(t *testing.T) {
func TestMoveMultipleToSameBlock(t *testing.T) {
t.Parallel()
s, teardown, err := testStore()
s, db, teardown, err := testStore()
defer teardown()
if err != nil {
t.Fatal(err)
}
dbtx, err := db.BeginReadWriteTx()
if err != nil {
t.Fatal(err)
}
defer dbtx.Commit()
ns := dbtx.ReadWriteBucket(namespaceKey)
b100 := BlockMeta{
Block: Block{Height: 100},
Time: time.Now(),
@ -1051,15 +1084,15 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
}
// Insert coinbase and mark both outputs as credits.
err = s.InsertTx(cbRec, &b100)
err = s.InsertTx(ns, cbRec, &b100)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(cbRec, &b100, 0, false)
err = s.AddCredit(ns, cbRec, &b100, 0, false)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(cbRec, &b100, 1, false)
err = s.AddCredit(ns, cbRec, &b100, 1, false)
if err != nil {
t.Fatal(err)
}
@ -1072,15 +1105,15 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = s.InsertTx(spenderARec, nil)
err = s.InsertTx(ns, spenderARec, nil)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(spenderARec, nil, 0, false)
err = s.AddCredit(ns, spenderARec, nil, 0, false)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(spenderARec, nil, 1, false)
err = s.AddCredit(ns, spenderARec, nil, 1, false)
if err != nil {
t.Fatal(err)
}
@ -1090,15 +1123,15 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = s.InsertTx(spenderBRec, nil)
err = s.InsertTx(ns, spenderBRec, nil)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(spenderBRec, nil, 0, false)
err = s.AddCredit(ns, spenderBRec, nil, 0, false)
if err != nil {
t.Fatal(err)
}
err = s.AddCredit(spenderBRec, nil, 1, false)
err = s.AddCredit(ns, spenderBRec, nil, 1, false)
if err != nil {
t.Fatal(err)
}
@ -1110,24 +1143,24 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
Block: Block{Height: b100.Height + coinbaseMaturity},
Time: time.Now(),
}
err = s.InsertTx(spenderARec, &bMaturity)
err = s.InsertTx(ns, spenderARec, &bMaturity)
if err != nil {
t.Fatal(err)
}
err = s.InsertTx(spenderBRec, &bMaturity)
err = s.InsertTx(ns, spenderBRec, &bMaturity)
if err != nil {
t.Fatal(err)
}
// 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 {
t.Fatal(err)
}
if detailsA == nil {
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 {
t.Fatal(err)
}
@ -1177,7 +1210,7 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
},
}
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 {
t.Fatalf("Balance test %d: Store.Balance failed: %v", i, err)
}
@ -1188,7 +1221,7 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
if t.Failed() {
t.Fatal("Failed balance checks after moving both coinbase spenders")
}
unminedTxs, err := s.UnminedTxs()
unminedTxs, err := s.UnminedTxs(ns)
if err != nil {
t.Fatal(err)
}
@ -1203,25 +1236,32 @@ func TestMoveMultipleToSameBlock(t *testing.T) {
func TestInsertUnserializedTx(t *testing.T) {
t.Parallel()
s, teardown, err := testStore()
s, db, teardown, err := testStore()
defer teardown()
if err != nil {
t.Fatal(err)
}
dbtx, err := db.BeginReadWriteTx()
if err != nil {
t.Fatal(err)
}
defer dbtx.Commit()
ns := dbtx.ReadWriteBucket(namespaceKey)
tx := newCoinBase(50e8)
rec, err := NewTxRecordFromMsgTx(tx, timeNow())
if err != nil {
t.Fatal(err)
}
b100 := makeBlockMeta(100)
err = s.InsertTx(stripSerializedTx(rec), &b100)
err = s.InsertTx(ns, stripSerializedTx(rec), &b100)
if err != nil {
t.Fatalf("Insert for stripped TxRecord failed: %v", err)
}
// 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 {
t.Fatal(err)
}
@ -1239,11 +1279,11 @@ func TestInsertUnserializedTx(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = s.InsertTx(rec, nil)
err = s.InsertTx(ns, rec, nil)
if err != nil {
t.Fatal(err)
}
details, err = s.UniqueTxDetails(&rec.Hash, nil)
details, err = s.UniqueTxDetails(ns, &rec.Hash, nil)
if err != nil {
t.Fatal(err)
}