interface_test.go raw

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