302 lines
7.8 KiB
Go
302 lines
7.8 KiB
Go
// Copyright (c) 2013 Conformal Systems LLC.
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package sqlite3_test
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/conformal/btcdb"
|
|
"github.com/conformal/btcdb/db_sqlite"
|
|
"github.com/conformal/btcwire"
|
|
"github.com/conformal/seelog"
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
// array of shas
|
|
var testShas []btcwire.ShaHash = []btcwire.ShaHash{
|
|
{
|
|
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
|
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
|
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
|
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
|
},
|
|
{
|
|
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
|
|
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
|
|
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
|
|
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
|
|
},
|
|
{
|
|
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
|
|
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
|
|
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
|
|
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
|
|
},
|
|
{
|
|
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
|
|
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
|
|
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
|
|
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
|
|
},
|
|
{
|
|
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
|
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
|
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
|
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
|
},
|
|
}
|
|
|
|
// Work around stupid go vet bug where any non array should have named
|
|
// initializers. Since ShaHash is a glorified array it shouldn't matter.
|
|
var badShaArray = [32]byte{
|
|
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
|
|
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
|
|
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
|
|
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
|
|
}
|
|
var badSha btcwire.ShaHash = btcwire.ShaHash(badShaArray)
|
|
var zeroSha = btcwire.ShaHash{}
|
|
var zeroBlock []byte = make([]byte, 32)
|
|
|
|
func compareArray(t *testing.T, one, two []btcwire.ShaHash, test string,
|
|
sync string) {
|
|
if len(one) != len(two) {
|
|
t.Errorf("%s: lengths don't match for arrays (%s)", test, sync)
|
|
return
|
|
}
|
|
|
|
for i := range one {
|
|
if !one[i].IsEqual(&two[i]) {
|
|
t.Errorf("%s: %dth sha doesn't match (%s)", test, i,
|
|
sync)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testNewestSha(t *testing.T, db btcdb.Db, expSha btcwire.ShaHash,
|
|
expBlk int64, situation string) {
|
|
|
|
newestsha, blkid, err := db.NewestSha()
|
|
if err != nil {
|
|
t.Errorf("NewestSha failed %v (%s)", err, situation)
|
|
return
|
|
}
|
|
if blkid != expBlk {
|
|
t.Errorf("NewestSha blkid is %d not %d (%s)", blkid, expBlk,
|
|
situation)
|
|
}
|
|
if !newestsha.IsEqual(&expSha) {
|
|
t.Errorf("Newestsha isn't the last sha we inserted %v %v (%s)",
|
|
newestsha, &expSha, situation)
|
|
}
|
|
}
|
|
|
|
type fetchIdxTest struct {
|
|
start int64
|
|
end int64
|
|
exp []btcwire.ShaHash
|
|
test string
|
|
}
|
|
|
|
func testFetch(t *testing.T, db btcdb.Db, shas []btcwire.ShaHash,
|
|
sync string) {
|
|
|
|
// Test the newest sha is what we expect and call it twice to ensure
|
|
// caching is working working properly.
|
|
numShas := int64(len(shas))
|
|
newestSha := shas[numShas-1]
|
|
newestBlockID := int64(numShas)
|
|
testNewestSha(t, db, newestSha, newestBlockID, sync)
|
|
testNewestSha(t, db, newestSha, newestBlockID, sync+" cached")
|
|
|
|
for i, sha := range shas {
|
|
// Add one for genesis block skew.
|
|
i = i + 1
|
|
|
|
// Ensure the sha exists in the db as expected.
|
|
if !db.ExistsSha(&sha) {
|
|
t.Errorf("testSha %d doesn't exists (%s)", i, sync)
|
|
break
|
|
}
|
|
|
|
// Fetch the sha from the db and ensure all fields are expected
|
|
// values.
|
|
buf, pver, idx, err := sqlite3.FetchSha(db, &sha)
|
|
if err != nil {
|
|
t.Errorf("Failed to fetch testSha %d (%s)", i, sync)
|
|
}
|
|
if !bytes.Equal(zeroBlock, buf) {
|
|
t.Errorf("testSha %d incorrect block return (%s)", i,
|
|
sync)
|
|
}
|
|
if pver != 1 {
|
|
t.Errorf("pver is %d and not 1 for testSha %d (%s)",
|
|
pver, i, sync)
|
|
}
|
|
if idx != int64(i) {
|
|
t.Errorf("index isn't as expected %d vs %d (%s)",
|
|
idx, i, sync)
|
|
}
|
|
|
|
// Fetch the sha by index and ensure it matches.
|
|
tsha, err := db.FetchBlockShaByIdx(int64(i))
|
|
if err != nil {
|
|
t.Errorf("can't fetch sha at index %d: %v", i, err)
|
|
continue
|
|
}
|
|
if !tsha.IsEqual(&sha) {
|
|
t.Errorf("sha for index %d isn't shas[%d]", i, i)
|
|
}
|
|
}
|
|
|
|
endBlockID := numShas + 1
|
|
midBlockID := endBlockID / 2
|
|
fetchIdxTests := []fetchIdxTest{
|
|
// All shas.
|
|
{1, btcdb.AllShas, shas, "fetch all shas"},
|
|
|
|
//// All shas using known bounds.
|
|
{1, endBlockID, shas, "fetch all shas2"},
|
|
|
|
// Partial list starting at beginning.
|
|
{1, midBlockID, shas[:midBlockID-1], "fetch first half"},
|
|
|
|
// Partial list ending at end.
|
|
{midBlockID, endBlockID, shas[midBlockID-1 : endBlockID-1],
|
|
"fetch second half"},
|
|
|
|
// Nonexistant off the end.
|
|
{endBlockID, endBlockID * 2, []btcwire.ShaHash{},
|
|
"fetch nonexistant"},
|
|
}
|
|
|
|
for _, test := range fetchIdxTests {
|
|
t.Logf("numSha: %d - Fetch from %d to %d\n", numShas, test.start, test.end)
|
|
if shalist, err := db.FetchIdxRange(test.start, test.end); err == nil {
|
|
compareArray(t, shalist, test.exp, test.test, sync)
|
|
} else {
|
|
t.Errorf("failed to fetch index range for %s (%s)",
|
|
test.test, sync)
|
|
}
|
|
}
|
|
|
|
// Try and fetch nonexistant sha.
|
|
if db.ExistsSha(&badSha) {
|
|
t.Errorf("non existant sha exists (%s)!", sync)
|
|
}
|
|
_, _, _, err := sqlite3.FetchSha(db, &badSha)
|
|
if err == nil {
|
|
t.Errorf("Success when fetching a bad sha! (%s)", sync)
|
|
}
|
|
// XXX if not check to see it is the right value?
|
|
|
|
testIterator(t, db, shas, sync)
|
|
}
|
|
|
|
func testIterator(t *testing.T, db btcdb.Db, shas []btcwire.ShaHash,
|
|
sync string) {
|
|
|
|
// Iterate over the whole list of shas.
|
|
iter, err := db.NewIterateBlocks()
|
|
if err != nil {
|
|
t.Errorf("failed to create iterated blocks")
|
|
return
|
|
}
|
|
|
|
// Skip the genesis block.
|
|
_ = iter.NextRow()
|
|
|
|
i := 0
|
|
for ; iter.NextRow(); i++ {
|
|
key, pver, buf, err := iter.Row()
|
|
if err != nil {
|
|
t.Errorf("iter.NextRow() failed: %v (%s)", err, sync)
|
|
break
|
|
}
|
|
if i >= len(shas) {
|
|
t.Errorf("iterator returned more shas than "+
|
|
"expected - %d (%s)", i, sync)
|
|
break
|
|
}
|
|
if !key.IsEqual(&shas[i]) {
|
|
t.Errorf("iterator test: %dth sha doesn't match (%s)",
|
|
i, sync)
|
|
}
|
|
if !bytes.Equal(zeroBlock, buf) {
|
|
t.Errorf("iterator test: %d buf incorrect (%s)", i,
|
|
sync)
|
|
}
|
|
if pver != 1 {
|
|
t.Errorf("iterator: %dth pver is %d and not 1 (%s)",
|
|
i, pver, sync)
|
|
}
|
|
}
|
|
if i < len(shas) {
|
|
t.Errorf("iterator got no rows on %dth loop, should have %d "+
|
|
"(%s)", i, len(shas), sync)
|
|
}
|
|
if _, _, _, err = iter.Row(); err == nil {
|
|
t.Errorf("done iterator didn't return failure")
|
|
}
|
|
iter.Close()
|
|
}
|
|
|
|
func TestBdb(t *testing.T) {
|
|
log, err := seelog.LoggerFromWriterWithMinLevel(os.Stdout,
|
|
seelog.InfoLvl)
|
|
if err != nil {
|
|
t.Errorf("failed to create logger: %v", err)
|
|
return
|
|
}
|
|
defer log.Flush()
|
|
btcdb.UseLogger(log)
|
|
|
|
// Ignore db remove errors since it means we didn't have an old one.
|
|
_ = os.Remove("tstdb1")
|
|
db, err := btcdb.CreateDB("sqlite", "tstdb1")
|
|
if err != nil {
|
|
t.Errorf("Failed to open test database %v", err)
|
|
return
|
|
}
|
|
defer os.Remove("tstdb1")
|
|
|
|
for i := range testShas {
|
|
var previous btcwire.ShaHash
|
|
if i == 0 {
|
|
previous = btcwire.GenesisHash
|
|
} else {
|
|
previous = testShas[i-1]
|
|
}
|
|
_, err := db.InsertBlockData(&testShas[i], &previous, 1, zeroBlock)
|
|
if err != nil {
|
|
t.Errorf("Failed to insert testSha %d. Error: %v",
|
|
i, err)
|
|
return
|
|
}
|
|
|
|
testFetch(t, db, testShas[0:i+1], "pre sync ")
|
|
}
|
|
|
|
// XXX insert enough so that we hit the transaction limit
|
|
// XXX try and insert a with a bad previous
|
|
|
|
db.Sync()
|
|
|
|
testFetch(t, db, testShas, "post sync")
|
|
|
|
for i := len(testShas) - 1; i >= 0; i-- {
|
|
err := db.DropAfterBlockBySha(testShas[i])
|
|
if err != nil {
|
|
t.Errorf("drop after %d failed %v", i, err)
|
|
break
|
|
}
|
|
testFetch(t, db, testShas[:i+1],
|
|
fmt.Sprintf("post DropAfter for sha %d", i))
|
|
}
|
|
|
|
// Just tests that it doesn't crash, no return value
|
|
db.Close()
|
|
}
|