github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/database/ffldb/interface_test.go (about)

     1  // Copyright (c) 2015-2016 The btcsuite developers
     2  // Copyright (c) 2016 The Dash developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  // This file intended to be copied into each backend driver directory.  Each
     7  // driver should have their own driver_test.go file which creates a database and
     8  // invokes the testInterface function in this file to ensure the driver properly
     9  // implements the interface.
    10  //
    11  // NOTE: When copying this file into the backend driver folder, the package name
    12  // will need to be changed accordingly.
    13  
    14  package ffldb_test
    15  
    16  import (
    17  	"bytes"
    18  	"compress/bzip2"
    19  	"encoding/binary"
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"path/filepath"
    24  	"reflect"
    25  	"sync/atomic"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/dashpay/godash/chaincfg"
    30  	"github.com/dashpay/godash/database"
    31  	"github.com/dashpay/godash/wire"
    32  	"github.com/dashpay/godashutil"
    33  )
    34  
    35  var (
    36  	// blockDataNet is the expected network in the test block data.
    37  	blockDataNet = wire.MainNet
    38  
    39  	// blockDataFile is the path to a file containing the first 256 blocks
    40  	// of the block chain.
    41  	blockDataFile = filepath.Join("..", "testdata", "blocks1-256.bz2")
    42  
    43  	// errSubTestFail is used to signal that a sub test returned false.
    44  	errSubTestFail = fmt.Errorf("sub test failure")
    45  )
    46  
    47  // loadBlocks loads the blocks contained in the testdata directory and returns
    48  // a slice of them.
    49  func loadBlocks(t *testing.T, dataFile string, network wire.BitcoinNet) ([]*godashutil.Block, error) {
    50  	// Open the file that contains the blocks for reading.
    51  	fi, err := os.Open(dataFile)
    52  	if err != nil {
    53  		t.Errorf("failed to open file %v, err %v", dataFile, err)
    54  		return nil, err
    55  	}
    56  	defer func() {
    57  		if err := fi.Close(); err != nil {
    58  			t.Errorf("failed to close file %v %v", dataFile,
    59  				err)
    60  		}
    61  	}()
    62  	dr := bzip2.NewReader(fi)
    63  
    64  	// Set the first block as the genesis block.
    65  	blocks := make([]*godashutil.Block, 0, 256)
    66  	genesis := godashutil.NewBlock(chaincfg.MainNetParams.GenesisBlock)
    67  	blocks = append(blocks, genesis)
    68  
    69  	// Load the remaining blocks.
    70  	for height := 1; ; height++ {
    71  		var net uint32
    72  		err := binary.Read(dr, binary.LittleEndian, &net)
    73  		if err == io.EOF {
    74  			// Hit end of file at the expected offset.  No error.
    75  			break
    76  		}
    77  		if err != nil {
    78  			t.Errorf("Failed to load network type for block %d: %v",
    79  				height, err)
    80  			return nil, err
    81  		}
    82  		if net != uint32(network) {
    83  			t.Errorf("Block doesn't match network: %v expects %v",
    84  				net, network)
    85  			return nil, err
    86  		}
    87  
    88  		var blockLen uint32
    89  		err = binary.Read(dr, binary.LittleEndian, &blockLen)
    90  		if err != nil {
    91  			t.Errorf("Failed to load block size for block %d: %v",
    92  				height, err)
    93  			return nil, err
    94  		}
    95  
    96  		// Read the block.
    97  		blockBytes := make([]byte, blockLen)
    98  		_, err = io.ReadFull(dr, blockBytes)
    99  		if err != nil {
   100  			t.Errorf("Failed to load block %d: %v", height, err)
   101  			return nil, err
   102  		}
   103  
   104  		// Deserialize and store the block.
   105  		block, err := godashutil.NewBlockFromBytes(blockBytes)
   106  		if err != nil {
   107  			t.Errorf("Failed to parse block %v: %v", height, err)
   108  			return nil, err
   109  		}
   110  		blocks = append(blocks, block)
   111  	}
   112  
   113  	return blocks, nil
   114  }
   115  
   116  // checkDbError ensures the passed error is a database.Error with an error code
   117  // that matches the passed  error code.
   118  func checkDbError(t *testing.T, testName string, gotErr error, wantErrCode database.ErrorCode) bool {
   119  	dbErr, ok := gotErr.(database.Error)
   120  	if !ok {
   121  		t.Errorf("%s: unexpected error type - got %T, want %T",
   122  			testName, gotErr, database.Error{})
   123  		return false
   124  	}
   125  	if dbErr.ErrorCode != wantErrCode {
   126  		t.Errorf("%s: unexpected error code - got %s (%s), want %s",
   127  			testName, dbErr.ErrorCode, dbErr.Description,
   128  			wantErrCode)
   129  		return false
   130  	}
   131  
   132  	return true
   133  }
   134  
   135  // testContext is used to store context information about a running test which
   136  // is passed into helper functions.
   137  type testContext struct {
   138  	t           *testing.T
   139  	db          database.DB
   140  	bucketDepth int
   141  	isWritable  bool
   142  	blocks      []*godashutil.Block
   143  }
   144  
   145  // keyPair houses a key/value pair.  It is used over maps so ordering can be
   146  // maintained.
   147  type keyPair struct {
   148  	key   []byte
   149  	value []byte
   150  }
   151  
   152  // lookupKey is a convenience method to lookup the requested key from the
   153  // provided keypair slice along with whether or not the key was found.
   154  func lookupKey(key []byte, values []keyPair) ([]byte, bool) {
   155  	for _, item := range values {
   156  		if bytes.Equal(item.key, key) {
   157  			return item.value, true
   158  		}
   159  	}
   160  
   161  	return nil, false
   162  }
   163  
   164  // toGetValues returns a copy of the provided keypairs with all of the nil
   165  // values set to an empty byte slice.  This is used to ensure that keys set to
   166  // nil values result in empty byte slices when retrieved instead of nil.
   167  func toGetValues(values []keyPair) []keyPair {
   168  	ret := make([]keyPair, len(values))
   169  	copy(ret, values)
   170  	for i := range ret {
   171  		if ret[i].value == nil {
   172  			ret[i].value = make([]byte, 0)
   173  		}
   174  	}
   175  	return ret
   176  }
   177  
   178  // rollbackValues returns a copy of the provided keypairs with all values set to
   179  // nil.  This is used to test that values are properly rolled back.
   180  func rollbackValues(values []keyPair) []keyPair {
   181  	ret := make([]keyPair, len(values))
   182  	copy(ret, values)
   183  	for i := range ret {
   184  		ret[i].value = nil
   185  	}
   186  	return ret
   187  }
   188  
   189  // testCursorKeyPair checks that the provide key and value match the expected
   190  // keypair at the provided index.  It also ensures the index is in range for the
   191  // provided slice of expected keypairs.
   192  func testCursorKeyPair(tc *testContext, k, v []byte, index int, values []keyPair) bool {
   193  	if index >= len(values) || index < 0 {
   194  		tc.t.Errorf("Cursor: exceeded the expected range of values - "+
   195  			"index %d, num values %d", index, len(values))
   196  		return false
   197  	}
   198  
   199  	pair := &values[index]
   200  	if !bytes.Equal(k, pair.key) {
   201  		tc.t.Errorf("Mismatched cursor key: index %d does not match "+
   202  			"the expected key - got %q, want %q", index, k,
   203  			pair.key)
   204  		return false
   205  	}
   206  	if !bytes.Equal(v, pair.value) {
   207  		tc.t.Errorf("Mismatched cursor value: index %d does not match "+
   208  			"the expected value - got %q, want %q", index, v,
   209  			pair.value)
   210  		return false
   211  	}
   212  
   213  	return true
   214  }
   215  
   216  // testGetValues checks that all of the provided key/value pairs can be
   217  // retrieved from the database and the retrieved values match the provided
   218  // values.
   219  func testGetValues(tc *testContext, bucket database.Bucket, values []keyPair) bool {
   220  	for _, item := range values {
   221  		gotValue := bucket.Get(item.key)
   222  		if !reflect.DeepEqual(gotValue, item.value) {
   223  			tc.t.Errorf("Get: unexpected value for %q - got %q, "+
   224  				"want %q", item.key, gotValue, item.value)
   225  			return false
   226  		}
   227  	}
   228  
   229  	return true
   230  }
   231  
   232  // testPutValues stores all of the provided key/value pairs in the provided
   233  // bucket while checking for errors.
   234  func testPutValues(tc *testContext, bucket database.Bucket, values []keyPair) bool {
   235  	for _, item := range values {
   236  		if err := bucket.Put(item.key, item.value); err != nil {
   237  			tc.t.Errorf("Put: unexpected error: %v", err)
   238  			return false
   239  		}
   240  	}
   241  
   242  	return true
   243  }
   244  
   245  // testDeleteValues removes all of the provided key/value pairs from the
   246  // provided bucket.
   247  func testDeleteValues(tc *testContext, bucket database.Bucket, values []keyPair) bool {
   248  	for _, item := range values {
   249  		if err := bucket.Delete(item.key); err != nil {
   250  			tc.t.Errorf("Delete: unexpected error: %v", err)
   251  			return false
   252  		}
   253  	}
   254  
   255  	return true
   256  }
   257  
   258  // testCursorInterface ensures the cursor itnerface is working properly by
   259  // exercising all of its functions on the passed bucket.
   260  func testCursorInterface(tc *testContext, bucket database.Bucket) bool {
   261  	// Ensure a cursor can be obtained for the bucket.
   262  	cursor := bucket.Cursor()
   263  	if cursor == nil {
   264  		tc.t.Error("Bucket.Cursor: unexpected nil cursor returned")
   265  		return false
   266  	}
   267  
   268  	// Ensure the cursor returns the same bucket it was created for.
   269  	if cursor.Bucket() != bucket {
   270  		tc.t.Error("Cursor.Bucket: does not match the bucket it was " +
   271  			"created for")
   272  		return false
   273  	}
   274  
   275  	if tc.isWritable {
   276  		unsortedValues := []keyPair{
   277  			{[]byte("cursor"), []byte("val1")},
   278  			{[]byte("abcd"), []byte("val2")},
   279  			{[]byte("bcd"), []byte("val3")},
   280  			{[]byte("defg"), nil},
   281  		}
   282  		sortedValues := []keyPair{
   283  			{[]byte("abcd"), []byte("val2")},
   284  			{[]byte("bcd"), []byte("val3")},
   285  			{[]byte("cursor"), []byte("val1")},
   286  			{[]byte("defg"), nil},
   287  		}
   288  
   289  		// Store the values to be used in the cursor tests in unsorted
   290  		// order and ensure they were actually stored.
   291  		if !testPutValues(tc, bucket, unsortedValues) {
   292  			return false
   293  		}
   294  		if !testGetValues(tc, bucket, toGetValues(unsortedValues)) {
   295  			return false
   296  		}
   297  
   298  		// Ensure the cursor returns all items in byte-sorted order when
   299  		// iterating forward.
   300  		curIdx := 0
   301  		for ok := cursor.First(); ok; ok = cursor.Next() {
   302  			k, v := cursor.Key(), cursor.Value()
   303  			if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) {
   304  				return false
   305  			}
   306  			curIdx++
   307  		}
   308  		if curIdx != len(unsortedValues) {
   309  			tc.t.Errorf("Cursor: expected to iterate %d values, "+
   310  				"but only iterated %d", len(unsortedValues),
   311  				curIdx)
   312  			return false
   313  		}
   314  
   315  		// Ensure the cursor returns all items in reverse byte-sorted
   316  		// order when iterating in reverse.
   317  		curIdx = len(sortedValues) - 1
   318  		for ok := cursor.Last(); ok; ok = cursor.Prev() {
   319  			k, v := cursor.Key(), cursor.Value()
   320  			if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) {
   321  				return false
   322  			}
   323  			curIdx--
   324  		}
   325  		if curIdx > -1 {
   326  			tc.t.Errorf("Reverse cursor: expected to iterate %d "+
   327  				"values, but only iterated %d",
   328  				len(sortedValues), len(sortedValues)-(curIdx+1))
   329  			return false
   330  		}
   331  
   332  		// Ensure forward iteration works as expected after seeking.
   333  		middleIdx := (len(sortedValues) - 1) / 2
   334  		seekKey := sortedValues[middleIdx].key
   335  		curIdx = middleIdx
   336  		for ok := cursor.Seek(seekKey); ok; ok = cursor.Next() {
   337  			k, v := cursor.Key(), cursor.Value()
   338  			if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) {
   339  				return false
   340  			}
   341  			curIdx++
   342  		}
   343  		if curIdx != len(sortedValues) {
   344  			tc.t.Errorf("Cursor after seek: expected to iterate "+
   345  				"%d values, but only iterated %d",
   346  				len(sortedValues)-middleIdx, curIdx-middleIdx)
   347  			return false
   348  		}
   349  
   350  		// Ensure reverse iteration works as expected after seeking.
   351  		curIdx = middleIdx
   352  		for ok := cursor.Seek(seekKey); ok; ok = cursor.Prev() {
   353  			k, v := cursor.Key(), cursor.Value()
   354  			if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) {
   355  				return false
   356  			}
   357  			curIdx--
   358  		}
   359  		if curIdx > -1 {
   360  			tc.t.Errorf("Reverse cursor after seek: expected to "+
   361  				"iterate %d values, but only iterated %d",
   362  				len(sortedValues)-middleIdx, middleIdx-curIdx)
   363  			return false
   364  		}
   365  
   366  		// Ensure the cursor deletes items properly.
   367  		if !cursor.First() {
   368  			tc.t.Errorf("Cursor.First: no value")
   369  			return false
   370  		}
   371  		k := cursor.Key()
   372  		if err := cursor.Delete(); err != nil {
   373  			tc.t.Errorf("Cursor.Delete: unexpected error: %v", err)
   374  			return false
   375  		}
   376  		if val := bucket.Get(k); val != nil {
   377  			tc.t.Errorf("Cursor.Delete: value for key %q was not "+
   378  				"deleted", k)
   379  			return false
   380  		}
   381  	}
   382  
   383  	return true
   384  }
   385  
   386  // testNestedBucket reruns the testBucketInterface against a nested bucket along
   387  // with a counter to only test a couple of level deep.
   388  func testNestedBucket(tc *testContext, testBucket database.Bucket) bool {
   389  	// Don't go more than 2 nested levels deep.
   390  	if tc.bucketDepth > 1 {
   391  		return true
   392  	}
   393  
   394  	tc.bucketDepth++
   395  	defer func() {
   396  		tc.bucketDepth--
   397  	}()
   398  	if !testBucketInterface(tc, testBucket) {
   399  		return false
   400  	}
   401  
   402  	return true
   403  }
   404  
   405  // testBucketInterface ensures the bucket interface is working properly by
   406  // exercising all of its functions.  This includes the cursor interface for the
   407  // cursor returned from the bucket.
   408  func testBucketInterface(tc *testContext, bucket database.Bucket) bool {
   409  	if bucket.Writable() != tc.isWritable {
   410  		tc.t.Errorf("Bucket writable state does not match.")
   411  		return false
   412  	}
   413  
   414  	if tc.isWritable {
   415  		// keyValues holds the keys and values to use when putting
   416  		// values into the bucket.
   417  		keyValues := []keyPair{
   418  			{[]byte("bucketkey1"), []byte("foo1")},
   419  			{[]byte("bucketkey2"), []byte("foo2")},
   420  			{[]byte("bucketkey3"), []byte("foo3")},
   421  			{[]byte("bucketkey4"), nil},
   422  		}
   423  		expectedKeyValues := toGetValues(keyValues)
   424  		if !testPutValues(tc, bucket, keyValues) {
   425  			return false
   426  		}
   427  
   428  		if !testGetValues(tc, bucket, expectedKeyValues) {
   429  			return false
   430  		}
   431  
   432  		// Ensure errors returned from the user-supplied ForEach
   433  		// function are returned.
   434  		forEachError := fmt.Errorf("example foreach error")
   435  		err := bucket.ForEach(func(k, v []byte) error {
   436  			return forEachError
   437  		})
   438  		if err != forEachError {
   439  			tc.t.Errorf("ForEach: inner function error not "+
   440  				"returned - got %v, want %v", err, forEachError)
   441  			return false
   442  		}
   443  
   444  		// Iterate all of the keys using ForEach while making sure the
   445  		// stored values are the expected values.
   446  		keysFound := make(map[string]struct{}, len(keyValues))
   447  		err = bucket.ForEach(func(k, v []byte) error {
   448  			wantV, found := lookupKey(k, expectedKeyValues)
   449  			if !found {
   450  				return fmt.Errorf("ForEach: key '%s' should "+
   451  					"exist", k)
   452  			}
   453  
   454  			if !reflect.DeepEqual(v, wantV) {
   455  				return fmt.Errorf("ForEach: value for key '%s' "+
   456  					"does not match - got %s, want %s", k,
   457  					v, wantV)
   458  			}
   459  
   460  			keysFound[string(k)] = struct{}{}
   461  			return nil
   462  		})
   463  		if err != nil {
   464  			tc.t.Errorf("%v", err)
   465  			return false
   466  		}
   467  
   468  		// Ensure all keys were iterated.
   469  		for _, item := range keyValues {
   470  			if _, ok := keysFound[string(item.key)]; !ok {
   471  				tc.t.Errorf("ForEach: key '%s' was not iterated "+
   472  					"when it should have been", item.key)
   473  				return false
   474  			}
   475  		}
   476  
   477  		// Delete the keys and ensure they were deleted.
   478  		if !testDeleteValues(tc, bucket, keyValues) {
   479  			return false
   480  		}
   481  		if !testGetValues(tc, bucket, rollbackValues(keyValues)) {
   482  			return false
   483  		}
   484  
   485  		// Ensure creating a new bucket works as expected.
   486  		testBucketName := []byte("testbucket")
   487  		testBucket, err := bucket.CreateBucket(testBucketName)
   488  		if err != nil {
   489  			tc.t.Errorf("CreateBucket: unexpected error: %v", err)
   490  			return false
   491  		}
   492  		if !testNestedBucket(tc, testBucket) {
   493  			return false
   494  		}
   495  
   496  		// Ensure errors returned from the user-supplied ForEachBucket
   497  		// function are returned.
   498  		err = bucket.ForEachBucket(func(k []byte) error {
   499  			return forEachError
   500  		})
   501  		if err != forEachError {
   502  			tc.t.Errorf("ForEachBucket: inner function error not "+
   503  				"returned - got %v, want %v", err, forEachError)
   504  			return false
   505  		}
   506  
   507  		// Ensure creating a bucket that already exists fails with the
   508  		// expected error.
   509  		wantErrCode := database.ErrBucketExists
   510  		_, err = bucket.CreateBucket(testBucketName)
   511  		if !checkDbError(tc.t, "CreateBucket", err, wantErrCode) {
   512  			return false
   513  		}
   514  
   515  		// Ensure CreateBucketIfNotExists returns an existing bucket.
   516  		testBucket, err = bucket.CreateBucketIfNotExists(testBucketName)
   517  		if err != nil {
   518  			tc.t.Errorf("CreateBucketIfNotExists: unexpected "+
   519  				"error: %v", err)
   520  			return false
   521  		}
   522  		if !testNestedBucket(tc, testBucket) {
   523  			return false
   524  		}
   525  
   526  		// Ensure retrieving an existing bucket works as expected.
   527  		testBucket = bucket.Bucket(testBucketName)
   528  		if !testNestedBucket(tc, testBucket) {
   529  			return false
   530  		}
   531  
   532  		// Ensure deleting a bucket works as intended.
   533  		if err := bucket.DeleteBucket(testBucketName); err != nil {
   534  			tc.t.Errorf("DeleteBucket: unexpected error: %v", err)
   535  			return false
   536  		}
   537  		if b := bucket.Bucket(testBucketName); b != nil {
   538  			tc.t.Errorf("DeleteBucket: bucket '%s' still exists",
   539  				testBucketName)
   540  			return false
   541  		}
   542  
   543  		// Ensure deleting a bucket that doesn't exist returns the
   544  		// expected error.
   545  		wantErrCode = database.ErrBucketNotFound
   546  		err = bucket.DeleteBucket(testBucketName)
   547  		if !checkDbError(tc.t, "DeleteBucket", err, wantErrCode) {
   548  			return false
   549  		}
   550  
   551  		// Ensure CreateBucketIfNotExists creates a new bucket when
   552  		// it doesn't already exist.
   553  		testBucket, err = bucket.CreateBucketIfNotExists(testBucketName)
   554  		if err != nil {
   555  			tc.t.Errorf("CreateBucketIfNotExists: unexpected "+
   556  				"error: %v", err)
   557  			return false
   558  		}
   559  		if !testNestedBucket(tc, testBucket) {
   560  			return false
   561  		}
   562  
   563  		// Ensure the cursor interface works as expected.
   564  		if !testCursorInterface(tc, testBucket) {
   565  			return false
   566  		}
   567  
   568  		// Delete the test bucket to avoid leaving it around for future
   569  		// calls.
   570  		if err := bucket.DeleteBucket(testBucketName); err != nil {
   571  			tc.t.Errorf("DeleteBucket: unexpected error: %v", err)
   572  			return false
   573  		}
   574  		if b := bucket.Bucket(testBucketName); b != nil {
   575  			tc.t.Errorf("DeleteBucket: bucket '%s' still exists",
   576  				testBucketName)
   577  			return false
   578  		}
   579  	} else {
   580  		// Put should fail with bucket that is not writable.
   581  		testName := "unwritable tx put"
   582  		wantErrCode := database.ErrTxNotWritable
   583  		failBytes := []byte("fail")
   584  		err := bucket.Put(failBytes, failBytes)
   585  		if !checkDbError(tc.t, testName, err, wantErrCode) {
   586  			return false
   587  		}
   588  
   589  		// Delete should fail with bucket that is not writable.
   590  		testName = "unwritable tx delete"
   591  		err = bucket.Delete(failBytes)
   592  		if !checkDbError(tc.t, testName, err, wantErrCode) {
   593  			return false
   594  		}
   595  
   596  		// CreateBucket should fail with bucket that is not writable.
   597  		testName = "unwritable tx create bucket"
   598  		_, err = bucket.CreateBucket(failBytes)
   599  		if !checkDbError(tc.t, testName, err, wantErrCode) {
   600  			return false
   601  		}
   602  
   603  		// CreateBucketIfNotExists should fail with bucket that is not
   604  		// writable.
   605  		testName = "unwritable tx create bucket if not exists"
   606  		_, err = bucket.CreateBucketIfNotExists(failBytes)
   607  		if !checkDbError(tc.t, testName, err, wantErrCode) {
   608  			return false
   609  		}
   610  
   611  		// DeleteBucket should fail with bucket that is not writable.
   612  		testName = "unwritable tx delete bucket"
   613  		err = bucket.DeleteBucket(failBytes)
   614  		if !checkDbError(tc.t, testName, err, wantErrCode) {
   615  			return false
   616  		}
   617  
   618  		// Ensure the cursor interface works as expected with read-only
   619  		// buckets.
   620  		if !testCursorInterface(tc, bucket) {
   621  			return false
   622  		}
   623  	}
   624  
   625  	return true
   626  }
   627  
   628  // rollbackOnPanic rolls the passed transaction back if the code in the calling
   629  // function panics.  This is useful in case the tests unexpectedly panic which
   630  // would leave any manually created transactions with the database mutex locked
   631  // thereby leading to a deadlock and masking the real reason for the panic.  It
   632  // also logs a test error and repanics so the original panic can be traced.
   633  func rollbackOnPanic(t *testing.T, tx database.Tx) {
   634  	if err := recover(); err != nil {
   635  		t.Errorf("Unexpected panic: %v", err)
   636  		_ = tx.Rollback()
   637  		panic(err)
   638  	}
   639  }
   640  
   641  // testMetadataManualTxInterface ensures that the manual transactions metadata
   642  // interface works as expected.
   643  func testMetadataManualTxInterface(tc *testContext) bool {
   644  	// populateValues tests that populating values works as expected.
   645  	//
   646  	// When the writable flag is false, a read-only tranasction is created,
   647  	// standard bucket tests for read-only transactions are performed, and
   648  	// the Commit function is checked to ensure it fails as expected.
   649  	//
   650  	// Otherwise, a read-write transaction is created, the values are
   651  	// written, standard bucket tests for read-write transactions are
   652  	// performed, and then the transaction is either committed or rolled
   653  	// back depending on the flag.
   654  	bucket1Name := []byte("bucket1")
   655  	populateValues := func(writable, rollback bool, putValues []keyPair) bool {
   656  		tx, err := tc.db.Begin(writable)
   657  		if err != nil {
   658  			tc.t.Errorf("Begin: unexpected error %v", err)
   659  			return false
   660  		}
   661  		defer rollbackOnPanic(tc.t, tx)
   662  
   663  		metadataBucket := tx.Metadata()
   664  		if metadataBucket == nil {
   665  			tc.t.Errorf("Metadata: unexpected nil bucket")
   666  			_ = tx.Rollback()
   667  			return false
   668  		}
   669  
   670  		bucket1 := metadataBucket.Bucket(bucket1Name)
   671  		if bucket1 == nil {
   672  			tc.t.Errorf("Bucket1: unexpected nil bucket")
   673  			return false
   674  		}
   675  
   676  		tc.isWritable = writable
   677  		if !testBucketInterface(tc, bucket1) {
   678  			_ = tx.Rollback()
   679  			return false
   680  		}
   681  
   682  		if !writable {
   683  			// The transaction is not writable, so it should fail
   684  			// the commit.
   685  			testName := "unwritable tx commit"
   686  			wantErrCode := database.ErrTxNotWritable
   687  			err := tx.Commit()
   688  			if !checkDbError(tc.t, testName, err, wantErrCode) {
   689  				_ = tx.Rollback()
   690  				return false
   691  			}
   692  		} else {
   693  			if !testPutValues(tc, bucket1, putValues) {
   694  				return false
   695  			}
   696  
   697  			if rollback {
   698  				// Rollback the transaction.
   699  				if err := tx.Rollback(); err != nil {
   700  					tc.t.Errorf("Rollback: unexpected "+
   701  						"error %v", err)
   702  					return false
   703  				}
   704  			} else {
   705  				// The commit should succeed.
   706  				if err := tx.Commit(); err != nil {
   707  					tc.t.Errorf("Commit: unexpected error "+
   708  						"%v", err)
   709  					return false
   710  				}
   711  			}
   712  		}
   713  
   714  		return true
   715  	}
   716  
   717  	// checkValues starts a read-only transaction and checks that all of
   718  	// the key/value pairs specified in the expectedValues parameter match
   719  	// what's in the database.
   720  	checkValues := func(expectedValues []keyPair) bool {
   721  		tx, err := tc.db.Begin(false)
   722  		if err != nil {
   723  			tc.t.Errorf("Begin: unexpected error %v", err)
   724  			return false
   725  		}
   726  		defer rollbackOnPanic(tc.t, tx)
   727  
   728  		metadataBucket := tx.Metadata()
   729  		if metadataBucket == nil {
   730  			tc.t.Errorf("Metadata: unexpected nil bucket")
   731  			_ = tx.Rollback()
   732  			return false
   733  		}
   734  
   735  		bucket1 := metadataBucket.Bucket(bucket1Name)
   736  		if bucket1 == nil {
   737  			tc.t.Errorf("Bucket1: unexpected nil bucket")
   738  			return false
   739  		}
   740  
   741  		if !testGetValues(tc, bucket1, expectedValues) {
   742  			_ = tx.Rollback()
   743  			return false
   744  		}
   745  
   746  		// Rollback the read-only transaction.
   747  		if err := tx.Rollback(); err != nil {
   748  			tc.t.Errorf("Commit: unexpected error %v", err)
   749  			return false
   750  		}
   751  
   752  		return true
   753  	}
   754  
   755  	// deleteValues starts a read-write transaction and deletes the keys
   756  	// in the passed key/value pairs.
   757  	deleteValues := func(values []keyPair) bool {
   758  		tx, err := tc.db.Begin(true)
   759  		if err != nil {
   760  
   761  		}
   762  		defer rollbackOnPanic(tc.t, tx)
   763  
   764  		metadataBucket := tx.Metadata()
   765  		if metadataBucket == nil {
   766  			tc.t.Errorf("Metadata: unexpected nil bucket")
   767  			_ = tx.Rollback()
   768  			return false
   769  		}
   770  
   771  		bucket1 := metadataBucket.Bucket(bucket1Name)
   772  		if bucket1 == nil {
   773  			tc.t.Errorf("Bucket1: unexpected nil bucket")
   774  			return false
   775  		}
   776  
   777  		// Delete the keys and ensure they were deleted.
   778  		if !testDeleteValues(tc, bucket1, values) {
   779  			_ = tx.Rollback()
   780  			return false
   781  		}
   782  		if !testGetValues(tc, bucket1, rollbackValues(values)) {
   783  			_ = tx.Rollback()
   784  			return false
   785  		}
   786  
   787  		// Commit the changes and ensure it was successful.
   788  		if err := tx.Commit(); err != nil {
   789  			tc.t.Errorf("Commit: unexpected error %v", err)
   790  			return false
   791  		}
   792  
   793  		return true
   794  	}
   795  
   796  	// keyValues holds the keys and values to use when putting values into a
   797  	// bucket.
   798  	var keyValues = []keyPair{
   799  		{[]byte("umtxkey1"), []byte("foo1")},
   800  		{[]byte("umtxkey2"), []byte("foo2")},
   801  		{[]byte("umtxkey3"), []byte("foo3")},
   802  		{[]byte("umtxkey4"), nil},
   803  	}
   804  
   805  	// Ensure that attempting populating the values using a read-only
   806  	// transaction fails as expected.
   807  	if !populateValues(false, true, keyValues) {
   808  		return false
   809  	}
   810  	if !checkValues(rollbackValues(keyValues)) {
   811  		return false
   812  	}
   813  
   814  	// Ensure that attempting populating the values using a read-write
   815  	// transaction and then rolling it back yields the expected values.
   816  	if !populateValues(true, true, keyValues) {
   817  		return false
   818  	}
   819  	if !checkValues(rollbackValues(keyValues)) {
   820  		return false
   821  	}
   822  
   823  	// Ensure that attempting populating the values using a read-write
   824  	// transaction and then committing it stores the expected values.
   825  	if !populateValues(true, false, keyValues) {
   826  		return false
   827  	}
   828  	if !checkValues(toGetValues(keyValues)) {
   829  		return false
   830  	}
   831  
   832  	// Clean up the keys.
   833  	if !deleteValues(keyValues) {
   834  		return false
   835  	}
   836  
   837  	return true
   838  }
   839  
   840  // testManagedTxPanics ensures calling Rollback of Commit inside a managed
   841  // transaction panics.
   842  func testManagedTxPanics(tc *testContext) bool {
   843  	testPanic := func(fn func()) (paniced bool) {
   844  		// Setup a defer to catch the expected panic and update the
   845  		// return variable.
   846  		defer func() {
   847  			if err := recover(); err != nil {
   848  				paniced = true
   849  			}
   850  		}()
   851  
   852  		fn()
   853  		return false
   854  	}
   855  
   856  	// Ensure calling Commit on a managed read-only transaction panics.
   857  	paniced := testPanic(func() {
   858  		tc.db.View(func(tx database.Tx) error {
   859  			tx.Commit()
   860  			return nil
   861  		})
   862  	})
   863  	if !paniced {
   864  		tc.t.Error("Commit called inside View did not panic")
   865  		return false
   866  	}
   867  
   868  	// Ensure calling Rollback on a managed read-only transaction panics.
   869  	paniced = testPanic(func() {
   870  		tc.db.View(func(tx database.Tx) error {
   871  			tx.Rollback()
   872  			return nil
   873  		})
   874  	})
   875  	if !paniced {
   876  		tc.t.Error("Rollback called inside View did not panic")
   877  		return false
   878  	}
   879  
   880  	// Ensure calling Commit on a managed read-write transaction panics.
   881  	paniced = testPanic(func() {
   882  		tc.db.Update(func(tx database.Tx) error {
   883  			tx.Commit()
   884  			return nil
   885  		})
   886  	})
   887  	if !paniced {
   888  		tc.t.Error("Commit called inside Update did not panic")
   889  		return false
   890  	}
   891  
   892  	// Ensure calling Rollback on a managed read-write transaction panics.
   893  	paniced = testPanic(func() {
   894  		tc.db.Update(func(tx database.Tx) error {
   895  			tx.Rollback()
   896  			return nil
   897  		})
   898  	})
   899  	if !paniced {
   900  		tc.t.Error("Rollback called inside Update did not panic")
   901  		return false
   902  	}
   903  
   904  	return true
   905  }
   906  
   907  // testMetadataTxInterface tests all facets of the managed read/write and
   908  // manual transaction metadata interfaces as well as the bucket interfaces under
   909  // them.
   910  func testMetadataTxInterface(tc *testContext) bool {
   911  	if !testManagedTxPanics(tc) {
   912  		return false
   913  	}
   914  
   915  	bucket1Name := []byte("bucket1")
   916  	err := tc.db.Update(func(tx database.Tx) error {
   917  		_, err := tx.Metadata().CreateBucket(bucket1Name)
   918  		return err
   919  	})
   920  	if err != nil {
   921  		tc.t.Errorf("Update: unexpected error creating bucket: %v", err)
   922  		return false
   923  	}
   924  
   925  	if !testMetadataManualTxInterface(tc) {
   926  		return false
   927  	}
   928  
   929  	// keyValues holds the keys and values to use when putting values
   930  	// into a bucket.
   931  	keyValues := []keyPair{
   932  		{[]byte("mtxkey1"), []byte("foo1")},
   933  		{[]byte("mtxkey2"), []byte("foo2")},
   934  		{[]byte("mtxkey3"), []byte("foo3")},
   935  		{[]byte("mtxkey4"), nil},
   936  	}
   937  
   938  	// Test the bucket interface via a managed read-only transaction.
   939  	err = tc.db.View(func(tx database.Tx) error {
   940  		metadataBucket := tx.Metadata()
   941  		if metadataBucket == nil {
   942  			return fmt.Errorf("Metadata: unexpected nil bucket")
   943  		}
   944  
   945  		bucket1 := metadataBucket.Bucket(bucket1Name)
   946  		if bucket1 == nil {
   947  			return fmt.Errorf("Bucket1: unexpected nil bucket")
   948  		}
   949  
   950  		tc.isWritable = false
   951  		if !testBucketInterface(tc, bucket1) {
   952  			return errSubTestFail
   953  		}
   954  
   955  		return nil
   956  	})
   957  	if err != nil {
   958  		if err != errSubTestFail {
   959  			tc.t.Errorf("%v", err)
   960  		}
   961  		return false
   962  	}
   963  
   964  	// Ensure errors returned from the user-supplied View function are
   965  	// returned.
   966  	viewError := fmt.Errorf("example view error")
   967  	err = tc.db.View(func(tx database.Tx) error {
   968  		return viewError
   969  	})
   970  	if err != viewError {
   971  		tc.t.Errorf("View: inner function error not returned - got "+
   972  			"%v, want %v", err, viewError)
   973  		return false
   974  	}
   975  
   976  	// Test the bucket interface via a managed read-write transaction.
   977  	// Also, put a series of values and force a rollback so the following
   978  	// code can ensure the values were not stored.
   979  	forceRollbackError := fmt.Errorf("force rollback")
   980  	err = tc.db.Update(func(tx database.Tx) error {
   981  		metadataBucket := tx.Metadata()
   982  		if metadataBucket == nil {
   983  			return fmt.Errorf("Metadata: unexpected nil bucket")
   984  		}
   985  
   986  		bucket1 := metadataBucket.Bucket(bucket1Name)
   987  		if bucket1 == nil {
   988  			return fmt.Errorf("Bucket1: unexpected nil bucket")
   989  		}
   990  
   991  		tc.isWritable = true
   992  		if !testBucketInterface(tc, bucket1) {
   993  			return errSubTestFail
   994  		}
   995  
   996  		if !testPutValues(tc, bucket1, keyValues) {
   997  			return errSubTestFail
   998  		}
   999  
  1000  		// Return an error to force a rollback.
  1001  		return forceRollbackError
  1002  	})
  1003  	if err != forceRollbackError {
  1004  		if err == errSubTestFail {
  1005  			return false
  1006  		}
  1007  
  1008  		tc.t.Errorf("Update: inner function error not returned - got "+
  1009  			"%v, want %v", err, forceRollbackError)
  1010  		return false
  1011  	}
  1012  
  1013  	// Ensure the values that should not have been stored due to the forced
  1014  	// rollback above were not actually stored.
  1015  	err = tc.db.View(func(tx database.Tx) error {
  1016  		metadataBucket := tx.Metadata()
  1017  		if metadataBucket == nil {
  1018  			return fmt.Errorf("Metadata: unexpected nil bucket")
  1019  		}
  1020  
  1021  		if !testGetValues(tc, metadataBucket, rollbackValues(keyValues)) {
  1022  			return errSubTestFail
  1023  		}
  1024  
  1025  		return nil
  1026  	})
  1027  	if err != nil {
  1028  		if err != errSubTestFail {
  1029  			tc.t.Errorf("%v", err)
  1030  		}
  1031  		return false
  1032  	}
  1033  
  1034  	// Store a series of values via a managed read-write transaction.
  1035  	err = tc.db.Update(func(tx database.Tx) error {
  1036  		metadataBucket := tx.Metadata()
  1037  		if metadataBucket == nil {
  1038  			return fmt.Errorf("Metadata: unexpected nil bucket")
  1039  		}
  1040  
  1041  		bucket1 := metadataBucket.Bucket(bucket1Name)
  1042  		if bucket1 == nil {
  1043  			return fmt.Errorf("Bucket1: unexpected nil bucket")
  1044  		}
  1045  
  1046  		if !testPutValues(tc, bucket1, keyValues) {
  1047  			return errSubTestFail
  1048  		}
  1049  
  1050  		return nil
  1051  	})
  1052  	if err != nil {
  1053  		if err != errSubTestFail {
  1054  			tc.t.Errorf("%v", err)
  1055  		}
  1056  		return false
  1057  	}
  1058  
  1059  	// Ensure the values stored above were committed as expected.
  1060  	err = tc.db.View(func(tx database.Tx) error {
  1061  		metadataBucket := tx.Metadata()
  1062  		if metadataBucket == nil {
  1063  			return fmt.Errorf("Metadata: unexpected nil bucket")
  1064  		}
  1065  
  1066  		bucket1 := metadataBucket.Bucket(bucket1Name)
  1067  		if bucket1 == nil {
  1068  			return fmt.Errorf("Bucket1: unexpected nil bucket")
  1069  		}
  1070  
  1071  		if !testGetValues(tc, bucket1, toGetValues(keyValues)) {
  1072  			return errSubTestFail
  1073  		}
  1074  
  1075  		return nil
  1076  	})
  1077  	if err != nil {
  1078  		if err != errSubTestFail {
  1079  			tc.t.Errorf("%v", err)
  1080  		}
  1081  		return false
  1082  	}
  1083  
  1084  	// Clean up the values stored above in a managed read-write transaction.
  1085  	err = tc.db.Update(func(tx database.Tx) error {
  1086  		metadataBucket := tx.Metadata()
  1087  		if metadataBucket == nil {
  1088  			return fmt.Errorf("Metadata: unexpected nil bucket")
  1089  		}
  1090  
  1091  		bucket1 := metadataBucket.Bucket(bucket1Name)
  1092  		if bucket1 == nil {
  1093  			return fmt.Errorf("Bucket1: unexpected nil bucket")
  1094  		}
  1095  
  1096  		if !testDeleteValues(tc, bucket1, keyValues) {
  1097  			return errSubTestFail
  1098  		}
  1099  
  1100  		return nil
  1101  	})
  1102  	if err != nil {
  1103  		if err != errSubTestFail {
  1104  			tc.t.Errorf("%v", err)
  1105  		}
  1106  		return false
  1107  	}
  1108  
  1109  	return true
  1110  }
  1111  
  1112  // testFetchBlockIOMissing ensures that all of the block retrieval API functions
  1113  // work as expected when requesting blocks that don't exist.
  1114  func testFetchBlockIOMissing(tc *testContext, tx database.Tx) bool {
  1115  	wantErrCode := database.ErrBlockNotFound
  1116  
  1117  	// ---------------------
  1118  	// Non-bulk Block IO API
  1119  	// ---------------------
  1120  
  1121  	// Test the individual block APIs one block at a time to ensure they
  1122  	// return the expected error.  Also, build the data needed to test the
  1123  	// bulk APIs below while looping.
  1124  	allBlockHashes := make([]wire.ShaHash, len(tc.blocks))
  1125  	allBlockRegions := make([]database.BlockRegion, len(tc.blocks))
  1126  	for i, block := range tc.blocks {
  1127  		blockHash := block.Sha()
  1128  		allBlockHashes[i] = *blockHash
  1129  
  1130  		txLocs, err := block.TxLoc()
  1131  		if err != nil {
  1132  			tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i,
  1133  				err)
  1134  			return false
  1135  		}
  1136  
  1137  		// Ensure FetchBlock returns expected error.
  1138  		testName := fmt.Sprintf("FetchBlock #%d on missing block", i)
  1139  		_, err = tx.FetchBlock(blockHash)
  1140  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1141  			return false
  1142  		}
  1143  
  1144  		// Ensure FetchBlockHeader returns expected error.
  1145  		testName = fmt.Sprintf("FetchBlockHeader #%d on missing block",
  1146  			i)
  1147  		_, err = tx.FetchBlockHeader(blockHash)
  1148  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1149  			return false
  1150  		}
  1151  
  1152  		// Ensure the first transaction fetched as a block region from
  1153  		// the database returns the expected error.
  1154  		region := database.BlockRegion{
  1155  			Hash:   blockHash,
  1156  			Offset: uint32(txLocs[0].TxStart),
  1157  			Len:    uint32(txLocs[0].TxLen),
  1158  		}
  1159  		allBlockRegions[i] = region
  1160  		_, err = tx.FetchBlockRegion(&region)
  1161  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1162  			return false
  1163  		}
  1164  
  1165  		// Ensure HasBlock returns false.
  1166  		hasBlock, err := tx.HasBlock(blockHash)
  1167  		if err != nil {
  1168  			tc.t.Errorf("HasBlock #%d: unexpected err: %v", i, err)
  1169  			return false
  1170  		}
  1171  		if hasBlock {
  1172  			tc.t.Errorf("HasBlock #%d: should not have block", i)
  1173  			return false
  1174  		}
  1175  	}
  1176  
  1177  	// -----------------
  1178  	// Bulk Block IO API
  1179  	// -----------------
  1180  
  1181  	// Ensure FetchBlocks returns expected error.
  1182  	testName := "FetchBlocks on missing blocks"
  1183  	_, err := tx.FetchBlocks(allBlockHashes)
  1184  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1185  		return false
  1186  	}
  1187  
  1188  	// Ensure FetchBlockHeaders returns expected error.
  1189  	testName = "FetchBlockHeaders on missing blocks"
  1190  	_, err = tx.FetchBlockHeaders(allBlockHashes)
  1191  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1192  		return false
  1193  	}
  1194  
  1195  	// Ensure FetchBlockRegions returns expected error.
  1196  	testName = "FetchBlockRegions on missing blocks"
  1197  	_, err = tx.FetchBlockRegions(allBlockRegions)
  1198  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1199  		return false
  1200  	}
  1201  
  1202  	// Ensure HasBlocks returns false for all blocks.
  1203  	hasBlocks, err := tx.HasBlocks(allBlockHashes)
  1204  	if err != nil {
  1205  		tc.t.Errorf("HasBlocks: unexpected err: %v", err)
  1206  	}
  1207  	for i, hasBlock := range hasBlocks {
  1208  		if hasBlock {
  1209  			tc.t.Errorf("HasBlocks #%d: should not have block", i)
  1210  			return false
  1211  		}
  1212  	}
  1213  
  1214  	return true
  1215  }
  1216  
  1217  // testFetchBlockIO ensures all of the block retrieval API functions work as
  1218  // expected for the provide set of blocks.  The blocks must already be stored in
  1219  // the database, or at least stored into the the passed transaction.  It also
  1220  // tests several error conditions such as ensuring the expected errors are
  1221  // returned when fetching blocks, headers, and regions that don't exist.
  1222  func testFetchBlockIO(tc *testContext, tx database.Tx) bool {
  1223  	// ---------------------
  1224  	// Non-bulk Block IO API
  1225  	// ---------------------
  1226  
  1227  	// Test the individual block APIs one block at a time.  Also, build the
  1228  	// data needed to test the bulk APIs below while looping.
  1229  	allBlockHashes := make([]wire.ShaHash, len(tc.blocks))
  1230  	allBlockBytes := make([][]byte, len(tc.blocks))
  1231  	allBlockTxLocs := make([][]wire.TxLoc, len(tc.blocks))
  1232  	allBlockRegions := make([]database.BlockRegion, len(tc.blocks))
  1233  	for i, block := range tc.blocks {
  1234  		blockHash := block.Sha()
  1235  		allBlockHashes[i] = *blockHash
  1236  
  1237  		blockBytes, err := block.Bytes()
  1238  		if err != nil {
  1239  			tc.t.Errorf("block.Bytes(%d): unexpected error: %v", i,
  1240  				err)
  1241  			return false
  1242  		}
  1243  		allBlockBytes[i] = blockBytes
  1244  
  1245  		txLocs, err := block.TxLoc()
  1246  		if err != nil {
  1247  			tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i,
  1248  				err)
  1249  			return false
  1250  		}
  1251  		allBlockTxLocs[i] = txLocs
  1252  
  1253  		// Ensure the block data fetched from the database matches the
  1254  		// expected bytes.
  1255  		gotBlockBytes, err := tx.FetchBlock(blockHash)
  1256  		if err != nil {
  1257  			tc.t.Errorf("FetchBlock(%s): unexpected error: %v",
  1258  				blockHash, err)
  1259  			return false
  1260  		}
  1261  		if !bytes.Equal(gotBlockBytes, blockBytes) {
  1262  			tc.t.Errorf("FetchBlock(%s): bytes mismatch: got %x, "+
  1263  				"want %x", blockHash, gotBlockBytes, blockBytes)
  1264  			return false
  1265  		}
  1266  
  1267  		// Ensure the block header fetched from the database matches the
  1268  		// expected bytes.
  1269  		wantHeaderBytes := blockBytes[0:wire.MaxBlockHeaderPayload]
  1270  		gotHeaderBytes, err := tx.FetchBlockHeader(blockHash)
  1271  		if err != nil {
  1272  			tc.t.Errorf("FetchBlockHeader(%s): unexpected error: %v",
  1273  				blockHash, err)
  1274  			return false
  1275  		}
  1276  		if !bytes.Equal(gotHeaderBytes, wantHeaderBytes) {
  1277  			tc.t.Errorf("FetchBlockHeader(%s): bytes mismatch: "+
  1278  				"got %x, want %x", blockHash, gotHeaderBytes,
  1279  				wantHeaderBytes)
  1280  			return false
  1281  		}
  1282  
  1283  		// Ensure the first transaction fetched as a block region from
  1284  		// the database matches the expected bytes.
  1285  		region := database.BlockRegion{
  1286  			Hash:   blockHash,
  1287  			Offset: uint32(txLocs[0].TxStart),
  1288  			Len:    uint32(txLocs[0].TxLen),
  1289  		}
  1290  		allBlockRegions[i] = region
  1291  		endRegionOffset := region.Offset + region.Len
  1292  		wantRegionBytes := blockBytes[region.Offset:endRegionOffset]
  1293  		gotRegionBytes, err := tx.FetchBlockRegion(&region)
  1294  		if err != nil {
  1295  			tc.t.Errorf("FetchBlockRegion(%s): unexpected error: %v",
  1296  				blockHash, err)
  1297  			return false
  1298  		}
  1299  		if !bytes.Equal(gotRegionBytes, wantRegionBytes) {
  1300  			tc.t.Errorf("FetchBlockRegion(%s): bytes mismatch: "+
  1301  				"got %x, want %x", blockHash, gotRegionBytes,
  1302  				wantRegionBytes)
  1303  			return false
  1304  		}
  1305  
  1306  		// Ensure the block header fetched from the database matches the
  1307  		// expected bytes.
  1308  		hasBlock, err := tx.HasBlock(blockHash)
  1309  		if err != nil {
  1310  			tc.t.Errorf("HasBlock(%s): unexpected error: %v",
  1311  				blockHash, err)
  1312  			return false
  1313  		}
  1314  		if !hasBlock {
  1315  			tc.t.Errorf("HasBlock(%s): database claims it doesn't "+
  1316  				"have the block when it should", blockHash)
  1317  			return false
  1318  		}
  1319  
  1320  		// -----------------------
  1321  		// Invalid blocks/regions.
  1322  		// -----------------------
  1323  
  1324  		// Ensure fetching a block that doesn't exist returns the
  1325  		// expected error.
  1326  		badBlockHash := &wire.ShaHash{}
  1327  		testName := fmt.Sprintf("FetchBlock(%s) invalid block",
  1328  			badBlockHash)
  1329  		wantErrCode := database.ErrBlockNotFound
  1330  		_, err = tx.FetchBlock(badBlockHash)
  1331  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1332  			return false
  1333  		}
  1334  
  1335  		// Ensure fetching a block header that doesn't exist returns
  1336  		// the expected error.
  1337  		testName = fmt.Sprintf("FetchBlockHeader(%s) invalid block",
  1338  			badBlockHash)
  1339  		_, err = tx.FetchBlockHeader(badBlockHash)
  1340  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1341  			return false
  1342  		}
  1343  
  1344  		// Ensure fetching a block region in a block that doesn't exist
  1345  		// return the expected error.
  1346  		testName = fmt.Sprintf("FetchBlockRegion(%s) invalid hash",
  1347  			badBlockHash)
  1348  		wantErrCode = database.ErrBlockNotFound
  1349  		region.Hash = badBlockHash
  1350  		region.Offset = ^uint32(0)
  1351  		_, err = tx.FetchBlockRegion(&region)
  1352  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1353  			return false
  1354  		}
  1355  
  1356  		// Ensure fetching a block region that is out of bounds returns
  1357  		// the expected error.
  1358  		testName = fmt.Sprintf("FetchBlockRegion(%s) invalid region",
  1359  			blockHash)
  1360  		wantErrCode = database.ErrBlockRegionInvalid
  1361  		region.Hash = blockHash
  1362  		region.Offset = ^uint32(0)
  1363  		_, err = tx.FetchBlockRegion(&region)
  1364  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1365  			return false
  1366  		}
  1367  	}
  1368  
  1369  	// -----------------
  1370  	// Bulk Block IO API
  1371  	// -----------------
  1372  
  1373  	// Ensure the bulk block data fetched from the database matches the
  1374  	// expected bytes.
  1375  	blockData, err := tx.FetchBlocks(allBlockHashes)
  1376  	if err != nil {
  1377  		tc.t.Errorf("FetchBlocks: unexpected error: %v", err)
  1378  		return false
  1379  	}
  1380  	if len(blockData) != len(allBlockBytes) {
  1381  		tc.t.Errorf("FetchBlocks: unexpected number of results - got "+
  1382  			"%d, want %d", len(blockData), len(allBlockBytes))
  1383  		return false
  1384  	}
  1385  	for i := 0; i < len(blockData); i++ {
  1386  		blockHash := allBlockHashes[i]
  1387  		wantBlockBytes := allBlockBytes[i]
  1388  		gotBlockBytes := blockData[i]
  1389  		if !bytes.Equal(gotBlockBytes, wantBlockBytes) {
  1390  			tc.t.Errorf("FetchBlocks(%s): bytes mismatch: got %x, "+
  1391  				"want %x", blockHash, gotBlockBytes,
  1392  				wantBlockBytes)
  1393  			return false
  1394  		}
  1395  	}
  1396  
  1397  	// Ensure the bulk block headers fetched from the database match the
  1398  	// expected bytes.
  1399  	blockHeaderData, err := tx.FetchBlockHeaders(allBlockHashes)
  1400  	if err != nil {
  1401  		tc.t.Errorf("FetchBlockHeaders: unexpected error: %v", err)
  1402  		return false
  1403  	}
  1404  	if len(blockHeaderData) != len(allBlockBytes) {
  1405  		tc.t.Errorf("FetchBlockHeaders: unexpected number of results "+
  1406  			"- got %d, want %d", len(blockHeaderData),
  1407  			len(allBlockBytes))
  1408  		return false
  1409  	}
  1410  	for i := 0; i < len(blockHeaderData); i++ {
  1411  		blockHash := allBlockHashes[i]
  1412  		wantHeaderBytes := allBlockBytes[i][0:wire.MaxBlockHeaderPayload]
  1413  		gotHeaderBytes := blockHeaderData[i]
  1414  		if !bytes.Equal(gotHeaderBytes, wantHeaderBytes) {
  1415  			tc.t.Errorf("FetchBlockHeaders(%s): bytes mismatch: "+
  1416  				"got %x, want %x", blockHash, gotHeaderBytes,
  1417  				wantHeaderBytes)
  1418  			return false
  1419  		}
  1420  	}
  1421  
  1422  	// Ensure the first transaction of every block fetched in bulk block
  1423  	// regions from the database matches the expected bytes.
  1424  	allRegionBytes, err := tx.FetchBlockRegions(allBlockRegions)
  1425  	if err != nil {
  1426  		tc.t.Errorf("FetchBlockRegions: unexpected error: %v", err)
  1427  		return false
  1428  
  1429  	}
  1430  	if len(allRegionBytes) != len(allBlockRegions) {
  1431  		tc.t.Errorf("FetchBlockRegions: unexpected number of results "+
  1432  			"- got %d, want %d", len(allRegionBytes),
  1433  			len(allBlockRegions))
  1434  		return false
  1435  	}
  1436  	for i, gotRegionBytes := range allRegionBytes {
  1437  		region := &allBlockRegions[i]
  1438  		endRegionOffset := region.Offset + region.Len
  1439  		wantRegionBytes := blockData[i][region.Offset:endRegionOffset]
  1440  		if !bytes.Equal(gotRegionBytes, wantRegionBytes) {
  1441  			tc.t.Errorf("FetchBlockRegions(%d): bytes mismatch: "+
  1442  				"got %x, want %x", i, gotRegionBytes,
  1443  				wantRegionBytes)
  1444  			return false
  1445  		}
  1446  	}
  1447  
  1448  	// Ensure the bulk determination of whether a set of block hashes are in
  1449  	// the database returns true for all loaded blocks.
  1450  	hasBlocks, err := tx.HasBlocks(allBlockHashes)
  1451  	if err != nil {
  1452  		tc.t.Errorf("HasBlocks: unexpected error: %v", err)
  1453  		return false
  1454  	}
  1455  	for i, hasBlock := range hasBlocks {
  1456  		if !hasBlock {
  1457  			tc.t.Errorf("HasBlocks(%d): should have block", i)
  1458  			return false
  1459  		}
  1460  	}
  1461  
  1462  	// -----------------------
  1463  	// Invalid blocks/regions.
  1464  	// -----------------------
  1465  
  1466  	// Ensure fetching blocks for which one doesn't exist returns the
  1467  	// expected error.
  1468  	testName := "FetchBlocks invalid hash"
  1469  	badBlockHashes := make([]wire.ShaHash, len(allBlockHashes)+1)
  1470  	copy(badBlockHashes, allBlockHashes)
  1471  	badBlockHashes[len(badBlockHashes)-1] = wire.ShaHash{}
  1472  	wantErrCode := database.ErrBlockNotFound
  1473  	_, err = tx.FetchBlocks(badBlockHashes)
  1474  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1475  		return false
  1476  	}
  1477  
  1478  	// Ensure fetching block headers for which one doesn't exist returns the
  1479  	// expected error.
  1480  	testName = "FetchBlockHeaders invalid hash"
  1481  	_, err = tx.FetchBlockHeaders(badBlockHashes)
  1482  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1483  		return false
  1484  	}
  1485  
  1486  	// Ensure fetching block regions for which one of blocks doesn't exist
  1487  	// returns expected error.
  1488  	testName = "FetchBlockRegions invalid hash"
  1489  	badBlockRegions := make([]database.BlockRegion, len(allBlockRegions)+1)
  1490  	copy(badBlockRegions, allBlockRegions)
  1491  	badBlockRegions[len(badBlockRegions)-1].Hash = &wire.ShaHash{}
  1492  	wantErrCode = database.ErrBlockNotFound
  1493  	_, err = tx.FetchBlockRegions(badBlockRegions)
  1494  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1495  		return false
  1496  	}
  1497  
  1498  	// Ensure fetching block regions that are out of bounds returns the
  1499  	// expected error.
  1500  	testName = "FetchBlockRegions invalid regions"
  1501  	badBlockRegions = badBlockRegions[:len(badBlockRegions)-1]
  1502  	for i := range badBlockRegions {
  1503  		badBlockRegions[i].Offset = ^uint32(0)
  1504  	}
  1505  	wantErrCode = database.ErrBlockRegionInvalid
  1506  	_, err = tx.FetchBlockRegions(badBlockRegions)
  1507  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1508  		return false
  1509  	}
  1510  
  1511  	return true
  1512  }
  1513  
  1514  // testBlockIOTxInterface ensures that the block IO interface works as expected
  1515  // for both managed read/write and manual transactions.  This function leaves
  1516  // all of the stored blocks in the database.
  1517  func testBlockIOTxInterface(tc *testContext) bool {
  1518  	// Ensure attempting to store a block with a read-only transaction fails
  1519  	// with the expected error.
  1520  	err := tc.db.View(func(tx database.Tx) error {
  1521  		wantErrCode := database.ErrTxNotWritable
  1522  		for i, block := range tc.blocks {
  1523  			testName := fmt.Sprintf("StoreBlock(%d) on ro tx", i)
  1524  			err := tx.StoreBlock(block)
  1525  			if !checkDbError(tc.t, testName, err, wantErrCode) {
  1526  				return errSubTestFail
  1527  			}
  1528  		}
  1529  
  1530  		return nil
  1531  	})
  1532  	if err != nil {
  1533  		if err != errSubTestFail {
  1534  			tc.t.Errorf("%v", err)
  1535  		}
  1536  		return false
  1537  	}
  1538  
  1539  	// Populate the database with loaded blocks and ensure all of the data
  1540  	// fetching APIs work properly on them within the transaction before a
  1541  	// commit or rollback.  Then, force a rollback so the code below can
  1542  	// ensure none of the data actually gets stored.
  1543  	forceRollbackError := fmt.Errorf("force rollback")
  1544  	err = tc.db.Update(func(tx database.Tx) error {
  1545  		// Store all blocks in the same transaction.
  1546  		for i, block := range tc.blocks {
  1547  			err := tx.StoreBlock(block)
  1548  			if err != nil {
  1549  				tc.t.Errorf("StoreBlock #%d: unexpected error: "+
  1550  					"%v", i, err)
  1551  				return errSubTestFail
  1552  			}
  1553  		}
  1554  
  1555  		// Ensure attempting to store the same block again, before the
  1556  		// transaction has been committed, returns the expected error.
  1557  		wantErrCode := database.ErrBlockExists
  1558  		for i, block := range tc.blocks {
  1559  			testName := fmt.Sprintf("duplicate block entry #%d "+
  1560  				"(before commit)", i)
  1561  			err := tx.StoreBlock(block)
  1562  			if !checkDbError(tc.t, testName, err, wantErrCode) {
  1563  				return errSubTestFail
  1564  			}
  1565  		}
  1566  
  1567  		// Ensure that all data fetches from the stored blocks before
  1568  		// the transaction has been committed work as expected.
  1569  		if !testFetchBlockIO(tc, tx) {
  1570  			return errSubTestFail
  1571  		}
  1572  
  1573  		return forceRollbackError
  1574  	})
  1575  	if err != forceRollbackError {
  1576  		if err == errSubTestFail {
  1577  			return false
  1578  		}
  1579  
  1580  		tc.t.Errorf("Update: inner function error not returned - got "+
  1581  			"%v, want %v", err, forceRollbackError)
  1582  		return false
  1583  	}
  1584  
  1585  	// Ensure rollback was successful
  1586  	err = tc.db.View(func(tx database.Tx) error {
  1587  		if !testFetchBlockIOMissing(tc, tx) {
  1588  			return errSubTestFail
  1589  		}
  1590  		return nil
  1591  	})
  1592  	if err != nil {
  1593  		if err != errSubTestFail {
  1594  			tc.t.Errorf("%v", err)
  1595  		}
  1596  		return false
  1597  	}
  1598  
  1599  	// Populate the database with loaded blocks and ensure all of the data
  1600  	// fetching APIs work properly.
  1601  	err = tc.db.Update(func(tx database.Tx) error {
  1602  		// Store a bunch of blocks in the same transaction.
  1603  		for i, block := range tc.blocks {
  1604  			err := tx.StoreBlock(block)
  1605  			if err != nil {
  1606  				tc.t.Errorf("StoreBlock #%d: unexpected error: "+
  1607  					"%v", i, err)
  1608  				return errSubTestFail
  1609  			}
  1610  		}
  1611  
  1612  		// Ensure attempting to store the same block again while in the
  1613  		// same transaction, but before it has been committed, returns
  1614  		// the expected error.
  1615  		for i, block := range tc.blocks {
  1616  			testName := fmt.Sprintf("duplicate block entry #%d "+
  1617  				"(before commit)", i)
  1618  			wantErrCode := database.ErrBlockExists
  1619  			err := tx.StoreBlock(block)
  1620  			if !checkDbError(tc.t, testName, err, wantErrCode) {
  1621  				return errSubTestFail
  1622  			}
  1623  		}
  1624  
  1625  		// Ensure that all data fetches from the stored blocks before
  1626  		// the transaction has been committed work as expected.
  1627  		if !testFetchBlockIO(tc, tx) {
  1628  			return errSubTestFail
  1629  		}
  1630  
  1631  		return nil
  1632  	})
  1633  	if err != nil {
  1634  		if err != errSubTestFail {
  1635  			tc.t.Errorf("%v", err)
  1636  		}
  1637  		return false
  1638  	}
  1639  
  1640  	// Ensure all data fetch tests work as expected using a managed
  1641  	// read-only transaction after the data was successfully committed
  1642  	// above.
  1643  	err = tc.db.View(func(tx database.Tx) error {
  1644  		if !testFetchBlockIO(tc, tx) {
  1645  			return errSubTestFail
  1646  		}
  1647  
  1648  		return nil
  1649  	})
  1650  	if err != nil {
  1651  		if err != errSubTestFail {
  1652  			tc.t.Errorf("%v", err)
  1653  		}
  1654  		return false
  1655  	}
  1656  
  1657  	// Ensure all data fetch tests work as expected using a managed
  1658  	// read-write transaction after the data was successfully committed
  1659  	// above.
  1660  	err = tc.db.Update(func(tx database.Tx) error {
  1661  		if !testFetchBlockIO(tc, tx) {
  1662  			return errSubTestFail
  1663  		}
  1664  
  1665  		// Ensure attempting to store existing blocks again returns the
  1666  		// expected error.  Note that this is different from the
  1667  		// previous version since this is a new transaction after the
  1668  		// blocks have been committed.
  1669  		wantErrCode := database.ErrBlockExists
  1670  		for i, block := range tc.blocks {
  1671  			testName := fmt.Sprintf("duplicate block entry #%d "+
  1672  				"(before commit)", i)
  1673  			err := tx.StoreBlock(block)
  1674  			if !checkDbError(tc.t, testName, err, wantErrCode) {
  1675  				return errSubTestFail
  1676  			}
  1677  		}
  1678  
  1679  		return nil
  1680  	})
  1681  	if err != nil {
  1682  		if err != errSubTestFail {
  1683  			tc.t.Errorf("%v", err)
  1684  		}
  1685  		return false
  1686  	}
  1687  
  1688  	return true
  1689  }
  1690  
  1691  // testClosedTxInterface ensures that both the metadata and block IO API
  1692  // functions behave as expected when attempted against a closed transaction.
  1693  func testClosedTxInterface(tc *testContext, tx database.Tx) bool {
  1694  	wantErrCode := database.ErrTxClosed
  1695  	bucket := tx.Metadata()
  1696  	cursor := tx.Metadata().Cursor()
  1697  	bucketName := []byte("closedtxbucket")
  1698  	keyName := []byte("closedtxkey")
  1699  
  1700  	// ------------
  1701  	// Metadata API
  1702  	// ------------
  1703  
  1704  	// Ensure that attempting to get an existing bucket returns nil when the
  1705  	// transaction is closed.
  1706  	if b := bucket.Bucket(bucketName); b != nil {
  1707  		tc.t.Errorf("Bucket: did not return nil on closed tx")
  1708  		return false
  1709  	}
  1710  
  1711  	// Ensure CreateBucket returns expected error.
  1712  	testName := "CreateBucket on closed tx"
  1713  	_, err := bucket.CreateBucket(bucketName)
  1714  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1715  		return false
  1716  	}
  1717  
  1718  	// Ensure CreateBucketIfNotExists returns expected error.
  1719  	testName = "CreateBucketIfNotExists on closed tx"
  1720  	_, err = bucket.CreateBucketIfNotExists(bucketName)
  1721  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1722  		return false
  1723  	}
  1724  
  1725  	// Ensure Delete returns expected error.
  1726  	testName = "Delete on closed tx"
  1727  	err = bucket.Delete(keyName)
  1728  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1729  		return false
  1730  	}
  1731  
  1732  	// Ensure DeleteBucket returns expected error.
  1733  	testName = "DeleteBucket on closed tx"
  1734  	err = bucket.DeleteBucket(bucketName)
  1735  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1736  		return false
  1737  	}
  1738  
  1739  	// Ensure ForEach returns expected error.
  1740  	testName = "ForEach on closed tx"
  1741  	err = bucket.ForEach(nil)
  1742  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1743  		return false
  1744  	}
  1745  
  1746  	// Ensure ForEachBucket returns expected error.
  1747  	testName = "ForEachBucket on closed tx"
  1748  	err = bucket.ForEachBucket(nil)
  1749  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1750  		return false
  1751  	}
  1752  
  1753  	// Ensure Get returns expected error.
  1754  	testName = "Get on closed tx"
  1755  	if k := bucket.Get(keyName); k != nil {
  1756  		tc.t.Errorf("Get: did not return nil on closed tx")
  1757  		return false
  1758  	}
  1759  
  1760  	// Ensure Put returns expected error.
  1761  	testName = "Put on closed tx"
  1762  	err = bucket.Put(keyName, []byte("test"))
  1763  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1764  		return false
  1765  	}
  1766  
  1767  	// -------------------
  1768  	// Metadata Cursor API
  1769  	// -------------------
  1770  
  1771  	// Ensure attempting to get a bucket from a cursor on a closed tx gives
  1772  	// back nil.
  1773  	if b := cursor.Bucket(); b != nil {
  1774  		tc.t.Error("Cursor.Bucket: returned non-nil on closed tx")
  1775  		return false
  1776  	}
  1777  
  1778  	// Ensure Cursor.Delete returns expected error.
  1779  	testName = "Cursor.Delete on closed tx"
  1780  	err = cursor.Delete()
  1781  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1782  		return false
  1783  	}
  1784  
  1785  	// Ensure Cursor.First on a closed tx returns false and nil key/value.
  1786  	if cursor.First() {
  1787  		tc.t.Error("Cursor.First: claims ok on closed tx")
  1788  		return false
  1789  	}
  1790  	if cursor.Key() != nil || cursor.Value() != nil {
  1791  		tc.t.Error("Cursor.First: key and/or value are not nil on " +
  1792  			"closed tx")
  1793  		return false
  1794  	}
  1795  
  1796  	// Ensure Cursor.Last on a closed tx returns false and nil key/value.
  1797  	if cursor.Last() {
  1798  		tc.t.Error("Cursor.Last: claims ok on closed tx")
  1799  		return false
  1800  	}
  1801  	if cursor.Key() != nil || cursor.Value() != nil {
  1802  		tc.t.Error("Cursor.Last: key and/or value are not nil on " +
  1803  			"closed tx")
  1804  		return false
  1805  	}
  1806  
  1807  	// Ensure Cursor.Next on a closed tx returns false and nil key/value.
  1808  	if cursor.Next() {
  1809  		tc.t.Error("Cursor.Next: claims ok on closed tx")
  1810  		return false
  1811  	}
  1812  	if cursor.Key() != nil || cursor.Value() != nil {
  1813  		tc.t.Error("Cursor.Next: key and/or value are not nil on " +
  1814  			"closed tx")
  1815  		return false
  1816  	}
  1817  
  1818  	// Ensure Cursor.Prev on a closed tx returns false and nil key/value.
  1819  	if cursor.Prev() {
  1820  		tc.t.Error("Cursor.Prev: claims ok on closed tx")
  1821  		return false
  1822  	}
  1823  	if cursor.Key() != nil || cursor.Value() != nil {
  1824  		tc.t.Error("Cursor.Prev: key and/or value are not nil on " +
  1825  			"closed tx")
  1826  		return false
  1827  	}
  1828  
  1829  	// Ensure Cursor.Seek on a closed tx returns false and nil key/value.
  1830  	if cursor.Seek([]byte{}) {
  1831  		tc.t.Error("Cursor.Seek: claims ok on closed tx")
  1832  		return false
  1833  	}
  1834  	if cursor.Key() != nil || cursor.Value() != nil {
  1835  		tc.t.Error("Cursor.Seek: key and/or value are not nil on " +
  1836  			"closed tx")
  1837  		return false
  1838  	}
  1839  
  1840  	// ---------------------
  1841  	// Non-bulk Block IO API
  1842  	// ---------------------
  1843  
  1844  	// Test the individual block APIs one block at a time to ensure they
  1845  	// return the expected error.  Also, build the data needed to test the
  1846  	// bulk APIs below while looping.
  1847  	allBlockHashes := make([]wire.ShaHash, len(tc.blocks))
  1848  	allBlockRegions := make([]database.BlockRegion, len(tc.blocks))
  1849  	for i, block := range tc.blocks {
  1850  		blockHash := block.Sha()
  1851  		allBlockHashes[i] = *blockHash
  1852  
  1853  		txLocs, err := block.TxLoc()
  1854  		if err != nil {
  1855  			tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i,
  1856  				err)
  1857  			return false
  1858  		}
  1859  
  1860  		// Ensure StoreBlock returns expected error.
  1861  		testName = "StoreBlock on closed tx"
  1862  		err = tx.StoreBlock(block)
  1863  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1864  			return false
  1865  		}
  1866  
  1867  		// Ensure FetchBlock returns expected error.
  1868  		testName = fmt.Sprintf("FetchBlock #%d on closed tx", i)
  1869  		_, err = tx.FetchBlock(blockHash)
  1870  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1871  			return false
  1872  		}
  1873  
  1874  		// Ensure FetchBlockHeader returns expected error.
  1875  		testName = fmt.Sprintf("FetchBlockHeader #%d on closed tx", i)
  1876  		_, err = tx.FetchBlockHeader(blockHash)
  1877  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1878  			return false
  1879  		}
  1880  
  1881  		// Ensure the first transaction fetched as a block region from
  1882  		// the database returns the expected error.
  1883  		region := database.BlockRegion{
  1884  			Hash:   blockHash,
  1885  			Offset: uint32(txLocs[0].TxStart),
  1886  			Len:    uint32(txLocs[0].TxLen),
  1887  		}
  1888  		allBlockRegions[i] = region
  1889  		_, err = tx.FetchBlockRegion(&region)
  1890  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1891  			return false
  1892  		}
  1893  
  1894  		// Ensure HasBlock returns expected error.
  1895  		testName = fmt.Sprintf("HasBlock #%d on closed tx", i)
  1896  		_, err = tx.HasBlock(blockHash)
  1897  		if !checkDbError(tc.t, testName, err, wantErrCode) {
  1898  			return false
  1899  		}
  1900  	}
  1901  
  1902  	// -----------------
  1903  	// Bulk Block IO API
  1904  	// -----------------
  1905  
  1906  	// Ensure FetchBlocks returns expected error.
  1907  	testName = "FetchBlocks on closed tx"
  1908  	_, err = tx.FetchBlocks(allBlockHashes)
  1909  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1910  		return false
  1911  	}
  1912  
  1913  	// Ensure FetchBlockHeaders returns expected error.
  1914  	testName = "FetchBlockHeaders on closed tx"
  1915  	_, err = tx.FetchBlockHeaders(allBlockHashes)
  1916  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1917  		return false
  1918  	}
  1919  
  1920  	// Ensure FetchBlockRegions returns expected error.
  1921  	testName = "FetchBlockRegions on closed tx"
  1922  	_, err = tx.FetchBlockRegions(allBlockRegions)
  1923  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1924  		return false
  1925  	}
  1926  
  1927  	// Ensure HasBlocks returns expected error.
  1928  	testName = "HasBlocks on closed tx"
  1929  	_, err = tx.HasBlocks(allBlockHashes)
  1930  	if !checkDbError(tc.t, testName, err, wantErrCode) {
  1931  		return false
  1932  	}
  1933  
  1934  	// ---------------
  1935  	// Commit/Rollback
  1936  	// ---------------
  1937  
  1938  	// Ensure that attempting to rollback or commit a transaction that is
  1939  	// already closed returns the expected error.
  1940  	err = tx.Rollback()
  1941  	if !checkDbError(tc.t, "closed tx rollback", err, wantErrCode) {
  1942  		return false
  1943  	}
  1944  	err = tx.Commit()
  1945  	if !checkDbError(tc.t, "closed tx commit", err, wantErrCode) {
  1946  		return false
  1947  	}
  1948  
  1949  	return true
  1950  }
  1951  
  1952  // testTxClosed ensures that both the metadata and block IO API functions behave
  1953  // as expected when attempted against both read-only and read-write
  1954  // transactions.
  1955  func testTxClosed(tc *testContext) bool {
  1956  	bucketName := []byte("closedtxbucket")
  1957  	keyName := []byte("closedtxkey")
  1958  
  1959  	// Start a transaction, create a bucket and key used for testing, and
  1960  	// immediately perform a commit on it so it is closed.
  1961  	tx, err := tc.db.Begin(true)
  1962  	if err != nil {
  1963  		tc.t.Errorf("Begin(true): unexpected error: %v", err)
  1964  		return false
  1965  	}
  1966  	defer rollbackOnPanic(tc.t, tx)
  1967  	if _, err := tx.Metadata().CreateBucket(bucketName); err != nil {
  1968  		tc.t.Errorf("CreateBucket: unexpected error: %v", err)
  1969  		return false
  1970  	}
  1971  	if err := tx.Metadata().Put(keyName, []byte("test")); err != nil {
  1972  		tc.t.Errorf("Put: unexpected error: %v", err)
  1973  		return false
  1974  	}
  1975  	if err := tx.Commit(); err != nil {
  1976  		tc.t.Errorf("Commit: unexpected error: %v", err)
  1977  		return false
  1978  	}
  1979  
  1980  	// Ensure invoking all of the functions on the closed read-write
  1981  	// transaction behave as expected.
  1982  	if !testClosedTxInterface(tc, tx) {
  1983  		return false
  1984  	}
  1985  
  1986  	// Repeat the tests with a rolled-back read-only transaction.
  1987  	tx, err = tc.db.Begin(false)
  1988  	if err != nil {
  1989  		tc.t.Errorf("Begin(false): unexpected error: %v", err)
  1990  		return false
  1991  	}
  1992  	defer rollbackOnPanic(tc.t, tx)
  1993  	if err := tx.Rollback(); err != nil {
  1994  		tc.t.Errorf("Rollback: unexpected error: %v", err)
  1995  		return false
  1996  	}
  1997  
  1998  	// Ensure invoking all of the functions on the closed read-only
  1999  	// transaction behave as expected.
  2000  	return testClosedTxInterface(tc, tx)
  2001  }
  2002  
  2003  // testConcurrecy ensure the database properly supports concurrent readers and
  2004  // only a single writer.  It also ensures views act as snapshots at the time
  2005  // they are acquired.
  2006  func testConcurrecy(tc *testContext) bool {
  2007  	// sleepTime is how long each of the concurrent readers should sleep to
  2008  	// aid in detection of whether or not the data is actually being read
  2009  	// concurrently.  It starts with a sane lower bound.
  2010  	var sleepTime = time.Millisecond * 250
  2011  
  2012  	// Determine about how long it takes for a single block read.  When it's
  2013  	// longer than the default minimum sleep time, adjust the sleep time to
  2014  	// help prevent durations that are too short which would cause erroneous
  2015  	// test failures on slower systems.
  2016  	startTime := time.Now()
  2017  	err := tc.db.View(func(tx database.Tx) error {
  2018  		_, err := tx.FetchBlock(tc.blocks[0].Sha())
  2019  		if err != nil {
  2020  			return err
  2021  		}
  2022  		return nil
  2023  	})
  2024  	if err != nil {
  2025  		tc.t.Errorf("Unexpected error in view: %v", err)
  2026  		return false
  2027  	}
  2028  	elapsed := time.Now().Sub(startTime)
  2029  	if sleepTime < elapsed {
  2030  		sleepTime = elapsed
  2031  	}
  2032  	tc.t.Logf("Time to load block 0: %v, using sleep time: %v", elapsed,
  2033  		sleepTime)
  2034  
  2035  	// reader takes a block number to load and channel to return the result
  2036  	// of the operation on.  It is used below to launch multiple concurrent
  2037  	// readers.
  2038  	numReaders := len(tc.blocks)
  2039  	resultChan := make(chan bool, numReaders)
  2040  	reader := func(blockNum int) {
  2041  		err := tc.db.View(func(tx database.Tx) error {
  2042  			time.Sleep(sleepTime)
  2043  			_, err := tx.FetchBlock(tc.blocks[blockNum].Sha())
  2044  			if err != nil {
  2045  				return err
  2046  			}
  2047  			return nil
  2048  		})
  2049  		if err != nil {
  2050  			tc.t.Errorf("Unexpected error in concurrent view: %v",
  2051  				err)
  2052  			resultChan <- false
  2053  		}
  2054  		resultChan <- true
  2055  	}
  2056  
  2057  	// Start up several concurrent readers for the same block and wait for
  2058  	// the results.
  2059  	startTime = time.Now()
  2060  	for i := 0; i < numReaders; i++ {
  2061  		go reader(0)
  2062  	}
  2063  	for i := 0; i < numReaders; i++ {
  2064  		if result := <-resultChan; !result {
  2065  			return false
  2066  		}
  2067  	}
  2068  	elapsed = time.Now().Sub(startTime)
  2069  	tc.t.Logf("%d concurrent reads of same block elapsed: %v", numReaders,
  2070  		elapsed)
  2071  
  2072  	// Consider it a failure if it took longer than half the time it would
  2073  	// take with no concurrency.
  2074  	if elapsed > sleepTime*time.Duration(numReaders/2) {
  2075  		tc.t.Errorf("Concurrent views for same block did not appear to "+
  2076  			"run simultaneously: elapsed %v", elapsed)
  2077  		return false
  2078  	}
  2079  
  2080  	// Start up several concurrent readers for different blocks and wait for
  2081  	// the results.
  2082  	startTime = time.Now()
  2083  	for i := 0; i < numReaders; i++ {
  2084  		go reader(i)
  2085  	}
  2086  	for i := 0; i < numReaders; i++ {
  2087  		if result := <-resultChan; !result {
  2088  			return false
  2089  		}
  2090  	}
  2091  	elapsed = time.Now().Sub(startTime)
  2092  	tc.t.Logf("%d concurrent reads of different blocks elapsed: %v",
  2093  		numReaders, elapsed)
  2094  
  2095  	// Consider it a failure if it took longer than half the time it would
  2096  	// take with no concurrency.
  2097  	if elapsed > sleepTime*time.Duration(numReaders/2) {
  2098  		tc.t.Errorf("Concurrent views for different blocks did not "+
  2099  			"appear to run simultaneously: elapsed %v", elapsed)
  2100  		return false
  2101  	}
  2102  
  2103  	// Start up a few readers and wait for them to acquire views.  Each
  2104  	// reader waits for a signal from the writer to be finished to ensure
  2105  	// that the data written by the writer is not seen by the view since it
  2106  	// was started before the data was set.
  2107  	concurrentKey := []byte("notthere")
  2108  	concurrentVal := []byte("someval")
  2109  	started := make(chan struct{})
  2110  	writeComplete := make(chan struct{})
  2111  	reader = func(blockNum int) {
  2112  		err := tc.db.View(func(tx database.Tx) error {
  2113  			started <- struct{}{}
  2114  
  2115  			// Wait for the writer to complete.
  2116  			<-writeComplete
  2117  
  2118  			// Since this reader was created before the write took
  2119  			// place, the data it added should not be visible.
  2120  			val := tx.Metadata().Get(concurrentKey)
  2121  			if val != nil {
  2122  				return fmt.Errorf("%s should not be visible",
  2123  					concurrentKey)
  2124  			}
  2125  			return nil
  2126  		})
  2127  		if err != nil {
  2128  			tc.t.Errorf("Unexpected error in concurrent view: %v",
  2129  				err)
  2130  			resultChan <- false
  2131  		}
  2132  		resultChan <- true
  2133  	}
  2134  	for i := 0; i < numReaders; i++ {
  2135  		go reader(0)
  2136  	}
  2137  	for i := 0; i < numReaders; i++ {
  2138  		<-started
  2139  	}
  2140  
  2141  	// All readers are started and waiting for completion of the writer.
  2142  	// Set some data the readers are expecting to not find and signal the
  2143  	// readers the write is done by closing the writeComplete channel.
  2144  	err = tc.db.Update(func(tx database.Tx) error {
  2145  		err := tx.Metadata().Put(concurrentKey, concurrentVal)
  2146  		if err != nil {
  2147  			return err
  2148  		}
  2149  		return nil
  2150  	})
  2151  	if err != nil {
  2152  		tc.t.Errorf("Unexpected error in update: %v", err)
  2153  		return false
  2154  	}
  2155  	close(writeComplete)
  2156  
  2157  	// Wait for reader results.
  2158  	for i := 0; i < numReaders; i++ {
  2159  		if result := <-resultChan; !result {
  2160  			return false
  2161  		}
  2162  	}
  2163  
  2164  	// Start a few writers and ensure the total time is at least the
  2165  	// writeSleepTime * numWriters.  This ensures only one write transaction
  2166  	// can be active at a time.
  2167  	writeSleepTime := time.Millisecond * 250
  2168  	writer := func() {
  2169  		err := tc.db.Update(func(tx database.Tx) error {
  2170  			time.Sleep(writeSleepTime)
  2171  			return nil
  2172  		})
  2173  		if err != nil {
  2174  			tc.t.Errorf("Unexpected error in concurrent view: %v",
  2175  				err)
  2176  			resultChan <- false
  2177  		}
  2178  		resultChan <- true
  2179  	}
  2180  	numWriters := 3
  2181  	startTime = time.Now()
  2182  	for i := 0; i < numWriters; i++ {
  2183  		go writer()
  2184  	}
  2185  	for i := 0; i < numWriters; i++ {
  2186  		if result := <-resultChan; !result {
  2187  			return false
  2188  		}
  2189  	}
  2190  	elapsed = time.Now().Sub(startTime)
  2191  	tc.t.Logf("%d concurrent writers elapsed using sleep time %v: %v",
  2192  		numWriters, writeSleepTime, elapsed)
  2193  
  2194  	// The total time must have been at least the sum of all sleeps if the
  2195  	// writes blocked properly.
  2196  	if elapsed < writeSleepTime*time.Duration(numWriters) {
  2197  		tc.t.Errorf("Concurrent writes appeared to run simultaneously: "+
  2198  			"elapsed %v", elapsed)
  2199  		return false
  2200  	}
  2201  
  2202  	return true
  2203  }
  2204  
  2205  // testConcurrentClose ensures that closing the database with open transactions
  2206  // blocks until the transactions are finished.
  2207  //
  2208  // The database will be closed upon returning from this function.
  2209  func testConcurrentClose(tc *testContext) bool {
  2210  	// Start up a few readers and wait for them to acquire views.  Each
  2211  	// reader waits for a signal to complete to ensure the transactions stay
  2212  	// open until they are explicitly signalled to be closed.
  2213  	var activeReaders int32
  2214  	numReaders := 3
  2215  	started := make(chan struct{})
  2216  	finishReaders := make(chan struct{})
  2217  	resultChan := make(chan bool, numReaders+1)
  2218  	reader := func() {
  2219  		err := tc.db.View(func(tx database.Tx) error {
  2220  			atomic.AddInt32(&activeReaders, 1)
  2221  			started <- struct{}{}
  2222  			<-finishReaders
  2223  			atomic.AddInt32(&activeReaders, -1)
  2224  			return nil
  2225  		})
  2226  		if err != nil {
  2227  			tc.t.Errorf("Unexpected error in concurrent view: %v",
  2228  				err)
  2229  			resultChan <- false
  2230  		}
  2231  		resultChan <- true
  2232  	}
  2233  	for i := 0; i < numReaders; i++ {
  2234  		go reader()
  2235  	}
  2236  	for i := 0; i < numReaders; i++ {
  2237  		<-started
  2238  	}
  2239  
  2240  	// Close the database in a separate goroutine.  This should block until
  2241  	// the transactions are finished.  Once the close has taken place, the
  2242  	// dbClosed channel is closed to signal the main goroutine below.
  2243  	dbClosed := make(chan struct{})
  2244  	go func() {
  2245  		started <- struct{}{}
  2246  		err := tc.db.Close()
  2247  		if err != nil {
  2248  			tc.t.Errorf("Unexpected error in concurrent view: %v",
  2249  				err)
  2250  			resultChan <- false
  2251  		}
  2252  		close(dbClosed)
  2253  		resultChan <- true
  2254  	}()
  2255  	<-started
  2256  
  2257  	// Wait a short period and then signal the reader transactions to
  2258  	// finish.  When the db closed channel is received, ensure there are no
  2259  	// active readers open.
  2260  	time.AfterFunc(time.Millisecond*250, func() { close(finishReaders) })
  2261  	<-dbClosed
  2262  	if nr := atomic.LoadInt32(&activeReaders); nr != 0 {
  2263  		tc.t.Errorf("Close did not appear to block with active "+
  2264  			"readers: %d active", nr)
  2265  		return false
  2266  	}
  2267  
  2268  	// Wait for all results.
  2269  	for i := 0; i < numReaders+1; i++ {
  2270  		if result := <-resultChan; !result {
  2271  			return false
  2272  		}
  2273  	}
  2274  
  2275  	return true
  2276  }
  2277  
  2278  // testInterface tests performs tests for the various interfaces of the database
  2279  // package which require state in the database for the given database type.
  2280  func testInterface(t *testing.T, db database.DB) {
  2281  	// Create a test context to pass around.
  2282  	context := testContext{t: t, db: db}
  2283  
  2284  	// Load the test blocks and store in the test context for use throughout
  2285  	// the tests.
  2286  	blocks, err := loadBlocks(t, blockDataFile, blockDataNet)
  2287  	if err != nil {
  2288  		t.Errorf("loadBlocks: Unexpected error: %v", err)
  2289  		return
  2290  	}
  2291  	context.blocks = blocks
  2292  
  2293  	// Test the transaction metadata interface including managed and manual
  2294  	// transactions as well as buckets.
  2295  	if !testMetadataTxInterface(&context) {
  2296  		return
  2297  	}
  2298  
  2299  	// Test the transaction block IO interface using managed and manual
  2300  	// transactions.  This function leaves all of the stored blocks in the
  2301  	// database since they're used later.
  2302  	if !testBlockIOTxInterface(&context) {
  2303  		return
  2304  	}
  2305  
  2306  	// Test all of the transaction interface functions against a closed
  2307  	// transaction work as expected.
  2308  	if !testTxClosed(&context) {
  2309  		return
  2310  	}
  2311  
  2312  	// Test the database properly supports concurrency.
  2313  	if !testConcurrecy(&context) {
  2314  		return
  2315  	}
  2316  
  2317  	// Test that closing the database with open transactions blocks until
  2318  	// the transactions are finished.
  2319  	//
  2320  	// The database will be closed upon returning from this function, so it
  2321  	// must be the last thing called.
  2322  	testConcurrentClose(&context)
  2323  }