mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-23 15:26:42 +00:00

* Fix ps.currentPruningPointIndexCache * Remove redundant dependency from block builder * Fix typo
318 lines
8.9 KiB
Go
318 lines
8.9 KiB
Go
package blockrelay
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/kaspanet/kaspad/app/appmessage"
|
|
"github.com/kaspanet/kaspad/app/protocol/common"
|
|
"github.com/kaspanet/kaspad/app/protocol/protocolerrors"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func (flow *handleRelayInvsFlow) ibdWithHeadersProof(highHash *externalapi.DomainHash) error {
|
|
err := flow.Domain().InitStagingConsensus()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = flow.downloadHeadersAndPruningUTXOSet(highHash)
|
|
if err != nil {
|
|
if !flow.IsRecoverableError(err) {
|
|
return err
|
|
}
|
|
|
|
deleteStagingConsensusErr := flow.Domain().DeleteStagingConsensus()
|
|
if deleteStagingConsensusErr != nil {
|
|
return deleteStagingConsensusErr
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
err = flow.Domain().CommitStagingConsensus()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) shouldSyncAndShouldDownloadHeadersProof(highHash *externalapi.DomainHash,
|
|
highestSharedBlockFound bool) (shouldDownload, shouldSync bool, err error) {
|
|
|
|
if !highestSharedBlockFound {
|
|
hasMoreBlueWorkThanSelectedTip, err := flow.checkIfHighHashHasMoreBlueWorkThanSelectedTip(highHash)
|
|
if err != nil {
|
|
return false, false, err
|
|
}
|
|
|
|
if hasMoreBlueWorkThanSelectedTip {
|
|
return true, true, nil
|
|
}
|
|
|
|
return false, false, nil
|
|
}
|
|
|
|
return false, true, nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) checkIfHighHashHasMoreBlueWorkThanSelectedTip(highHash *externalapi.DomainHash) (bool, error) {
|
|
err := flow.outgoingRoute.Enqueue(appmessage.NewRequestBlockBlueWork(highHash))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
msgBlockBlueWork, ok := message.(*appmessage.MsgBlockBlueWork)
|
|
if !ok {
|
|
return false,
|
|
protocolerrors.Errorf(true, "received unexpected message type. "+
|
|
"expected: %s, got: %s", appmessage.CmdBlockBlueWork, message.Command())
|
|
}
|
|
|
|
headersSelectedTip, err := flow.Domain().Consensus().GetHeadersSelectedTip()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
headersSelectedTipInfo, err := flow.Domain().Consensus().GetBlockInfo(headersSelectedTip)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return msgBlockBlueWork.BlueWork.Cmp(headersSelectedTipInfo.BlueWork) > 0, nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) downloadHeadersProof() error {
|
|
// TODO: Implement headers proof mechanism
|
|
return nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) downloadHeadersAndPruningUTXOSet(highHash *externalapi.DomainHash) error {
|
|
err := flow.downloadHeadersProof()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pruningPoint, err := flow.syncPruningPointsAndPruningPointAnticone()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO: Remove this condition once there's more proper way to check finality violation
|
|
// in the headers proof.
|
|
if pruningPoint.Equal(flow.Config().NetParams().GenesisHash) {
|
|
return protocolerrors.Errorf(true, "the genesis pruning point violates finality")
|
|
}
|
|
|
|
err = flow.syncPruningPointFutureHeaders(flow.Domain().StagingConsensus(), pruningPoint, highHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Debugf("Blocks downloaded from peer %s", flow.peer)
|
|
|
|
log.Debugf("Syncing the current pruning point UTXO set")
|
|
syncedPruningPointUTXOSetSuccessfully, err := flow.syncPruningPointUTXOSet(flow.Domain().StagingConsensus(), pruningPoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !syncedPruningPointUTXOSetSuccessfully {
|
|
log.Debugf("Aborting IBD because the pruning point UTXO set failed to sync")
|
|
return nil
|
|
}
|
|
log.Debugf("Finished syncing the current pruning point UTXO set")
|
|
return nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) syncPruningPointsAndPruningPointAnticone() (*externalapi.DomainHash, error) {
|
|
|
|
log.Infof("Downloading the past pruning points and the pruning point anticone from %s", flow.peer)
|
|
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestPruningPointAndItsAnticone())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = flow.validateAndInsertPruningPoints()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pruningPoint, done, err := flow.receiveBlockWithTrustedData()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if done {
|
|
return nil, protocolerrors.Errorf(true, "got `done` message before receiving the pruning point")
|
|
}
|
|
|
|
err = flow.processBlockWithTrustedData(flow.Domain().StagingConsensus(), pruningPoint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for {
|
|
blockWithTrustedData, done, err := flow.receiveBlockWithTrustedData()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if done {
|
|
break
|
|
}
|
|
|
|
err = flow.processBlockWithTrustedData(flow.Domain().StagingConsensus(), blockWithTrustedData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
log.Infof("Finished downloading pruning point and its anticone from %s", flow.peer)
|
|
return pruningPoint.Block.Header.BlockHash(), nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) processBlockWithTrustedData(
|
|
consensus externalapi.Consensus, block *appmessage.MsgBlockWithTrustedData) error {
|
|
|
|
_, err := consensus.ValidateAndInsertBlockWithTrustedData(appmessage.BlockWithTrustedDataToDomainBlockWithTrustedData(block), false)
|
|
return err
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) receiveBlockWithTrustedData() (*appmessage.MsgBlockWithTrustedData, bool, error) {
|
|
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
switch downCastedMessage := message.(type) {
|
|
case *appmessage.MsgBlockWithTrustedData:
|
|
return downCastedMessage, false, nil
|
|
case *appmessage.MsgDoneBlocksWithTrustedData:
|
|
return nil, true, nil
|
|
default:
|
|
return nil, false,
|
|
protocolerrors.Errorf(true, "received unexpected message type. "+
|
|
"expected: %s or %s, got: %s",
|
|
(&appmessage.MsgBlockWithTrustedData{}).Command(),
|
|
(&appmessage.MsgDoneBlocksWithTrustedData{}).Command(),
|
|
downCastedMessage.Command())
|
|
}
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) receivePruningPoints() (*appmessage.MsgPruningPoints, error) {
|
|
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
msgPruningPoints, ok := message.(*appmessage.MsgPruningPoints)
|
|
if !ok {
|
|
return nil,
|
|
protocolerrors.Errorf(true, "received unexpected message type. "+
|
|
"expected: %s, got: %s", appmessage.CmdPruningPoints, message.Command())
|
|
}
|
|
|
|
return msgPruningPoints, nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) validateAndInsertPruningPoints() error {
|
|
pruningPoints, err := flow.receivePruningPoints()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
headers := make([]externalapi.BlockHeader, len(pruningPoints.Headers))
|
|
for i, header := range pruningPoints.Headers {
|
|
headers[i] = appmessage.BlockHeaderToDomainBlockHeader(header)
|
|
}
|
|
|
|
arePruningPointsViolatingFinality, err := flow.Domain().Consensus().ArePruningPointsViolatingFinality(headers)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if arePruningPointsViolatingFinality {
|
|
// TODO: Find a better way to deal with finality conflicts.
|
|
return protocolerrors.Errorf(false, "pruning points are violating finality")
|
|
}
|
|
|
|
err = flow.Domain().StagingConsensus().ImportPruningPoints(headers)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) syncPruningPointUTXOSet(consensus externalapi.Consensus,
|
|
pruningPoint *externalapi.DomainHash) (bool, error) {
|
|
|
|
log.Infof("Checking if the suggested pruning point %s is compatible to the node DAG", pruningPoint)
|
|
isValid, err := flow.Domain().StagingConsensus().IsValidPruningPoint(pruningPoint)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if !isValid {
|
|
return false, protocolerrors.Errorf(true, "invalid pruning point %s", pruningPoint)
|
|
}
|
|
|
|
log.Info("Fetching the pruning point UTXO set")
|
|
isSuccessful, err := flow.fetchMissingUTXOSet(consensus, pruningPoint)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if !isSuccessful {
|
|
log.Infof("Couldn't successfully fetch the pruning point UTXO set. Stopping IBD.")
|
|
return false, nil
|
|
}
|
|
|
|
log.Info("Fetched the new pruning point UTXO set")
|
|
return true, nil
|
|
}
|
|
|
|
func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(consensus externalapi.Consensus, pruningPointHash *externalapi.DomainHash) (succeed bool, err error) {
|
|
defer func() {
|
|
err := flow.Domain().StagingConsensus().ClearImportedPruningPointData()
|
|
if err != nil {
|
|
panic(fmt.Sprintf("failed to clear imported pruning point data: %s", err))
|
|
}
|
|
}()
|
|
|
|
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestPruningPointUTXOSet(pruningPointHash))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
receivedAll, err := flow.receiveAndInsertPruningPointUTXOSet(consensus, pruningPointHash)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if !receivedAll {
|
|
return false, nil
|
|
}
|
|
|
|
err = flow.Domain().StagingConsensus().ValidateAndInsertImportedPruningPoint(pruningPointHash)
|
|
if err != nil {
|
|
// TODO: Find a better way to deal with finality conflicts.
|
|
if errors.Is(err, ruleerrors.ErrSuggestedPruningViolatesFinality) {
|
|
return false, nil
|
|
}
|
|
return false, protocolerrors.ConvertToBanningProtocolErrorIfRuleError(err, "error with pruning point UTXO set")
|
|
}
|
|
|
|
err = flow.OnPruningPointUTXOSetOverride()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}
|