158 lines
4.5 KiB
Go

package consensusstatemanager
import (
"github.com/golang/protobuf/proto"
"github.com/kaspanet/kaspad/domain/consensus/model"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/pkg/errors"
)
var virtualHeaderHash = &externalapi.DomainHash{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
}
func (csm *consensusStateManager) SetPruningPointUTXOSet(serializedUTXOSet []byte) error {
onEnd := logger.LogAndMeasureExecutionTime(log, "SetPruningPointUTXOSet")
defer onEnd()
err := csm.setPruningPointUTXOSet(serializedUTXOSet)
if err != nil {
csm.discardSetPruningPointUTXOSetChanges()
return err
}
return csm.commitSetPruningPointUTXOSetAll()
}
func (csm *consensusStateManager) setPruningPointUTXOSet(serializedUTXOSet []byte) error {
headerTipsPruningPoint, err := csm.HeaderTipsPruningPoint()
if err != nil {
return err
}
protoUTXOSet := &utxoserialization.ProtoUTXOSet{}
err = proto.Unmarshal(serializedUTXOSet, protoUTXOSet)
if err != nil {
return err
}
utxoSetMultiSet, err := calcMultisetFromProtoUTXOSet(protoUTXOSet)
if err != nil {
return err
}
headerTipsPruningPointHeader, err := csm.blockHeaderStore.BlockHeader(csm.databaseContext, headerTipsPruningPoint)
if err != nil {
return err
}
if headerTipsPruningPointHeader.UTXOCommitment != *utxoSetMultiSet.Hash() {
return errors.Wrapf(ruleerrors.ErrBadPruningPointUTXOSet, "the expected multiset hash of the pruning "+
"point UTXO set is %s but got %s", headerTipsPruningPointHeader.UTXOCommitment, *utxoSetMultiSet.Hash())
}
csm.consensusStateStore.StageTips(headerTipsPruningPointHeader.ParentHashes)
err = csm.dagTopologyManager.SetParents(model.VirtualBlockHash, headerTipsPruningPointHeader.ParentHashes)
if err != nil {
return err
}
err = csm.consensusStateStore.StageVirtualUTXOSet(protoUTXOSetToReadOnlyUTXOSetIterator(protoUTXOSet))
if err != nil {
return err
}
err = csm.ghostdagManager.GHOSTDAG(model.VirtualBlockHash)
if err != nil {
return err
}
err = csm.updateVirtualDiffParents(headerTipsPruningPoint, model.NewUTXODiff())
if err != nil {
return err
}
csm.blockStatusStore.Stage(headerTipsPruningPoint, externalapi.StatusValid)
return nil
}
func (csm *consensusStateManager) discardSetPruningPointUTXOSetChanges() {
for _, store := range csm.stores {
store.Discard()
}
}
func (csm *consensusStateManager) commitSetPruningPointUTXOSetAll() error {
dbTx, err := csm.databaseContext.Begin()
if err != nil {
return err
}
for _, store := range csm.stores {
err = store.Commit(dbTx)
if err != nil {
return err
}
}
return dbTx.Commit()
}
type protoUTXOSetIterator struct {
utxoSet *utxoserialization.ProtoUTXOSet
index int
}
func (p protoUTXOSetIterator) Next() bool {
p.index++
return p.index != len(p.utxoSet.Utxos)
}
func (p protoUTXOSetIterator) Get() (outpoint *externalapi.DomainOutpoint, utxoEntry *externalapi.UTXOEntry, err error) {
entry, outpoint, err := consensusserialization.DeserializeUTXO(p.utxoSet.Utxos[p.index].EntryOutpointPair)
if err != nil {
if consensusserialization.IsMalformedError(err) {
return nil, nil, errors.Wrap(ruleerrors.ErrMalformedUTXO, "malformed utxo")
}
return nil, nil, err
}
return outpoint, entry, nil
}
func protoUTXOSetToReadOnlyUTXOSetIterator(protoUTXOSet *utxoserialization.ProtoUTXOSet) model.ReadOnlyUTXOSetIterator {
return &protoUTXOSetIterator{utxoSet: protoUTXOSet}
}
func (csm *consensusStateManager) HeaderTipsPruningPoint() (*externalapi.DomainHash, error) {
headerTips, err := csm.headerTipsStore.Tips(csm.databaseContext)
if err != nil {
return nil, err
}
csm.blockRelationStore.StageBlockRelation(virtualHeaderHash, &model.BlockRelations{
Parents: headerTips,
})
defer csm.blockRelationStore.Discard()
err = csm.ghostdagManager.GHOSTDAG(virtualHeaderHash)
if err != nil {
return nil, err
}
defer csm.ghostdagDataStore.Discard()
virtualHeaderGHOSTDAGData, err := csm.ghostdagDataStore.Get(csm.databaseContext, virtualHeaderHash)
if err != nil {
return nil, err
}
return csm.dagTraversalManager.HighestChainBlockBelowBlueScore(virtualHeaderHash, virtualHeaderGHOSTDAGData.BlueScore-csm.pruningDepth)
}