mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
Add sanity check that makes sure that the utxoset that we save fits the pruningPoints's commitment. (#1366)
Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
This commit is contained in:
parent
a04a5462ae
commit
9ea4c0fa38
@ -234,6 +234,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
multisetStore,
|
||||
acceptanceDataStore,
|
||||
blockStore,
|
||||
blockHeaderStore,
|
||||
utxoDiffStore,
|
||||
genesisHash,
|
||||
dagParams.FinalityDepth(),
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/multiset"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization"
|
||||
)
|
||||
|
||||
func (csm *consensusStateManager) calculateMultiset(
|
||||
@ -107,11 +106,3 @@ func removeUTXOFromMultiset(multiset model.Multiset, entry externalapi.UTXOEntry
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func calcMultisetFromProtoUTXOSet(protoUTXOSet *utxoserialization.ProtoUTXOSet) (model.Multiset, error) {
|
||||
ms := multiset.New()
|
||||
for _, utxo := range protoUTXOSet.Utxos {
|
||||
ms.Add(utxo.EntryOutpointPair)
|
||||
}
|
||||
return ms, nil
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalap
|
||||
return err
|
||||
}
|
||||
|
||||
utxoSetMultiSet, err := calcMultisetFromProtoUTXOSet(protoUTXOSet)
|
||||
utxoSetMultiSet, err := utxoserialization.CalculateMultisetFromProtoUTXOSet(protoUTXOSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxoserialization"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// pruningManager resolves and manages the current pruning point
|
||||
@ -24,6 +25,7 @@ type pruningManager struct {
|
||||
multiSetStore model.MultisetStore
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blocksStore model.BlockStore
|
||||
blockHeaderStore model.BlockHeaderStore
|
||||
utxoDiffStore model.UTXODiffStore
|
||||
|
||||
genesisHash *externalapi.DomainHash
|
||||
@ -47,6 +49,7 @@ func New(
|
||||
multiSetStore model.MultisetStore,
|
||||
acceptanceDataStore model.AcceptanceDataStore,
|
||||
blocksStore model.BlockStore,
|
||||
blockHeaderStore model.BlockHeaderStore,
|
||||
utxoDiffStore model.UTXODiffStore,
|
||||
|
||||
genesisHash *externalapi.DomainHash,
|
||||
@ -66,6 +69,7 @@ func New(
|
||||
multiSetStore: multiSetStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blocksStore: blocksStore,
|
||||
blockHeaderStore: blockHeaderStore,
|
||||
utxoDiffStore: utxoDiffStore,
|
||||
headerSelectedTipStore: headerSelectedTipStore,
|
||||
genesisHash: genesisHash,
|
||||
@ -297,19 +301,20 @@ func (pm *pruningManager) deletePastBlocks(pruningPoint *externalapi.DomainHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) savePruningPoint(blockHash *externalapi.DomainHash) error {
|
||||
func (pm *pruningManager) savePruningPoint(pruningPointHash *externalapi.DomainHash) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.savePruningPoint")
|
||||
defer onEnd()
|
||||
|
||||
utxoIter, err := pm.consensusStateManager.RestorePastUTXOSetIterator(blockHash)
|
||||
utxoIter, err := pm.consensusStateManager.RestorePastUTXOSetIterator(pruningPointHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serializedUtxo, err := serializeUTXOSetIterator(utxoIter)
|
||||
|
||||
serializedUtxo, err := pm.calculateAndValidateSerializedUTXOSet(utxoIter, pruningPointHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pm.pruningStore.StagePruningPoint(blockHash, serializedUtxo)
|
||||
pm.pruningStore.StagePruningPoint(pruningPointHash, serializedUtxo)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -396,14 +401,48 @@ func (pm *pruningManager) pruningPointCandidate() (*externalapi.DomainHash, erro
|
||||
return pm.pruningStore.PruningPointCandidate(pm.databaseContext)
|
||||
}
|
||||
|
||||
func serializeUTXOSetIterator(iter model.ReadOnlyUTXOSetIterator) ([]byte, error) {
|
||||
func (pm *pruningManager) calculateAndValidateSerializedUTXOSet(
|
||||
iter model.ReadOnlyUTXOSetIterator, pruningPointHash *externalapi.DomainHash) ([]byte, error) {
|
||||
|
||||
serializedUtxo, err := utxoserialization.ReadOnlyUTXOSetToProtoUTXOSet(iter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = pm.validateUTXOSetFitsCommitment(serializedUtxo, pruningPointHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return proto.Marshal(serializedUtxo)
|
||||
}
|
||||
|
||||
// validateUTXOSetFitsCommitment makes sure that the calculated UTXOSet of the new pruning point fits the commitment.
|
||||
// This is a sanity test, to make sure that kaspad doesn't store, and subsequently sends syncing peers the wrong UTXOSet.
|
||||
func (pm *pruningManager) validateUTXOSetFitsCommitment(
|
||||
serializedUtxo *utxoserialization.ProtoUTXOSet, pruningPointHash *externalapi.DomainHash) error {
|
||||
|
||||
utxoSetMultiSet, err := utxoserialization.CalculateMultisetFromProtoUTXOSet(serializedUtxo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utxoSetHash := utxoSetMultiSet.Hash()
|
||||
|
||||
header, err := pm.blockHeaderStore.BlockHeader(pm.databaseContext, pruningPointHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expectedUTXOCommitment := header.UTXOCommitment()
|
||||
|
||||
if !expectedUTXOCommitment.Equal(utxoSetHash) {
|
||||
return errors.Errorf("Calculated UTXOSet for next pruning point %s doesn't match it's UTXO commitment\n"+
|
||||
"Calculated UTXOSet hash: %s. Commitment: %s",
|
||||
pruningPointHash, utxoSetHash, expectedUTXOCommitment)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// finalityScore is the number of finality intervals passed since
|
||||
// the given block.
|
||||
func (pm *pruningManager) finalityScore(blueScore uint64) uint64 {
|
||||
|
15
domain/consensus/utils/utxoserialization/multiset.go
Normal file
15
domain/consensus/utils/utxoserialization/multiset.go
Normal file
@ -0,0 +1,15 @@
|
||||
package utxoserialization
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/multiset"
|
||||
)
|
||||
|
||||
// CalculateMultisetFromProtoUTXOSet calculates the Multiset corresponding to the given ProtuUTXOSet
|
||||
func CalculateMultisetFromProtoUTXOSet(protoUTXOSet *ProtoUTXOSet) (model.Multiset, error) {
|
||||
ms := multiset.New()
|
||||
for _, utxo := range protoUTXOSet.Utxos {
|
||||
ms.Add(utxo.EntryOutpointPair)
|
||||
}
|
||||
return ms, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user