2022-06-21 23:47:07 +02:00

226 lines
5.8 KiB
Go

package txindex
import (
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/infrastructure/db/database"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/pkg/errors"
)
var txAcceptedIndexBucket = database.MakeBucket([]byte("tx-index"))
var virtualParentsKey = database.MakeBucket([]byte("")).Key([]byte("tx-index-virtual-parent"))
var pruningPointKey = database.MakeBucket([]byte("")).Key([]byte("tx-index-prunning-point"))
type txIndexStore struct {
database database.Database
toAdd map[externalapi.DomainTransactionID]*externalapi.DomainHash
virtualParents []*externalapi.DomainHash
pruningPoint *externalapi.DomainHash
}
func newTXIndexStore(database database.Database) *txIndexStore {
return &txIndexStore{
database: database,
toAdd: make(map[externalapi.DomainTransactionID]*externalapi.DomainHash),
virtualParents: nil,
pruningPoint: nil,
}
}
func (tis *txIndexStore) deleteAll() error {
err := tis.database.Delete(virtualParentsKey)
if err != nil {
return err
}
err = tis.database.Delete(pruningPointKey)
if err != nil {
return err
}
cursor, err := tis.database.Cursor(txAcceptedIndexBucket)
if err != nil {
return err
}
defer cursor.Close()
for cursor.Next() {
key, err := cursor.Key()
if err != nil {
return err
}
err = tis.database.Delete(key)
if err != nil {
return err
}
}
return nil
}
func (tis *txIndexStore) add(txID externalapi.DomainTransactionID, blockHash *externalapi.DomainHash) {
log.Tracef("Adding %s Txs from blockHash %s", txID.String(), blockHash.String())
tis.toAdd[txID] = blockHash
}
func (tis *txIndexStore) discard() {
tis.toAdd = make(map[externalapi.DomainTransactionID]*externalapi.DomainHash)
tis.virtualParents = nil
tis.pruningPoint = nil
}
func (tis *txIndexStore) commitAndReturnRemoved() (
removed map[externalapi.DomainTransactionID]*externalapi.DomainHash,
err error) {
onEnd := logger.LogAndMeasureExecutionTime(log, "txIndexStore.commit")
defer onEnd()
dbTransaction, err := tis.database.Begin()
if err != nil {
return nil, err
}
defer dbTransaction.RollbackUnlessClosed()
removed = make(map[externalapi.DomainTransactionID]*externalapi.DomainHash)
for txID, blockHash := range tis.toAdd {
key := tis.convertTxIDToKey(txAcceptedIndexBucket, txID)
found, err := dbTransaction.Has(key)
if err != nil {
return nil, err
}
if found {
serializedRemovedBlockHash, err := dbTransaction.Get(key)
if err != nil {
return nil, err
}
removedBlockHash, err := externalapi.NewDomainHashFromByteSlice(serializedRemovedBlockHash)
if err != nil {
return nil, err
}
removed[txID] = removedBlockHash
}
dbTransaction.Put(key, blockHash.ByteSlice())
if err != nil {
return nil, err
}
}
err = dbTransaction.Put(virtualParentsKey, serializeHashes(tis.virtualParents))
if err != nil {
return nil, err
}
err = dbTransaction.Put(pruningPointKey, tis.pruningPoint.ByteSlice())
if err != nil {
return nil, err
}
err = dbTransaction.Commit()
if err != nil {
return nil, err
}
tis.discard()
return removed, nil
}
func (tis *txIndexStore) updateAndCommitVirtualParentsWithoutTransaction(virtualParents []*externalapi.DomainHash) error {
serializeParentHashes := serializeHashes(virtualParents)
return tis.database.Put(virtualParentsKey, serializeParentHashes)
}
func (tis *txIndexStore) updateAndCommitPruningPointWithoutTransaction(pruningPoint *externalapi.DomainHash) error {
return tis.database.Put(pruningPointKey, pruningPoint.ByteSlice())
}
func (tis *txIndexStore) updateVirtualParents(virtualParents []*externalapi.DomainHash) {
tis.virtualParents = virtualParents
}
func (tis *txIndexStore) CommitWithoutTransaction() error {
for txID, blockHash := range tis.toAdd {
key := tis.convertTxIDToKey(txAcceptedIndexBucket, txID)
err := tis.database.Put(key, blockHash.ByteSlice())
if err != nil {
return err
}
}
return nil
}
func (tis *txIndexStore) getVirtualParents() ([]*externalapi.DomainHash, error) {
if tis.isAnythingStaged() {
return nil, errors.Errorf("cannot get the virtual parent while staging isn't empty")
}
serializedVirtualParentHash, err := tis.database.Get(virtualParentsKey)
if err != nil {
return nil, err
}
return deserializeHashes(serializedVirtualParentHash)
}
func (tis *txIndexStore) getPruningPoint() (*externalapi.DomainHash, error) {
if tis.isAnythingStaged() {
return nil, errors.Errorf("cannot get the Pruning point while staging isn't empty")
}
serializedPruningPointHash, err := tis.database.Get(pruningPointKey)
if err != nil {
return nil, err
}
return externalapi.NewDomainHashFromByteSlice(serializedPruningPointHash)
}
func (tis *txIndexStore) convertTxIDToKey(bucket *database.Bucket, txID externalapi.DomainTransactionID) *database.Key {
return bucket.Key(txID.ByteSlice())
}
func (tis *txIndexStore) stagedData() (
toAdd map[externalapi.DomainTransactionID]*externalapi.DomainHash,
virtualParents []*externalapi.DomainHash,
pruningPoint *externalapi.DomainHash,) {
toAddClone := make(map[externalapi.DomainTransactionID]*externalapi.DomainHash)
for txID, blockHash := range tis.toAdd {
toAddClone[txID] = blockHash
}
return toAddClone, tis.virtualParents, tis.pruningPoint
}
func (tis *txIndexStore) isAnythingStaged() bool {
return len(tis.toAdd) > 0
}
func (tis *txIndexStore) getTxAcceptingBlockHash(txID *externalapi.DomainTransactionID) (*externalapi.DomainHash, error) {
if tis.isAnythingStaged() {
return nil, errors.Errorf("cannot get TX accepting Block hash while staging isn't empty")
}
key := tis.convertTxIDToKey(txAcceptedIndexBucket, *txID)
serializedAcceptingBlockHash, err := tis.database.Get(key)
if err != nil {
return nil, err
}
acceptingBlockHash, err := externalapi.NewDomainHashFromByteSlice(serializedAcceptingBlockHash)
if err != nil {
return nil, err
}
return acceptingBlockHash, nil
}