blockchain: Add utxoDatabaseInfo.
The adds utxoDatabaseInfo which tracks the version, compression version, utxo set version, and creation time of the UTXO database. The UTXO database will be created as a separate database in a subsequent commit. This is part of moving the UTXO set and state to a separate database.
This commit is contained in:
parent
c7315b6bf6
commit
105f0bad80
@ -24,12 +24,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// currentDatabaseVersion indicates what the current database
|
||||
// version is.
|
||||
// currentDatabaseVersion indicates the current database version.
|
||||
currentDatabaseVersion = 9
|
||||
|
||||
// currentBlockIndexVersion indicates what the current block index
|
||||
// database version.
|
||||
// currentBlockIndexVersion indicates the current block index database
|
||||
// version.
|
||||
currentBlockIndexVersion = 3
|
||||
|
||||
// blockHdrSize is the size of a block header. This is simply the
|
||||
|
||||
@ -8,6 +8,7 @@ package blockchain
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/blockchain/standalone/v2"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
@ -16,6 +17,29 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// utxoDbInfoBucketName is the name of the database bucket used to house
|
||||
// global versioning and date information for the UTXO database.
|
||||
utxoDbInfoBucketName = []byte("dbinfo")
|
||||
|
||||
// utxoDbInfoVersionKeyName is the name of the database key used to house the
|
||||
// database version. It is itself under the utxoDbInfoBucketName bucket.
|
||||
utxoDbInfoVersionKeyName = []byte("version")
|
||||
|
||||
// utxoDbInfoCompressionVerKeyName is the name of the database key used to
|
||||
// house the database compression version. It is itself under the
|
||||
// utxoDbInfoBucketName bucket.
|
||||
utxoDbInfoCompressionVerKeyName = []byte("compver")
|
||||
|
||||
// utxoDbInfoUtxoVerKeyName is the name of the database key used to house the
|
||||
// database UTXO set version. It is itself under the utxoDbInfoBucketName
|
||||
// bucket.
|
||||
utxoDbInfoUtxoVerKeyName = []byte("utxover")
|
||||
|
||||
// utxoDbInfoCreatedKeyName is the name of the database key used to house
|
||||
// the date the database was created. It is itself under the
|
||||
// utxoDbInfoBucketName bucket.
|
||||
utxoDbInfoCreatedKeyName = []byte("created")
|
||||
|
||||
// utxoSetBucketName is the name of the db bucket used to house the unspent
|
||||
// transaction output set.
|
||||
utxoSetBucketName = []byte("utxosetv3")
|
||||
@ -25,6 +49,120 @@ var (
|
||||
utxoSetStateKeyName = []byte("utxosetstate")
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// The UTXO database information contains information about the version and date
|
||||
// of the UTXO database.
|
||||
//
|
||||
// It consists of a separate key for each individual piece of information:
|
||||
//
|
||||
// Key Value Size Description
|
||||
// version uint32 4 bytes The version of the database
|
||||
// compver uint32 4 bytes The script compression version of the database
|
||||
// utxover uint32 4 bytes The UTXO set version of the database
|
||||
// created uint64 8 bytes The date of the creation of the database
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// utxoDatabaseInfo is the structure that holds the versioning and date
|
||||
// information for the UTXO database.
|
||||
type utxoDatabaseInfo struct {
|
||||
version uint32
|
||||
compVer uint32
|
||||
utxoVer uint32
|
||||
created time.Time
|
||||
}
|
||||
|
||||
// dbPutUtxoDatabaseInfo uses an existing database transaction to store the
|
||||
// database information.
|
||||
func dbPutUtxoDatabaseInfo(dbTx database.Tx, dbi *utxoDatabaseInfo) error {
|
||||
// uint32Bytes is a helper function to convert a uint32 to a byte slice
|
||||
// using the byte order specified by the database namespace.
|
||||
uint32Bytes := func(ui32 uint32) []byte {
|
||||
var b [4]byte
|
||||
byteOrder.PutUint32(b[:], ui32)
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// uint64Bytes is a helper function to convert a uint64 to a byte slice
|
||||
// using the byte order specified by the database namespace.
|
||||
uint64Bytes := func(ui64 uint64) []byte {
|
||||
var b [8]byte
|
||||
byteOrder.PutUint64(b[:], ui64)
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// Store the database version.
|
||||
meta := dbTx.Metadata()
|
||||
bucket := meta.Bucket(utxoDbInfoBucketName)
|
||||
err := bucket.Put(utxoDbInfoVersionKeyName, uint32Bytes(dbi.version))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store the compression version.
|
||||
err = bucket.Put(utxoDbInfoCompressionVerKeyName, uint32Bytes(dbi.compVer))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store the UTXO set version.
|
||||
err = bucket.Put(utxoDbInfoUtxoVerKeyName, uint32Bytes(dbi.utxoVer))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store the database creation date.
|
||||
return bucket.Put(utxoDbInfoCreatedKeyName,
|
||||
uint64Bytes(uint64(dbi.created.Unix())))
|
||||
}
|
||||
|
||||
// dbFetchUtxoDatabaseInfo uses an existing database transaction to fetch the
|
||||
// database versioning and creation information.
|
||||
func dbFetchUtxoDatabaseInfo(dbTx database.Tx) *utxoDatabaseInfo {
|
||||
meta := dbTx.Metadata()
|
||||
bucket := meta.Bucket(utxoDbInfoBucketName)
|
||||
|
||||
// Uninitialized state.
|
||||
if bucket == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load the database version.
|
||||
var version uint32
|
||||
versionBytes := bucket.Get(utxoDbInfoVersionKeyName)
|
||||
if versionBytes != nil {
|
||||
version = byteOrder.Uint32(versionBytes)
|
||||
}
|
||||
|
||||
// Load the database compression version.
|
||||
var compVer uint32
|
||||
compVerBytes := bucket.Get(utxoDbInfoCompressionVerKeyName)
|
||||
if compVerBytes != nil {
|
||||
compVer = byteOrder.Uint32(compVerBytes)
|
||||
}
|
||||
|
||||
// Load the database UTXO set version.
|
||||
var utxoVer uint32
|
||||
utxoVerBytes := bucket.Get(utxoDbInfoUtxoVerKeyName)
|
||||
if utxoVerBytes != nil {
|
||||
utxoVer = byteOrder.Uint32(utxoVerBytes)
|
||||
}
|
||||
|
||||
// Load the database creation date.
|
||||
var created time.Time
|
||||
createdBytes := bucket.Get(utxoDbInfoCreatedKeyName)
|
||||
if createdBytes != nil {
|
||||
ts := byteOrder.Uint64(createdBytes)
|
||||
created = time.Unix(int64(ts), 0)
|
||||
}
|
||||
|
||||
return &utxoDatabaseInfo{
|
||||
version: version,
|
||||
compVer: compVer,
|
||||
utxoVer: utxoVer,
|
||||
created: created,
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// The unspent transaction output (utxo) set consists of an entry for each
|
||||
// unspent output using a format that is optimized to reduce space using domain
|
||||
|
||||
@ -12,12 +12,82 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/blockchain/stake/v4"
|
||||
"github.com/decred/dcrd/database/v2"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// TestDbFetchUtxoDatabaseInfo ensures that putting and fetching the utxo
|
||||
// database info works as expected.
|
||||
func TestDbFetchUtxoDatabaseInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a test database.
|
||||
dbPath := filepath.Join(os.TempDir(), "test-dbfetchutxodatabaseinfo")
|
||||
_ = os.RemoveAll(dbPath)
|
||||
db, err := database.Create("ffldb", dbPath, wire.MainNet)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating test database: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dbPath)
|
||||
defer db.Close()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
dbInfo *utxoDatabaseInfo
|
||||
}{{
|
||||
name: "without UTXO database info (fresh database)",
|
||||
dbInfo: nil,
|
||||
}, {
|
||||
name: "with UTXO database info",
|
||||
dbInfo: &utxoDatabaseInfo{
|
||||
version: 1,
|
||||
compVer: 2,
|
||||
utxoVer: 3,
|
||||
created: time.Unix(1584246683, 0), // 2020-03-15 04:31:23 UTC
|
||||
},
|
||||
}}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.dbInfo != nil {
|
||||
// Create the UTXO database info bucket.
|
||||
err = db.Update(func(dbTx database.Tx) error {
|
||||
_, err := dbTx.Metadata().CreateBucketIfNotExists(utxoDbInfoBucketName)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("%q: error creating UTXO bucket: %v", test.name, err)
|
||||
}
|
||||
|
||||
// Update the UTXO database info in the database.
|
||||
err = db.Update(func(dbTx database.Tx) error {
|
||||
return dbPutUtxoDatabaseInfo(dbTx, test.dbInfo)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("%q: error putting UTXO database info: %v", test.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the UTXO database info from the database.
|
||||
err = db.View(func(dbTx database.Tx) error {
|
||||
gotDbInfo := dbFetchUtxoDatabaseInfo(dbTx)
|
||||
|
||||
// Ensure that the fetched UTXO database info matches the expected UTXO
|
||||
// database info.
|
||||
if !reflect.DeepEqual(gotDbInfo, test.dbInfo) {
|
||||
t.Fatalf("%q: mismatched db info:\nwant: %+v\n got: %+v\n", test.name,
|
||||
test.dbInfo, gotDbInfo)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("%q: error fetching UTXO database info: %v", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestUtxoSerialization ensures serializing and deserializing unspent
|
||||
// transaction output entries works as expected.
|
||||
func TestUtxoSerialization(t *testing.T) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user