mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-15 22:10:12 +00:00
Compare commits
6 Commits
master
...
v0.8.4-rc0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ce85b9cc09 | ||
![]() |
f0c7e03ece | ||
![]() |
863b9d9e07 | ||
![]() |
8e188adbc2 | ||
![]() |
c27da560c5 | ||
![]() |
23f093ca17 |
@ -55,6 +55,8 @@ const (
|
||||
CmdIBDRootUTXOSetAndBlock
|
||||
CmdRequestIBDBlocks
|
||||
CmdIBDRootNotFound
|
||||
CmdRequestIBDRootHash
|
||||
CmdIBDRootHash
|
||||
|
||||
// rpc
|
||||
CmdGetCurrentNetworkRequestMessage
|
||||
@ -145,6 +147,8 @@ var ProtocolMessageCommandToString = map[MessageCommand]string{
|
||||
CmdIBDRootUTXOSetAndBlock: "IBDRootUTXOSetAndBlock",
|
||||
CmdRequestIBDBlocks: "RequestIBDBlocks",
|
||||
CmdIBDRootNotFound: "IBDRootNotFound",
|
||||
CmdRequestIBDRootHash: "IBDRequestIBDRootHash",
|
||||
CmdIBDRootHash: "IBDIBDRootHash",
|
||||
}
|
||||
|
||||
// RPCMessageCommandToString maps all MessageCommands to their string representation
|
||||
|
@ -17,6 +17,6 @@ func (msg *MsgIBDRootNotFound) Command() MessageCommand {
|
||||
|
||||
// NewMsgIBDRootNotFound returns a new kaspa IBDRootNotFound message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgIBDRootNotFound() *MsgDoneHeaders {
|
||||
return &MsgDoneHeaders{}
|
||||
func NewMsgIBDRootNotFound() *MsgIBDRootNotFound {
|
||||
return &MsgIBDRootNotFound{}
|
||||
}
|
||||
|
26
app/appmessage/p2p_msgibdroothash.go
Normal file
26
app/appmessage/p2p_msgibdroothash.go
Normal file
@ -0,0 +1,26 @@
|
||||
package appmessage
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
)
|
||||
|
||||
// MsgIBDRootHash implements the Message interface and represents a kaspa
|
||||
// IBDRootHash message. It is used as a reply to IBD root hash requests.
|
||||
type MsgIBDRootHash struct {
|
||||
baseMessage
|
||||
Hash *externalapi.DomainHash
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgIBDRootHash) Command() MessageCommand {
|
||||
return CmdIBDRootHash
|
||||
}
|
||||
|
||||
// NewMsgIBDRootHash returns a new kaspa IBDRootHash message that conforms to
|
||||
// the Message interface. See MsgIBDRootHash for details.
|
||||
func NewMsgIBDRootHash(hash *externalapi.DomainHash) *MsgIBDRootHash {
|
||||
return &MsgIBDRootHash{
|
||||
Hash: hash,
|
||||
}
|
||||
}
|
22
app/appmessage/p2p_requestibdroothash.go
Normal file
22
app/appmessage/p2p_requestibdroothash.go
Normal file
@ -0,0 +1,22 @@
|
||||
package appmessage
|
||||
|
||||
// MsgRequestIBDRootHash implements the Message interface and represents a kaspa
|
||||
// MsgRequestIBDRootHash message. It is used to request the IBD root hash
|
||||
// from a peer during IBD.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgRequestIBDRootHash struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgRequestIBDRootHash) Command() MessageCommand {
|
||||
return CmdRequestIBDRootHash
|
||||
}
|
||||
|
||||
// NewMsgRequestIBDRootHash returns a new kaspa RequestIBDRootHash message that conforms to the
|
||||
// Message interface.
|
||||
func NewMsgRequestIBDRootHash() *MsgRequestIBDRootHash {
|
||||
return &MsgRequestIBDRootHash{}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleIBDRootHashRequestsFlowContext is the interface for the context needed for the handleIBDRootHashRequestsFlow flow.
|
||||
type HandleIBDRootHashRequestsFlowContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
type handleIBDRootHashRequestsFlow struct {
|
||||
HandleIBDRootHashRequestsFlowContext
|
||||
incomingRoute, outgoingRoute *router.Route
|
||||
}
|
||||
|
||||
// HandleIBDRootHashRequests listens to appmessage.MsgRequestIBDRootHash messages and sends
|
||||
// the IBD root hash as response.
|
||||
func HandleIBDRootHashRequests(context HandleIBDRootHashRequestsFlowContext, incomingRoute,
|
||||
outgoingRoute *router.Route) error {
|
||||
flow := &handleIBDRootHashRequestsFlow{
|
||||
HandleIBDRootHashRequestsFlowContext: context,
|
||||
incomingRoute: incomingRoute,
|
||||
outgoingRoute: outgoingRoute,
|
||||
}
|
||||
|
||||
return flow.start()
|
||||
}
|
||||
|
||||
func (flow *handleIBDRootHashRequestsFlow) start() error {
|
||||
for {
|
||||
_, err := flow.incomingRoute.Dequeue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pruningPoint, err := flow.Domain().Consensus().PruningPoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgIBDRootHash(pruningPoint))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
@ -30,22 +30,55 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
|
||||
log.Debugf("Finished downloading headers up to %s", highHash)
|
||||
|
||||
// Fetch the UTXO set if we don't already have it
|
||||
log.Debugf("Downloading the IBD root UTXO set under highHash %s", highHash)
|
||||
syncInfo, err := flow.Domain().Consensus().GetSyncInfo()
|
||||
log.Debugf("Checking if there's a new pruning point under %s", highHash)
|
||||
err = flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDRootHash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if syncInfo.IsAwaitingUTXOSet {
|
||||
found, err := flow.fetchMissingUTXOSet(syncInfo.IBDRootUTXOBlockHash)
|
||||
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgIBDRootHash, ok := message.(*appmessage.MsgIBDRootHash)
|
||||
if !ok {
|
||||
return protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdIBDRootHash, message.Command())
|
||||
}
|
||||
|
||||
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(msgIBDRootHash.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
isValid, err := flow.Domain().Consensus().IsValidPruningPoint(msgIBDRootHash.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
log.Infof("Cannot download the IBD root UTXO set under highHash %s", highHash)
|
||||
|
||||
if !isValid {
|
||||
log.Infof("The suggested pruning point is incompatible to this node DAG, so stopping IBD with this" +
|
||||
" peer")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("Fetching the pruning point UTXO set")
|
||||
succeed, err := flow.fetchMissingUTXOSet(msgIBDRootHash.Hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !succeed {
|
||||
log.Infof("Couldn't successfully fetch the pruning point UTXO set. Stopping IBD.")
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("Fetched the new pruning point UTXO set")
|
||||
} else {
|
||||
log.Debugf("Already has the block data of the new suggested pruning point %s", msgIBDRootHash.Hash)
|
||||
}
|
||||
log.Debugf("Finished downloading the IBD root UTXO set under highHash %s", highHash)
|
||||
|
||||
// Fetch the block bodies
|
||||
log.Debugf("Downloading block bodies up to %s", highHash)
|
||||
@ -58,6 +91,40 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
|
||||
return nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) fetchUTXOSetIfMissing() (bool, error) {
|
||||
err := flow.outgoingRoute.Enqueue(appmessage.NewMsgRequestIBDRootHash())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
msgIBDRootHash, ok := message.(*appmessage.MsgIBDRootHash)
|
||||
if !ok {
|
||||
return false, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdIBDRootHash, message.Command())
|
||||
}
|
||||
|
||||
isValid, err := flow.Domain().Consensus().IsValidPruningPoint(msgIBDRootHash.Hash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
found, err := flow.fetchMissingUTXOSet(msgIBDRootHash.Hash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return found, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) syncHeaders(highHash *externalapi.DomainHash) error {
|
||||
log.Debugf("Trying to find highest shared chain block with peer %s with high hash %s", flow.peer, highHash)
|
||||
highestSharedBlockHash, err := flow.findHighestSharedBlockHash(highHash)
|
||||
@ -207,21 +274,13 @@ func (flow *handleRelayInvsFlow) fetchMissingUTXOSet(ibdRootHash *externalapi.Do
|
||||
|
||||
err = flow.Domain().Consensus().ValidateAndInsertPruningPoint(block, utxoSet)
|
||||
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 IBD root UTXO set")
|
||||
}
|
||||
|
||||
syncInfo, err := flow.Domain().Consensus().GetSyncInfo()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// TODO: Find a better way to deal with finality conflicts.
|
||||
if syncInfo.IsAwaitingUTXOSet {
|
||||
log.Warnf("Still awaiting for UTXO set. This can happen only because the given pruning point violates " +
|
||||
"finality. If this keeps happening delete the data directory and restart your node.")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ func (m *Manager) registerBlockRelayFlows(router *routerpkg.Router, isStopping *
|
||||
m.registerFlow("HandleRelayInvs", router, []appmessage.MessageCommand{
|
||||
appmessage.CmdInvRelayBlock, appmessage.CmdBlock, appmessage.CmdBlockLocator, appmessage.CmdIBDBlock,
|
||||
appmessage.CmdDoneHeaders, appmessage.CmdIBDRootNotFound, appmessage.CmdIBDRootUTXOSetAndBlock,
|
||||
appmessage.CmdHeader}, isStopping, errChan,
|
||||
appmessage.CmdHeader, appmessage.CmdIBDRootHash}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return blockrelay.HandleRelayInvs(m.context, incomingRoute,
|
||||
outgoingRoute, peer)
|
||||
@ -176,6 +176,13 @@ func (m *Manager) registerBlockRelayFlows(router *routerpkg.Router, isStopping *
|
||||
return blockrelay.HandleIBDBlockRequests(m.context, incomingRoute, outgoingRoute)
|
||||
},
|
||||
),
|
||||
|
||||
m.registerFlow("HandleIBDRootHashRequests", router,
|
||||
[]appmessage.MessageCommand{appmessage.CmdRequestIBDRootHash}, isStopping, errChan,
|
||||
func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return blockrelay.HandleIBDRootHashRequests(m.context, incomingRoute, outgoingRoute)
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,6 +184,13 @@ func (s *consensus) GetPruningPointUTXOSet(expectedPruningPointHash *externalapi
|
||||
return serializedUTXOSet, nil
|
||||
}
|
||||
|
||||
func (s *consensus) PruningPoint() (*externalapi.DomainHash, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
return s.pruningStore.PruningPoint(s.databaseContext)
|
||||
}
|
||||
|
||||
func (s *consensus) ValidateAndInsertPruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
@ -259,6 +266,10 @@ func (s *consensus) GetSyncInfo() (*externalapi.SyncInfo, error) {
|
||||
return s.syncManager.GetSyncInfo()
|
||||
}
|
||||
|
||||
func (s *consensus) IsValidPruningPoint(block *externalapi.DomainHash) (bool, error) {
|
||||
return s.pruningManager.IsValidPruningPoint(block)
|
||||
}
|
||||
|
||||
func (s *consensus) GetVirtualSelectedParentChainFromBlock(blockHash *externalapi.DomainHash) (*externalapi.SelectedParentChainChanges, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
@ -9,13 +9,54 @@ import (
|
||||
)
|
||||
|
||||
var pruningBlockHashKey = dbkeys.MakeBucket().Key([]byte("pruning-block-hash"))
|
||||
var pruningSerializedUTXOSetkey = dbkeys.MakeBucket().Key([]byte("pruning-utxo-set"))
|
||||
var candidatePruningPointHashKey = dbkeys.MakeBucket().Key([]byte("candidate-pruning-point-hash"))
|
||||
var pruningSerializedUTXOSetKey = dbkeys.MakeBucket().Key([]byte("pruning-utxo-set"))
|
||||
|
||||
// pruningStore represents a store for the current pruning state
|
||||
type pruningStore struct {
|
||||
pruningPointStaging *externalapi.DomainHash
|
||||
serializedUTXOSetStaging []byte
|
||||
pruningPointCache *externalapi.DomainHash
|
||||
pruningPointStaging *externalapi.DomainHash
|
||||
pruningPointCandidateStaging *externalapi.DomainHash
|
||||
serializedUTXOSetStaging []byte
|
||||
pruningPointCandidateCache *externalapi.DomainHash
|
||||
pruningPointCache *externalapi.DomainHash
|
||||
}
|
||||
|
||||
func (ps *pruningStore) StagePruningPointCandidate(candidate *externalapi.DomainHash) {
|
||||
ps.pruningPointCandidateStaging = candidate.Clone()
|
||||
}
|
||||
|
||||
func (ps *pruningStore) PruningPointCandidate(dbContext model.DBReader) (*externalapi.DomainHash, error) {
|
||||
if ps.pruningPointCandidateStaging != nil {
|
||||
return ps.pruningPointCandidateStaging, nil
|
||||
}
|
||||
|
||||
if ps.pruningPointCandidateCache != nil {
|
||||
return ps.pruningPointCandidateCache, nil
|
||||
}
|
||||
|
||||
candidateBytes, err := dbContext.Get(pruningBlockHashKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
candidate, err := ps.deserializePruningPoint(candidateBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ps.pruningPointCandidateCache = candidate
|
||||
return candidate, nil
|
||||
}
|
||||
|
||||
func (ps *pruningStore) HasPruningPointCandidate(dbContext model.DBReader) (bool, error) {
|
||||
if ps.pruningPointCandidateStaging != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if ps.pruningPointCandidateCache != nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return dbContext.Has(candidatePruningPointHashKey)
|
||||
}
|
||||
|
||||
// New instantiates a new PruningStore
|
||||
@ -24,7 +65,7 @@ func New() model.PruningStore {
|
||||
}
|
||||
|
||||
// Stage stages the pruning state
|
||||
func (ps *pruningStore) Stage(pruningPointBlockHash *externalapi.DomainHash, pruningPointUTXOSetBytes []byte) {
|
||||
func (ps *pruningStore) StagePruningPoint(pruningPointBlockHash *externalapi.DomainHash, pruningPointUTXOSetBytes []byte) {
|
||||
ps.pruningPointStaging = pruningPointBlockHash.Clone()
|
||||
ps.serializedUTXOSetStaging = pruningPointUTXOSetBytes
|
||||
}
|
||||
@ -40,7 +81,7 @@ func (ps *pruningStore) Discard() {
|
||||
|
||||
func (ps *pruningStore) Commit(dbTx model.DBTransaction) error {
|
||||
if ps.pruningPointStaging != nil {
|
||||
pruningPointBytes, err := ps.serializePruningPoint(ps.pruningPointStaging)
|
||||
pruningPointBytes, err := ps.serializeHash(ps.pruningPointStaging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -51,12 +92,24 @@ func (ps *pruningStore) Commit(dbTx model.DBTransaction) error {
|
||||
ps.pruningPointCache = ps.pruningPointStaging
|
||||
}
|
||||
|
||||
if ps.pruningPointCandidateStaging != nil {
|
||||
candidateBytes, err := ps.serializeHash(ps.pruningPointCandidateStaging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(candidatePruningPointHashKey, candidateBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ps.pruningPointCandidateCache = ps.pruningPointCandidateStaging
|
||||
}
|
||||
|
||||
if ps.serializedUTXOSetStaging != nil {
|
||||
utxoSetBytes, err := ps.serializeUTXOSetBytes(ps.serializedUTXOSetStaging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dbTx.Put(pruningSerializedUTXOSetkey, utxoSetBytes)
|
||||
err = dbTx.Put(pruningSerializedUTXOSetKey, utxoSetBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -95,7 +148,7 @@ func (ps *pruningStore) PruningPointSerializedUTXOSet(dbContext model.DBReader)
|
||||
return ps.serializedUTXOSetStaging, nil
|
||||
}
|
||||
|
||||
dbPruningPointUTXOSetBytes, err := dbContext.Get(pruningSerializedUTXOSetkey)
|
||||
dbPruningPointUTXOSetBytes, err := dbContext.Get(pruningSerializedUTXOSetKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -107,8 +160,8 @@ func (ps *pruningStore) PruningPointSerializedUTXOSet(dbContext model.DBReader)
|
||||
return pruningPointUTXOSet, nil
|
||||
}
|
||||
|
||||
func (ps *pruningStore) serializePruningPoint(pruningPoint *externalapi.DomainHash) ([]byte, error) {
|
||||
return proto.Marshal(serialization.DomainHashToDbHash(pruningPoint))
|
||||
func (ps *pruningStore) serializeHash(hash *externalapi.DomainHash) ([]byte, error) {
|
||||
return proto.Marshal(serialization.DomainHashToDbHash(hash))
|
||||
}
|
||||
|
||||
func (ps *pruningStore) deserializePruningPoint(pruningPointBytes []byte) (*externalapi.DomainHash, error) {
|
||||
|
@ -105,6 +105,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
dbManager,
|
||||
dagTopologyManager,
|
||||
ghostdagDataStore,
|
||||
reachabilityDataStore,
|
||||
ghostdagManager)
|
||||
pastMedianTimeManager := pastmediantimemanager.New(
|
||||
dagParams.TimestampDeviationTolerance,
|
||||
|
@ -14,6 +14,7 @@ type Consensus interface {
|
||||
GetHashesBetween(lowHash, highHash *DomainHash) ([]*DomainHash, error)
|
||||
GetMissingBlockBodyHashes(highHash *DomainHash) ([]*DomainHash, error)
|
||||
GetPruningPointUTXOSet(expectedPruningPointHash *DomainHash) ([]byte, error)
|
||||
PruningPoint() (*DomainHash, error)
|
||||
ValidateAndInsertPruningPoint(newPruningPoint *DomainBlock, serializedUTXOSet []byte) error
|
||||
GetVirtualSelectedParent() (*DomainBlock, error)
|
||||
CreateBlockLocator(lowHash, highHash *DomainHash, limit uint32) (BlockLocator, error)
|
||||
@ -21,5 +22,6 @@ type Consensus interface {
|
||||
GetSyncInfo() (*SyncInfo, error)
|
||||
Tips() ([]*DomainHash, error)
|
||||
GetVirtualInfo() (*VirtualInfo, error)
|
||||
IsValidPruningPoint(block *DomainHash) (bool, error)
|
||||
GetVirtualSelectedParentChainFromBlock(blockHash *DomainHash) (*SelectedParentChainChanges, error)
|
||||
}
|
||||
|
@ -2,8 +2,6 @@ package externalapi
|
||||
|
||||
// SyncInfo holds info about the current sync state of the consensus
|
||||
type SyncInfo struct {
|
||||
IsAwaitingUTXOSet bool
|
||||
IBDRootUTXOBlockHash *DomainHash
|
||||
HeaderCount uint64
|
||||
BlockCount uint64
|
||||
HeaderCount uint64
|
||||
BlockCount uint64
|
||||
}
|
||||
|
@ -5,8 +5,11 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
// PruningStore represents a store for the current pruning state
|
||||
type PruningStore interface {
|
||||
Store
|
||||
Stage(pruningPointBlockHash *externalapi.DomainHash, pruningPointUTXOSetBytes []byte)
|
||||
StagePruningPoint(pruningPointBlockHash *externalapi.DomainHash, pruningPointUTXOSetBytes []byte)
|
||||
StagePruningPointCandidate(candidate *externalapi.DomainHash)
|
||||
IsStaged() bool
|
||||
PruningPointCandidate(dbContext DBReader) (*externalapi.DomainHash, error)
|
||||
HasPruningPointCandidate(dbContext DBReader) (bool, error)
|
||||
PruningPoint(dbContext DBReader) (*externalapi.DomainHash, error)
|
||||
HasPruningPoint(dbContext DBReader) (bool, error)
|
||||
PruningPointSerializedUTXOSet(dbContext DBReader) ([]byte, error)
|
||||
|
@ -5,5 +5,5 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
// PruningManager resolves and manages the current pruning point
|
||||
type PruningManager interface {
|
||||
UpdatePruningPointByVirtual() error
|
||||
CalculatePruningPointByHeaderSelectedTip() (*externalapi.DomainHash, error)
|
||||
IsValidPruningPoint(block *externalapi.DomainHash) (bool, error)
|
||||
}
|
||||
|
@ -10,16 +10,15 @@ import (
|
||||
func (bp *blockProcessor) validateAndInsertPruningPoint(newPruningPoint *externalapi.DomainBlock, serializedUTXOSet []byte) error {
|
||||
log.Info("Checking that the given pruning point is the expected pruning point")
|
||||
|
||||
expectedNewPruningPointHash, err := bp.pruningManager.CalculatePruningPointByHeaderSelectedTip()
|
||||
newPruningPointHash := consensushashing.BlockHash(newPruningPoint)
|
||||
isValidPruningPoint, err := bp.pruningManager.IsValidPruningPoint(newPruningPointHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newPruningPointHash := consensushashing.BlockHash(newPruningPoint)
|
||||
|
||||
if *expectedNewPruningPointHash != *newPruningPointHash {
|
||||
return errors.Wrapf(ruleerrors.ErrUnexpectedPruningPoint, "expected pruning point %s but got %s",
|
||||
expectedNewPruningPointHash, newPruningPointHash)
|
||||
if !isValidPruningPoint {
|
||||
return errors.Wrapf(ruleerrors.ErrUnexpectedPruningPoint, "%s is not a valid pruning point",
|
||||
newPruningPointHash)
|
||||
}
|
||||
|
||||
// We have to validate the pruning point block before we set the new pruning point in consensusStateManager.
|
||||
|
@ -41,7 +41,8 @@ func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalap
|
||||
|
||||
if isViolatingFinality {
|
||||
log.Warnf("Finality Violation Detected! The suggest pruning point %s violates finality!", newPruningPointHash)
|
||||
return nil
|
||||
return errors.Wrapf(ruleerrors.ErrSuggestedPruningViolatesFinality, "%s cannot be a pruning point because "+
|
||||
"it violates finality", newPruningPointHash)
|
||||
}
|
||||
|
||||
protoUTXOSet := &utxoserialization.ProtoUTXOSet{}
|
||||
@ -102,7 +103,7 @@ func (csm *consensusStateManager) updatePruningPoint(newPruningPoint *externalap
|
||||
}
|
||||
|
||||
log.Debugf("Staging the new pruning point and its UTXO set")
|
||||
csm.pruningStore.Stage(newPruningPointHash, serializedUTXOSet)
|
||||
csm.pruningStore.StagePruningPoint(newPruningPointHash, serializedUTXOSet)
|
||||
|
||||
// Before we manually mark the new pruning point as valid, we validate that all of its transactions are valid
|
||||
// against the provided UTXO set.
|
||||
|
@ -12,9 +12,10 @@ import (
|
||||
type dagTraversalManager struct {
|
||||
databaseContext model.DBReader
|
||||
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
}
|
||||
|
||||
// selectedParentIterator implements the `model.BlockIterator` API
|
||||
@ -45,12 +46,14 @@ func New(
|
||||
databaseContext model.DBReader,
|
||||
dagTopologyManager model.DAGTopologyManager,
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
reachabilityDataStore model.ReachabilityDataStore,
|
||||
ghostdagManager model.GHOSTDAGManager) model.DAGTraversalManager {
|
||||
return &dagTraversalManager{
|
||||
databaseContext: databaseContext,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
ghostdagManager: ghostdagManager,
|
||||
databaseContext: databaseContext,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
ghostdagManager: ghostdagManager,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,21 +9,19 @@ import (
|
||||
type selectedChildIterator struct {
|
||||
databaseContext model.DBReader
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
highHash *externalapi.DomainHash
|
||||
current *externalapi.DomainHash
|
||||
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
highHash *externalapi.DomainHash
|
||||
current *externalapi.DomainHash
|
||||
}
|
||||
|
||||
func (s *selectedChildIterator) Next() bool {
|
||||
children, err := s.dagTopologyManager.Children(s.current)
|
||||
data, err := s.reachabilityDataStore.ReachabilityData(s.databaseContext, s.current)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, child := range children {
|
||||
if *child == *model.VirtualBlockHash {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, child := range data.TreeNode.Children {
|
||||
isChildInSelectedParentChainOfHighHash, err := s.dagTopologyManager.IsInSelectedParentChainOf(child, s.highHash)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -51,9 +49,10 @@ func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externa
|
||||
return nil, errors.Errorf("%s is not in the selected parent chain of %s", highHash, lowHash)
|
||||
}
|
||||
return &selectedChildIterator{
|
||||
databaseContext: dtm.databaseContext,
|
||||
dagTopologyManager: dtm.dagTopologyManager,
|
||||
highHash: highHash,
|
||||
current: lowHash,
|
||||
databaseContext: dtm.databaseContext,
|
||||
dagTopologyManager: dtm.dagTopologyManager,
|
||||
reachabilityDataStore: dtm.reachabilityDataStore,
|
||||
highHash: highHash,
|
||||
current: lowHash,
|
||||
}, nil
|
||||
}
|
||||
|
@ -26,10 +26,10 @@ type testJSON struct {
|
||||
func TestPruning(t *testing.T) {
|
||||
expectedPruningPointByNet := map[string]map[string]string{
|
||||
"chain-for-test-pruning.json": {
|
||||
"kaspa-mainnet": "84",
|
||||
"kaspa-simnet": "84",
|
||||
"kaspa-devnet": "84",
|
||||
"kaspa-testnet": "84",
|
||||
"kaspa-mainnet": "1582",
|
||||
"kaspa-simnet": "1582",
|
||||
"kaspa-devnet": "1582",
|
||||
"kaspa-testnet": "1582",
|
||||
},
|
||||
"dag-for-test-pruning.json": {
|
||||
"kaspa-mainnet": "503",
|
||||
@ -100,9 +100,29 @@ func TestPruning(t *testing.T) {
|
||||
|
||||
blockIDToHash[dagBlock.ID] = blockHash
|
||||
blockHashToID[*blockHash] = dagBlock.ID
|
||||
|
||||
pruningPoint, err := tc.PruningPoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pruningPointCandidate, err := tc.PruningStore().PruningPointCandidate(tc.DatabaseContext())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isValidPruningPoint, err := tc.IsValidPruningPoint(pruningPointCandidate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
shouldBeValid := *pruningPoint == *pruningPointCandidate
|
||||
if isValidPruningPoint != shouldBeValid {
|
||||
t.Fatalf("isValidPruningPoint is %t while expected %t", isValidPruningPoint, shouldBeValid)
|
||||
}
|
||||
}
|
||||
|
||||
pruningPoint, err := tc.PruningStore().PruningPoint(tc.DatabaseContext())
|
||||
pruningPoint, err := tc.PruningPoint()
|
||||
if err != nil {
|
||||
t.Fatalf("PruningPoint: %+v", err)
|
||||
}
|
||||
|
@ -88,7 +88,12 @@ func (pm *pruningManager) UpdatePruningPointByVirtual() error {
|
||||
}
|
||||
}
|
||||
|
||||
currentP, err := pm.pruningStore.PruningPoint(pm.databaseContext)
|
||||
currentCandidate, err := pm.pruningPointCandidate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentCandidateGHOSTDAGData, err := pm.ghostdagDataStore.Get(pm.databaseContext, currentCandidate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -103,28 +108,71 @@ func (pm *pruningManager) UpdatePruningPointByVirtual() error {
|
||||
return err
|
||||
}
|
||||
|
||||
currentPGhost, err := pm.ghostdagDataStore.Get(pm.databaseContext, currentP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentPBlueScore := currentPGhost.BlueScore()
|
||||
// Because the pruning point changes only once per finality, then there's no need to even check for that if a finality interval hasn't passed.
|
||||
if virtualSelectedParent.BlueScore() <= currentPBlueScore+pm.finalityInterval {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This means the pruning point is still genesis.
|
||||
if virtualSelectedParent.BlueScore() <= pm.pruningDepth+pm.finalityInterval {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get Virtual(pruningDepth)
|
||||
newPruningPoint, err := pm.calculatePruningPointFromBlock(model.VirtualBlockHash)
|
||||
currentPruningPoint, err := pm.pruningStore.PruningPoint(pm.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if *newPruningPoint != *currentP {
|
||||
currentPruningPointGHOSTDAGData, err := pm.ghostdagDataStore.Get(pm.databaseContext, currentPruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iterator, err := pm.dagTraversalManager.SelectedChildIterator(virtual.SelectedParent(), currentCandidate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Finding the next pruning point candidate: look for the latest
|
||||
// selected child of the current candidate that is in depth of at
|
||||
// least pm.pruningDepth blocks from the virtual selected parent.
|
||||
//
|
||||
// Note: Sometimes the current candidate is less than pm.pruningDepth
|
||||
// from the virtual. This can happen only if the virtual blue score
|
||||
// got smaller, because virtual blue score is not guaranteed to always
|
||||
// increase (because sometimes a block with higher blue work can have
|
||||
// lower blue score).
|
||||
// In such cases we still keep the same candidate because it's guaranteed
|
||||
// that a block that was once in depth of pm.pruningDepth cannot be
|
||||
// reorged without causing a finality conflict first.
|
||||
newCandidate := currentCandidate
|
||||
newCandidateGHOSTDAGData := currentCandidateGHOSTDAGData
|
||||
|
||||
newPruningPoint := currentPruningPoint
|
||||
newPruningPointGHOSTDAGData := currentPruningPointGHOSTDAGData
|
||||
for iterator.Next() {
|
||||
selectedChild := iterator.Get()
|
||||
selectedChildGHOSTDAGData, err := pm.ghostdagDataStore.Get(pm.databaseContext, selectedChild)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if virtualSelectedParent.BlueScore()-selectedChildGHOSTDAGData.BlueScore() < pm.pruningDepth {
|
||||
break
|
||||
}
|
||||
|
||||
newCandidate = selectedChild
|
||||
newCandidateGHOSTDAGData = selectedChildGHOSTDAGData
|
||||
|
||||
// We move the pruning point every time the candidate's finality score is
|
||||
// bigger than the current pruning point finality score.
|
||||
if pm.finalityScore(newCandidateGHOSTDAGData.BlueScore()) > pm.finalityScore(newPruningPointGHOSTDAGData.BlueScore()) {
|
||||
newPruningPoint = newCandidate
|
||||
newPruningPointGHOSTDAGData = newCandidateGHOSTDAGData
|
||||
}
|
||||
}
|
||||
|
||||
if *newCandidate != *currentCandidate {
|
||||
pm.pruningStore.StagePruningPointCandidate(newCandidate)
|
||||
}
|
||||
|
||||
// We move the pruning point every time the candidate's finality score is
|
||||
// bigger than the current pruning point finality score.
|
||||
if pm.finalityScore(newCandidateGHOSTDAGData.BlueScore()) <= pm.finalityScore(currentPruningPointGHOSTDAGData.BlueScore()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if *newPruningPoint != *currentPruningPoint {
|
||||
err = pm.savePruningPoint(newPruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -214,7 +262,7 @@ func (pm *pruningManager) savePruningPoint(blockHash *externalapi.DomainHash) er
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pm.pruningStore.Stage(blockHash, serializedUtxo)
|
||||
pm.pruningStore.StagePruningPoint(blockHash, serializedUtxo)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -237,28 +285,68 @@ func (pm *pruningManager) deleteBlock(blockHash *externalapi.DomainHash) (alread
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) CalculatePruningPointByHeaderSelectedTip() (*externalapi.DomainHash, error) {
|
||||
func (pm *pruningManager) IsValidPruningPoint(block *externalapi.DomainHash) (bool, error) {
|
||||
if *pm.genesisHash == *block {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
headersSelectedTip, err := pm.headerSelectedTipStore.HeadersSelectedTip(pm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return false, err
|
||||
}
|
||||
|
||||
return pm.calculatePruningPointFromBlock(headersSelectedTip)
|
||||
// A pruning point has to be in the selected chain of the headers selected tip.
|
||||
headersSelectedTipGHOSTDAGData, err := pm.ghostdagDataStore.Get(pm.databaseContext, headersSelectedTip)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
isInSelectedParentChainOfHeadersSelectedTip, err := pm.dagTopologyManager.IsInSelectedParentChainOf(block,
|
||||
headersSelectedTip)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !isInSelectedParentChainOfHeadersSelectedTip {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
ghostdagData, err := pm.ghostdagDataStore.Get(pm.databaseContext, block)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// A pruning point has to be at depth of at least pm.pruningDepth
|
||||
if headersSelectedTipGHOSTDAGData.BlueScore()-ghostdagData.BlueScore() < pm.pruningDepth {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
selectedParentGHOSTDAGData, err := pm.ghostdagDataStore.Get(pm.databaseContext, ghostdagData.SelectedParent())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// A pruning point has to be the lowest chain block with a certain finality score, so
|
||||
// if the block selected parent has the same finality score it means it cannot be a
|
||||
// pruning point.
|
||||
if pm.finalityScore(ghostdagData.BlueScore()) == pm.finalityScore(selectedParentGHOSTDAGData.BlueScore()) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) calculatePruningPointFromBlock(blockHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
|
||||
ghostdagData, err := pm.ghostdagDataStore.Get(pm.databaseContext, blockHash)
|
||||
func (pm *pruningManager) pruningPointCandidate() (*externalapi.DomainHash, error) {
|
||||
hasPruningPointCandidate, err := pm.pruningStore.HasPruningPointCandidate(pm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
targetBlueScore := uint64(0)
|
||||
if ghostdagData.BlueScore() > pm.pruningDepth {
|
||||
// The target blue is calculated by calculating ghostdagData.BlueScore() - pm.pruningDepth and rounding
|
||||
// down with the precision of finality interval.
|
||||
targetBlueScore = ((ghostdagData.BlueScore() - pm.pruningDepth) / pm.finalityInterval) * pm.finalityInterval
|
||||
if !hasPruningPointCandidate {
|
||||
return pm.genesisHash, nil
|
||||
}
|
||||
return pm.dagTraversalManager.LowestChainBlockAboveOrEqualToBlueScore(blockHash, targetBlueScore)
|
||||
|
||||
return pm.pruningStore.PruningPointCandidate(pm.databaseContext)
|
||||
}
|
||||
|
||||
func serializeUTXOSetIterator(iter model.ReadOnlyUTXOSetIterator) ([]byte, error) {
|
||||
@ -268,3 +356,9 @@ func serializeUTXOSetIterator(iter model.ReadOnlyUTXOSetIterator) ([]byte, error
|
||||
}
|
||||
return proto.Marshal(serializedUtxo)
|
||||
}
|
||||
|
||||
// finalityScore is the number of finality intervals passed since
|
||||
// the given block.
|
||||
func (pm *pruningManager) finalityScore(blueScore uint64) uint64 {
|
||||
return blueScore / pm.finalityInterval
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,44 +5,15 @@ import (
|
||||
)
|
||||
|
||||
func (sm *syncManager) syncInfo() (*externalapi.SyncInfo, error) {
|
||||
isAwaitingUTXOSet, ibdRootUTXOBlockHash, err := sm.isAwaitingUTXOSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headerCount := sm.getHeaderCount()
|
||||
blockCount := sm.getBlockCount()
|
||||
|
||||
return &externalapi.SyncInfo{
|
||||
IsAwaitingUTXOSet: isAwaitingUTXOSet,
|
||||
IBDRootUTXOBlockHash: ibdRootUTXOBlockHash,
|
||||
HeaderCount: headerCount,
|
||||
BlockCount: blockCount,
|
||||
HeaderCount: headerCount,
|
||||
BlockCount: blockCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sm *syncManager) isAwaitingUTXOSet() (isAwaitingUTXOSet bool, ibdRootUTXOBlockHash *externalapi.DomainHash,
|
||||
err error) {
|
||||
|
||||
pruningPointByHeaders, err := sm.pruningManager.CalculatePruningPointByHeaderSelectedTip()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
pruningPoint, err := sm.pruningStore.PruningPoint(sm.databaseContext)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
// If the pruning point by headers is different from the current point
|
||||
// it means we need to request the new pruning point UTXO set.
|
||||
if *pruningPoint != *pruningPointByHeaders {
|
||||
return true, pruningPointByHeaders, nil
|
||||
}
|
||||
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
func (sm *syncManager) getHeaderCount() uint64 {
|
||||
return sm.blockHeaderStore.Count()
|
||||
}
|
||||
|
@ -242,6 +242,8 @@ var (
|
||||
ErrBlockIsTooMuchInTheFuture = newRuleError("ErrBlockIsTooMuchInTheFuture")
|
||||
|
||||
ErrUnexpectedPruningPoint = newRuleError("ErrUnexpectedPruningPoint")
|
||||
|
||||
ErrSuggestedPruningViolatesFinality = newRuleError("ErrSuggestedPruningViolatesFinality")
|
||||
)
|
||||
|
||||
// RuleError identifies a rule violation. It is used to indicate that
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,8 @@ message KaspadMessage {
|
||||
IBDRootUTXOSetAndBlockMessage ibdRootUTXOSetAndBlock = 25;
|
||||
RequestIBDBlocksMessage requestIBDBlocks = 26;
|
||||
IBDRootNotFoundMessage ibdRootNotFound = 27;
|
||||
RequestIBDRootHash requestIBDRootHash = 28;
|
||||
IBDRootHash ibdRootHash = 29;
|
||||
|
||||
GetCurrentNetworkRequestMessage getCurrentNetworkRequest = 1001;
|
||||
GetCurrentNetworkResponseMessage getCurrentNetworkResponse = 1002;
|
||||
@ -297,6 +299,17 @@ message IBDRootNotFoundMessage{
|
||||
}
|
||||
// IBDRootNotFoundMessage end
|
||||
|
||||
// RequestIBDRootHash start
|
||||
message RequestIBDRootHash{
|
||||
}
|
||||
// RequestIBDRootHash end
|
||||
|
||||
// IBDRootHash start
|
||||
message IBDRootHash{
|
||||
Hash hash = 1;
|
||||
}
|
||||
// IBDRootHash end
|
||||
|
||||
service P2P {
|
||||
rpc MessageStream (stream KaspadMessage) returns (stream KaspadMessage) {}
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package protowire
|
||||
|
||||
import "github.com/kaspanet/kaspad/app/appmessage"
|
||||
|
||||
func (x *KaspadMessage_IbdRootHash) toAppMessage() (appmessage.Message, error) {
|
||||
hash, err := x.IbdRootHash.Hash.toDomain()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &appmessage.MsgIBDRootHash{Hash: hash}, nil
|
||||
}
|
||||
|
||||
func (x *KaspadMessage_IbdRootHash) fromAppMessage(msgIBDRootHash *appmessage.MsgIBDRootHash) error {
|
||||
x.IbdRootHash = &IBDRootHash{
|
||||
Hash: domainHashToProto(msgIBDRootHash.Hash),
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package protowire
|
||||
|
||||
import "github.com/kaspanet/kaspad/app/appmessage"
|
||||
|
||||
func (x *KaspadMessage_RequestIBDRootHash) toAppMessage() (appmessage.Message, error) {
|
||||
return &appmessage.MsgRequestIBDRootHash{}, nil
|
||||
}
|
||||
|
||||
func (x *KaspadMessage_RequestIBDRootHash) fromAppMessage(_ *appmessage.MsgRequestIBDRootHash) error {
|
||||
return nil
|
||||
}
|
@ -232,6 +232,22 @@ func toP2PPayload(message appmessage.Message) (isKaspadMessage_Payload, error) {
|
||||
}
|
||||
return payload, nil
|
||||
|
||||
case *appmessage.MsgRequestIBDRootHash:
|
||||
payload := new(KaspadMessage_RequestIBDRootHash)
|
||||
err := payload.fromAppMessage(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return payload, nil
|
||||
|
||||
case *appmessage.MsgIBDRootHash:
|
||||
payload := new(KaspadMessage_IbdRootHash)
|
||||
err := payload.fromAppMessage(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return payload, nil
|
||||
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user