mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-22 23:07:04 +00:00

* Add mutable and immutable header interfaces * Fix ShouldMine() * Remove false comment * Fix Equal signature * Fix Equal implementation
215 lines
5.8 KiB
Go
215 lines
5.8 KiB
Go
package blockheaderstore
|
|
|
|
import (
|
|
"github.com/golang/protobuf/proto"
|
|
"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/consensus/utils/dbkeys"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
|
|
)
|
|
|
|
var bucket = dbkeys.MakeBucket([]byte("block-headers"))
|
|
var countKey = dbkeys.MakeBucket().Key([]byte("block-headers-count"))
|
|
|
|
// blockHeaderStore represents a store of blocks
|
|
type blockHeaderStore struct {
|
|
staging map[externalapi.DomainHash]externalapi.BlockHeader
|
|
toDelete map[externalapi.DomainHash]struct{}
|
|
cache *lrucache.LRUCache
|
|
count uint64
|
|
}
|
|
|
|
// New instantiates a new BlockHeaderStore
|
|
func New(dbContext model.DBReader, cacheSize int) (model.BlockHeaderStore, error) {
|
|
blockHeaderStore := &blockHeaderStore{
|
|
staging: make(map[externalapi.DomainHash]externalapi.BlockHeader),
|
|
toDelete: make(map[externalapi.DomainHash]struct{}),
|
|
cache: lrucache.New(cacheSize),
|
|
}
|
|
|
|
err := blockHeaderStore.initializeCount(dbContext)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return blockHeaderStore, nil
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) initializeCount(dbContext model.DBReader) error {
|
|
count := uint64(0)
|
|
hasCountBytes, err := dbContext.Has(countKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if hasCountBytes {
|
|
countBytes, err := dbContext.Get(countKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
count, err = bhs.deserializeHeaderCount(countBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
bhs.count = count
|
|
return nil
|
|
}
|
|
|
|
// Stage stages the given block header for the given blockHash
|
|
func (bhs *blockHeaderStore) Stage(blockHash *externalapi.DomainHash, blockHeader externalapi.BlockHeader) {
|
|
bhs.staging[*blockHash] = blockHeader
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) IsStaged() bool {
|
|
return len(bhs.staging) != 0 || len(bhs.toDelete) != 0
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) Discard() {
|
|
bhs.staging = make(map[externalapi.DomainHash]externalapi.BlockHeader)
|
|
bhs.toDelete = make(map[externalapi.DomainHash]struct{})
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) Commit(dbTx model.DBTransaction) error {
|
|
for hash, header := range bhs.staging {
|
|
headerBytes, err := bhs.serializeHeader(header)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = dbTx.Put(bhs.hashAsKey(&hash), headerBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bhs.cache.Add(&hash, header)
|
|
}
|
|
|
|
for hash := range bhs.toDelete {
|
|
err := dbTx.Delete(bhs.hashAsKey(&hash))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bhs.cache.Remove(&hash)
|
|
}
|
|
|
|
err := bhs.commitCount(dbTx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bhs.Discard()
|
|
return nil
|
|
}
|
|
|
|
// BlockHeader gets the block header associated with the given blockHash
|
|
func (bhs *blockHeaderStore) BlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (externalapi.BlockHeader, error) {
|
|
if header, ok := bhs.staging[*blockHash]; ok {
|
|
return header, nil
|
|
}
|
|
|
|
if header, ok := bhs.cache.Get(blockHash); ok {
|
|
return header.(externalapi.BlockHeader), nil
|
|
}
|
|
|
|
headerBytes, err := dbContext.Get(bhs.hashAsKey(blockHash))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
header, err := bhs.deserializeHeader(headerBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bhs.cache.Add(blockHash, header)
|
|
return header, nil
|
|
}
|
|
|
|
// HasBlock returns whether a block header with a given hash exists in the store.
|
|
func (bhs *blockHeaderStore) HasBlockHeader(dbContext model.DBReader, blockHash *externalapi.DomainHash) (bool, error) {
|
|
if _, ok := bhs.staging[*blockHash]; ok {
|
|
return true, nil
|
|
}
|
|
|
|
if bhs.cache.Has(blockHash) {
|
|
return true, nil
|
|
}
|
|
|
|
exists, err := dbContext.Has(bhs.hashAsKey(blockHash))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return exists, nil
|
|
}
|
|
|
|
// BlockHeaders gets the block headers associated with the given blockHashes
|
|
func (bhs *blockHeaderStore) BlockHeaders(dbContext model.DBReader, blockHashes []*externalapi.DomainHash) ([]externalapi.BlockHeader, error) {
|
|
headers := make([]externalapi.BlockHeader, len(blockHashes))
|
|
for i, hash := range blockHashes {
|
|
var err error
|
|
headers[i], err = bhs.BlockHeader(dbContext, hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return headers, nil
|
|
}
|
|
|
|
// Delete deletes the block associated with the given blockHash
|
|
func (bhs *blockHeaderStore) Delete(blockHash *externalapi.DomainHash) {
|
|
if _, ok := bhs.staging[*blockHash]; ok {
|
|
delete(bhs.staging, *blockHash)
|
|
return
|
|
}
|
|
bhs.toDelete[*blockHash] = struct{}{}
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) hashAsKey(hash *externalapi.DomainHash) model.DBKey {
|
|
return bucket.Key(hash.ByteSlice())
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) serializeHeader(header externalapi.BlockHeader) ([]byte, error) {
|
|
dbBlockHeader := serialization.DomainBlockHeaderToDbBlockHeader(header)
|
|
return proto.Marshal(dbBlockHeader)
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) deserializeHeader(headerBytes []byte) (externalapi.BlockHeader, error) {
|
|
dbBlockHeader := &serialization.DbBlockHeader{}
|
|
err := proto.Unmarshal(headerBytes, dbBlockHeader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return serialization.DbBlockHeaderToDomainBlockHeader(dbBlockHeader)
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) Count() uint64 {
|
|
return bhs.count + uint64(len(bhs.staging)) - uint64(len(bhs.toDelete))
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) deserializeHeaderCount(countBytes []byte) (uint64, error) {
|
|
dbBlockHeaderCount := &serialization.DbBlockHeaderCount{}
|
|
err := proto.Unmarshal(countBytes, dbBlockHeaderCount)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return dbBlockHeaderCount.Count, nil
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) commitCount(dbTx model.DBTransaction) error {
|
|
count := bhs.Count()
|
|
countBytes, err := bhs.serializeHeaderCount(count)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = dbTx.Put(countKey, countBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
bhs.count = count
|
|
return nil
|
|
}
|
|
|
|
func (bhs *blockHeaderStore) serializeHeaderCount(count uint64) ([]byte, error) {
|
|
dbBlockHeaderCount := &serialization.DbBlockHeaderCount{Count: count}
|
|
return proto.Marshal(dbBlockHeaderCount)
|
|
}
|