From 4207c82f5a8b7adda5a9dc7d4b3bb1e034eca138 Mon Sep 17 00:00:00 2001 From: Ori Newman Date: Tue, 15 Jun 2021 17:47:17 +0300 Subject: [PATCH] Add database prefix (#1750) * Add prefix to stores * Add prefix to forgotten stores * Add a special type for prefix * Rename transaction->dbTx * Change error message * Use countKeyName * Rename Temporary Consesnsus to Staging * Add DeleteStagingConsensus to Domain interface * Add lock to staging consensus * Make prefix type-safer * Use ioutil.TempDir instead of t.TempDir --- .../flows/testing/handle_relay_invs_test.go | 16 ++ app/rpc/rpchandlers/get_blocks_test.go | 16 ++ .../acceptance_data_store.go | 13 +- .../block_header_staging_shard.go | 2 +- .../blockheaderstore/block_header_store.go | 19 +- .../block_relation_store.go | 13 +- .../blockstatusstore/block_status_store.go | 13 +- .../blockstore/block_staging_shard.go | 2 +- .../datastructures/blockstore/block_store.go | 20 ++- .../consensus_state_store.go | 19 +- .../consensusstatestore/tips.go | 7 +- .../consensusstatestore/utxo.go | 21 ++- .../consensusstatestore/virtual_utxo_set.go | 13 +- .../daablocksstore/daa_blocks_store.go | 15 +- .../finalitystore/finality_store.go | 13 +- .../ghostdagdatastore/ghostdag_data_store.go | 13 +- .../headers_selected_chain_staging_shard.go | 2 +- .../headers_selected_chain_store.go | 25 ++- .../headers_selected_tip_staging_shard.go | 2 +- .../headers_selected_tips_store.go | 14 +- .../multisetstore/multiset_store.go | 13 +- .../pruningstore/imported_pruning_point.go | 23 ++- .../pruningstore/pruning_staging_shard.go | 8 +- .../pruningstore/pruning_store.go | 55 ++++-- .../reachability_data_staging_shard.go | 2 +- .../reachability_data_store.go | 18 +- .../utxodiffstore/utxo_diff_store.go | 23 ++- domain/consensus/factory.go | 39 ++-- domain/consensus/factory_test.go | 3 +- domain/domain.go | 166 +++++++++++++++++- domain/domain_test.go | 132 ++++++++++++++ domain/prefixmanager/log.go | 9 + domain/prefixmanager/prefix.go | 106 +++++++++++ domain/prefixmanager/prefix/prefix.go | 46 +++++ 34 files changed, 727 insertions(+), 174 deletions(-) create mode 100644 domain/domain_test.go create mode 100644 domain/prefixmanager/log.go create mode 100644 domain/prefixmanager/prefix.go create mode 100644 domain/prefixmanager/prefix/prefix.go diff --git a/app/protocol/flows/testing/handle_relay_invs_test.go b/app/protocol/flows/testing/handle_relay_invs_test.go index 230638a01..adbd838d0 100644 --- a/app/protocol/flows/testing/handle_relay_invs_test.go +++ b/app/protocol/flows/testing/handle_relay_invs_test.go @@ -130,6 +130,22 @@ type fakeRelayInvsContext struct { rwLock sync.RWMutex } +func (f *fakeRelayInvsContext) DeleteStagingConsensus() error { + panic("implement me") +} + +func (f *fakeRelayInvsContext) StagingConsensus() externalapi.Consensus { + panic("implement me") +} + +func (f *fakeRelayInvsContext) InitStagingConsensus() error { + panic("implement me") +} + +func (f *fakeRelayInvsContext) CommitStagingConsensus() error { + panic("implement me") +} + func (f *fakeRelayInvsContext) EstimateNetworkHashesPerSecond(startHash *externalapi.DomainHash, windowSize int) (uint64, error) { panic(errors.Errorf("called unimplemented function from test '%s'", f.testName)) } diff --git a/app/rpc/rpchandlers/get_blocks_test.go b/app/rpc/rpchandlers/get_blocks_test.go index bd7e61b82..05caf6aa2 100644 --- a/app/rpc/rpchandlers/get_blocks_test.go +++ b/app/rpc/rpchandlers/get_blocks_test.go @@ -23,6 +23,22 @@ type fakeDomain struct { testapi.TestConsensus } +func (d fakeDomain) DeleteStagingConsensus() error { + panic("implement me") +} + +func (d fakeDomain) StagingConsensus() externalapi.Consensus { + panic("implement me") +} + +func (d fakeDomain) InitStagingConsensus() error { + panic("implement me") +} + +func (d fakeDomain) CommitStagingConsensus() error { + panic("implement me") +} + func (d fakeDomain) Consensus() externalapi.Consensus { return d } func (d fakeDomain) MiningManager() miningmanager.MiningManager { return nil } diff --git a/domain/consensus/datastructures/acceptancedatastore/acceptance_data_store.go b/domain/consensus/datastructures/acceptancedatastore/acceptance_data_store.go index e690d1cbe..2fb8c0ad7 100644 --- a/domain/consensus/datastructures/acceptancedatastore/acceptance_data_store.go +++ b/domain/consensus/datastructures/acceptancedatastore/acceptance_data_store.go @@ -6,20 +6,23 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" "google.golang.org/protobuf/proto" ) -var bucket = database.MakeBucket([]byte("acceptance-data")) +var bucketName = []byte("acceptance-data") // acceptanceDataStore represents a store of AcceptanceData type acceptanceDataStore struct { - cache *lrucache.LRUCache + cache *lrucache.LRUCache + bucket model.DBBucket } // New instantiates a new AcceptanceDataStore -func New(cacheSize int, preallocate bool) model.AcceptanceDataStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.AcceptanceDataStore { return &acceptanceDataStore{ - cache: lrucache.New(cacheSize, preallocate), + cache: lrucache.New(cacheSize, preallocate), + bucket: database.MakeBucket(prefix.Serialize()).Bucket(bucketName), } } @@ -84,5 +87,5 @@ func (ads *acceptanceDataStore) deserializeAcceptanceData(acceptanceDataBytes [] } func (ads *acceptanceDataStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucket.Key(hash.ByteSlice()) + return ads.bucket.Key(hash.ByteSlice()) } diff --git a/domain/consensus/datastructures/blockheaderstore/block_header_staging_shard.go b/domain/consensus/datastructures/blockheaderstore/block_header_staging_shard.go index e3813b9f7..0ecccafd8 100644 --- a/domain/consensus/datastructures/blockheaderstore/block_header_staging_shard.go +++ b/domain/consensus/datastructures/blockheaderstore/block_header_staging_shard.go @@ -56,7 +56,7 @@ func (bhss *blockHeaderStagingShard) commitCount(dbTx model.DBTransaction) error if err != nil { return err } - err = dbTx.Put(countKey, countBytes) + err = dbTx.Put(bhss.store.countKey, countBytes) if err != nil { return err } diff --git a/domain/consensus/datastructures/blockheaderstore/block_header_store.go b/domain/consensus/datastructures/blockheaderstore/block_header_store.go index 1cbd7e2d7..92776842c 100644 --- a/domain/consensus/datastructures/blockheaderstore/block_header_store.go +++ b/domain/consensus/datastructures/blockheaderstore/block_header_store.go @@ -7,21 +7,26 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var bucket = database.MakeBucket([]byte("block-headers")) -var countKey = database.MakeBucket(nil).Key([]byte("block-headers-count")) +var bucketName = []byte("block-headers") +var countKeyName = []byte("block-headers-count") // blockHeaderStore represents a store of blocks type blockHeaderStore struct { cache *lrucache.LRUCache countCached uint64 + bucket model.DBBucket + countKey model.DBKey } // New instantiates a new BlockHeaderStore -func New(dbContext model.DBReader, cacheSize int, preallocate bool) (model.BlockHeaderStore, error) { +func New(dbContext model.DBReader, prefix *prefix.Prefix, cacheSize int, preallocate bool) (model.BlockHeaderStore, error) { blockHeaderStore := &blockHeaderStore{ - cache: lrucache.New(cacheSize, preallocate), + cache: lrucache.New(cacheSize, preallocate), + bucket: database.MakeBucket(prefix.Serialize()).Bucket(bucketName), + countKey: database.MakeBucket(prefix.Serialize()).Key(countKeyName), } err := blockHeaderStore.initializeCount(dbContext) @@ -34,12 +39,12 @@ func New(dbContext model.DBReader, cacheSize int, preallocate bool) (model.Block func (bhs *blockHeaderStore) initializeCount(dbContext model.DBReader) error { count := uint64(0) - hasCountBytes, err := dbContext.Has(countKey) + hasCountBytes, err := dbContext.Has(bhs.countKey) if err != nil { return err } if hasCountBytes { - countBytes, err := dbContext.Get(countKey) + countBytes, err := dbContext.Get(bhs.countKey) if err != nil { return err } @@ -144,7 +149,7 @@ func (bhs *blockHeaderStore) Delete(stagingArea *model.StagingArea, blockHash *e } func (bhs *blockHeaderStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucket.Key(hash.ByteSlice()) + return bhs.bucket.Key(hash.ByteSlice()) } func (bhs *blockHeaderStore) serializeHeader(header externalapi.BlockHeader) ([]byte, error) { diff --git a/domain/consensus/datastructures/blockrelationstore/block_relation_store.go b/domain/consensus/datastructures/blockrelationstore/block_relation_store.go index 01dd1d027..102711889 100644 --- a/domain/consensus/datastructures/blockrelationstore/block_relation_store.go +++ b/domain/consensus/datastructures/blockrelationstore/block_relation_store.go @@ -7,19 +7,22 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var bucket = database.MakeBucket([]byte("block-relations")) +var bucketName = []byte("block-relations") // blockRelationStore represents a store of BlockRelations type blockRelationStore struct { - cache *lrucache.LRUCache + cache *lrucache.LRUCache + bucket model.DBBucket } // New instantiates a new BlockRelationStore -func New(cacheSize int, preallocate bool) model.BlockRelationStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.BlockRelationStore { return &blockRelationStore{ - cache: lrucache.New(cacheSize, preallocate), + cache: lrucache.New(cacheSize, preallocate), + bucket: database.MakeBucket(prefix.Serialize()).Bucket(bucketName), } } @@ -72,7 +75,7 @@ func (brs *blockRelationStore) Has(dbContext model.DBReader, stagingArea *model. } func (brs *blockRelationStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucket.Key(hash.ByteSlice()) + return brs.bucket.Key(hash.ByteSlice()) } func (brs *blockRelationStore) serializeBlockRelations(blockRelations *model.BlockRelations) ([]byte, error) { diff --git a/domain/consensus/datastructures/blockstatusstore/block_status_store.go b/domain/consensus/datastructures/blockstatusstore/block_status_store.go index 93272da36..7d800292b 100644 --- a/domain/consensus/datastructures/blockstatusstore/block_status_store.go +++ b/domain/consensus/datastructures/blockstatusstore/block_status_store.go @@ -7,19 +7,22 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var bucket = database.MakeBucket([]byte("block-statuses")) +var bucketName = []byte("block-statuses") // blockStatusStore represents a store of BlockStatuses type blockStatusStore struct { - cache *lrucache.LRUCache + cache *lrucache.LRUCache + bucket model.DBBucket } // New instantiates a new BlockStatusStore -func New(cacheSize int, preallocate bool) model.BlockStatusStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.BlockStatusStore { return &blockStatusStore{ - cache: lrucache.New(cacheSize, preallocate), + cache: lrucache.New(cacheSize, preallocate), + bucket: database.MakeBucket(prefix.Serialize()).Bucket(bucketName), } } @@ -93,5 +96,5 @@ func (bss *blockStatusStore) deserializeBlockStatus(statusBytes []byte) (externa } func (bss *blockStatusStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucket.Key(hash.ByteSlice()) + return bss.bucket.Key(hash.ByteSlice()) } diff --git a/domain/consensus/datastructures/blockstore/block_staging_shard.go b/domain/consensus/datastructures/blockstore/block_staging_shard.go index 9bbcaa333..e26ce112d 100644 --- a/domain/consensus/datastructures/blockstore/block_staging_shard.go +++ b/domain/consensus/datastructures/blockstore/block_staging_shard.go @@ -56,7 +56,7 @@ func (bss *blockStagingShard) commitCount(dbTx model.DBTransaction) error { if err != nil { return err } - err = dbTx.Put(countKey, countBytes) + err = dbTx.Put(bss.store.countKey, countBytes) if err != nil { return err } diff --git a/domain/consensus/datastructures/blockstore/block_store.go b/domain/consensus/datastructures/blockstore/block_store.go index 677f37ce9..791c3f46b 100644 --- a/domain/consensus/datastructures/blockstore/block_store.go +++ b/domain/consensus/datastructures/blockstore/block_store.go @@ -7,22 +7,26 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" "github.com/pkg/errors" ) -var bucket = database.MakeBucket([]byte("blocks")) -var countKey = database.MakeBucket(nil).Key([]byte("blocks-count")) +var bucketName = []byte("blocks") // blockStore represents a store of blocks type blockStore struct { cache *lrucache.LRUCache countCached uint64 + bucket model.DBBucket + countKey model.DBKey } // New instantiates a new BlockStore -func New(dbContext model.DBReader, cacheSize int, preallocate bool) (model.BlockStore, error) { +func New(dbContext model.DBReader, prefix *prefix.Prefix, cacheSize int, preallocate bool) (model.BlockStore, error) { blockStore := &blockStore{ - cache: lrucache.New(cacheSize, preallocate), + cache: lrucache.New(cacheSize, preallocate), + bucket: database.MakeBucket(prefix.Serialize()).Bucket(bucketName), + countKey: database.MakeBucket(prefix.Serialize()).Key([]byte("blocks-count")), } err := blockStore.initializeCount(dbContext) @@ -35,12 +39,12 @@ func New(dbContext model.DBReader, cacheSize int, preallocate bool) (model.Block func (bs *blockStore) initializeCount(dbContext model.DBReader) error { count := uint64(0) - hasCountBytes, err := dbContext.Has(countKey) + hasCountBytes, err := dbContext.Has(bs.countKey) if err != nil { return err } if hasCountBytes { - countBytes, err := dbContext.Get(countKey) + countBytes, err := dbContext.Get(bs.countKey) if err != nil { return err } @@ -153,7 +157,7 @@ func (bs *blockStore) deserializeBlock(blockBytes []byte) (*externalapi.DomainBl } func (bs *blockStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucket.Key(hash.ByteSlice()) + return bs.bucket.Key(hash.ByteSlice()) } func (bs *blockStore) Count(stagingArea *model.StagingArea) uint64 { @@ -225,7 +229,7 @@ func (a allBlockHashesIterator) Close() error { } func (bs *blockStore) AllBlockHashesIterator(dbContext model.DBReader) (model.BlockIterator, error) { - cursor, err := dbContext.Cursor(bucket) + cursor, err := dbContext.Cursor(bs.bucket) if err != nil { return nil, err } diff --git a/domain/consensus/datastructures/consensusstatestore/consensus_state_store.go b/domain/consensus/datastructures/consensusstatestore/consensus_state_store.go index c885df461..d1ea4a466 100644 --- a/domain/consensus/datastructures/consensusstatestore/consensus_state_store.go +++ b/domain/consensus/datastructures/consensusstatestore/consensus_state_store.go @@ -1,22 +1,31 @@ package consensusstatestore import ( + "github.com/kaspanet/kaspad/domain/consensus/database" "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/utxolrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) +var importingPruningPointUTXOSetKeyName = []byte("importing-pruning-point-utxo-set") + // consensusStateStore represents a store for the current consensus state type consensusStateStore struct { - virtualUTXOSetCache *utxolrucache.LRUCache - - tipsCache []*externalapi.DomainHash + virtualUTXOSetCache *utxolrucache.LRUCache + tipsCache []*externalapi.DomainHash + tipsKey model.DBKey + utxoSetBucket model.DBBucket + importingPruningPointUTXOSetKey model.DBKey } // New instantiates a new ConsensusStateStore -func New(utxoSetCacheSize int, preallocate bool) model.ConsensusStateStore { +func New(prefix *prefix.Prefix, utxoSetCacheSize int, preallocate bool) model.ConsensusStateStore { return &consensusStateStore{ - virtualUTXOSetCache: utxolrucache.New(utxoSetCacheSize, preallocate), + virtualUTXOSetCache: utxolrucache.New(utxoSetCacheSize, preallocate), + tipsKey: database.MakeBucket(prefix.Serialize()).Key(tipsKeyName), + importingPruningPointUTXOSetKey: database.MakeBucket(prefix.Serialize()).Key(importingPruningPointUTXOSetKeyName), + utxoSetBucket: database.MakeBucket(prefix.Serialize()).Bucket(utxoSetBucketName), } } diff --git a/domain/consensus/datastructures/consensusstatestore/tips.go b/domain/consensus/datastructures/consensusstatestore/tips.go index 41d9f9892..eb2723b1f 100644 --- a/domain/consensus/datastructures/consensusstatestore/tips.go +++ b/domain/consensus/datastructures/consensusstatestore/tips.go @@ -2,13 +2,12 @@ package consensusstatestore import ( "github.com/golang/protobuf/proto" - "github.com/kaspanet/kaspad/domain/consensus/database" "github.com/kaspanet/kaspad/domain/consensus/database/serialization" "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" ) -var tipsKey = database.MakeBucket(nil).Key([]byte("tips")) +var tipsKeyName = []byte("tips") func (css *consensusStateStore) Tips(stagingArea *model.StagingArea, dbContext model.DBReader) ([]*externalapi.DomainHash, error) { stagingShard := css.stagingShard(stagingArea) @@ -21,7 +20,7 @@ func (css *consensusStateStore) Tips(stagingArea *model.StagingArea, dbContext m return externalapi.CloneHashes(css.tipsCache), nil } - tipsBytes, err := dbContext.Get(tipsKey) + tipsBytes, err := dbContext.Get(css.tipsKey) if err != nil { return nil, err } @@ -66,7 +65,7 @@ func (csss *consensusStateStagingShard) commitTips(dbTx model.DBTransaction) err if err != nil { return err } - err = dbTx.Put(tipsKey, tipsBytes) + err = dbTx.Put(csss.store.tipsKey, tipsBytes) if err != nil { return err } diff --git a/domain/consensus/datastructures/consensusstatestore/utxo.go b/domain/consensus/datastructures/consensusstatestore/utxo.go index 79cee7cd7..4663564d8 100644 --- a/domain/consensus/datastructures/consensusstatestore/utxo.go +++ b/domain/consensus/datastructures/consensusstatestore/utxo.go @@ -1,22 +1,21 @@ package consensusstatestore import ( - "github.com/kaspanet/kaspad/domain/consensus/database" "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/utxo" "github.com/pkg/errors" ) -var utxoSetBucket = database.MakeBucket([]byte("virtual-utxo-set")) +var utxoSetBucketName = []byte("virtual-utxo-set") -func utxoKey(outpoint *externalapi.DomainOutpoint) (model.DBKey, error) { +func (css *consensusStateStore) utxoKey(outpoint *externalapi.DomainOutpoint) (model.DBKey, error) { serializedOutpoint, err := serializeOutpoint(outpoint) if err != nil { return nil, err } - return utxoSetBucket.Key(serializedOutpoint), nil + return css.utxoSetBucket.Key(serializedOutpoint), nil } func (css *consensusStateStore) StageVirtualUTXODiff(stagingArea *model.StagingArea, virtualUTXODiff externalapi.UTXODiff) { @@ -48,7 +47,7 @@ func (csss *consensusStateStagingShard) commitVirtualUTXODiff(dbTx model.DBTrans csss.store.virtualUTXOSetCache.Remove(toRemoveOutpoint) - dbKey, err := utxoKey(toRemoveOutpoint) + dbKey, err := csss.store.utxoKey(toRemoveOutpoint) if err != nil { return err } @@ -68,7 +67,7 @@ func (csss *consensusStateStagingShard) commitVirtualUTXODiff(dbTx model.DBTrans csss.store.virtualUTXOSetCache.Add(toAddOutpoint, toAddEntry) - dbKey, err := utxoKey(toAddOutpoint) + dbKey, err := csss.store.utxoKey(toAddOutpoint) if err != nil { return err } @@ -111,7 +110,7 @@ func (css *consensusStateStore) utxoByOutpointFromStagedVirtualUTXODiff(dbContex return entry, nil } - key, err := utxoKey(outpoint) + key, err := css.utxoKey(outpoint) if err != nil { return nil, err } @@ -150,7 +149,7 @@ func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbCon } } - key, err := utxoKey(outpoint) + key, err := css.utxoKey(outpoint) if err != nil { return false, err } @@ -161,7 +160,7 @@ func (css *consensusStateStore) hasUTXOByOutpointFromStagedVirtualUTXODiff(dbCon func (css *consensusStateStore) VirtualUTXOs(dbContext model.DBReader, fromOutpoint *externalapi.DomainOutpoint, limit int) ( []*externalapi.OutpointAndUTXOEntryPair, error) { - cursor, err := dbContext.Cursor(utxoSetBucket) + cursor, err := dbContext.Cursor(css.utxoSetBucket) if err != nil { return nil, err } @@ -172,7 +171,7 @@ func (css *consensusStateStore) VirtualUTXOs(dbContext model.DBReader, fromOutpo if err != nil { return nil, err } - seekKey := utxoSetBucket.Key(serializedFromOutpoint) + seekKey := css.utxoSetBucket.Key(serializedFromOutpoint) err = cursor.Seek(seekKey) if err != nil { return nil, err @@ -201,7 +200,7 @@ func (css *consensusStateStore) VirtualUTXOSetIterator(dbContext model.DBReader, stagingShard := css.stagingShard(stagingArea) - cursor, err := dbContext.Cursor(utxoSetBucket) + cursor, err := dbContext.Cursor(css.utxoSetBucket) if err != nil { return nil, err } diff --git a/domain/consensus/datastructures/consensusstatestore/virtual_utxo_set.go b/domain/consensus/datastructures/consensusstatestore/virtual_utxo_set.go index 34a175b22..a39232bbd 100644 --- a/domain/consensus/datastructures/consensusstatestore/virtual_utxo_set.go +++ b/domain/consensus/datastructures/consensusstatestore/virtual_utxo_set.go @@ -1,24 +1,21 @@ package consensusstatestore import ( - "github.com/kaspanet/kaspad/domain/consensus/database" "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/pkg/errors" ) -var importingPruningPointUTXOSetKey = database.MakeBucket(nil).Key([]byte("importing-pruning-point-utxo-set")) - func (css *consensusStateStore) StartImportingPruningPointUTXOSet(dbContext model.DBWriter) error { - return dbContext.Put(importingPruningPointUTXOSetKey, []byte{0}) + return dbContext.Put(css.importingPruningPointUTXOSetKey, []byte{0}) } func (css *consensusStateStore) HadStartedImportingPruningPointUTXOSet(dbContext model.DBWriter) (bool, error) { - return dbContext.Has(importingPruningPointUTXOSetKey) + return dbContext.Has(css.importingPruningPointUTXOSetKey) } func (css *consensusStateStore) FinishImportingPruningPointUTXOSet(dbContext model.DBWriter) error { - return dbContext.Delete(importingPruningPointUTXOSetKey) + return dbContext.Delete(css.importingPruningPointUTXOSetKey) } func (css *consensusStateStore) ImportPruningPointUTXOSetIntoVirtualUTXOSet(dbContext model.DBWriter, @@ -37,7 +34,7 @@ func (css *consensusStateStore) ImportPruningPointUTXOSetIntoVirtualUTXOSet(dbCo css.virtualUTXOSetCache.Clear() // Delete all the old UTXOs from the database - deleteCursor, err := dbContext.Cursor(utxoSetBucket) + deleteCursor, err := dbContext.Cursor(css.utxoSetBucket) if err != nil { return err } @@ -60,7 +57,7 @@ func (css *consensusStateStore) ImportPruningPointUTXOSetIntoVirtualUTXOSet(dbCo return err } - key, err := utxoKey(outpoint) + key, err := css.utxoKey(outpoint) if err != nil { return err } diff --git a/domain/consensus/datastructures/daablocksstore/daa_blocks_store.go b/domain/consensus/datastructures/daablocksstore/daa_blocks_store.go index 7782fa538..4e2c8c6aa 100644 --- a/domain/consensus/datastructures/daablocksstore/daa_blocks_store.go +++ b/domain/consensus/datastructures/daablocksstore/daa_blocks_store.go @@ -6,22 +6,27 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var daaScoreBucket = database.MakeBucket([]byte("daa-score")) -var daaAddedBlocksBucket = database.MakeBucket([]byte("daa-added-blocks")) +var daaScoreBucketName = []byte("daa-score") +var daaAddedBlocksBucketName = []byte("daa-added-blocks") // daaBlocksStore represents a store of DAABlocksStore type daaBlocksStore struct { daaScoreLRUCache *lrucache.LRUCache daaAddedBlocksLRUCache *lrucache.LRUCache + daaScoreBucket model.DBBucket + daaAddedBlocksBucket model.DBBucket } // New instantiates a new DAABlocksStore -func New(daaScoreCacheSize int, daaAddedBlocksCacheSize int, preallocate bool) model.DAABlocksStore { +func New(prefix *prefix.Prefix, daaScoreCacheSize int, daaAddedBlocksCacheSize int, preallocate bool) model.DAABlocksStore { return &daaBlocksStore{ daaScoreLRUCache: lrucache.New(daaScoreCacheSize, preallocate), daaAddedBlocksLRUCache: lrucache.New(daaAddedBlocksCacheSize, preallocate), + daaScoreBucket: database.MakeBucket(prefix.Serialize()).Bucket(daaScoreBucketName), + daaAddedBlocksBucket: database.MakeBucket(prefix.Serialize()).Bucket(daaAddedBlocksBucketName), } } @@ -90,11 +95,11 @@ func (daas *daaBlocksStore) DAAAddedBlocks(dbContext model.DBReader, stagingArea } func (daas *daaBlocksStore) daaScoreHashAsKey(hash *externalapi.DomainHash) model.DBKey { - return daaScoreBucket.Key(hash.ByteSlice()) + return daas.daaScoreBucket.Key(hash.ByteSlice()) } func (daas *daaBlocksStore) daaAddedBlocksHashAsKey(hash *externalapi.DomainHash) model.DBKey { - return daaAddedBlocksBucket.Key(hash.ByteSlice()) + return daas.daaAddedBlocksBucket.Key(hash.ByteSlice()) } func (daas *daaBlocksStore) Delete(stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) { diff --git a/domain/consensus/datastructures/finalitystore/finality_store.go b/domain/consensus/datastructures/finalitystore/finality_store.go index b9f3d661e..4821f7bd7 100644 --- a/domain/consensus/datastructures/finalitystore/finality_store.go +++ b/domain/consensus/datastructures/finalitystore/finality_store.go @@ -5,18 +5,21 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var bucket = database.MakeBucket([]byte("finality-points")) +var bucketName = []byte("finality-points") type finalityStore struct { - cache *lrucache.LRUCache + cache *lrucache.LRUCache + bucket model.DBBucket } // New instantiates a new FinalityStore -func New(cacheSize int, preallocate bool) model.FinalityStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.FinalityStore { return &finalityStore{ - cache: lrucache.New(cacheSize, preallocate), + cache: lrucache.New(cacheSize, preallocate), + bucket: database.MakeBucket(prefix.Serialize()).Bucket(bucketName), } } @@ -55,5 +58,5 @@ func (fs *finalityStore) IsStaged(stagingArea *model.StagingArea) bool { } func (fs *finalityStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucket.Key(hash.ByteSlice()) + return fs.bucket.Key(hash.ByteSlice()) } diff --git a/domain/consensus/datastructures/ghostdagdatastore/ghostdag_data_store.go b/domain/consensus/datastructures/ghostdagdatastore/ghostdag_data_store.go index adfe19e98..1f3606130 100644 --- a/domain/consensus/datastructures/ghostdagdatastore/ghostdag_data_store.go +++ b/domain/consensus/datastructures/ghostdagdatastore/ghostdag_data_store.go @@ -7,19 +7,22 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var bucket = database.MakeBucket([]byte("block-ghostdag-data")) +var bucketName = []byte("block-ghostdag-data") // ghostdagDataStore represents a store of BlockGHOSTDAGData type ghostdagDataStore struct { - cache *lrucache.LRUCache + cache *lrucache.LRUCache + bucket model.DBBucket } // New instantiates a new GHOSTDAGDataStore -func New(cacheSize int, preallocate bool) model.GHOSTDAGDataStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.GHOSTDAGDataStore { return &ghostdagDataStore{ - cache: lrucache.New(cacheSize, preallocate), + cache: lrucache.New(cacheSize, preallocate), + bucket: database.MakeBucket(prefix.Serialize()).Bucket(bucketName), } } @@ -60,7 +63,7 @@ func (gds *ghostdagDataStore) Get(dbContext model.DBReader, stagingArea *model.S } func (gds *ghostdagDataStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucket.Key(hash.ByteSlice()) + return gds.bucket.Key(hash.ByteSlice()) } func (gds *ghostdagDataStore) serializeBlockGHOSTDAGData(blockGHOSTDAGData *model.BlockGHOSTDAGData) ([]byte, error) { diff --git a/domain/consensus/datastructures/headersselectedchainstore/headers_selected_chain_staging_shard.go b/domain/consensus/datastructures/headersselectedchainstore/headers_selected_chain_staging_shard.go index 4e865ec77..295bc5175 100644 --- a/domain/consensus/datastructures/headersselectedchainstore/headers_selected_chain_staging_shard.go +++ b/domain/consensus/datastructures/headersselectedchainstore/headers_selected_chain_staging_shard.go @@ -69,7 +69,7 @@ func (hscss *headersSelectedChainStagingShard) Commit(dbTx model.DBTransaction) } } - err := dbTx.Put(highestChainBlockIndexKey, hscss.store.serializeIndex(highestIndex)) + err := dbTx.Put(hscss.store.highestChainBlockIndexKey, hscss.store.serializeIndex(highestIndex)) if err != nil { return err } diff --git a/domain/consensus/datastructures/headersselectedchainstore/headers_selected_chain_store.go b/domain/consensus/datastructures/headersselectedchainstore/headers_selected_chain_store.go index 0cf521b51..1d38ca4ac 100644 --- a/domain/consensus/datastructures/headersselectedchainstore/headers_selected_chain_store.go +++ b/domain/consensus/datastructures/headersselectedchainstore/headers_selected_chain_store.go @@ -2,6 +2,7 @@ package headersselectedchainstore import ( "encoding/binary" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" "github.com/kaspanet/kaspad/domain/consensus/database" "github.com/kaspanet/kaspad/domain/consensus/database/binaryserialization" @@ -12,21 +13,27 @@ import ( "github.com/pkg/errors" ) -var bucketChainBlockHashByIndex = database.MakeBucket([]byte("chain-block-hash-by-index")) -var bucketChainBlockIndexByHash = database.MakeBucket([]byte("chain-block-index-by-hash")) -var highestChainBlockIndexKey = database.MakeBucket(nil).Key([]byte("highest-chain-block-index")) +var bucketChainBlockHashByIndexName = []byte("chain-block-hash-by-index") +var bucketChainBlockIndexByHashName = []byte("chain-block-index-by-hash") +var highestChainBlockIndexKeyName = []byte("highest-chain-block-index") type headersSelectedChainStore struct { cacheByIndex *lrucacheuint64tohash.LRUCache cacheByHash *lrucache.LRUCache cacheHighestChainBlockIndex uint64 + bucketChainBlockHashByIndex model.DBBucket + bucketChainBlockIndexByHash model.DBBucket + highestChainBlockIndexKey model.DBKey } // New instantiates a new HeadersSelectedChainStore -func New(cacheSize int, preallocate bool) model.HeadersSelectedChainStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.HeadersSelectedChainStore { return &headersSelectedChainStore{ - cacheByIndex: lrucacheuint64tohash.New(cacheSize, preallocate), - cacheByHash: lrucache.New(cacheSize, preallocate), + cacheByIndex: lrucacheuint64tohash.New(cacheSize, preallocate), + cacheByHash: lrucache.New(cacheSize, preallocate), + bucketChainBlockHashByIndex: database.MakeBucket(prefix.Serialize()).Bucket(bucketChainBlockHashByIndexName), + bucketChainBlockIndexByHash: database.MakeBucket(prefix.Serialize()).Bucket(bucketChainBlockIndexByHashName), + highestChainBlockIndexKey: database.MakeBucket(prefix.Serialize()).Key(highestChainBlockIndexKeyName), } } @@ -138,13 +145,13 @@ func (hscs *headersSelectedChainStore) deserializeIndex(indexBytes []byte) (uint } func (hscs *headersSelectedChainStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucketChainBlockIndexByHash.Key(hash.ByteSlice()) + return hscs.bucketChainBlockIndexByHash.Key(hash.ByteSlice()) } func (hscs *headersSelectedChainStore) indexAsKey(index uint64) model.DBKey { var keyBytes [8]byte binary.BigEndian.PutUint64(keyBytes[:], index) - return bucketChainBlockHashByIndex.Key(keyBytes[:]) + return hscs.bucketChainBlockHashByIndex.Key(keyBytes[:]) } func (hscs *headersSelectedChainStore) highestChainBlockIndex(dbContext model.DBReader) (uint64, bool, error) { @@ -152,7 +159,7 @@ func (hscs *headersSelectedChainStore) highestChainBlockIndex(dbContext model.DB return hscs.cacheHighestChainBlockIndex, true, nil } - indexBytes, err := dbContext.Get(highestChainBlockIndexKey) + indexBytes, err := dbContext.Get(hscs.highestChainBlockIndexKey) if err != nil { if errors.Is(err, database.ErrNotFound) { return 0, false, nil diff --git a/domain/consensus/datastructures/headersselectedtipstore/headers_selected_tip_staging_shard.go b/domain/consensus/datastructures/headersselectedtipstore/headers_selected_tip_staging_shard.go index ddc9a158c..4372cdd90 100644 --- a/domain/consensus/datastructures/headersselectedtipstore/headers_selected_tip_staging_shard.go +++ b/domain/consensus/datastructures/headersselectedtipstore/headers_selected_tip_staging_shard.go @@ -28,7 +28,7 @@ func (hstss *headersSelectedTipStagingShard) Commit(dbTx model.DBTransaction) er if err != nil { return err } - err = dbTx.Put(headerSelectedTipKey, selectedTipBytes) + err = dbTx.Put(hstss.store.key, selectedTipBytes) if err != nil { return err } diff --git a/domain/consensus/datastructures/headersselectedtipstore/headers_selected_tips_store.go b/domain/consensus/datastructures/headersselectedtipstore/headers_selected_tips_store.go index 410c7e240..04128b38e 100644 --- a/domain/consensus/datastructures/headersselectedtipstore/headers_selected_tips_store.go +++ b/domain/consensus/datastructures/headersselectedtipstore/headers_selected_tips_store.go @@ -6,17 +6,21 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/database/serialization" "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var headerSelectedTipKey = database.MakeBucket(nil).Key([]byte("headers-selected-tip")) +var keyName = []byte("headers-selected-tip") type headerSelectedTipStore struct { cache *externalapi.DomainHash + key model.DBKey } // New instantiates a new HeaderSelectedTipStore -func New() model.HeaderSelectedTipStore { - return &headerSelectedTipStore{} +func New(prefix *prefix.Prefix) model.HeaderSelectedTipStore { + return &headerSelectedTipStore{ + key: database.MakeBucket(prefix.Serialize()).Key(keyName), + } } func (hsts *headerSelectedTipStore) Has(dbContext model.DBReader, stagingArea *model.StagingArea) (bool, error) { @@ -30,7 +34,7 @@ func (hsts *headerSelectedTipStore) Has(dbContext model.DBReader, stagingArea *m return true, nil } - return dbContext.Has(headerSelectedTipKey) + return dbContext.Has(hsts.key) } func (hsts *headerSelectedTipStore) Stage(stagingArea *model.StagingArea, selectedTip *externalapi.DomainHash) { @@ -55,7 +59,7 @@ func (hsts *headerSelectedTipStore) HeadersSelectedTip(dbContext model.DBReader, return hsts.cache, nil } - selectedTipBytes, err := dbContext.Get(headerSelectedTipKey) + selectedTipBytes, err := dbContext.Get(hsts.key) if err != nil { return nil, err } diff --git a/domain/consensus/datastructures/multisetstore/multiset_store.go b/domain/consensus/datastructures/multisetstore/multiset_store.go index 3cc57ff23..314157be7 100644 --- a/domain/consensus/datastructures/multisetstore/multiset_store.go +++ b/domain/consensus/datastructures/multisetstore/multiset_store.go @@ -7,19 +7,22 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var bucket = database.MakeBucket([]byte("multisets")) +var bucketName = []byte("multisets") // multisetStore represents a store of Multisets type multisetStore struct { - cache *lrucache.LRUCache + cache *lrucache.LRUCache + bucket model.DBBucket } // New instantiates a new MultisetStore -func New(cacheSize int, preallocate bool) model.MultisetStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.MultisetStore { return &multisetStore{ - cache: lrucache.New(cacheSize, preallocate), + cache: lrucache.New(cacheSize, preallocate), + bucket: database.MakeBucket(prefix.Serialize()).Bucket(bucketName), } } @@ -71,7 +74,7 @@ func (ms *multisetStore) Delete(stagingArea *model.StagingArea, blockHash *exter } func (ms *multisetStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey { - return bucket.Key(hash.ByteSlice()) + return ms.bucket.Key(hash.ByteSlice()) } func (ms *multisetStore) serializeMultiset(multiset model.Multiset) ([]byte, error) { diff --git a/domain/consensus/datastructures/pruningstore/imported_pruning_point.go b/domain/consensus/datastructures/pruningstore/imported_pruning_point.go index 5f7d19a6e..f07a59114 100644 --- a/domain/consensus/datastructures/pruningstore/imported_pruning_point.go +++ b/domain/consensus/datastructures/pruningstore/imported_pruning_point.go @@ -2,18 +2,17 @@ package pruningstore import ( "github.com/golang/protobuf/proto" - "github.com/kaspanet/kaspad/domain/consensus/database" "github.com/kaspanet/kaspad/domain/consensus/database/serialization" "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/pkg/errors" ) -var importedPruningPointUTXOsBucket = database.MakeBucket([]byte("imported-pruning-point-utxos")) -var importedPruningPointMultiset = database.MakeBucket(nil).Key([]byte("imported-pruning-point-multiset")) +var importedPruningPointUTXOsBucketName = []byte("imported-pruning-point-utxos") +var importedPruningPointMultisetKeyName = []byte("imported-pruning-point-multiset") func (ps *pruningStore) ClearImportedPruningPointUTXOs(dbContext model.DBWriter) error { - cursor, err := dbContext.Cursor(importedPruningPointUTXOsBucket) + cursor, err := dbContext.Cursor(ps.importedPruningPointUTXOsBucket) if err != nil { return err } @@ -54,7 +53,7 @@ func (ps *pruningStore) AppendImportedPruningPointUTXOs(dbTx model.DBTransaction } func (ps *pruningStore) ImportedPruningPointUTXOIterator(dbContext model.DBReader) (externalapi.ReadOnlyUTXOSetIterator, error) { - cursor, err := dbContext.Cursor(importedPruningPointUTXOsBucket) + cursor, err := dbContext.Cursor(ps.importedPruningPointUTXOsBucket) if err != nil { return nil, err } @@ -130,7 +129,7 @@ func (ps *pruningStore) importedPruningPointUTXOKey(outpoint *externalapi.Domain return nil, err } - return importedPruningPointUTXOsBucket.Key(serializedOutpoint), nil + return ps.importedPruningPointUTXOsBucket.Key(serializedOutpoint), nil } func serializeOutpoint(outpoint *externalapi.DomainOutpoint) ([]byte, error) { @@ -161,11 +160,11 @@ func deserializeUTXOEntry(entryBytes []byte) (externalapi.UTXOEntry, error) { } func (ps *pruningStore) ClearImportedPruningPointMultiset(dbContext model.DBWriter) error { - return dbContext.Delete(importedPruningPointMultiset) + return dbContext.Delete(ps.importedPruningPointMultisetKey) } func (ps *pruningStore) ImportedPruningPointMultiset(dbContext model.DBReader) (model.Multiset, error) { - multisetBytes, err := dbContext.Get(importedPruningPointMultiset) + multisetBytes, err := dbContext.Get(ps.importedPruningPointMultisetKey) if err != nil { return nil, err } @@ -177,7 +176,7 @@ func (ps *pruningStore) UpdateImportedPruningPointMultiset(dbTx model.DBTransact if err != nil { return err } - return dbTx.Put(importedPruningPointMultiset, multisetBytes) + return dbTx.Put(ps.importedPruningPointMultisetKey, multisetBytes) } func (ps *pruningStore) serializeMultiset(multiset model.Multiset) ([]byte, error) { @@ -196,7 +195,7 @@ func (ps *pruningStore) deserializeMultiset(multisetBytes []byte) (model.Multise func (ps *pruningStore) CommitImportedPruningPointUTXOSet(dbContext model.DBWriter) error { // Delete all the old UTXOs from the database - deleteCursor, err := dbContext.Cursor(pruningPointUTXOSetBucket) + deleteCursor, err := dbContext.Cursor(ps.pruningPointUTXOSetBucket) if err != nil { return err } @@ -213,7 +212,7 @@ func (ps *pruningStore) CommitImportedPruningPointUTXOSet(dbContext model.DBWrit } // Insert all the new UTXOs into the database - insertCursor, err := dbContext.Cursor(importedPruningPointUTXOsBucket) + insertCursor, err := dbContext.Cursor(ps.importedPruningPointUTXOsBucket) if err != nil { return err } @@ -223,7 +222,7 @@ func (ps *pruningStore) CommitImportedPruningPointUTXOSet(dbContext model.DBWrit if err != nil { return err } - pruningPointUTXOSetKey := pruningPointUTXOSetBucket.Key(importedPruningPointUTXOSetKey.Suffix()) + pruningPointUTXOSetKey := ps.pruningPointUTXOSetBucket.Key(importedPruningPointUTXOSetKey.Suffix()) serializedUTXOEntry, err := insertCursor.Value() if err != nil { diff --git a/domain/consensus/datastructures/pruningstore/pruning_staging_shard.go b/domain/consensus/datastructures/pruningstore/pruning_staging_shard.go index 251e8fa23..67da9f7c3 100644 --- a/domain/consensus/datastructures/pruningstore/pruning_staging_shard.go +++ b/domain/consensus/datastructures/pruningstore/pruning_staging_shard.go @@ -32,7 +32,7 @@ func (mss *pruningStagingShard) Commit(dbTx model.DBTransaction) error { if err != nil { return err } - err = dbTx.Put(pruningBlockHashKey, pruningPointBytes) + err = dbTx.Put(mss.store.pruningBlockHashKey, pruningPointBytes) if err != nil { return err } @@ -44,7 +44,7 @@ func (mss *pruningStagingShard) Commit(dbTx model.DBTransaction) error { if err != nil { return err } - err = dbTx.Put(previousPruningBlockHashKey, oldPruningPointBytes) + err = dbTx.Put(mss.store.previousPruningBlockHashKey, oldPruningPointBytes) if err != nil { return err } @@ -56,7 +56,7 @@ func (mss *pruningStagingShard) Commit(dbTx model.DBTransaction) error { if err != nil { return err } - err = dbTx.Put(candidatePruningPointHashKey, candidateBytes) + err = dbTx.Put(mss.store.candidatePruningPointHashKey, candidateBytes) if err != nil { return err } @@ -64,7 +64,7 @@ func (mss *pruningStagingShard) Commit(dbTx model.DBTransaction) error { } if mss.startUpdatingPruningPointUTXOSet { - err := dbTx.Put(updatingPruningPointUTXOSetKey, []byte{0}) + err := dbTx.Put(mss.store.updatingPruningPointUTXOSetKey, []byte{0}) if err != nil { return err } diff --git a/domain/consensus/datastructures/pruningstore/pruning_store.go b/domain/consensus/datastructures/pruningstore/pruning_store.go index 3d88fc173..b39c06118 100644 --- a/domain/consensus/datastructures/pruningstore/pruning_store.go +++ b/domain/consensus/datastructures/pruningstore/pruning_store.go @@ -6,24 +6,41 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/database/serialization" "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var pruningBlockHashKey = database.MakeBucket(nil).Key([]byte("pruning-block-hash")) -var previousPruningBlockHashKey = database.MakeBucket(nil).Key([]byte("previous-pruning-block-hash")) -var candidatePruningPointHashKey = database.MakeBucket(nil).Key([]byte("candidate-pruning-point-hash")) -var pruningPointUTXOSetBucket = database.MakeBucket([]byte("pruning-point-utxo-set")) -var updatingPruningPointUTXOSetKey = database.MakeBucket(nil).Key([]byte("updating-pruning-point-utxo-set")) +var pruningBlockHashKeyName = []byte("pruning-block-hash") +var previousPruningBlockHashKeyName = []byte("previous-pruning-block-hash") +var candidatePruningPointHashKeyName = []byte("candidate-pruning-point-hash") +var pruningPointUTXOSetBucketName = []byte("pruning-point-utxo-set") +var updatingPruningPointUTXOSetKeyName = []byte("updating-pruning-point-utxo-set") // pruningStore represents a store for the current pruning state type pruningStore struct { pruningPointCache *externalapi.DomainHash oldPruningPointCache *externalapi.DomainHash pruningPointCandidateCache *externalapi.DomainHash + + pruningBlockHashKey model.DBKey + previousPruningBlockHashKey model.DBKey + candidatePruningPointHashKey model.DBKey + pruningPointUTXOSetBucket model.DBBucket + updatingPruningPointUTXOSetKey model.DBKey + importedPruningPointUTXOsBucket model.DBBucket + importedPruningPointMultisetKey model.DBKey } // New instantiates a new PruningStore -func New() model.PruningStore { - return &pruningStore{} +func New(prefix *prefix.Prefix) model.PruningStore { + return &pruningStore{ + pruningBlockHashKey: database.MakeBucket(prefix.Serialize()).Key(pruningBlockHashKeyName), + previousPruningBlockHashKey: database.MakeBucket(prefix.Serialize()).Key(previousPruningBlockHashKeyName), + candidatePruningPointHashKey: database.MakeBucket(prefix.Serialize()).Key(candidatePruningPointHashKeyName), + pruningPointUTXOSetBucket: database.MakeBucket(prefix.Serialize()).Bucket(pruningPointUTXOSetBucketName), + importedPruningPointUTXOsBucket: database.MakeBucket(prefix.Serialize()).Bucket(importedPruningPointUTXOsBucketName), + updatingPruningPointUTXOSetKey: database.MakeBucket(prefix.Serialize()).Key(updatingPruningPointUTXOSetKeyName), + importedPruningPointMultisetKey: database.MakeBucket(prefix.Serialize()).Key(importedPruningPointMultisetKeyName), + } } func (ps *pruningStore) StagePruningPointCandidate(stagingArea *model.StagingArea, candidate *externalapi.DomainHash) { @@ -43,7 +60,7 @@ func (ps *pruningStore) PruningPointCandidate(dbContext model.DBReader, stagingA return ps.pruningPointCandidateCache, nil } - candidateBytes, err := dbContext.Get(pruningBlockHashKey) + candidateBytes, err := dbContext.Get(ps.pruningBlockHashKey) if err != nil { return nil, err } @@ -67,7 +84,7 @@ func (ps *pruningStore) HasPruningPointCandidate(dbContext model.DBReader, stagi return true, nil } - return dbContext.Has(candidatePruningPointHashKey) + return dbContext.Has(ps.candidatePruningPointHashKey) } // Stage stages the pruning state @@ -98,7 +115,7 @@ func (ps *pruningStore) UpdatePruningPointUTXOSet(dbContext model.DBWriter, diff if err != nil { return err } - err = dbContext.Delete(pruningPointUTXOSetBucket.Key(serializedOutpoint)) + err = dbContext.Delete(ps.pruningPointUTXOSetBucket.Key(serializedOutpoint)) if err != nil { return err } @@ -119,7 +136,7 @@ func (ps *pruningStore) UpdatePruningPointUTXOSet(dbContext model.DBWriter, diff if err != nil { return err } - err = dbContext.Put(pruningPointUTXOSetBucket.Key(serializedOutpoint), serializedUTXOEntry) + err = dbContext.Put(ps.pruningPointUTXOSetBucket.Key(serializedOutpoint), serializedUTXOEntry) if err != nil { return err } @@ -139,7 +156,7 @@ func (ps *pruningStore) PruningPoint(dbContext model.DBReader, stagingArea *mode return ps.pruningPointCache, nil } - pruningPointBytes, err := dbContext.Get(pruningBlockHashKey) + pruningPointBytes, err := dbContext.Get(ps.pruningBlockHashKey) if err != nil { return nil, err } @@ -163,7 +180,7 @@ func (ps *pruningStore) PreviousPruningPoint(dbContext model.DBReader, stagingAr return ps.oldPruningPointCache, nil } - oldPruningPointBytes, err := dbContext.Get(previousPruningBlockHashKey) + oldPruningPointBytes, err := dbContext.Get(ps.previousPruningBlockHashKey) if err != nil { return nil, err } @@ -201,11 +218,11 @@ func (ps *pruningStore) HasPruningPoint(dbContext model.DBReader, stagingArea *m return true, nil } - return dbContext.Has(pruningBlockHashKey) + return dbContext.Has(ps.pruningBlockHashKey) } func (ps *pruningStore) PruningPointUTXOIterator(dbContext model.DBReader) (externalapi.ReadOnlyUTXOSetIterator, error) { - cursor, err := dbContext.Cursor(pruningPointUTXOSetBucket) + cursor, err := dbContext.Cursor(ps.pruningPointUTXOSetBucket) if err != nil { return nil, err } @@ -215,7 +232,7 @@ func (ps *pruningStore) PruningPointUTXOIterator(dbContext model.DBReader) (exte func (ps *pruningStore) PruningPointUTXOs(dbContext model.DBReader, fromOutpoint *externalapi.DomainOutpoint, limit int) ([]*externalapi.OutpointAndUTXOEntryPair, error) { - cursor, err := dbContext.Cursor(pruningPointUTXOSetBucket) + cursor, err := dbContext.Cursor(ps.pruningPointUTXOSetBucket) if err != nil { return nil, err } @@ -226,7 +243,7 @@ func (ps *pruningStore) PruningPointUTXOs(dbContext model.DBReader, if err != nil { return nil, err } - seekKey := pruningPointUTXOSetBucket.Key(serializedFromOutpoint) + seekKey := ps.pruningPointUTXOSetBucket.Key(serializedFromOutpoint) err = cursor.Seek(seekKey) if err != nil { return nil, err @@ -257,9 +274,9 @@ func (ps *pruningStore) StageStartUpdatingPruningPointUTXOSet(stagingArea *model } func (ps *pruningStore) HadStartedUpdatingPruningPointUTXOSet(dbContext model.DBWriter) (bool, error) { - return dbContext.Has(updatingPruningPointUTXOSetKey) + return dbContext.Has(ps.updatingPruningPointUTXOSetKey) } func (ps *pruningStore) FinishUpdatingPruningPointUTXOSet(dbContext model.DBWriter) error { - return dbContext.Delete(updatingPruningPointUTXOSetKey) + return dbContext.Delete(ps.updatingPruningPointUTXOSetKey) } diff --git a/domain/consensus/datastructures/reachabilitydatastore/reachability_data_staging_shard.go b/domain/consensus/datastructures/reachabilitydatastore/reachability_data_staging_shard.go index b2e9401c0..04779b19e 100644 --- a/domain/consensus/datastructures/reachabilitydatastore/reachability_data_staging_shard.go +++ b/domain/consensus/datastructures/reachabilitydatastore/reachability_data_staging_shard.go @@ -27,7 +27,7 @@ func (rdss *reachabilityDataStagingShard) Commit(dbTx model.DBTransaction) error if err != nil { return err } - err = dbTx.Put(reachabilityReindexRootKey, reachabilityReindexRootBytes) + err = dbTx.Put(rdss.store.reachabilityReindexRootKey, reachabilityReindexRootBytes) if err != nil { return err } diff --git a/domain/consensus/datastructures/reachabilitydatastore/reachability_data_store.go b/domain/consensus/datastructures/reachabilitydatastore/reachability_data_store.go index 7a483d20c..0fecaa135 100644 --- a/domain/consensus/datastructures/reachabilitydatastore/reachability_data_store.go +++ b/domain/consensus/datastructures/reachabilitydatastore/reachability_data_store.go @@ -7,21 +7,27 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" ) -var reachabilityDataBucket = database.MakeBucket([]byte("reachability-data")) -var reachabilityReindexRootKey = database.MakeBucket(nil).Key([]byte("reachability-reindex-root")) +var reachabilityDataBucketName = []byte("reachability-data") +var reachabilityReindexRootKeyName = []byte("reachability-reindex-root") // reachabilityDataStore represents a store of ReachabilityData type reachabilityDataStore struct { reachabilityDataCache *lrucache.LRUCache reachabilityReindexRootCache *externalapi.DomainHash + + reachabilityDataBucket model.DBBucket + reachabilityReindexRootKey model.DBKey } // New instantiates a new ReachabilityDataStore -func New(cacheSize int, preallocate bool) model.ReachabilityDataStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.ReachabilityDataStore { return &reachabilityDataStore{ - reachabilityDataCache: lrucache.New(cacheSize, preallocate), + reachabilityDataCache: lrucache.New(cacheSize, preallocate), + reachabilityDataBucket: database.MakeBucket(prefix.Serialize()).Bucket(reachabilityDataBucketName), + reachabilityReindexRootKey: database.MakeBucket(prefix.Serialize()).Key(reachabilityReindexRootKeyName), } } @@ -94,7 +100,7 @@ func (rds *reachabilityDataStore) ReachabilityReindexRoot(dbContext model.DBRead return rds.reachabilityReindexRootCache, nil } - reachabilityReindexRootBytes, err := dbContext.Get(reachabilityReindexRootKey) + reachabilityReindexRootBytes, err := dbContext.Get(rds.reachabilityReindexRootKey) if err != nil { return nil, err } @@ -108,7 +114,7 @@ func (rds *reachabilityDataStore) ReachabilityReindexRoot(dbContext model.DBRead } func (rds *reachabilityDataStore) reachabilityDataBlockHashAsKey(hash *externalapi.DomainHash) model.DBKey { - return reachabilityDataBucket.Key(hash.ByteSlice()) + return rds.reachabilityDataBucket.Key(hash.ByteSlice()) } func (rds *reachabilityDataStore) serializeReachabilityData(reachabilityData model.ReachabilityData) ([]byte, error) { diff --git a/domain/consensus/datastructures/utxodiffstore/utxo_diff_store.go b/domain/consensus/datastructures/utxodiffstore/utxo_diff_store.go index b6ec53dfc..8f8f31f24 100644 --- a/domain/consensus/datastructures/utxodiffstore/utxo_diff_store.go +++ b/domain/consensus/datastructures/utxodiffstore/utxo_diff_store.go @@ -7,23 +7,28 @@ import ( "github.com/kaspanet/kaspad/domain/consensus/model" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/lrucache" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" "github.com/pkg/errors" ) -var utxoDiffBucket = database.MakeBucket([]byte("utxo-diffs")) -var utxoDiffChildBucket = database.MakeBucket([]byte("utxo-diff-children")) +var utxoDiffBucketName = []byte("utxo-diffs") +var utxoDiffChildBucketName = []byte("utxo-diff-children") // utxoDiffStore represents a store of UTXODiffs type utxoDiffStore struct { - utxoDiffCache *lrucache.LRUCache - utxoDiffChildCache *lrucache.LRUCache + utxoDiffCache *lrucache.LRUCache + utxoDiffChildCache *lrucache.LRUCache + utxoDiffBucket model.DBBucket + utxoDiffChildBucket model.DBBucket } // New instantiates a new UTXODiffStore -func New(cacheSize int, preallocate bool) model.UTXODiffStore { +func New(prefix *prefix.Prefix, cacheSize int, preallocate bool) model.UTXODiffStore { return &utxoDiffStore{ - utxoDiffCache: lrucache.New(cacheSize, preallocate), - utxoDiffChildCache: lrucache.New(cacheSize, preallocate), + utxoDiffCache: lrucache.New(cacheSize, preallocate), + utxoDiffChildCache: lrucache.New(cacheSize, preallocate), + utxoDiffBucket: database.MakeBucket(prefix.Serialize()).Bucket(utxoDiffBucketName), + utxoDiffChildBucket: database.MakeBucket(prefix.Serialize()).Bucket(utxoDiffChildBucketName), } } @@ -134,11 +139,11 @@ func (uds *utxoDiffStore) Delete(stagingArea *model.StagingArea, blockHash *exte } func (uds *utxoDiffStore) utxoDiffHashAsKey(hash *externalapi.DomainHash) model.DBKey { - return utxoDiffBucket.Key(hash.ByteSlice()) + return uds.utxoDiffBucket.Key(hash.ByteSlice()) } func (uds *utxoDiffStore) utxoDiffChildHashAsKey(hash *externalapi.DomainHash) model.DBKey { - return utxoDiffChildBucket.Key(hash.ByteSlice()) + return uds.utxoDiffChildBucket.Key(hash.ByteSlice()) } func (uds *utxoDiffStore) serializeUTXODiff(utxoDiff externalapi.UTXODiff) ([]byte, error) { diff --git a/domain/consensus/factory.go b/domain/consensus/factory.go index dbbc687bc..0ed9619af 100644 --- a/domain/consensus/factory.go +++ b/domain/consensus/factory.go @@ -2,6 +2,7 @@ package consensus import ( "github.com/kaspanet/kaspad/domain/consensus/model" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" "github.com/pkg/errors" "io/ioutil" "os" @@ -64,7 +65,7 @@ type Config struct { // Factory instantiates new Consensuses type Factory interface { - NewConsensus(config *Config, db infrastructuredatabase.Database) ( + NewConsensus(config *Config, db infrastructuredatabase.Database, dbPrefix *prefix.Prefix) ( externalapi.Consensus, error) NewTestConsensus(config *Config, testName string) ( tc testapi.TestConsensus, teardown func(keepDataDir bool), err error) @@ -96,7 +97,7 @@ func NewFactory() Factory { } // NewConsensus instantiates a new Consensus -func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Database) ( +func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Database, dbPrefix *prefix.Prefix) ( externalapi.Consensus, error) { dbManager := consensusdatabase.New(db) @@ -115,23 +116,23 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas pruningWindowSizePlusFinalityDepthForCache := int(config.PruningDepth() + config.FinalityDepth()) // Data Structures - acceptanceDataStore := acceptancedatastore.New(200, preallocateCaches) - blockStore, err := blockstore.New(dbManager, 200, preallocateCaches) + acceptanceDataStore := acceptancedatastore.New(dbPrefix, 200, preallocateCaches) + blockStore, err := blockstore.New(dbManager, dbPrefix, 200, preallocateCaches) if err != nil { return nil, err } - blockHeaderStore, err := blockheaderstore.New(dbManager, 10_000, preallocateCaches) + blockHeaderStore, err := blockheaderstore.New(dbManager, dbPrefix, 10_000, preallocateCaches) if err != nil { return nil, err } - blockRelationStore := blockrelationstore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches) + blockRelationStore := blockrelationstore.New(dbPrefix, pruningWindowSizePlusFinalityDepthForCache, preallocateCaches) - blockStatusStore := blockstatusstore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches) - multisetStore := multisetstore.New(200, preallocateCaches) - pruningStore := pruningstore.New() - reachabilityDataStore := reachabilitydatastore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches) - utxoDiffStore := utxodiffstore.New(200, preallocateCaches) - consensusStateStore := consensusstatestore.New(10_000, preallocateCaches) + blockStatusStore := blockstatusstore.New(dbPrefix, pruningWindowSizePlusFinalityDepthForCache, preallocateCaches) + multisetStore := multisetstore.New(dbPrefix, 200, preallocateCaches) + pruningStore := pruningstore.New(dbPrefix) + reachabilityDataStore := reachabilitydatastore.New(dbPrefix, pruningWindowSizePlusFinalityDepthForCache, preallocateCaches) + utxoDiffStore := utxodiffstore.New(dbPrefix, 200, preallocateCaches) + consensusStateStore := consensusstatestore.New(dbPrefix, 10_000, preallocateCaches) // Some tests artificially decrease the pruningWindowSize, thus making the GhostDagStore cache too small for a // a single DifficultyAdjustmentWindow. To alleviate this problem we make sure that the cache size is at least @@ -140,12 +141,12 @@ func (f *factory) NewConsensus(config *Config, db infrastructuredatabase.Databas if ghostdagDataCacheSize < config.DifficultyAdjustmentWindowSize { ghostdagDataCacheSize = config.DifficultyAdjustmentWindowSize } - ghostdagDataStore := ghostdagdatastore.New(ghostdagDataCacheSize, preallocateCaches) + ghostdagDataStore := ghostdagdatastore.New(dbPrefix, ghostdagDataCacheSize, preallocateCaches) - headersSelectedTipStore := headersselectedtipstore.New() - finalityStore := finalitystore.New(200, preallocateCaches) - headersSelectedChainStore := headersselectedchainstore.New(pruningWindowSizeForCaches, preallocateCaches) - daaBlocksStore := daablocksstore.New(pruningWindowSizeForCaches, int(config.FinalityDepth()), preallocateCaches) + headersSelectedTipStore := headersselectedtipstore.New(dbPrefix) + finalityStore := finalitystore.New(dbPrefix, 200, preallocateCaches) + headersSelectedChainStore := headersselectedchainstore.New(dbPrefix, pruningWindowSizeForCaches, preallocateCaches) + daaBlocksStore := daablocksstore.New(dbPrefix, pruningWindowSizeForCaches, int(config.FinalityDepth()), preallocateCaches) // Processes reachabilityManager := reachabilitymanager.New( @@ -469,7 +470,9 @@ func (f *factory) NewTestConsensus(config *Config, testName string) ( if err != nil { return nil, nil, err } - consensusAsInterface, err := f.NewConsensus(config, db) + + testConsensusDBPrefix := &prefix.Prefix{} + consensusAsInterface, err := f.NewConsensus(config, db, testConsensusDBPrefix) if err != nil { return nil, nil, err } diff --git a/domain/consensus/factory_test.go b/domain/consensus/factory_test.go index 9317717bb..9f7e2aa47 100644 --- a/domain/consensus/factory_test.go +++ b/domain/consensus/factory_test.go @@ -1,6 +1,7 @@ package consensus import ( + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" "io/ioutil" "testing" @@ -23,7 +24,7 @@ func TestNewConsensus(t *testing.T) { t.Fatalf("error in NewLevelDB: %s", err) } - _, err = f.NewConsensus(config, db) + _, err = f.NewConsensus(config, db, &prefix.Prefix{}) if err != nil { t.Fatalf("error in NewConsensus: %+v", err) } diff --git a/domain/domain.go b/domain/domain.go index e1bfb0541..87d7fd3e3 100644 --- a/domain/domain.go +++ b/domain/domain.go @@ -4,32 +4,180 @@ import ( "github.com/kaspanet/kaspad/domain/consensus" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/miningmanager" + "github.com/kaspanet/kaspad/domain/prefixmanager" + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" infrastructuredatabase "github.com/kaspanet/kaspad/infrastructure/db/database" + "github.com/pkg/errors" + "sync" + "sync/atomic" + "unsafe" ) // Domain provides a reference to the domain's external aps type Domain interface { MiningManager() miningmanager.MiningManager Consensus() externalapi.Consensus + StagingConsensus() externalapi.Consensus + InitStagingConsensus() error + CommitStagingConsensus() error + DeleteStagingConsensus() error } type domain struct { - miningManager miningmanager.MiningManager - consensus externalapi.Consensus + miningManager miningmanager.MiningManager + consensus *externalapi.Consensus + stagingConsensus *externalapi.Consensus + stagingConsensusLock sync.RWMutex + consensusConfig *consensus.Config + db infrastructuredatabase.Database } -func (d domain) Consensus() externalapi.Consensus { - return d.consensus +func (d *domain) Consensus() externalapi.Consensus { + return *d.consensus } -func (d domain) MiningManager() miningmanager.MiningManager { +func (d *domain) StagingConsensus() externalapi.Consensus { + d.stagingConsensusLock.RLock() + defer d.stagingConsensusLock.RUnlock() + return *d.stagingConsensus +} + +func (d *domain) MiningManager() miningmanager.MiningManager { return d.miningManager } +func (d *domain) InitStagingConsensus() error { + d.stagingConsensusLock.Lock() + defer d.stagingConsensusLock.Unlock() + + _, hasInactivePrefix, err := prefixmanager.InactivePrefix(d.db) + if err != nil { + return err + } + + if hasInactivePrefix { + return errors.Errorf("cannot create staging consensus when a staging consensus already exists") + } + + activePrefix, exists, err := prefixmanager.ActivePrefix(d.db) + if err != nil { + return err + } + + if !exists { + return errors.Errorf("cannot create a staging consensus when there's " + + "no active consensus") + } + + inactivePrefix := activePrefix.Flip() + err = prefixmanager.SetPrefixAsInactive(d.db, inactivePrefix) + if err != nil { + return err + } + + consensusFactory := consensus.NewFactory() + consensusInstance, err := consensusFactory.NewConsensus(d.consensusConfig, d.db, inactivePrefix) + if err != nil { + return err + } + + d.stagingConsensus = &consensusInstance + return nil +} + +func (d *domain) CommitStagingConsensus() error { + d.stagingConsensusLock.Lock() + defer d.stagingConsensusLock.Unlock() + + dbTx, err := d.db.Begin() + if err != nil { + return err + } + defer dbTx.RollbackUnlessClosed() + + inactivePrefix, hasInactivePrefix, err := prefixmanager.InactivePrefix(d.db) + if err != nil { + return err + } + + if !hasInactivePrefix { + return errors.Errorf("there's no inactive prefix to commit") + } + + activePrefix, exists, err := prefixmanager.ActivePrefix(dbTx) + if err != nil { + return err + } + + if !exists { + return errors.Errorf("cannot commit a staging consensus when there's " + + "no active consensus") + } + + err = prefixmanager.SetPrefixAsActive(dbTx, inactivePrefix) + if err != nil { + return err + } + + err = prefixmanager.SetPrefixAsInactive(dbTx, activePrefix) + if err != nil { + return err + } + + err = dbTx.Commit() + if err != nil { + return err + } + + // We delete anything associated with the old prefix outside + // of the transaction in order to save memory. + err = prefixmanager.DeleteInactivePrefix(d.db) + if err != nil { + return err + } + + tempConsensusPointer := unsafe.Pointer(d.stagingConsensus) + consensusPointer := (*unsafe.Pointer)(unsafe.Pointer(&d.consensus)) + atomic.StorePointer(consensusPointer, tempConsensusPointer) + d.stagingConsensus = nil + return nil +} + +func (d *domain) DeleteStagingConsensus() error { + d.stagingConsensusLock.Lock() + defer d.stagingConsensusLock.Unlock() + + err := prefixmanager.DeleteInactivePrefix(d.db) + if err != nil { + return err + } + + d.stagingConsensus = nil + return nil +} + // New instantiates a new instance of a Domain object func New(consensusConfig *consensus.Config, db infrastructuredatabase.Database) (Domain, error) { + err := prefixmanager.DeleteInactivePrefix(db) + if err != nil { + return nil, err + } + + activePrefix, exists, err := prefixmanager.ActivePrefix(db) + if err != nil { + return nil, err + } + + if !exists { + activePrefix = &prefix.Prefix{} + err = prefixmanager.SetPrefixAsActive(db, activePrefix) + if err != nil { + return nil, err + } + } + consensusFactory := consensus.NewFactory() - consensusInstance, err := consensusFactory.NewConsensus(consensusConfig, db) + consensusInstance, err := consensusFactory.NewConsensus(consensusConfig, db, activePrefix) if err != nil { return nil, err } @@ -38,7 +186,9 @@ func New(consensusConfig *consensus.Config, db infrastructuredatabase.Database) miningManager := miningManagerFactory.NewMiningManager(consensusInstance, &consensusConfig.Params) return &domain{ - consensus: consensusInstance, - miningManager: miningManager, + consensus: &consensusInstance, + miningManager: miningManager, + consensusConfig: consensusConfig, + db: db, }, nil } diff --git a/domain/domain_test.go b/domain/domain_test.go new file mode 100644 index 000000000..9e0982b22 --- /dev/null +++ b/domain/domain_test.go @@ -0,0 +1,132 @@ +package domain_test + +import ( + "fmt" + "github.com/kaspanet/kaspad/domain" + "github.com/kaspanet/kaspad/domain/consensus" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing" + "github.com/kaspanet/kaspad/domain/consensus/utils/testutils" + "github.com/kaspanet/kaspad/infrastructure/db/database/ldb" + "io/ioutil" + "os" + "strings" + "testing" +) + +func TestCreateStagingConsensus(t *testing.T) { + testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) { + dataDir, err := ioutil.TempDir("", fmt.Sprintf("TestCreateStagingConsensus-%s", consensusConfig.Name)) + if err != nil { + t.Fatalf("ioutil.TempDir: %+v", err) + } + defer os.RemoveAll(dataDir) + + db, err := ldb.NewLevelDB(dataDir, 8) + if err != nil { + t.Fatalf("NewLevelDB: %+v", err) + } + + domainInstance, err := domain.New(consensusConfig, db) + if err != nil { + t.Fatalf("New: %+v", err) + } + + err = domainInstance.InitStagingConsensus() + if err != nil { + t.Fatalf("InitStagingConsensus: %+v", err) + } + + err = domainInstance.InitStagingConsensus() + if !strings.Contains(err.Error(), "cannot create staging consensus when a staging consensus already exists") { + t.Fatalf("unexpected error: %+v", err) + } + + coinbaseData := &externalapi.DomainCoinbaseData{ + ScriptPublicKey: &externalapi.ScriptPublicKey{}, + ExtraData: []byte{}, + } + block, err := domainInstance.StagingConsensus().BuildBlock(coinbaseData, nil) + if err != nil { + t.Fatalf("BuildBlock: %+v", err) + } + + _, err = domainInstance.StagingConsensus().ValidateAndInsertBlock(block) + if err != nil { + t.Fatalf("ValidateAndInsertBlock: %+v", err) + } + + blockHash := consensushashing.BlockHash(block) + blockInfo, err := domainInstance.StagingConsensus().GetBlockInfo(blockHash) + if err != nil { + t.Fatalf("GetBlockInfo: %+v", err) + } + + if !blockInfo.Exists { + t.Fatalf("block not found on staging consensus") + } + + blockInfo, err = domainInstance.Consensus().GetBlockInfo(blockHash) + if err != nil { + t.Fatalf("GetBlockInfo: %+v", err) + } + + if blockInfo.Exists { + t.Fatalf("a block from staging consensus was found on consensus") + } + + err = domainInstance.CommitStagingConsensus() + if err != nil { + t.Fatalf("CommitStagingConsensus: %+v", err) + } + + blockInfo, err = domainInstance.Consensus().GetBlockInfo(blockHash) + if err != nil { + t.Fatalf("GetBlockInfo: %+v", err) + } + + if !blockInfo.Exists { + t.Fatalf("a block from staging consensus was not found on consensus after commit") + } + + // Now we create a new staging consensus and check that it's deleted once we init a new domain. We also + // validate that the main consensus persisted the data from the committed temp consensus. + err = domainInstance.InitStagingConsensus() + if err != nil { + t.Fatalf("InitStagingConsensus: %+v", err) + } + + _, err = domainInstance.StagingConsensus().ValidateAndInsertBlock(block) + if err != nil { + t.Fatalf("ValidateAndInsertBlock: %+v", err) + } + + domainInstance2, err := domain.New(consensusConfig, db) + if err != nil { + t.Fatalf("New: %+v", err) + } + + blockInfo, err = domainInstance2.Consensus().GetBlockInfo(blockHash) + if err != nil { + t.Fatalf("GetBlockInfo: %+v", err) + } + + if !blockInfo.Exists { + t.Fatalf("a block from committed staging consensus was not persisted to the active consensus") + } + + err = domainInstance2.InitStagingConsensus() + if err != nil { + t.Fatalf("InitStagingConsensus: %+v", err) + } + + blockInfo, err = domainInstance2.StagingConsensus().GetBlockInfo(blockHash) + if err != nil { + t.Fatalf("GetBlockInfo: %+v", err) + } + + if blockInfo.Exists { + t.Fatalf("block from previous temp consensus shouldn't be found on a fresh temp consensus") + } + }) +} diff --git a/domain/prefixmanager/log.go b/domain/prefixmanager/log.go new file mode 100644 index 000000000..6a3abff43 --- /dev/null +++ b/domain/prefixmanager/log.go @@ -0,0 +1,9 @@ +package prefixmanager + +import ( + "github.com/kaspanet/kaspad/infrastructure/logger" + "github.com/kaspanet/kaspad/util/panics" +) + +var log = logger.RegisterSubSystem("PRFX") +var spawn = panics.GoroutineWrapperFunc(log) diff --git a/domain/prefixmanager/prefix.go b/domain/prefixmanager/prefix.go new file mode 100644 index 000000000..b1b389c44 --- /dev/null +++ b/domain/prefixmanager/prefix.go @@ -0,0 +1,106 @@ +package prefixmanager + +import ( + "github.com/kaspanet/kaspad/domain/prefixmanager/prefix" + "github.com/kaspanet/kaspad/infrastructure/db/database" +) + +var activePrefixKey = database.MakeBucket(nil).Key([]byte("active-prefix")) +var inactivePrefixKey = database.MakeBucket(nil).Key([]byte("inactive-prefix")) + +// ActivePrefix returns the current active database prefix, and whether it exists +func ActivePrefix(dataAccessor database.DataAccessor) (*prefix.Prefix, bool, error) { + prefixBytes, err := dataAccessor.Get(activePrefixKey) + if database.IsNotFoundError(err) { + return nil, false, nil + } + + if err != nil { + return nil, false, err + } + + prefix, err := prefix.Deserialize(prefixBytes) + if err != nil { + return nil, false, err + } + + return prefix, true, nil +} + +// InactivePrefix returns the current inactive database prefix, and whether it exists +func InactivePrefix(dataAccessor database.DataAccessor) (*prefix.Prefix, bool, error) { + prefixBytes, err := dataAccessor.Get(inactivePrefixKey) + if database.IsNotFoundError(err) { + return nil, false, nil + } + + if err != nil { + return nil, false, err + } + + prefix, err := prefix.Deserialize(prefixBytes) + if err != nil { + return nil, false, err + } + + return prefix, true, nil +} + +// DeleteInactivePrefix deletes all data associated with the inactive database prefix, including itself. +func DeleteInactivePrefix(dataAccessor database.DataAccessor) error { + prefixBytes, err := dataAccessor.Get(inactivePrefixKey) + if database.IsNotFoundError(err) { + return nil + } + + if err != nil { + return err + } + + prefix, err := prefix.Deserialize(prefixBytes) + if err != nil { + return err + } + + err = deletePrefix(dataAccessor, prefix) + if err != nil { + return err + } + + return dataAccessor.Delete(inactivePrefixKey) +} + +func deletePrefix(dataAccessor database.DataAccessor, prefix *prefix.Prefix) error { + log.Infof("Deleting database prefix %x", prefix) + prefixBucket := database.MakeBucket(prefix.Serialize()) + cursor, err := dataAccessor.Cursor(prefixBucket) + if err != nil { + return err + } + + defer cursor.Close() + + for ok := cursor.First(); ok; ok = cursor.Next() { + key, err := cursor.Key() + if err != nil { + return err + } + + err = dataAccessor.Delete(key) + if err != nil { + return err + } + } + + return nil +} + +// SetPrefixAsActive sets the given prefix as the active prefix +func SetPrefixAsActive(dataAccessor database.DataAccessor, prefix *prefix.Prefix) error { + return dataAccessor.Put(activePrefixKey, prefix.Serialize()) +} + +// SetPrefixAsInactive sets the given prefix as the inactive prefix +func SetPrefixAsInactive(dataAccessor database.DataAccessor, prefix *prefix.Prefix) error { + return dataAccessor.Put(inactivePrefixKey, prefix.Serialize()) +} diff --git a/domain/prefixmanager/prefix/prefix.go b/domain/prefixmanager/prefix/prefix.go new file mode 100644 index 000000000..348ad1a9c --- /dev/null +++ b/domain/prefixmanager/prefix/prefix.go @@ -0,0 +1,46 @@ +package prefix + +import "github.com/pkg/errors" + +const ( + prefixZero byte = 0 + prefixOne byte = 1 +) + +// Prefix is a database prefix that is used to manage more than one database at once. +type Prefix struct { + value byte +} + +// Serialize serializes the prefix into a byte slice +func (p *Prefix) Serialize() []byte { + return []byte{p.value} +} + +// Equal returns whether p equals to other +func (p *Prefix) Equal(other *Prefix) bool { + return p.value == other.value +} + +// Flip returns the opposite of the current prefix +func (p *Prefix) Flip() *Prefix { + value := prefixZero + if p.value == prefixZero { + value = prefixOne + } + + return &Prefix{value: value} +} + +// Deserialize deserializes a prefix from a byte slice +func Deserialize(prefixBytes []byte) (*Prefix, error) { + if len(prefixBytes) > 1 { + return nil, errors.Errorf("invalid length %d for prefix", len(prefixBytes)) + } + + if prefixBytes[0] != prefixZero && prefixBytes[0] != prefixOne { + return nil, errors.Errorf("invalid prefix %x", prefixBytes) + } + + return &Prefix{value: prefixBytes[0]}, nil +}