Ori Newman 48e1a2c396
New headers first flow (#1211)
* Get rid of insertMode

* Rename AddBlockToVirtual->AddBlock

* When F is not in the future of P, enforce finality with P and not with F.

* Don't allow blocks with invalid parents or with missing block body

* Check finality violation before checking block status

* Implement CalculateIndependentPruningPoint

* Move checkBlockStatus to validateBlock

* Add ValidateBlock to block processor interface

* Adjust SetPruningPoint to the new IBD flow

* Add pruning store to CSM's constructor

* Flip wrong condition on AddHeaderTip

* Fix func (hts *headerSelectedTipStore) Has

* Fix block stage order

* Call to ValidateBodyInContext from validatePostProofOfWork

* Enable overrideDAGParams

* Update log

* Rename SetPruningPoint to ValidateAndInsertPruningPoint and move most of its logic inside block processor

* Rename hasValidatedHeader->hasValidatedOnlyHeader

* Fix typo

* Name return values for fetchMissingUTXOSet

* Add comment

* Return ErrMissingParents when block body is missing

* Add logs and comments

* Fix merge error

* Fix pruning point calculation to be by virtual selected parent

* Replace CalculateIndependentPruningPoint to CalculatePruningPointByHeaderSelectedTip

* Fix isAwaitingUTXOSet to check pruning point by headers

* Change isAwaitingUTXOSet indication

* Remove IsBlockInHeaderPruningPointFuture from BlockInfo

* Fix LowestChainBlockAboveOrEqualToBlueScore

* Add validateNewPruningPointTransactions

* Add validateNewPruningAgainstPastUTXO

* Rename set_pruning_utxo_set.go to update_pruning_utxo_set.go

* Check missing block body hashes by missing block instead of status

* Validate pruning point against past UTXO with the pruning point as block hash

* Remove virtualHeaderHash

* Fix comment

* Fix imports
2020-12-14 17:53:08 +02:00

160 lines
5.1 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/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/serialization"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization"
"github.com/kaspanet/kaspad/infrastructure/logger"
"github.com/pkg/errors"
)
func (csm *consensusStateManager) UpdatePruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
onEnd := logger.LogAndMeasureExecutionTime(log, "UpdatePruningPoint")
defer onEnd()
err := csm.updatePruningPoint(newPruningPoint, serializedUTXOSet)
if err != nil {
csm.discardSetPruningPointUTXOSetChanges()
return err
}
return csm.commitSetPruningPointUTXOSetAll()
}
func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
log.Tracef("updatePruningPoint start")
defer log.Tracef("updatePruningPoint end")
newPruningPointHash := consensushashing.BlockHash(newPruningPoint)
// We ignore the shouldSendNotification return value because we always want to send finality conflict notification
// in case the new pruning point violates finality
isViolatingFinality, _, err := csm.isViolatingFinality(newPruningPointHash)
if err != nil {
return err
}
if isViolatingFinality {
log.Warnf("Finality Violation Detected! The suggest pruning point %s violates finality!", newPruningPointHash)
return nil
}
protoUTXOSet := &utxoserialization.ProtoUTXOSet{}
err = proto.Unmarshal(serializedUTXOSet, protoUTXOSet)
if err != nil {
return err
}
utxoSetMultiSet, err := calcMultisetFromProtoUTXOSet(protoUTXOSet)
if err != nil {
return err
}
log.Tracef("Calculated multiset for given UTXO set: %s", utxoSetMultiSet.Hash())
newPruningPointHeader, err := csm.blockHeaderStore.BlockHeader(csm.databaseContext, newPruningPointHash)
if err != nil {
return err
}
log.Tracef("The UTXO commitment of the pruning point: %s",
newPruningPointHeader.UTXOCommitment)
if newPruningPointHeader.UTXOCommitment != *utxoSetMultiSet.Hash() {
return errors.Wrapf(ruleerrors.ErrBadPruningPointUTXOSet, "the expected multiset hash of the pruning "+
"point UTXO set is %s but got %s", newPruningPointHeader.UTXOCommitment, *utxoSetMultiSet.Hash())
}
log.Tracef("The new pruning point UTXO commitment validation passed")
log.Tracef("Staging the parent hashes for pruning point as the DAG tips")
csm.consensusStateStore.StageTips(newPruningPointHeader.ParentHashes)
log.Tracef("Setting the parent hashes for the header tips pruning point as the virtual parents")
err = csm.dagTopologyManager.SetParents(model.VirtualBlockHash, newPruningPointHeader.ParentHashes)
if err != nil {
return err
}
log.Tracef("Staging the virtual UTXO set")
err = csm.consensusStateStore.StageVirtualUTXOSet(protoUTXOSetToReadOnlyUTXOSetIterator(protoUTXOSet))
if err != nil {
return err
}
// Before we manually mark the new pruning point as valid, we validate that all of its transactions are valid
// against the provided UTXO set.
err = csm.validateBlockTransactionsAgainstPastUTXO(newPruningPoint, utxo.NewUTXODiff())
if err != nil {
return err
}
err = csm.ghostdagManager.GHOSTDAG(model.VirtualBlockHash)
if err != nil {
return err
}
log.Tracef("Updating the header tips pruning point diff parents with an empty UTXO diff")
err = csm.updateVirtualDiffParents(utxo.NewUTXODiff())
if err != nil {
return err
}
log.Tracef("Staging the new pruning point and its UTXO set")
csm.pruningStore.Stage(newPruningPointHash, serializedUTXOSet)
log.Tracef("Staging the new pruning point as %s", externalapi.StatusValid)
csm.blockStatusStore.Stage(newPruningPointHash, 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 := utxo.DeserializeUTXO(p.utxoSet.Utxos[p.index].EntryOutpointPair)
if err != nil {
if serialization.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}
}