mirror of
https://github.com/kaspanet/kaspad.git
synced 2026-02-21 11:17:05 +00:00
Compare commits
7 Commits
v0.8.8-dev
...
optimize-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5f2495522 | ||
|
|
8588774837 | ||
|
|
94c3f4b80c | ||
|
|
a5a84e9215 | ||
|
|
1cec4c91cf | ||
|
|
3c0b74208a | ||
|
|
08d983b84a |
7
.github/workflows/go.yml
vendored
7
.github/workflows/go.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
go-version: 1.14
|
||||
|
||||
|
||||
# Source: https://github.com/actions/cache/blob/main/examples.md#go---modules
|
||||
@@ -60,10 +60,11 @@ jobs:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
go-version: 1.14
|
||||
|
||||
- name: Create coverage file
|
||||
run: go test -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./...
|
||||
# Because of https://github.com/golang/go/issues/27333 this seem to "fail" even though nothing is wrong, so ignore the failure
|
||||
run: go test -json -covermode=atomic -coverpkg=./... -coverprofile coverage.txt ./... || true
|
||||
|
||||
- name: Upload coverage file
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
@@ -18,7 +18,7 @@ Kaspa is an attempt at a proof-of-work cryptocurrency with instant confirmations
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.15 or later.
|
||||
Go 1.14 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -65,7 +65,7 @@ is used for this project.
|
||||
|
||||
## Documentation
|
||||
|
||||
The [documentation](https://github.com/kaspanet/docs) is a work-in-progress
|
||||
The documentation is a work-in-progress.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/os/winservice"
|
||||
)
|
||||
|
||||
const leveldbCacheSizeMiB = 256
|
||||
|
||||
var desiredLimits = &limits.DesiredLimits{
|
||||
FileLimitWant: 2048,
|
||||
FileLimitMin: 1024,
|
||||
@@ -183,5 +181,5 @@ func removeDatabase(cfg *config.Config) error {
|
||||
func openDB(cfg *config.Config) (database.Database, error) {
|
||||
dbPath := databasePath(cfg)
|
||||
log.Infof("Loading database from '%s'", dbPath)
|
||||
return ldb.NewLevelDB(dbPath, leveldbCacheSizeMiB)
|
||||
return ldb.NewLevelDB(dbPath)
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ const (
|
||||
CmdPruningPointHash
|
||||
CmdIBDBlockLocator
|
||||
CmdIBDBlockLocatorHighestHash
|
||||
CmdIBDBlockLocatorHighestHashNotFound
|
||||
CmdBlockHeaders
|
||||
CmdRequestNextPruningPointUTXOSetChunk
|
||||
CmdDonePruningPointUTXOSetChunks
|
||||
@@ -124,12 +123,6 @@ const (
|
||||
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage
|
||||
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage
|
||||
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage
|
||||
CmdBanRequestMessage
|
||||
CmdBanResponseMessage
|
||||
CmdUnbanRequestMessage
|
||||
CmdUnbanResponseMessage
|
||||
CmdGetInfoRequestMessage
|
||||
CmdGetInfoResponseMessage
|
||||
)
|
||||
|
||||
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
|
||||
@@ -163,7 +156,6 @@ var ProtocolMessageCommandToString = map[MessageCommand]string{
|
||||
CmdPruningPointHash: "PruningPointHash",
|
||||
CmdIBDBlockLocator: "IBDBlockLocator",
|
||||
CmdIBDBlockLocatorHighestHash: "IBDBlockLocatorHighestHash",
|
||||
CmdIBDBlockLocatorHighestHashNotFound: "IBDBlockLocatorHighestHashNotFound",
|
||||
CmdBlockHeaders: "BlockHeaders",
|
||||
CmdRequestNextPruningPointUTXOSetChunk: "RequestNextPruningPointUTXOSetChunk",
|
||||
CmdDonePruningPointUTXOSetChunks: "DonePruningPointUTXOSetChunks",
|
||||
@@ -228,12 +220,6 @@ var RPCMessageCommandToString = map[MessageCommand]string{
|
||||
CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: "NotifyVirtualSelectedParentBlueScoreChangedRequest",
|
||||
CmdNotifyVirtualSelectedParentBlueScoreChangedResponseMessage: "NotifyVirtualSelectedParentBlueScoreChangedResponse",
|
||||
CmdVirtualSelectedParentBlueScoreChangedNotificationMessage: "VirtualSelectedParentBlueScoreChangedNotification",
|
||||
CmdBanRequestMessage: "BanRequest",
|
||||
CmdBanResponseMessage: "BanResponse",
|
||||
CmdUnbanRequestMessage: "UnbanRequest",
|
||||
CmdUnbanResponseMessage: "UnbanResponse",
|
||||
CmdGetInfoRequestMessage: "GetInfoRequestMessage",
|
||||
CmdGetInfoResponseMessage: "GeInfoResponseMessage",
|
||||
}
|
||||
|
||||
// Message is an interface that describes a kaspa message. A type that
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// MsgIBDBlockLocatorHighestHashNotFound represents a kaspa BlockLocatorHighestHashNotFound message
|
||||
type MsgIBDBlockLocatorHighestHashNotFound struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *MsgIBDBlockLocatorHighestHashNotFound) Command() MessageCommand {
|
||||
return CmdIBDBlockLocatorHighestHashNotFound
|
||||
}
|
||||
|
||||
// NewMsgIBDBlockLocatorHighestHashNotFound returns a new IBDBlockLocatorHighestHashNotFound message
|
||||
func NewMsgIBDBlockLocatorHighestHashNotFound() *MsgIBDBlockLocatorHighestHashNotFound {
|
||||
return &MsgIBDBlockLocatorHighestHashNotFound{}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// BanRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type BanRequestMessage struct {
|
||||
baseMessage
|
||||
|
||||
IP string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *BanRequestMessage) Command() MessageCommand {
|
||||
return CmdBanRequestMessage
|
||||
}
|
||||
|
||||
// NewBanRequestMessage returns an instance of the message
|
||||
func NewBanRequestMessage(ip string) *BanRequestMessage {
|
||||
return &BanRequestMessage{
|
||||
IP: ip,
|
||||
}
|
||||
}
|
||||
|
||||
// BanResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type BanResponseMessage struct {
|
||||
baseMessage
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *BanResponseMessage) Command() MessageCommand {
|
||||
return CmdBanResponseMessage
|
||||
}
|
||||
|
||||
// NewBanResponseMessage returns a instance of the message
|
||||
func NewBanResponseMessage() *BanResponseMessage {
|
||||
return &BanResponseMessage{}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ package appmessage
|
||||
// its respective RPC message
|
||||
type GetBlocksRequestMessage struct {
|
||||
baseMessage
|
||||
LowHash string
|
||||
IncludeBlockVerboseData bool
|
||||
IncludeTransactionVerboseData bool
|
||||
LowHash string
|
||||
IncludeBlockHexes bool
|
||||
IncludeBlockVerboseData bool
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -15,12 +15,11 @@ func (msg *GetBlocksRequestMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewGetBlocksRequestMessage returns a instance of the message
|
||||
func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
|
||||
includeTransactionVerboseData bool) *GetBlocksRequestMessage {
|
||||
func NewGetBlocksRequestMessage(lowHash string, includeBlockHexes bool, includeBlockVerboseData bool) *GetBlocksRequestMessage {
|
||||
return &GetBlocksRequestMessage{
|
||||
LowHash: lowHash,
|
||||
IncludeBlockVerboseData: includeBlockVerboseData,
|
||||
IncludeTransactionVerboseData: includeTransactionVerboseData,
|
||||
LowHash: lowHash,
|
||||
IncludeBlockHexes: includeBlockHexes,
|
||||
IncludeBlockVerboseData: includeBlockVerboseData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +28,7 @@ func NewGetBlocksRequestMessage(lowHash string, includeBlockVerboseData bool,
|
||||
type GetBlocksResponseMessage struct {
|
||||
baseMessage
|
||||
BlockHashes []string
|
||||
BlockHexes []string
|
||||
BlockVerboseData []*BlockVerboseData
|
||||
|
||||
Error *RPCError
|
||||
@@ -45,6 +45,7 @@ func NewGetBlocksResponseMessage(blockHashes []string, blockHexes []string,
|
||||
|
||||
return &GetBlocksResponseMessage{
|
||||
BlockHashes: blockHashes,
|
||||
BlockHexes: blockHexes,
|
||||
BlockVerboseData: blockVerboseData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// GetInfoRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetInfoRequestMessage struct {
|
||||
baseMessage
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetInfoRequestMessage) Command() MessageCommand {
|
||||
return CmdGetInfoRequestMessage
|
||||
}
|
||||
|
||||
// NewGeInfoRequestMessage returns a instance of the message
|
||||
func NewGeInfoRequestMessage() *GetInfoRequestMessage {
|
||||
return &GetInfoRequestMessage{}
|
||||
}
|
||||
|
||||
// GetInfoResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type GetInfoResponseMessage struct {
|
||||
baseMessage
|
||||
P2PID string
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *GetInfoResponseMessage) Command() MessageCommand {
|
||||
return CmdGetInfoResponseMessage
|
||||
}
|
||||
|
||||
// NewGetInfoResponseMessage returns a instance of the message
|
||||
func NewGetInfoResponseMessage(p2pID string) *GetInfoResponseMessage {
|
||||
return &GetInfoResponseMessage{
|
||||
P2PID: p2pID,
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,7 @@ func NewNotifyBlockAddedResponseMessage() *NotifyBlockAddedResponseMessage {
|
||||
// its respective RPC message
|
||||
type BlockAddedNotificationMessage struct {
|
||||
baseMessage
|
||||
Block *MsgBlock
|
||||
BlockVerboseData *BlockVerboseData
|
||||
Block *MsgBlock
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
@@ -47,9 +46,8 @@ func (msg *BlockAddedNotificationMessage) Command() MessageCommand {
|
||||
}
|
||||
|
||||
// NewBlockAddedNotificationMessage returns a instance of the message
|
||||
func NewBlockAddedNotificationMessage(block *MsgBlock, blockVerboseData *BlockVerboseData) *BlockAddedNotificationMessage {
|
||||
func NewBlockAddedNotificationMessage(block *MsgBlock) *BlockAddedNotificationMessage {
|
||||
return &BlockAddedNotificationMessage{
|
||||
Block: block,
|
||||
BlockVerboseData: blockVerboseData,
|
||||
Block: block,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package appmessage
|
||||
|
||||
// UnbanRequestMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type UnbanRequestMessage struct {
|
||||
baseMessage
|
||||
|
||||
IP string
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *UnbanRequestMessage) Command() MessageCommand {
|
||||
return CmdUnbanRequestMessage
|
||||
}
|
||||
|
||||
// NewUnbanRequestMessage returns an instance of the message
|
||||
func NewUnbanRequestMessage(ip string) *UnbanRequestMessage {
|
||||
return &UnbanRequestMessage{
|
||||
IP: ip,
|
||||
}
|
||||
}
|
||||
|
||||
// UnbanResponseMessage is an appmessage corresponding to
|
||||
// its respective RPC message
|
||||
type UnbanResponseMessage struct {
|
||||
baseMessage
|
||||
|
||||
Error *RPCError
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message
|
||||
func (msg *UnbanResponseMessage) Command() MessageCommand {
|
||||
return CmdUnbanResponseMessage
|
||||
}
|
||||
|
||||
// NewUnbanResponseMessage returns a instance of the message
|
||||
func NewUnbanResponseMessage() *UnbanResponseMessage {
|
||||
return &UnbanResponseMessage{}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
package blocklogger
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@@ -12,55 +13,46 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
receivedLogBlocks int64
|
||||
receivedLogHeaders int64
|
||||
receivedLogTransactions int64
|
||||
lastBlockLogTime time.Time
|
||||
receivedLogBlocks int64
|
||||
receivedLogTx int64
|
||||
lastBlockLogTime = mstime.Now()
|
||||
mtx sync.Mutex
|
||||
)
|
||||
|
||||
// LogBlock logs a new block blue score as an information message
|
||||
// to show progress to the user. In order to prevent spam, it limits logging to
|
||||
// one message every 10 seconds with duration and totals included.
|
||||
func LogBlock(block *externalapi.DomainBlock) {
|
||||
if len(block.Transactions) == 0 {
|
||||
receivedLogHeaders++
|
||||
} else {
|
||||
receivedLogBlocks++
|
||||
}
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
|
||||
receivedLogTransactions += int64(len(block.Transactions))
|
||||
receivedLogBlocks++
|
||||
receivedLogTx += int64(len(block.Transactions))
|
||||
|
||||
now := time.Now()
|
||||
now := mstime.Now()
|
||||
duration := now.Sub(lastBlockLogTime)
|
||||
if duration < time.Second*10 {
|
||||
return
|
||||
}
|
||||
|
||||
// Truncate the duration to 10s of milliseconds.
|
||||
truncatedDuration := duration.Round(10 * time.Millisecond)
|
||||
tDuration := duration.Round(10 * time.Millisecond)
|
||||
|
||||
// Log information about new block blue score.
|
||||
blockStr := "blocks"
|
||||
if receivedLogBlocks == 1 {
|
||||
blockStr = "block"
|
||||
}
|
||||
|
||||
txStr := "transactions"
|
||||
if receivedLogTransactions == 1 {
|
||||
if receivedLogTx == 1 {
|
||||
txStr = "transaction"
|
||||
}
|
||||
|
||||
headerStr := "headers"
|
||||
if receivedLogBlocks == 1 {
|
||||
headerStr = "header"
|
||||
}
|
||||
|
||||
log.Infof("Processed %d %s and %d %s in the last %s (%d %s, %s)",
|
||||
receivedLogBlocks, blockStr, receivedLogHeaders, headerStr, truncatedDuration, receivedLogTransactions,
|
||||
log.Infof("Processed %d %s in the last %s (%d %s, %s)",
|
||||
receivedLogBlocks, blockStr, tDuration, receivedLogTx,
|
||||
txStr, mstime.UnixMilliseconds(block.Header.TimeInMilliseconds()))
|
||||
|
||||
receivedLogBlocks = 0
|
||||
receivedLogHeaders = 0
|
||||
receivedLogTransactions = 0
|
||||
receivedLogTx = 0
|
||||
lastBlockLogTime = now
|
||||
}
|
||||
@@ -8,4 +8,4 @@ import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
)
|
||||
|
||||
var log, _ = logger.Get(logger.SubsystemTags.BDAG)
|
||||
var log, _ = logger.Get(logger.SubsystemTags.PROT)
|
||||
@@ -1,6 +1,7 @@
|
||||
package flowcontext
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/blocklogger"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/pkg/errors"
|
||||
@@ -37,6 +38,8 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock,
|
||||
}
|
||||
|
||||
for i, newBlock := range newBlocks {
|
||||
blocklogger.LogBlock(block)
|
||||
|
||||
log.Debugf("OnNewBlock: passing block %s transactions to mining manager", hash)
|
||||
_, err = f.Domain().MiningManager().HandleNewBlockTransactions(newBlock.Transactions)
|
||||
if err != nil {
|
||||
|
||||
@@ -22,7 +22,7 @@ func (*FlowContext) HandleError(err error, flowName string, isStopping *uint32,
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Errorf("error from %s: %s", flowName, err)
|
||||
log.Errorf("error from %s: %+v", flowName, err)
|
||||
}
|
||||
|
||||
if atomic.AddUint32(isStopping, 1) == 1 {
|
||||
|
||||
@@ -194,9 +194,6 @@ func (f *FlowContext) GetOrphanRoots(orphan *externalapi.DomainHash) ([]*externa
|
||||
|
||||
if !blockInfo.Exists || blockInfo.BlockStatus == externalapi.StatusHeaderOnly {
|
||||
roots = append(roots, current)
|
||||
} else {
|
||||
log.Debugf("Block %s was skipped when checking for orphan roots: "+
|
||||
"exists: %t, status: %s", current, blockInfo.Exists, blockInfo.BlockStatus)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -70,14 +70,8 @@ func HandleIBDBlockLocator(context HandleIBDBlockLocatorContext, incomingRoute *
|
||||
}
|
||||
|
||||
if !foundHighestHashInTheSelectedParentChainOfTargetHash {
|
||||
log.Warnf("no hash was found in the blockLocator "+
|
||||
return protocolerrors.Errorf(true, "no hash was found in the blockLocator "+
|
||||
"that was in the selected parent chain of targetHash %s", targetHash)
|
||||
|
||||
ibdBlockLocatorHighestHashNotFoundMessage := appmessage.NewMsgIBDBlockLocatorHighestHashNotFound()
|
||||
err = outgoingRoute.Enqueue(ibdBlockLocatorHighestHashNotFoundMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,10 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
|
||||
log.Debugf("IBD started with peer %s and highHash %s", flow.peer, highHash)
|
||||
|
||||
log.Debugf("Syncing headers up to %s", highHash)
|
||||
headersSynced, err := flow.syncHeaders(highHash)
|
||||
err := flow.syncHeaders(highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !headersSynced {
|
||||
log.Debugf("Aborting IBD because the headers failed to sync")
|
||||
return nil
|
||||
}
|
||||
log.Debugf("Finished syncing headers up to %s", highHash)
|
||||
|
||||
log.Debugf("Syncing the current pruning point UTXO set")
|
||||
@@ -59,61 +55,47 @@ func (flow *handleRelayInvsFlow) runIBDIfNotRunning(highHash *externalapi.Domain
|
||||
return nil
|
||||
}
|
||||
|
||||
// syncHeaders attempts to sync headers from the peer. This method may fail
|
||||
// because the peer and us have conflicting pruning points. In that case we
|
||||
// return (false, nil) so that we may stop IBD gracefully.
|
||||
func (flow *handleRelayInvsFlow) syncHeaders(highHash *externalapi.DomainHash) (bool, error) {
|
||||
log.Debugf("Trying to find highest shared chain block with peer %s with high hash %s", flow.peer, highHash)
|
||||
highestSharedBlockHash, highestSharedBlockFound, err := flow.findHighestSharedBlockHash(highHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !highestSharedBlockFound {
|
||||
return false, nil
|
||||
}
|
||||
log.Debugf("Found highest shared chain block %s with peer %s", highestSharedBlockHash, flow.peer)
|
||||
func (flow *handleRelayInvsFlow) syncHeaders(highHash *externalapi.DomainHash) error {
|
||||
highHashReceived := false
|
||||
for !highHashReceived {
|
||||
log.Debugf("Trying to find highest shared chain block with peer %s with high hash %s", flow.peer, highHash)
|
||||
highestSharedBlockHash, err := flow.findHighestSharedBlockHash(highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Found highest shared chain block %s with peer %s", highestSharedBlockHash, flow.peer)
|
||||
|
||||
err = flow.downloadHeaders(highestSharedBlockHash, highHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = flow.downloadHeaders(highestSharedBlockHash, highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the highHash has not been received, the peer is misbehaving
|
||||
highHashBlockInfo, err := flow.Domain().Consensus().GetBlockInfo(highHash)
|
||||
if err != nil {
|
||||
return false, err
|
||||
// We're finished once highHash has been inserted into the DAG
|
||||
blockInfo, err := flow.Domain().Consensus().GetBlockInfo(highHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
highHashReceived = blockInfo.Exists
|
||||
log.Debugf("Headers downloaded from peer %s. Are further headers required: %t", flow.peer, !highHashReceived)
|
||||
}
|
||||
if !highHashBlockInfo.Exists {
|
||||
return false, protocolerrors.Errorf(true, "did not receive "+
|
||||
"highHash header %s from peer %s during header download", highHash, flow.peer)
|
||||
}
|
||||
log.Debugf("Headers downloaded from peer %s", flow.peer)
|
||||
return true, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// findHighestSharedBlock attempts to find the highest shared block between the peer
|
||||
// and this node. This method may fail because the peer and us have conflicting pruning
|
||||
// points. In that case we return (nil, false, nil) so that we may stop IBD gracefully.
|
||||
func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(
|
||||
targetHash *externalapi.DomainHash) (*externalapi.DomainHash, bool, error) {
|
||||
|
||||
func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(targetHash *externalapi.DomainHash) (*externalapi.DomainHash, error) {
|
||||
log.Debugf("Sending a blockLocator to %s between pruning point and headers selected tip", flow.peer)
|
||||
blockLocator, err := flow.Domain().Consensus().CreateFullHeadersSelectedChainBlockLocator()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for {
|
||||
highestHash, highestHashFound, err := flow.fetchHighestHash(targetHash, blockLocator)
|
||||
highestHash, err := flow.fetchHighestHash(targetHash, blockLocator)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if !highestHashFound {
|
||||
return nil, false, nil
|
||||
return nil, err
|
||||
}
|
||||
highestHashIndex, err := flow.findHighestHashIndex(highestHash, blockLocator)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if highestHashIndex == 0 ||
|
||||
@@ -122,7 +104,7 @@ func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(
|
||||
// an endless loop, we explicitly stop the loop in such situation.
|
||||
(len(blockLocator) == 2 && highestHashIndex == 1) {
|
||||
|
||||
return highestHash, true, nil
|
||||
return highestHash, nil
|
||||
}
|
||||
|
||||
locatorHashAboveHighestHash := highestHash
|
||||
@@ -132,7 +114,7 @@ func (flow *handleRelayInvsFlow) findHighestSharedBlockHash(
|
||||
|
||||
blockLocator, err = flow.nextBlockLocator(highestHash, locatorHashAboveHighestHash)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,35 +159,27 @@ func (flow *handleRelayInvsFlow) findHighestHashIndex(
|
||||
return highestHashIndex, nil
|
||||
}
|
||||
|
||||
// fetchHighestHash attempts to fetch the highest hash the peer knows amongst the given
|
||||
// blockLocator. This method may fail because the peer and us have conflicting pruning
|
||||
// points. In that case we return (nil, false, nil) so that we may stop IBD gracefully.
|
||||
func (flow *handleRelayInvsFlow) fetchHighestHash(
|
||||
targetHash *externalapi.DomainHash, blockLocator externalapi.BlockLocator) (*externalapi.DomainHash, bool, error) {
|
||||
targetHash *externalapi.DomainHash, blockLocator externalapi.BlockLocator) (*externalapi.DomainHash, error) {
|
||||
|
||||
ibdBlockLocatorMessage := appmessage.NewMsgIBDBlockLocator(targetHash, blockLocator)
|
||||
err := flow.outgoingRoute.Enqueue(ibdBlockLocatorMessage)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
message, err := flow.dequeueIncomingMessageAndSkipInvs(common.DefaultTimeout)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
switch message := message.(type) {
|
||||
case *appmessage.MsgIBDBlockLocatorHighestHash:
|
||||
highestHash := message.HighestHash
|
||||
log.Debugf("The highest hash the peer %s knows is %s", flow.peer, highestHash)
|
||||
|
||||
return highestHash, true, nil
|
||||
case *appmessage.MsgIBDBlockLocatorHighestHashNotFound:
|
||||
log.Debugf("Peer %s does not know any block within our blockLocator. "+
|
||||
"This should only happen if there's a DAG split deeper than the pruning point.", flow.peer)
|
||||
return nil, false, nil
|
||||
default:
|
||||
return nil, false, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
ibdBlockLocatorHighestHashMessage, ok := message.(*appmessage.MsgIBDBlockLocatorHighestHash)
|
||||
if !ok {
|
||||
return nil, protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||
"expected: %s, got: %s", appmessage.CmdIBDBlockLocatorHighestHash, message.Command())
|
||||
}
|
||||
highestHash := ibdBlockLocatorHighestHashMessage.HighestHash
|
||||
log.Debugf("The highest hash the peer %s knows is %s", flow.peer, highestHash)
|
||||
|
||||
return highestHash, nil
|
||||
}
|
||||
|
||||
func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externalapi.DomainHash,
|
||||
@@ -221,6 +195,7 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
|
||||
// headers
|
||||
blockHeadersMessageChan := make(chan *appmessage.BlockHeadersMessage, 2)
|
||||
errChan := make(chan error)
|
||||
doneChan := make(chan interface{})
|
||||
spawn("handleRelayInvsFlow-downloadHeaders", func() {
|
||||
for {
|
||||
blockHeadersMessage, doneIBD, err := flow.receiveHeaders()
|
||||
@@ -229,7 +204,7 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
|
||||
return
|
||||
}
|
||||
if doneIBD {
|
||||
close(blockHeadersMessageChan)
|
||||
doneChan <- struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -245,10 +220,7 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
|
||||
|
||||
for {
|
||||
select {
|
||||
case blockHeadersMessage, ok := <-blockHeadersMessageChan:
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
case blockHeadersMessage := <-blockHeadersMessageChan:
|
||||
for _, header := range blockHeadersMessage.BlockHeaders {
|
||||
err = flow.processHeader(header)
|
||||
if err != nil {
|
||||
@@ -257,6 +229,8 @@ func (flow *handleRelayInvsFlow) downloadHeaders(highestSharedBlockHash *externa
|
||||
}
|
||||
case err := <-errChan:
|
||||
return err
|
||||
case <-doneChan:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -494,13 +468,6 @@ func (flow *handleRelayInvsFlow) syncMissingBlockBodies(highHash *externalapi.Do
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(hashes) == 0 {
|
||||
// Blocks can be inserted inside the DAG during IBD if those were requested before IBD started.
|
||||
// In rare cases, all the IBD blocks might be already inserted by the time we reach this point.
|
||||
// In these cases - GetMissingBlockBodyHashes would return an empty array.
|
||||
log.Debugf("No missing block body hashes found.")
|
||||
return nil
|
||||
}
|
||||
|
||||
for offset := 0; offset < len(hashes); offset += ibdBatchSize {
|
||||
var hashesToRequest []*externalapi.DomainHash
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package blockrelay
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
"github.com/kaspanet/kaspad/domain"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// SendVirtualSelectedParentInvContext is the interface for the context needed for the SendVirtualSelectedParentInv flow.
|
||||
type SendVirtualSelectedParentInvContext interface {
|
||||
Domain() domain.Domain
|
||||
}
|
||||
|
||||
// SendVirtualSelectedParentInv sends a peer the selected parent hash of the virtual
|
||||
func SendVirtualSelectedParentInv(context SendVirtualSelectedParentInvContext,
|
||||
outgoingRoute *router.Route, peer *peerpkg.Peer) error {
|
||||
|
||||
virtualSelectedParent, err := context.Domain().Consensus().GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Sending virtual selected parent hash %s to peer %s", virtualSelectedParent, peer)
|
||||
|
||||
virtualSelectedParentInv := appmessage.NewMsgInvBlock(virtualSelectedParent)
|
||||
return outgoingRoute.Enqueue(virtualSelectedParentInv)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func (flow *receiveVersionFlow) start() (*appmessage.NetAddress, error) {
|
||||
}
|
||||
|
||||
if !allowSelfConnections && flow.NetAdapter().ID().IsEqual(msgVersion.ID) {
|
||||
return nil, protocolerrors.New(false, "connected to self")
|
||||
return nil, protocolerrors.New(true, "connected to self")
|
||||
}
|
||||
|
||||
// Disconnect and ban peers from a different network
|
||||
|
||||
@@ -2,10 +2,6 @@ package testing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/blockrelay"
|
||||
peerpkg "github.com/kaspanet/kaspad/app/protocol/peer"
|
||||
@@ -22,6 +18,8 @@ import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
"github.com/pkg/errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var orphanBlock = &externalapi.DomainBlock{
|
||||
@@ -107,11 +105,6 @@ type fakeRelayInvsContext struct {
|
||||
validateAndInsertImportedPruningPointResponse error
|
||||
getBlockInfoResponse *externalapi.BlockInfo
|
||||
validateAndInsertBlockResponse error
|
||||
rwLock sync.RWMutex
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
panic(errors.Errorf("called unimplemented function from test '%s'", f.testName))
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData, transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error) {
|
||||
@@ -135,8 +128,6 @@ func (f *fakeRelayInvsContext) GetBlockHeader(blockHash *externalapi.DomainHash)
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetBlockInfo(blockHash *externalapi.DomainHash) (*externalapi.BlockInfo, error) {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
if f.getBlockInfoResponse != nil {
|
||||
return f.getBlockInfoResponse, nil
|
||||
}
|
||||
@@ -176,8 +167,6 @@ func (f *fakeRelayInvsContext) AppendImportedPruningPointUTXOs(outpointAndUTXOEn
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) ValidateAndInsertImportedPruningPoint(newPruningPoint *externalapi.DomainBlock) error {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
return f.validateAndInsertImportedPruningPointResponse
|
||||
}
|
||||
|
||||
@@ -190,16 +179,12 @@ func (f *fakeRelayInvsContext) CreateBlockLocator(lowHash, highHash *externalapi
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) CreateHeadersSelectedChainBlockLocator(lowHash, highHash *externalapi.DomainHash) (externalapi.BlockLocator, error) {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
return externalapi.BlockLocator{
|
||||
f.params.GenesisHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) CreateFullHeadersSelectedChainBlockLocator() (externalapi.BlockLocator, error) {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
return externalapi.BlockLocator{
|
||||
f.params.GenesisHash,
|
||||
}, nil
|
||||
@@ -218,8 +203,6 @@ func (f *fakeRelayInvsContext) GetVirtualInfo() (*externalapi.VirtualInfo, error
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) IsValidPruningPoint(blockHash *externalapi.DomainHash) (bool, error) {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
return f.isValidPruningPointResponse, nil
|
||||
}
|
||||
|
||||
@@ -248,8 +231,6 @@ func (f *fakeRelayInvsContext) Domain() domain.Domain {
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) Config() *config.Config {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
return &config.Config{
|
||||
Flags: &config.Flags{
|
||||
NetworkFlags: config.NetworkFlags{
|
||||
@@ -288,59 +269,13 @@ func (f *fakeRelayInvsContext) IsIBDRunning() bool {
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) TrySetIBDRunning(ibdPeer *peerpkg.Peer) bool {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
return f.trySetIBDRunningResponse
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) UnsetIBDRunning() {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
close(f.finishedIBD)
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) SetValidateAndInsertBlockResponse(err error) {
|
||||
f.rwLock.Lock()
|
||||
defer f.rwLock.Unlock()
|
||||
f.validateAndInsertBlockResponse = err
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) SetValidateAndInsertImportedPruningPointResponse(err error) {
|
||||
f.rwLock.Lock()
|
||||
defer f.rwLock.Unlock()
|
||||
f.validateAndInsertImportedPruningPointResponse = err
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) SetGetBlockInfoResponse(info externalapi.BlockInfo) {
|
||||
f.rwLock.Lock()
|
||||
defer f.rwLock.Unlock()
|
||||
f.getBlockInfoResponse = &info
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) SetTrySetIBDRunningResponse(b bool) {
|
||||
f.rwLock.Lock()
|
||||
defer f.rwLock.Unlock()
|
||||
f.trySetIBDRunningResponse = b
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) SetIsValidPruningPointResponse(b bool) {
|
||||
f.rwLock.Lock()
|
||||
defer f.rwLock.Unlock()
|
||||
f.isValidPruningPointResponse = b
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetGenesisHeader() externalapi.BlockHeader {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
return f.params.GenesisBlock.Header
|
||||
}
|
||||
|
||||
func (f *fakeRelayInvsContext) GetFinishedIBDChan() chan struct{} {
|
||||
f.rwLock.RLock()
|
||||
defer f.rwLock.RUnlock()
|
||||
return f.finishedIBD
|
||||
}
|
||||
|
||||
func TestHandleRelayInvs(t *testing.T) {
|
||||
triggerIBD := func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
||||
err := incomingRoute.Enqueue(appmessage.NewMsgInvBlock(consensushashing.BlockHash(orphanBlock)))
|
||||
@@ -354,7 +289,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestRelayBlocks)
|
||||
|
||||
context.SetValidateAndInsertBlockResponse(ruleerrors.NewErrMissingParents(orphanBlock.Header.ParentHashes()))
|
||||
context.validateAndInsertBlockResponse = ruleerrors.NewErrMissingParents(orphanBlock.Header.ParentHashes())
|
||||
defer func() {
|
||||
context.validateAndInsertBlockResponse = nil
|
||||
}()
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.DomainBlockToMsgBlock(orphanBlock))
|
||||
if err != nil {
|
||||
@@ -404,10 +342,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
name: "sending a known invalid inv",
|
||||
funcToExecute: func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
||||
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusInvalid,
|
||||
})
|
||||
}
|
||||
|
||||
err := incomingRoute.Enqueue(appmessage.NewMsgInvBlock(knownInvalidBlockHash))
|
||||
if err != nil {
|
||||
@@ -464,7 +402,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestRelayBlocks)
|
||||
|
||||
context.SetValidateAndInsertBlockResponse(ruleerrors.ErrBadMerkleRoot)
|
||||
context.validateAndInsertBlockResponse = ruleerrors.ErrBadMerkleRoot
|
||||
err = incomingRoute.Enqueue(appmessage.DomainBlockToMsgBlock(invalidBlock))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
@@ -488,7 +426,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestRelayBlocks)
|
||||
|
||||
context.SetValidateAndInsertBlockResponse(ruleerrors.NewErrMissingParents(orphanBlock.Header.ParentHashes()))
|
||||
context.validateAndInsertBlockResponse = ruleerrors.NewErrMissingParents(orphanBlock.Header.ParentHashes())
|
||||
err = incomingRoute.Enqueue(appmessage.DomainBlockToMsgBlock(orphanBlock))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
@@ -514,7 +452,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
{
|
||||
name: "starting IBD when peer is already in IBD",
|
||||
funcToExecute: func(t *testing.T, incomingRoute, outgoingRoute *router.Route, context *fakeRelayInvsContext) {
|
||||
context.SetTrySetIBDRunningResponse(false)
|
||||
context.trySetIBDRunningResponse = false
|
||||
triggerIBD(t, incomingRoute, outgoingRoute, context)
|
||||
|
||||
checkNoActivity(t, outgoingRoute)
|
||||
@@ -620,15 +558,15 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestHeaders)
|
||||
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(
|
||||
appmessage.NewBlockHeadersMessage(
|
||||
[]*appmessage.MsgBlockHeader{
|
||||
appmessage.DomainBlockHeaderToBlockHeader(context.GetGenesisHeader())},
|
||||
appmessage.DomainBlockHeaderToBlockHeader(context.params.GenesisBlock.Header)},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -643,10 +581,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
// Finish the IBD by sending DoneHeaders and send incompatible pruning point
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
@@ -660,7 +598,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestPruningPointHashMessage)
|
||||
|
||||
context.SetIsValidPruningPointResponse(false)
|
||||
context.isValidPruningPointResponse = false
|
||||
err = incomingRoute.Enqueue(appmessage.NewPruningPointHashMessage(invalidPruningPointHash))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
@@ -692,11 +630,11 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestHeaders)
|
||||
|
||||
context.SetValidateAndInsertBlockResponse(ruleerrors.ErrDuplicateBlock)
|
||||
context.validateAndInsertBlockResponse = ruleerrors.ErrDuplicateBlock
|
||||
err = incomingRoute.Enqueue(
|
||||
appmessage.NewBlockHeadersMessage(
|
||||
[]*appmessage.MsgBlockHeader{
|
||||
appmessage.DomainBlockHeaderToBlockHeader(context.GetGenesisHeader())},
|
||||
appmessage.DomainBlockHeaderToBlockHeader(context.params.GenesisBlock.Header)},
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -711,10 +649,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
// Finish the IBD by sending DoneHeaders and send incompatible pruning point
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
@@ -728,7 +666,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestPruningPointHashMessage)
|
||||
|
||||
context.SetIsValidPruningPointResponse(false)
|
||||
context.isValidPruningPointResponse = false
|
||||
err = incomingRoute.Enqueue(appmessage.NewPruningPointHashMessage(invalidPruningPointHash))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
@@ -760,7 +698,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestHeaders)
|
||||
|
||||
context.SetValidateAndInsertBlockResponse(ruleerrors.ErrBadMerkleRoot)
|
||||
context.validateAndInsertBlockResponse = ruleerrors.ErrBadMerkleRoot
|
||||
err = incomingRoute.Enqueue(
|
||||
appmessage.NewBlockHeadersMessage(
|
||||
[]*appmessage.MsgBlockHeader{
|
||||
@@ -800,10 +738,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -852,10 +790,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -868,7 +806,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestPruningPointHashMessage)
|
||||
|
||||
context.SetIsValidPruningPointResponse(false)
|
||||
context.isValidPruningPointResponse = false
|
||||
err = incomingRoute.Enqueue(appmessage.NewPruningPointHashMessage(invalidPruningPointHash))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
@@ -902,10 +840,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -967,10 +905,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -1030,10 +968,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -1099,10 +1037,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -1126,7 +1064,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestPruningPointUTXOSetAndBlock)
|
||||
|
||||
context.SetValidateAndInsertImportedPruningPointResponse(ruleerrors.ErrBadMerkleRoot)
|
||||
context.validateAndInsertImportedPruningPointResponse = ruleerrors.ErrBadMerkleRoot
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgIBDBlock(appmessage.DomainBlockToMsgBlock(invalidPruningPointBlock)))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
@@ -1166,10 +1104,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -1193,7 +1131,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestPruningPointUTXOSetAndBlock)
|
||||
|
||||
context.SetValidateAndInsertImportedPruningPointResponse(ruleerrors.ErrSuggestedPruningViolatesFinality)
|
||||
context.validateAndInsertImportedPruningPointResponse = ruleerrors.ErrSuggestedPruningViolatesFinality
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgIBDBlock(appmessage.DomainBlockToMsgBlock(validPruningPointBlock)))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
@@ -1230,10 +1168,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -1309,10 +1247,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -1386,10 +1324,10 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
|
||||
// This is done so it'll think it added the high hash to the DAG and proceed with fetching
|
||||
// the pruning point UTXO set.
|
||||
context.SetGetBlockInfoResponse(externalapi.BlockInfo{
|
||||
context.getBlockInfoResponse = &externalapi.BlockInfo{
|
||||
Exists: true,
|
||||
BlockStatus: externalapi.StatusHeaderOnly,
|
||||
})
|
||||
}
|
||||
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgDoneHeaders())
|
||||
if err != nil {
|
||||
@@ -1429,7 +1367,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
}
|
||||
_ = msg.(*appmessage.MsgRequestIBDBlocks)
|
||||
|
||||
context.SetValidateAndInsertImportedPruningPointResponse(ruleerrors.ErrBadMerkleRoot)
|
||||
context.validateAndInsertBlockResponse = ruleerrors.ErrBadMerkleRoot
|
||||
err = incomingRoute.Enqueue(appmessage.NewMsgIBDBlock(appmessage.DomainBlockToMsgBlock(invalidBlock)))
|
||||
if err != nil {
|
||||
t.Fatalf("Enqueue: %+v", err)
|
||||
@@ -1473,17 +1411,17 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
select {
|
||||
case err := <-errChan:
|
||||
checkFlowError(t, err, test.expectsProtocolError, test.expectsBan, test.expectsErrToContain)
|
||||
case <-time.After(10 * time.Second):
|
||||
t.Fatalf("waiting for error timed out after %s", 10*time.Second)
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("waiting for error timed out after %s", time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-context.GetFinishedIBDChan():
|
||||
case <-context.finishedIBD:
|
||||
if !test.expectsIBDToFinish {
|
||||
t.Fatalf("IBD unexpecetedly finished")
|
||||
}
|
||||
case <-time.After(10 * time.Second):
|
||||
case <-time.After(time.Second):
|
||||
if test.expectsIBDToFinish {
|
||||
t.Fatalf("IBD didn't finished after %d", time.Second)
|
||||
}
|
||||
@@ -1498,7 +1436,7 @@ func TestHandleRelayInvs(t *testing.T) {
|
||||
if !errors.Is(err, router.ErrRouteClosed) {
|
||||
t.Fatalf("unexpected error %+v", err)
|
||||
}
|
||||
case <-time.After(10 * time.Second):
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("waiting for flow to finish timed out after %s", time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
package testing
|
||||
|
||||
// Because of a bug in Go coverage fails if you have packages with test files only. See https://github.com/golang/go/issues/27333
|
||||
// So this is a dummy non-test go file in the package.
|
||||
@@ -2,7 +2,6 @@ package protocol
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/protocol/flows/rejects"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/connmanager"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
@@ -80,7 +79,7 @@ func (m *Manager) handleError(err error, netConnection *netadapter.NetConnection
|
||||
log.Warnf("Banning %s (reason: %s)", netConnection, protocolErr.Cause)
|
||||
|
||||
err := m.context.ConnectionManager().Ban(netConnection)
|
||||
if !errors.Is(err, connmanager.ErrCannotBanPermanent) {
|
||||
if err != nil && !errors.Is(err, addressmanager.ErrAddressNotFound) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -136,16 +135,11 @@ func (m *Manager) registerBlockRelayFlows(router *routerpkg.Router, isStopping *
|
||||
outgoingRoute := router.OutgoingRoute()
|
||||
|
||||
return []*flow{
|
||||
m.registerOneTimeFlow("SendVirtualSelectedParentInv", router, []appmessage.MessageCommand{},
|
||||
isStopping, errChan, func(route *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return blockrelay.SendVirtualSelectedParentInv(m.context, outgoingRoute, peer)
|
||||
}),
|
||||
|
||||
m.registerFlow("HandleRelayInvs", router, []appmessage.MessageCommand{
|
||||
appmessage.CmdInvRelayBlock, appmessage.CmdBlock, appmessage.CmdBlockLocator, appmessage.CmdIBDBlock,
|
||||
appmessage.CmdDoneHeaders, appmessage.CmdUnexpectedPruningPoint, appmessage.CmdPruningPointUTXOSetChunk,
|
||||
appmessage.CmdBlockHeaders, appmessage.CmdPruningPointHash, appmessage.CmdIBDBlockLocatorHighestHash,
|
||||
appmessage.CmdIBDBlockLocatorHighestHashNotFound, appmessage.CmdDonePruningPointUTXOSetChunks},
|
||||
appmessage.CmdDonePruningPointUTXOSetChunks},
|
||||
isStopping, errChan, func(incomingRoute *routerpkg.Route, peer *peerpkg.Peer) error {
|
||||
return blockrelay.HandleRelayInvs(m.context, incomingRoute,
|
||||
outgoingRoute, peer)
|
||||
|
||||
@@ -69,12 +69,7 @@ func (m *Manager) NotifyBlockAddedToDAG(block *externalapi.DomainBlock, blockIns
|
||||
return err
|
||||
}
|
||||
|
||||
msgBlock := appmessage.DomainBlockToMsgBlock(block)
|
||||
blockVerboseData, err := m.context.BuildBlockVerboseData(block.Header, block, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(msgBlock, blockVerboseData)
|
||||
blockAddedNotification := appmessage.NewBlockAddedNotificationMessage(appmessage.DomainBlockToMsgBlock(block))
|
||||
return m.context.NotificationManager.NotifyBlockAdded(blockAddedNotification)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,6 @@ var handlers = map[appmessage.MessageCommand]handler{
|
||||
appmessage.CmdGetUTXOsByAddressesRequestMessage: rpchandlers.HandleGetUTXOsByAddresses,
|
||||
appmessage.CmdGetVirtualSelectedParentBlueScoreRequestMessage: rpchandlers.HandleGetVirtualSelectedParentBlueScore,
|
||||
appmessage.CmdNotifyVirtualSelectedParentBlueScoreChangedRequestMessage: rpchandlers.HandleNotifyVirtualSelectedParentBlueScoreChanged,
|
||||
appmessage.CmdBanRequestMessage: rpchandlers.HandleBan,
|
||||
appmessage.CmdUnbanRequestMessage: rpchandlers.HandleUnban,
|
||||
appmessage.CmdGetInfoRequestMessage: rpchandlers.HandleGetInfo,
|
||||
}
|
||||
|
||||
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"math"
|
||||
"math/big"
|
||||
@@ -24,14 +23,8 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/pointers"
|
||||
)
|
||||
|
||||
// BuildBlockVerboseData builds a BlockVerboseData from the given blockHeader.
|
||||
// A block may optionally also be given if it's available in the calling context.
|
||||
func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, block *externalapi.DomainBlock,
|
||||
includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildBlockVerboseData")
|
||||
defer onEnd()
|
||||
|
||||
// BuildBlockVerboseData builds a BlockVerboseData from the given block.
|
||||
func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, includeTransactionVerboseData bool) (*appmessage.BlockVerboseData, error) {
|
||||
hash := consensushashing.HeaderHash(blockHeader)
|
||||
|
||||
blockInfo, err := ctx.Domain.Consensus().GetBlockInfo(hash)
|
||||
@@ -55,11 +48,9 @@ func (ctx *Context) BuildBlockVerboseData(blockHeader externalapi.BlockHeader, b
|
||||
}
|
||||
|
||||
if blockInfo.BlockStatus != externalapi.StatusHeaderOnly {
|
||||
if block == nil {
|
||||
block, err = ctx.Domain.Consensus().GetBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, err := ctx.Domain.Consensus().GetBlock(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txIDs := make([]string, len(block.Transactions))
|
||||
@@ -109,9 +100,6 @@ func (ctx *Context) BuildTransactionVerboseData(tx *externalapi.DomainTransactio
|
||||
blockHeader externalapi.BlockHeader, blockHash string) (
|
||||
*appmessage.TransactionVerboseData, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "BuildTransactionVerboseData")
|
||||
defer onEnd()
|
||||
|
||||
var payloadHash string
|
||||
if tx.SubnetworkID != subnetworks.SubnetworkIDNative {
|
||||
payloadHash = tx.PayloadHash.String()
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"net"
|
||||
)
|
||||
|
||||
// HandleBan handles the respectively named RPC command
|
||||
func HandleBan(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
banRequest := request.(*appmessage.BanRequestMessage)
|
||||
ip := net.ParseIP(banRequest.IP)
|
||||
if ip == nil {
|
||||
errorMessage := &appmessage.BanResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not parse IP %s", banRequest.IP)
|
||||
return errorMessage, nil
|
||||
}
|
||||
|
||||
err := context.ConnectionManager.BanByIP(ip)
|
||||
if err != nil {
|
||||
errorMessage := &appmessage.BanResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not ban IP: %s", err)
|
||||
return errorMessage, nil
|
||||
}
|
||||
response := appmessage.NewBanResponseMessage()
|
||||
return response, nil
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func HandleGetBlock(context *rpccontext.Context, _ *router.Router, request appme
|
||||
|
||||
response := appmessage.NewGetBlockResponseMessage()
|
||||
|
||||
blockVerboseData, err := context.BuildBlockVerboseData(header, nil, getBlockRequest.IncludeTransactionVerboseData)
|
||||
blockVerboseData, err := context.BuildBlockVerboseData(header, getBlockRequest.IncludeTransactionVerboseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package rpchandlers
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashes"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
@@ -16,77 +14,7 @@ const (
|
||||
|
||||
// HandleGetBlocks handles the respectively named RPC command
|
||||
func HandleGetBlocks(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
getBlocksRequest := request.(*appmessage.GetBlocksRequestMessage)
|
||||
|
||||
// Validate that user didn't set IncludeTransactionVerboseData without setting IncludeBlockVerboseData
|
||||
if !getBlocksRequest.IncludeBlockVerboseData && getBlocksRequest.IncludeTransactionVerboseData {
|
||||
return &appmessage.GetBlocksResponseMessage{
|
||||
Error: appmessage.RPCErrorf(
|
||||
"If includeTransactionVerboseData is set, then includeBlockVerboseData must be set as well"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Decode lowHash
|
||||
// If lowHash is empty - use genesis instead.
|
||||
lowHash := context.Config.ActiveNetParams.GenesisHash
|
||||
if getBlocksRequest.LowHash != "" {
|
||||
var err error
|
||||
lowHash, err = externalapi.NewDomainHashFromString(getBlocksRequest.LowHash)
|
||||
if err != nil {
|
||||
return &appmessage.GetBlocksResponseMessage{
|
||||
Error: appmessage.RPCErrorf("Could not decode lowHash %s: %s", getBlocksRequest.LowHash, err),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Get hashes between lowHash and virtualSelectedParent
|
||||
virtualSelectedParent, err := context.Domain.Consensus().GetVirtualSelectedParent()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockHashes, err := context.Domain.Consensus().GetHashesBetween(
|
||||
lowHash, virtualSelectedParent, maxBlocksInGetBlocksResponse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there are no maxBlocksInGetBlocksResponse between lowHash and virtualSelectedParent -
|
||||
// add virtualSelectedParent's anticone
|
||||
if len(blockHashes) < maxBlocksInGetBlocksResponse {
|
||||
virtualSelectedParentAnticone, err := context.Domain.Consensus().Anticone(virtualSelectedParent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockHashes = append(blockHashes, virtualSelectedParentAnticone...)
|
||||
}
|
||||
|
||||
// Both GetHashesBetween and Anticone might return more then the allowed number of blocks, so
|
||||
// trim any extra blocks.
|
||||
if len(blockHashes) > maxBlocksInGetBlocksResponse {
|
||||
blockHashes = blockHashes[:maxBlocksInGetBlocksResponse]
|
||||
}
|
||||
|
||||
// Prepare the response
|
||||
response := &appmessage.GetBlocksResponseMessage{
|
||||
BlockHashes: hashes.ToStrings(blockHashes),
|
||||
}
|
||||
|
||||
// Retrieve all block data in case BlockVerboseData was requested
|
||||
if getBlocksRequest.IncludeBlockVerboseData {
|
||||
response.BlockVerboseData = make([]*appmessage.BlockVerboseData, len(blockHashes))
|
||||
for i, blockHash := range blockHashes {
|
||||
blockHeader, err := context.Domain.Consensus().GetBlockHeader(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockVerboseData, err := context.BuildBlockVerboseData(blockHeader, nil,
|
||||
getBlocksRequest.IncludeTransactionVerboseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.BlockVerboseData[i] = blockVerboseData
|
||||
}
|
||||
}
|
||||
response := &appmessage.GetBlocksResponseMessage{}
|
||||
response.Error = appmessage.RPCErrorf("not implemented")
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
)
|
||||
|
||||
// HandleGetInfo handles the respectively named RPC command
|
||||
func HandleGetInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
|
||||
response := appmessage.NewGetInfoResponseMessage(context.NetAdapter.ID().String())
|
||||
return response, nil
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package rpchandlers
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
|
||||
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
|
||||
"net"
|
||||
)
|
||||
|
||||
// HandleUnban handles the respectively named RPC command
|
||||
func HandleUnban(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
|
||||
unbanRequest := request.(*appmessage.UnbanRequestMessage)
|
||||
ip := net.ParseIP(unbanRequest.IP)
|
||||
if ip == nil {
|
||||
errorMessage := &appmessage.UnbanResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not parse IP %s", unbanRequest.IP)
|
||||
return errorMessage, nil
|
||||
}
|
||||
err := context.AddressManager.Unban(appmessage.NewNetAddressIPPort(ip, 0, 0))
|
||||
if err != nil {
|
||||
errorMessage := &appmessage.UnbanResponseMessage{}
|
||||
errorMessage.Error = appmessage.RPCErrorf("Could not unban IP: %s", err)
|
||||
return errorMessage, nil
|
||||
}
|
||||
response := appmessage.NewUnbanResponseMessage()
|
||||
return response, nil
|
||||
}
|
||||
@@ -4,7 +4,7 @@ kaspactl is an RPC client for kaspad
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.15 or later.
|
||||
Go 1.14 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ var commandTypes = []reflect.Type{
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetConnectedPeerInfoRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetPeerAddressesRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetCurrentNetworkRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetInfoRequest{}),
|
||||
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetBlockRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetBlocksRequest{}),
|
||||
@@ -33,9 +32,6 @@ var commandTypes = []reflect.Type{
|
||||
reflect.TypeOf(protowire.KaspadMessage_SubmitTransactionRequest{}),
|
||||
|
||||
reflect.TypeOf(protowire.KaspadMessage_GetUtxosByAddressesRequest{}),
|
||||
|
||||
reflect.TypeOf(protowire.KaspadMessage_BanRequest{}),
|
||||
reflect.TypeOf(protowire.KaspadMessage_UnbanRequest{}),
|
||||
}
|
||||
|
||||
type commandDescription struct {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.15-alpine AS build
|
||||
FROM golang:1.14-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Kaspaminer is a CPU-based miner for kaspad
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.15 or later.
|
||||
Go 1.14 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -17,9 +17,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultLogFilename = "kaspaminer.log"
|
||||
defaultErrLogFilename = "kaspaminer_err.log"
|
||||
defaultTargetBlockRateRatio = 2.0
|
||||
defaultLogFilename = "kaspaminer.log"
|
||||
defaultErrLogFilename = "kaspaminer_err.log"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,13 +30,13 @@ var (
|
||||
)
|
||||
|
||||
type configFlags struct {
|
||||
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
||||
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
|
||||
MiningAddr string `long:"miningaddr" description:"Address to mine to"`
|
||||
NumberOfBlocks uint64 `short:"n" long:"numblocks" description:"Number of blocks to mine. If omitted, will mine until the process is interrupted."`
|
||||
MineWhenNotSynced bool `long:"mine-when-not-synced" description:"Mine even if the node is not synced with the rest of the network."`
|
||||
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
|
||||
TargetBlocksPerSecond *float64 `long:"target-blocks-per-second" description:"Sets a maximum block rate. 0 means no limit (The default one is 2 * target network block rate)"`
|
||||
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
|
||||
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
|
||||
MiningAddr string `long:"miningaddr" description:"Address to mine to"`
|
||||
NumberOfBlocks uint64 `short:"n" long:"numblocks" description:"Number of blocks to mine. If omitted, will mine until the process is interrupted."`
|
||||
MineWhenNotSynced bool `long:"mine-when-not-synced" description:"Mine even if the node is not synced with the rest of the network."`
|
||||
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
|
||||
TargetBlocksPerSecond float64 `long:"target-blocks-per-second" description:"Sets a maximum block rate. This flag is for debugging purposes."`
|
||||
config.NetworkFlags
|
||||
}
|
||||
|
||||
@@ -65,11 +64,6 @@ func parseConfig() (*configFlags, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg.TargetBlocksPerSecond == nil {
|
||||
targetBlocksPerSecond := defaultTargetBlockRateRatio / cfg.NetParams().TargetTimePerBlock.Seconds()
|
||||
cfg.TargetBlocksPerSecond = &targetBlocksPerSecond
|
||||
}
|
||||
|
||||
if cfg.Profile != "" {
|
||||
profilePort, err := strconv.Atoi(cfg.Profile)
|
||||
if err != nil || profilePort < 1024 || profilePort > 65535 {
|
||||
@@ -77,10 +71,6 @@ func parseConfig() (*configFlags, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.MiningAddr == "" {
|
||||
return nil, errors.New("--miningaddr is required")
|
||||
}
|
||||
|
||||
initLog(defaultLogFile, defaultErrLogFile)
|
||||
|
||||
return cfg, nil
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.15-alpine AS build
|
||||
FROM golang:1.14-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ func main() {
|
||||
|
||||
doneChan := make(chan struct{})
|
||||
spawn("mineLoop", func() {
|
||||
err = mineLoop(client, cfg.NumberOfBlocks, *cfg.TargetBlocksPerSecond, cfg.MineWhenNotSynced, miningAddr)
|
||||
err = mineLoop(client, cfg.NumberOfBlocks, cfg.TargetBlocksPerSecond, cfg.MineWhenNotSynced, miningAddr)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "error in mine loop"))
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
nativeerrors "errors"
|
||||
"github.com/kaspanet/kaspad/cmd/kaspaminer/templatemanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/pow"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@@ -29,19 +29,11 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, targetBlocksPerSecond
|
||||
rand.Seed(time.Now().UnixNano()) // Seed the global concurrent-safe random source.
|
||||
|
||||
errChan := make(chan error)
|
||||
|
||||
templateStopChan := make(chan struct{})
|
||||
|
||||
doneChan := make(chan struct{})
|
||||
|
||||
// We don't want to send router.DefaultMaxMessages blocks at once because there's
|
||||
// a high chance we'll get disconnected from the node, so we make the channel
|
||||
// capacity router.DefaultMaxMessages/2 (we give some slack for getBlockTemplate
|
||||
// requests)
|
||||
foundBlockChan := make(chan *externalapi.DomainBlock, router.DefaultMaxMessages/2)
|
||||
|
||||
spawn("templatesLoop", func() {
|
||||
templatesLoop(client, miningAddr, errChan)
|
||||
})
|
||||
|
||||
spawn("blocksLoop", func() {
|
||||
spawn("mineLoop-internalLoop", func() {
|
||||
const windowSize = 10
|
||||
var expectedDurationForWindow time.Duration
|
||||
var windowExpectedEndTime time.Time
|
||||
@@ -52,8 +44,16 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, targetBlocksPerSecond
|
||||
}
|
||||
blockInWindowIndex := 0
|
||||
|
||||
for {
|
||||
foundBlockChan <- mineNextBlock(mineWhenNotSynced)
|
||||
for i := uint64(0); numberOfBlocks == 0 || i < numberOfBlocks; i++ {
|
||||
|
||||
foundBlock := make(chan *externalapi.DomainBlock)
|
||||
mineNextBlock(client, miningAddr, foundBlock, mineWhenNotSynced, templateStopChan, errChan)
|
||||
block := <-foundBlock
|
||||
templateStopChan <- struct{}{}
|
||||
err := handleFoundBlock(client, block)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
if hasBlockRateTarget {
|
||||
blockInWindowIndex++
|
||||
@@ -70,17 +70,6 @@ func mineLoop(client *minerClient, numberOfBlocks uint64, targetBlocksPerSecond
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
spawn("handleFoundBlock", func() {
|
||||
for i := uint64(0); numberOfBlocks == 0 || i < numberOfBlocks; i++ {
|
||||
block := <-foundBlockChan
|
||||
err := handleFoundBlock(client, block)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
doneChan <- struct{}{}
|
||||
})
|
||||
|
||||
@@ -110,20 +99,26 @@ func logHashRate() {
|
||||
})
|
||||
}
|
||||
|
||||
func mineNextBlock(client *minerClient, miningAddr util.Address, foundBlock chan *externalapi.DomainBlock, mineWhenNotSynced bool,
|
||||
templateStopChan chan struct{}, errChan chan error) {
|
||||
|
||||
newTemplateChan := make(chan *appmessage.GetBlockTemplateResponseMessage)
|
||||
spawn("templatesLoop", func() {
|
||||
templatesLoop(client, miningAddr, newTemplateChan, errChan, templateStopChan)
|
||||
})
|
||||
spawn("solveLoop", func() {
|
||||
solveLoop(newTemplateChan, foundBlock, mineWhenNotSynced)
|
||||
})
|
||||
}
|
||||
|
||||
func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error {
|
||||
blockHash := consensushashing.BlockHash(block)
|
||||
log.Infof("Submitting block %s to %s", blockHash, client.Address())
|
||||
log.Infof("Found block %s with parents %s. Submitting to %s", blockHash, block.Header.ParentHashes(), client.Address())
|
||||
|
||||
rejectReason, err := client.SubmitBlock(block)
|
||||
if err != nil {
|
||||
if nativeerrors.Is(err, router.ErrTimeout) {
|
||||
log.Warnf("Got timeout while submitting block %s to %s: %s", blockHash, client.Address(), err)
|
||||
return nil
|
||||
}
|
||||
if rejectReason == appmessage.RejectReasonIsInIBD {
|
||||
const waitTime = 1 * time.Second
|
||||
log.Warnf("Block %s was rejected because the node is in IBD. Waiting for %s", blockHash, waitTime)
|
||||
time.Sleep(waitTime)
|
||||
log.Warnf("Block %s was rejected because the node is in IBD", blockHash)
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf("Error submitting block %s to %s: %s", blockHash, client.Address(), err)
|
||||
@@ -131,78 +126,77 @@ func handleFoundBlock(client *minerClient, block *externalapi.DomainBlock) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func mineNextBlock(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
nonce := rand.Uint64() // Use the global concurrent-safe random source.
|
||||
for {
|
||||
nonce++
|
||||
// For each nonce we try to build a block from the most up to date
|
||||
// block template.
|
||||
// In the rare case where the nonce space is exhausted for a specific
|
||||
// block, it'll keep looping the nonce until a new block template
|
||||
// is discovered.
|
||||
block := getBlockForMining(mineWhenNotSynced)
|
||||
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
|
||||
headerForMining := block.Header.ToMutable()
|
||||
headerForMining.SetNonce(nonce)
|
||||
atomic.AddUint64(&hashesTried, 1)
|
||||
if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
|
||||
block.Header = headerForMining.ToImmutable()
|
||||
log.Infof("Found block %s with parents %s", consensushashing.BlockHash(block), block.Header.ParentHashes())
|
||||
return block
|
||||
func solveBlock(block *externalapi.DomainBlock, stopChan chan struct{}, foundBlock chan *externalapi.DomainBlock) {
|
||||
targetDifficulty := difficulty.CompactToBig(block.Header.Bits())
|
||||
headerForMining := block.Header.ToMutable()
|
||||
initialNonce := rand.Uint64() // Use the global concurrent-safe random source.
|
||||
for i := initialNonce; i != initialNonce-1; i++ {
|
||||
select {
|
||||
case <-stopChan:
|
||||
return
|
||||
default:
|
||||
headerForMining.SetNonce(i)
|
||||
atomic.AddUint64(&hashesTried, 1)
|
||||
if pow.CheckProofOfWorkWithTarget(headerForMining, targetDifficulty) {
|
||||
block.Header = headerForMining.ToImmutable()
|
||||
foundBlock <- block
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getBlockForMining(mineWhenNotSynced bool) *externalapi.DomainBlock {
|
||||
tryCount := 0
|
||||
for {
|
||||
tryCount++
|
||||
const sleepTime = 500 * time.Millisecond
|
||||
shouldLog := (tryCount-1)%10 == 0
|
||||
template := templatemanager.Get()
|
||||
if template == nil {
|
||||
if shouldLog {
|
||||
log.Info("Waiting for the initial template")
|
||||
}
|
||||
time.Sleep(sleepTime)
|
||||
continue
|
||||
}
|
||||
if !template.IsSynced && !mineWhenNotSynced {
|
||||
if shouldLog {
|
||||
log.Warnf("Kaspad is not synced. Skipping current block template")
|
||||
}
|
||||
time.Sleep(sleepTime)
|
||||
continue
|
||||
}
|
||||
func templatesLoop(client *minerClient, miningAddr util.Address,
|
||||
newTemplateChan chan *appmessage.GetBlockTemplateResponseMessage, errChan chan error, stopChan chan struct{}) {
|
||||
|
||||
return appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
||||
}
|
||||
}
|
||||
|
||||
func templatesLoop(client *minerClient, miningAddr util.Address, errChan chan error) {
|
||||
getBlockTemplate := func() {
|
||||
template, err := client.GetBlockTemplate(miningAddr.String())
|
||||
if nativeerrors.Is(err, router.ErrTimeout) {
|
||||
log.Warnf("Got timeout while requesting block template from %s: %s", client.Address(), err)
|
||||
log.Infof("Got timeout while requesting block template from %s", client.Address())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
} else if err != nil {
|
||||
errChan <- errors.Errorf("Error getting block template from %s: %s", client.Address(), err)
|
||||
return
|
||||
}
|
||||
templatemanager.Set(template)
|
||||
newTemplateChan <- template
|
||||
}
|
||||
|
||||
getBlockTemplate()
|
||||
const tickerTime = 500 * time.Millisecond
|
||||
ticker := time.NewTicker(tickerTime)
|
||||
for {
|
||||
select {
|
||||
case <-stopChan:
|
||||
close(newTemplateChan)
|
||||
return
|
||||
case <-client.blockAddedNotificationChan:
|
||||
getBlockTemplate()
|
||||
ticker.Reset(tickerTime)
|
||||
case <-ticker.C:
|
||||
case <-time.Tick(500 * time.Millisecond):
|
||||
getBlockTemplate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func solveLoop(newTemplateChan chan *appmessage.GetBlockTemplateResponseMessage, foundBlock chan *externalapi.DomainBlock,
|
||||
mineWhenNotSynced bool) {
|
||||
|
||||
var stopOldTemplateSolving chan struct{}
|
||||
for template := range newTemplateChan {
|
||||
if !template.IsSynced && !mineWhenNotSynced {
|
||||
log.Warnf("Kaspad is not synced. Skipping current block template")
|
||||
continue
|
||||
}
|
||||
|
||||
if stopOldTemplateSolving != nil {
|
||||
close(stopOldTemplateSolving)
|
||||
}
|
||||
|
||||
stopOldTemplateSolving = make(chan struct{})
|
||||
block := appmessage.MsgBlockToDomainBlock(template.MsgBlock)
|
||||
|
||||
stopOldTemplateSolvingCopy := stopOldTemplateSolving
|
||||
spawn("solveBlock", func() {
|
||||
solveBlock(block, stopOldTemplateSolvingCopy, foundBlock)
|
||||
})
|
||||
}
|
||||
if stopOldTemplateSolving != nil {
|
||||
close(stopOldTemplateSolving)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package templatemanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var currentTemplate *appmessage.GetBlockTemplateResponseMessage
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
// Get returns the template to work on
|
||||
func Get() *appmessage.GetBlockTemplateResponseMessage {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
return currentTemplate
|
||||
}
|
||||
|
||||
// Set sets the current template to work on
|
||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
currentTemplate = template
|
||||
}
|
||||
@@ -10,7 +10,7 @@ It is capable of generating wallet key-pairs, printing a wallet's current balanc
|
||||
|
||||
## Requirements
|
||||
|
||||
Go 1.15 or later.
|
||||
Go 1.14 or later.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -- multistage docker build: stage #1: build stage
|
||||
FROM golang:1.15-alpine AS build
|
||||
FROM golang:1.14-alpine AS build
|
||||
|
||||
RUN mkdir -p /go/src/github.com/kaspanet/kaspad
|
||||
|
||||
@@ -27,7 +27,7 @@ WORKDIR /app
|
||||
RUN apk add --no-cache ca-certificates tini
|
||||
|
||||
COPY --from=build /go/src/github.com/kaspanet/kaspad/kaspad /app/
|
||||
COPY --from=build /go/src/github.com/kaspanet/kaspad/infrastructure/config/sample-kaspad.conf /app/
|
||||
COPY --from=build /go/src/github.com/kaspanet/kaspad/sample-kaspad.conf /app/
|
||||
|
||||
USER nobody
|
||||
ENTRYPOINT [ "/sbin/tini", "--" ]
|
||||
|
||||
@@ -403,15 +403,3 @@ func (s *consensus) GetHeadersSelectedTip() (*externalapi.DomainHash, error) {
|
||||
|
||||
return s.headersSelectedTipStore.HeadersSelectedTip(s.databaseContext)
|
||||
}
|
||||
|
||||
func (s *consensus) Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
err := s.validateBlockHashExists(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.dagTraversalManager.Anticone(blockHash)
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package consensus
|
||||
|
||||
import "github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
|
||||
// GHOSTDAGManagerConstructor is the function signature for a constructor of a type implementing model.GHOSTDAGManager
|
||||
type GHOSTDAGManagerConstructor func(model.DBReader, model.DAGTopologyManager, model.GHOSTDAGDataStore, model.BlockHeaderStore, model.KType) model.GHOSTDAGManager
|
||||
@@ -0,0 +1,52 @@
|
||||
package binaryserialization
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"github.com/kaspanet/kaspad/util/binaryserializer"
|
||||
"io"
|
||||
)
|
||||
|
||||
// SerializeUTXOCollection serializes the given utxoCollection into the given writer
|
||||
func SerializeUTXOCollection(writer io.Writer, utxoCollection model.UTXOCollection) error {
|
||||
length := uint64(utxoCollection.Len())
|
||||
err := binaryserializer.PutUint64(writer, length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utxoIterator := utxoCollection.Iterator()
|
||||
for ok := utxoIterator.First(); ok; ok = utxoIterator.Next() {
|
||||
outpoint, utxoEntry, err := utxoIterator.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = utxo.SerializeUTXOIntoWriter(writer, utxoEntry, outpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeserializeUTXOCollection deserializes a utxoCollection out of the given reader
|
||||
func DeserializeUTXOCollection(reader io.Reader) (model.UTXOCollection, error) {
|
||||
length, err := binaryserializer.Uint64(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
utxoMap := make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry, length)
|
||||
for i := uint64(0); i < length; i++ {
|
||||
utxoEntry, outpoint, err := utxo.DeserializeUTXOOutOfReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utxoMap[*outpoint] = utxoEntry
|
||||
}
|
||||
|
||||
utxoCollection := utxo.NewUTXOCollection(utxoMap)
|
||||
return utxoCollection, nil
|
||||
}
|
||||
29
domain/consensus/database/binaryserialization/utxo_diff.go
Normal file
29
domain/consensus/database/binaryserialization/utxo_diff.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package binaryserialization
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"io"
|
||||
)
|
||||
|
||||
// SerializeUTXODiff serializes the given utxoDiff into the given writer
|
||||
func SerializeUTXODiff(writer io.Writer, utxoDiff model.UTXODiff) error {
|
||||
err := SerializeUTXOCollection(writer, utxoDiff.ToAdd())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return SerializeUTXOCollection(writer, utxoDiff.ToRemove())
|
||||
}
|
||||
|
||||
// DeserializeUTXODiff deserializes a utxoDiff out of the given reader
|
||||
func DeserializeUTXODiff(reader io.Reader) (model.UTXODiff, error) {
|
||||
toAdd, err := DeserializeUTXOCollection(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toRemove, err := DeserializeUTXOCollection(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return utxo.NewUTXODiffFromCollections(toAdd, toRemove)
|
||||
}
|
||||
@@ -19,11 +19,11 @@ type acceptanceDataStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new AcceptanceDataStore
|
||||
func New(cacheSize int, preallocate bool) model.AcceptanceDataStore {
|
||||
func New(cacheSize int) model.AcceptanceDataStore {
|
||||
return &acceptanceDataStore{
|
||||
staging: make(map[externalapi.DomainHash]externalapi.AcceptanceData),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
cache: lrucache.New(cacheSize, preallocate),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ type blockHeaderStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new BlockHeaderStore
|
||||
func New(dbContext model.DBReader, cacheSize int, preallocate bool) (model.BlockHeaderStore, error) {
|
||||
func New(dbContext model.DBReader, cacheSize int) (model.BlockHeaderStore, error) {
|
||||
blockHeaderStore := &blockHeaderStore{
|
||||
staging: make(map[externalapi.DomainHash]externalapi.BlockHeader),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
cache: lrucache.New(cacheSize, preallocate),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
|
||||
err := blockHeaderStore.initializeCount(dbContext)
|
||||
|
||||
@@ -18,10 +18,10 @@ type blockRelationStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new BlockRelationStore
|
||||
func New(cacheSize int, preallocate bool) model.BlockRelationStore {
|
||||
func New(cacheSize int) model.BlockRelationStore {
|
||||
return &blockRelationStore{
|
||||
staging: make(map[externalapi.DomainHash]*model.BlockRelations),
|
||||
cache: lrucache.New(cacheSize, preallocate),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ type blockStatusStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new BlockStatusStore
|
||||
func New(cacheSize int, preallocate bool) model.BlockStatusStore {
|
||||
func New(cacheSize int) model.BlockStatusStore {
|
||||
return &blockStatusStore{
|
||||
staging: make(map[externalapi.DomainHash]externalapi.BlockStatus),
|
||||
cache: lrucache.New(cacheSize, preallocate),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ type blockStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new BlockStore
|
||||
func New(dbContext model.DBReader, cacheSize int, preallocate bool) (model.BlockStore, error) {
|
||||
func New(dbContext model.DBReader, cacheSize int) (model.BlockStore, error) {
|
||||
blockStore := &blockStore{
|
||||
staging: make(map[externalapi.DomainHash]*externalapi.DomainBlock),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
cache: lrucache.New(cacheSize, preallocate),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
|
||||
err := blockStore.initializeCount(dbContext)
|
||||
@@ -212,34 +212,3 @@ func (bs *blockStore) serializeBlockCount(count uint64) ([]byte, error) {
|
||||
dbBlockCount := &serialization.DbBlockCount{Count: count}
|
||||
return proto.Marshal(dbBlockCount)
|
||||
}
|
||||
|
||||
type allBlockHashesIterator struct {
|
||||
cursor model.DBCursor
|
||||
}
|
||||
|
||||
func (a allBlockHashesIterator) First() bool {
|
||||
return a.cursor.First()
|
||||
}
|
||||
|
||||
func (a allBlockHashesIterator) Next() bool {
|
||||
return a.cursor.Next()
|
||||
}
|
||||
|
||||
func (a allBlockHashesIterator) Get() (*externalapi.DomainHash, error) {
|
||||
key, err := a.cursor.Key()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockHashBytes := key.Suffix()
|
||||
return externalapi.NewDomainHashFromByteSlice(blockHashBytes)
|
||||
}
|
||||
|
||||
func (bs *blockStore) AllBlockHashesIterator(dbContext model.DBReader) (model.BlockIterator, error) {
|
||||
cursor, err := dbContext.Cursor(bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &allBlockHashesIterator{cursor: cursor}, nil
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ type consensusStateStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new ConsensusStateStore
|
||||
func New(utxoSetCacheSize int, preallocate bool) model.ConsensusStateStore {
|
||||
func New(utxoSetCacheSize int) model.ConsensusStateStore {
|
||||
return &consensusStateStore{
|
||||
virtualUTXOSetCache: utxolrucache.New(utxoSetCacheSize, preallocate),
|
||||
virtualUTXOSetCache: utxolrucache.New(utxoSetCacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@ type finalityStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new FinalityStore
|
||||
func New(cacheSize int, preallocate bool) model.FinalityStore {
|
||||
func New(cacheSize int) model.FinalityStore {
|
||||
return &finalityStore{
|
||||
staging: make(map[externalapi.DomainHash]*externalapi.DomainHash),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
cache: lrucache.New(cacheSize, preallocate),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ type ghostdagDataStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new GHOSTDAGDataStore
|
||||
func New(cacheSize int, preallocate bool) model.GHOSTDAGDataStore {
|
||||
func New(cacheSize int) model.GHOSTDAGDataStore {
|
||||
return &ghostdagDataStore{
|
||||
staging: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData),
|
||||
cache: lrucache.New(cacheSize, preallocate),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@ type headersSelectedChainStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new HeadersSelectedChainStore
|
||||
func New(cacheSize int, preallocate bool) model.HeadersSelectedChainStore {
|
||||
func New(cacheSize int) model.HeadersSelectedChainStore {
|
||||
return &headersSelectedChainStore{
|
||||
stagingAddedByHash: make(map[externalapi.DomainHash]uint64),
|
||||
stagingRemovedByHash: make(map[externalapi.DomainHash]struct{}),
|
||||
stagingAddedByIndex: make(map[uint64]*externalapi.DomainHash),
|
||||
stagingRemovedByIndex: make(map[uint64]struct{}),
|
||||
cacheByIndex: lrucacheuint64tohash.New(cacheSize, preallocate),
|
||||
cacheByHash: lrucache.New(cacheSize, preallocate),
|
||||
cacheByIndex: lrucacheuint64tohash.New(cacheSize),
|
||||
cacheByHash: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ type multisetStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new MultisetStore
|
||||
func New(cacheSize int, preallocate bool) model.MultisetStore {
|
||||
func New(cacheSize int) model.MultisetStore {
|
||||
return &multisetStore{
|
||||
staging: make(map[externalapi.DomainHash]model.Multiset),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
cache: lrucache.New(cacheSize, preallocate),
|
||||
cache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ type reachabilityDataStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new ReachabilityDataStore
|
||||
func New(cacheSize int, preallocate bool) model.ReachabilityDataStore {
|
||||
func New(cacheSize int) model.ReachabilityDataStore {
|
||||
return &reachabilityDataStore{
|
||||
reachabilityDataStaging: make(map[externalapi.DomainHash]model.ReachabilityData),
|
||||
reachabilityDataCache: lrucache.New(cacheSize, preallocate),
|
||||
reachabilityDataCache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package utxodiffstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/database"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/database/binaryserialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
@@ -23,13 +25,13 @@ type utxoDiffStore struct {
|
||||
}
|
||||
|
||||
// New instantiates a new UTXODiffStore
|
||||
func New(cacheSize int, preallocate bool) model.UTXODiffStore {
|
||||
func New(cacheSize int) model.UTXODiffStore {
|
||||
return &utxoDiffStore{
|
||||
utxoDiffStaging: make(map[externalapi.DomainHash]model.UTXODiff),
|
||||
utxoDiffChildStaging: make(map[externalapi.DomainHash]*externalapi.DomainHash),
|
||||
toDelete: make(map[externalapi.DomainHash]struct{}),
|
||||
utxoDiffCache: lrucache.New(cacheSize, preallocate),
|
||||
utxoDiffChildCache: lrucache.New(cacheSize, preallocate),
|
||||
utxoDiffCache: lrucache.New(cacheSize),
|
||||
utxoDiffChildCache: lrucache.New(cacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,27 +190,17 @@ func (uds *utxoDiffStore) utxoDiffChildHashAsKey(hash *externalapi.DomainHash) m
|
||||
}
|
||||
|
||||
func (uds *utxoDiffStore) serializeUTXODiff(utxoDiff model.UTXODiff) ([]byte, error) {
|
||||
dbUtxoDiff, err := serialization.UTXODiffToDBUTXODiff(utxoDiff)
|
||||
writer := &bytes.Buffer{}
|
||||
err := binaryserialization.SerializeUTXODiff(writer, utxoDiff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bytes, err := proto.Marshal(dbUtxoDiff)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return bytes, nil
|
||||
return writer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (uds *utxoDiffStore) deserializeUTXODiff(utxoDiffBytes []byte) (model.UTXODiff, error) {
|
||||
dbUTXODiff := &serialization.DbUtxoDiff{}
|
||||
err := proto.Unmarshal(utxoDiffBytes, dbUTXODiff)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return serialization.DBUTXODiffToUTXODiff(dbUTXODiff)
|
||||
reader := bytes.NewReader(utxoDiffBytes)
|
||||
return binaryserialization.DeserializeUTXODiff(reader)
|
||||
}
|
||||
|
||||
func (uds *utxoDiffStore) serializeUTXODiffChild(utxoDiffChild *externalapi.DomainHash) ([]byte, error) {
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package utxodiffstore
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUTXODiffSerializationAndDeserialization(t *testing.T) {
|
||||
utxoDiffStore := New(0).(*utxoDiffStore)
|
||||
|
||||
testUTXODiff, err := buildTestUTXODiff()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not create UTXODiff from toAdd and toRemove collections: %s", err)
|
||||
}
|
||||
|
||||
serializedUTXODiff, err := utxoDiffStore.serializeUTXODiff(testUTXODiff)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not serialize UTXO diff: %s", err)
|
||||
}
|
||||
deserializedUTXODiff, err := utxoDiffStore.deserializeUTXODiff(serializedUTXODiff)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not deserialize UTXO diff: %s", err)
|
||||
}
|
||||
|
||||
if testUTXODiff.ToAdd().Len() != deserializedUTXODiff.ToAdd().Len() {
|
||||
t.Fatalf("Unexpected toAdd length in deserialized utxoDiff. Want: %d, got: %d",
|
||||
testUTXODiff.ToAdd().Len(), deserializedUTXODiff.ToAdd().Len())
|
||||
}
|
||||
if testUTXODiff.ToRemove().Len() != deserializedUTXODiff.ToRemove().Len() {
|
||||
t.Fatalf("Unexpected toRemove length in deserialized utxoDiff. Want: %d, got: %d",
|
||||
testUTXODiff.ToRemove().Len(), deserializedUTXODiff.ToRemove().Len())
|
||||
}
|
||||
|
||||
testToAddIterator := testUTXODiff.ToAdd().Iterator()
|
||||
for ok := testToAddIterator.First(); ok; ok = testToAddIterator.Next() {
|
||||
testOutpoint, testUTXOEntry, err := testToAddIterator.Get()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get an outpoint-utxoEntry pair out of the toAdd iterator: %s", err)
|
||||
}
|
||||
deserializedUTXOEntry, ok := deserializedUTXODiff.ToAdd().Get(testOutpoint)
|
||||
if !ok {
|
||||
t.Fatalf("Outpoint %s:%d not found in the deserialized toAdd collection",
|
||||
testOutpoint.TransactionID, testOutpoint.Index)
|
||||
}
|
||||
if !testUTXOEntry.Equal(deserializedUTXOEntry) {
|
||||
t.Fatalf("Deserialized UTXO entry is not equal to the original UTXO entry for outpoint %s:%d "+
|
||||
"in the toAdd collection", testOutpoint.TransactionID, testOutpoint.Index)
|
||||
}
|
||||
}
|
||||
|
||||
testToRemoveIterator := testUTXODiff.ToRemove().Iterator()
|
||||
for ok := testToRemoveIterator.First(); ok; ok = testToRemoveIterator.Next() {
|
||||
testOutpoint, testUTXOEntry, err := testToRemoveIterator.Get()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get an outpoint-utxoEntry pair out of the toRemove iterator: %s", err)
|
||||
}
|
||||
deserializedUTXOEntry, ok := deserializedUTXODiff.ToRemove().Get(testOutpoint)
|
||||
if !ok {
|
||||
t.Fatalf("Outpoint %s:%d not found in the deserialized toRemove collection",
|
||||
testOutpoint.TransactionID, testOutpoint.Index)
|
||||
}
|
||||
if !testUTXOEntry.Equal(deserializedUTXOEntry) {
|
||||
t.Fatalf("Deserialized UTXO entry is not equal to the original UTXO entry for outpoint %s:%d "+
|
||||
"in the toRemove collection", testOutpoint.TransactionID, testOutpoint.Index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUTXODiffSerialization(b *testing.B) {
|
||||
utxoDiffStore := New(0).(*utxoDiffStore)
|
||||
|
||||
testUTXODiff, err := buildTestUTXODiff()
|
||||
if err != nil {
|
||||
b.Fatalf("Could not create UTXODiff from toAdd and toRemove collections: %s", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := utxoDiffStore.serializeUTXODiff(testUTXODiff)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not serialize UTXO diff: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUTXODiffDeserialization(b *testing.B) {
|
||||
utxoDiffStore := New(0).(*utxoDiffStore)
|
||||
|
||||
testUTXODiff, err := buildTestUTXODiff()
|
||||
if err != nil {
|
||||
b.Fatalf("Could not create UTXODiff from toAdd and toRemove collections: %s", err)
|
||||
}
|
||||
serializedUTXODiff, err := utxoDiffStore.serializeUTXODiff(testUTXODiff)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not serialize UTXO diff: %s", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err = utxoDiffStore.deserializeUTXODiff(serializedUTXODiff)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not deserialize UTXO diff: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUTXODiffSerializationAndDeserialization(b *testing.B) {
|
||||
utxoDiffStore := New(0).(*utxoDiffStore)
|
||||
|
||||
testUTXODiff, err := buildTestUTXODiff()
|
||||
if err != nil {
|
||||
b.Fatalf("Could not create UTXODiff from toAdd and toRemove collections: %s", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
serializedUTXODiff, err := utxoDiffStore.serializeUTXODiff(testUTXODiff)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not serialize UTXO diff: %s", err)
|
||||
}
|
||||
_, err = utxoDiffStore.deserializeUTXODiff(serializedUTXODiff)
|
||||
if err != nil {
|
||||
b.Fatalf("Could not deserialize UTXO diff: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildTestUTXODiff() (model.UTXODiff, error) {
|
||||
toAdd := buildTestUTXOCollection()
|
||||
toRemove := buildTestUTXOCollection()
|
||||
|
||||
utxoDiff, err := utxo.NewUTXODiffFromCollections(toAdd, toRemove)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return utxoDiff, nil
|
||||
}
|
||||
|
||||
func buildTestUTXOCollection() model.UTXOCollection {
|
||||
utxoMap := make(map[externalapi.DomainOutpoint]externalapi.UTXOEntry)
|
||||
|
||||
for i := 0; i < 100_000; i++ {
|
||||
var outpointTransactionIDBytes [32]byte
|
||||
rand.Read(outpointTransactionIDBytes[:])
|
||||
outpointTransactionID := externalapi.NewDomainTransactionIDFromByteArray(&outpointTransactionIDBytes)
|
||||
outpointIndex := rand.Uint32()
|
||||
outpoint := externalapi.NewDomainOutpoint(outpointTransactionID, outpointIndex)
|
||||
|
||||
utxoEntryAmount := rand.Uint64()
|
||||
var utxoEntryScriptPublicKeyScript [256]byte
|
||||
rand.Read(utxoEntryScriptPublicKeyScript[:])
|
||||
utxoEntryScriptPublicKeyVersion := uint16(rand.Uint32())
|
||||
utxoEntryScriptPublicKey := &externalapi.ScriptPublicKey{
|
||||
Script: utxoEntryScriptPublicKeyScript[:],
|
||||
Version: utxoEntryScriptPublicKeyVersion,
|
||||
}
|
||||
utxoEntryIsCoinbase := rand.Float32() > 0.5
|
||||
utxoEntryBlockBlueScore := rand.Uint64()
|
||||
utxoEntry := utxo.NewUTXOEntry(utxoEntryAmount, utxoEntryScriptPublicKey, utxoEntryIsCoinbase, utxoEntryBlockBlueScore)
|
||||
|
||||
utxoMap[*outpoint] = utxoEntry
|
||||
}
|
||||
|
||||
return utxo.NewUTXOCollection(utxoMap)
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/headersselectedchainstore"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/datastructures/headersselectedchainstore"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/dagtraversalmanager"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/finalitymanager"
|
||||
|
||||
@@ -46,37 +45,21 @@ import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/db/database/ldb"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTestLeveldbCacheSizeMiB = 8
|
||||
defaultPreallocateCaches = true
|
||||
defaultTestPreallocateCaches = false
|
||||
)
|
||||
|
||||
// Factory instantiates new Consensuses
|
||||
type Factory interface {
|
||||
NewConsensus(dagParams *dagconfig.Params, db infrastructuredatabase.Database, isArchivalNode bool) (
|
||||
externalapi.Consensus, error)
|
||||
NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode bool, testName string) (
|
||||
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error)
|
||||
|
||||
SetTestDataDir(dataDir string)
|
||||
SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerConstructor)
|
||||
SetTestLevelDBCacheSize(cacheSizeMiB int)
|
||||
SetTestPreAllocateCache(preallocateCaches bool)
|
||||
NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataDir string, isArchivalNode bool) (
|
||||
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error)
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
dataDir string
|
||||
ghostdagConstructor GHOSTDAGManagerConstructor
|
||||
cacheSizeMiB *int
|
||||
preallocateCaches *bool
|
||||
}
|
||||
type factory struct{}
|
||||
|
||||
// NewFactory creates a new Consensus factory
|
||||
func NewFactory() Factory {
|
||||
return &factory{
|
||||
ghostdagConstructor: ghostdagmanager.New,
|
||||
}
|
||||
return &factory{}
|
||||
}
|
||||
|
||||
// NewConsensus instantiates a new Consensus
|
||||
@@ -87,39 +70,27 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
|
||||
pruningWindowSizeForCaches := int(dagParams.PruningDepth())
|
||||
|
||||
var preallocateCaches bool
|
||||
if f.preallocateCaches != nil {
|
||||
preallocateCaches = *f.preallocateCaches
|
||||
} else {
|
||||
preallocateCaches = defaultPreallocateCaches
|
||||
}
|
||||
|
||||
// This is used for caches that are used as part of deletePastBlocks that need to traverse until
|
||||
// the previous pruning point.
|
||||
pruningWindowSizePlusFinalityDepthForCache := int(dagParams.PruningDepth() + dagParams.FinalityDepth())
|
||||
|
||||
// Data Structures
|
||||
acceptanceDataStore := acceptancedatastore.New(200, preallocateCaches)
|
||||
blockStore, err := blockstore.New(dbManager, 200, preallocateCaches)
|
||||
acceptanceDataStore := acceptancedatastore.New(200)
|
||||
blockStore, err := blockstore.New(dbManager, 200)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockHeaderStore, err := blockheaderstore.New(dbManager, 10_000, preallocateCaches)
|
||||
blockHeaderStore, err := blockheaderstore.New(dbManager, 10_000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockRelationStore := blockrelationstore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches)
|
||||
|
||||
blockStatusStore := blockstatusstore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches)
|
||||
multisetStore := multisetstore.New(200, preallocateCaches)
|
||||
blockRelationStore := blockrelationstore.New(pruningWindowSizeForCaches)
|
||||
blockStatusStore := blockstatusstore.New(200)
|
||||
multisetStore := multisetstore.New(200)
|
||||
pruningStore := pruningstore.New()
|
||||
reachabilityDataStore := reachabilitydatastore.New(pruningWindowSizePlusFinalityDepthForCache, preallocateCaches)
|
||||
utxoDiffStore := utxodiffstore.New(200, preallocateCaches)
|
||||
consensusStateStore := consensusstatestore.New(10_000, preallocateCaches)
|
||||
ghostdagDataStore := ghostdagdatastore.New(pruningWindowSizeForCaches, preallocateCaches)
|
||||
reachabilityDataStore := reachabilitydatastore.New(pruningWindowSizeForCaches)
|
||||
utxoDiffStore := utxodiffstore.New(200)
|
||||
consensusStateStore := consensusstatestore.New(10_000)
|
||||
ghostdagDataStore := ghostdagdatastore.New(pruningWindowSizeForCaches)
|
||||
headersSelectedTipStore := headersselectedtipstore.New()
|
||||
finalityStore := finalitystore.New(200, preallocateCaches)
|
||||
headersSelectedChainStore := headersselectedchainstore.New(pruningWindowSizeForCaches, preallocateCaches)
|
||||
finalityStore := finalitystore.New(200)
|
||||
headersSelectedChainStore := headersselectedchainstore.New(pruningWindowSizeForCaches)
|
||||
|
||||
// Processes
|
||||
reachabilityManager := reachabilitymanager.New(
|
||||
@@ -131,7 +102,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
reachabilityManager,
|
||||
blockRelationStore,
|
||||
ghostdagDataStore)
|
||||
ghostdagManager := f.ghostdagConstructor(
|
||||
ghostdagManager := ghostdagmanager.New(
|
||||
dbManager,
|
||||
dagTopologyManager,
|
||||
ghostdagDataStore,
|
||||
@@ -142,8 +113,7 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
dagTopologyManager,
|
||||
ghostdagDataStore,
|
||||
reachabilityDataStore,
|
||||
ghostdagManager,
|
||||
consensusStateStore)
|
||||
ghostdagManager)
|
||||
pastMedianTimeManager := pastmediantimemanager.New(
|
||||
dagParams.TimestampDeviationTolerance,
|
||||
dbManager,
|
||||
@@ -300,8 +270,6 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
coinbaseManager,
|
||||
consensusStateManager,
|
||||
ghostdagManager,
|
||||
transactionValidator,
|
||||
|
||||
acceptanceDataStore,
|
||||
blockRelationStore,
|
||||
multisetStore,
|
||||
@@ -407,23 +375,19 @@ func (f *factory) NewConsensus(dagParams *dagconfig.Params, db infrastructuredat
|
||||
|
||||
func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode bool, testName string) (
|
||||
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error) {
|
||||
datadir := f.dataDir
|
||||
if datadir == "" {
|
||||
datadir, err = ioutil.TempDir("", testName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
dataDir, err := ioutil.TempDir("", testName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var cacheSizeMiB int
|
||||
if f.cacheSizeMiB != nil {
|
||||
cacheSizeMiB = *f.cacheSizeMiB
|
||||
} else {
|
||||
cacheSizeMiB = defaultTestLeveldbCacheSizeMiB
|
||||
}
|
||||
if f.preallocateCaches == nil {
|
||||
f.SetTestPreAllocateCache(defaultTestPreallocateCaches)
|
||||
}
|
||||
db, err := ldb.NewLevelDB(datadir, cacheSizeMiB)
|
||||
|
||||
return f.NewTestConsensusWithDataDir(dagParams, dataDir, isArchivalNode)
|
||||
}
|
||||
|
||||
func (f *factory) NewTestConsensusWithDataDir(dagParams *dagconfig.Params, dataDir string, isArchivalNode bool) (
|
||||
tc testapi.TestConsensus, teardown func(keepDataDir bool), err error) {
|
||||
|
||||
db, err := ldb.NewLevelDB(dataDir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -433,7 +397,9 @@ func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode b
|
||||
}
|
||||
|
||||
consensusAsImplementation := consensusAsInterface.(*consensus)
|
||||
|
||||
testConsensusStateManager := consensusstatemanager.NewTestConsensusStateManager(consensusAsImplementation.consensusStateManager)
|
||||
|
||||
testTransactionValidator := transactionvalidator.NewTestTransactionValidator(consensusAsImplementation.transactionValidator)
|
||||
|
||||
tstConsensus := &testConsensus{
|
||||
@@ -449,26 +415,12 @@ func (f *factory) NewTestConsensus(dagParams *dagconfig.Params, isArchivalNode b
|
||||
teardown = func(keepDataDir bool) {
|
||||
db.Close()
|
||||
if !keepDataDir {
|
||||
err := os.RemoveAll(f.dataDir)
|
||||
err := os.RemoveAll(dataDir)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing data directory for test consensus: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tstConsensus, teardown, nil
|
||||
}
|
||||
|
||||
func (f *factory) SetTestDataDir(dataDir string) {
|
||||
f.dataDir = dataDir
|
||||
}
|
||||
|
||||
func (f *factory) SetTestGHOSTDAGManager(ghostdagConstructor GHOSTDAGManagerConstructor) {
|
||||
f.ghostdagConstructor = ghostdagConstructor
|
||||
}
|
||||
|
||||
func (f *factory) SetTestLevelDBCacheSize(cacheSizeMiB int) {
|
||||
f.cacheSizeMiB = &cacheSizeMiB
|
||||
}
|
||||
func (f *factory) SetTestPreAllocateCache(preallocateCaches bool) {
|
||||
f.preallocateCaches = &preallocateCaches
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestNewConsensus(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
db, err := ldb.NewLevelDB(tmpDir, 8)
|
||||
db, err := ldb.NewLevelDB(tmpDir)
|
||||
if err != nil {
|
||||
t.Fatalf("error in NewLevelDB: %s", err)
|
||||
}
|
||||
|
||||
@@ -232,12 +232,12 @@ func TestBoundedMergeDepth(t *testing.T) {
|
||||
}
|
||||
|
||||
factory := NewFactory()
|
||||
consensusBuild, teardownFunc1, err := factory.NewTestConsensus(params, false, "TestBoundedMergeTestBuild")
|
||||
consensusBuild, teardownFunc1, err := factory.NewTestConsensus(params, false, "BoundedMergeTestBuild")
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Error setting up consensus: %+v", err)
|
||||
}
|
||||
|
||||
consensusReal, teardownFunc2, err := factory.NewTestConsensus(params, false, "TestBoundedMergeTestReal")
|
||||
consensusReal, teardownFunc2, err := factory.NewTestConsensus(params, false, "BoundedMergeTestReal")
|
||||
if err != nil {
|
||||
t.Fatalf("TestBoundedMergeDepth: Error setting up consensus: %+v", err)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
// BlockHeap represents a heap of block hashes, providing a priority-queue functionality
|
||||
type BlockHeap interface {
|
||||
Push(blockHash *externalapi.DomainHash) error
|
||||
PushSlice(blockHash []*externalapi.DomainHash) error
|
||||
Pop() *externalapi.DomainHash
|
||||
Len() int
|
||||
ToSlice() []*externalapi.DomainHash
|
||||
|
||||
@@ -4,7 +4,6 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
|
||||
// BlockIterator is an iterator over blocks according to some order.
|
||||
type BlockIterator interface {
|
||||
First() bool
|
||||
Next() bool
|
||||
Get() (*externalapi.DomainHash, error)
|
||||
Get() *externalapi.DomainHash
|
||||
}
|
||||
|
||||
@@ -29,5 +29,4 @@ type Consensus interface {
|
||||
GetVirtualSelectedParentChainFromBlock(blockHash *DomainHash) (*SelectedChainPath, error)
|
||||
IsInSelectedParentChainOf(blockHashA *DomainHash, blockHashB *DomainHash) (bool, error)
|
||||
GetHeadersSelectedTip() (*DomainHash, error)
|
||||
Anticone(blockHash *DomainHash) ([]*DomainHash, error)
|
||||
}
|
||||
|
||||
@@ -85,16 +85,6 @@ func (hash *DomainHash) Equal(other *DomainHash) bool {
|
||||
return hash.hashArray == other.hashArray
|
||||
}
|
||||
|
||||
// Less returns true if hash is less than other
|
||||
func (hash *DomainHash) Less(other *DomainHash) bool {
|
||||
return bytes.Compare(hash.hashArray[:], other.hashArray[:]) < 0
|
||||
}
|
||||
|
||||
// LessOrEqual returns true if hash is smaller or equal to other
|
||||
func (hash *DomainHash) LessOrEqual(other *DomainHash) bool {
|
||||
return bytes.Compare(hash.hashArray[:], other.hashArray[:]) <= 0
|
||||
}
|
||||
|
||||
// CloneHashes returns a clone of the given hashes slice.
|
||||
// Note: since DomainHash is a read-only type, the clone is shallow
|
||||
func CloneHashes(hashes []*DomainHash) []*DomainHash {
|
||||
@@ -116,3 +106,8 @@ func HashesEqual(a, b []*DomainHash) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Less returns true iff hash a is less than hash b
|
||||
func Less(a, b *DomainHash) bool {
|
||||
return bytes.Compare(a.hashArray[:], b.hashArray[:]) < 0
|
||||
}
|
||||
|
||||
@@ -313,16 +313,6 @@ func (id *DomainTransactionID) Equal(other *DomainTransactionID) bool {
|
||||
return (*DomainHash)(id).Equal((*DomainHash)(other))
|
||||
}
|
||||
|
||||
// Less returns true if id is less than other
|
||||
func (id *DomainTransactionID) Less(other *DomainTransactionID) bool {
|
||||
return (*DomainHash)(id).Less((*DomainHash)(other))
|
||||
}
|
||||
|
||||
// LessOrEqual returns true if id is smaller or equal to other
|
||||
func (id *DomainTransactionID) LessOrEqual(other *DomainTransactionID) bool {
|
||||
return (*DomainHash)(id).LessOrEqual((*DomainHash)(other))
|
||||
}
|
||||
|
||||
// ByteArray returns the bytes in this transactionID represented as a byte array.
|
||||
// The transactionID bytes are cloned, therefore it is safe to modify the resulting array.
|
||||
func (id *DomainTransactionID) ByteArray() *[DomainHashSize]byte {
|
||||
|
||||
@@ -12,5 +12,4 @@ type BlockStore interface {
|
||||
Blocks(dbContext DBReader, blockHashes []*externalapi.DomainHash) ([]*externalapi.DomainBlock, error)
|
||||
Delete(blockHash *externalapi.DomainHash)
|
||||
Count() uint64
|
||||
AllBlockHashesIterator(dbContext DBReader) (BlockIterator, error)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ type DAGTopologyManager interface {
|
||||
IsParentOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
|
||||
IsChildOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
|
||||
IsAncestorOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
|
||||
IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
|
||||
IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error)
|
||||
IsInSelectedParentChainOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error)
|
||||
ChildInSelectedParentChainOf(context, highHash *externalapi.DomainHash) (*externalapi.DomainHash, error)
|
||||
|
||||
@@ -7,10 +7,9 @@ import "github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
type DAGTraversalManager interface {
|
||||
BlockAtDepth(highHash *externalapi.DomainHash, depth uint64) (*externalapi.DomainHash, error)
|
||||
LowestChainBlockAboveOrEqualToBlueScore(highHash *externalapi.DomainHash, blueScore uint64) (*externalapi.DomainHash, error)
|
||||
// SelectedChildIterator should return a BlockIterator that iterates
|
||||
// from lowHash (exclusive) to highHash (inclusive) over highHash's selected parent chain
|
||||
SelectedParentIterator(highHash *externalapi.DomainHash) BlockIterator
|
||||
SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (BlockIterator, error)
|
||||
Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
AnticoneFromContext(context, lowHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error)
|
||||
BlueWindow(highHash *externalapi.DomainHash, windowSize int) ([]*externalapi.DomainHash, error)
|
||||
NewDownHeap() BlockHeap
|
||||
NewUpHeap() BlockHeap
|
||||
|
||||
@@ -9,5 +9,4 @@ type PruningManager interface {
|
||||
ClearImportedPruningPointData() error
|
||||
AppendImportedPruningPointUTXOs(outpointAndUTXOEntryPairs []*externalapi.OutpointAndUTXOEntryPair) error
|
||||
UpdatePruningPointUTXOSetIfRequired() error
|
||||
PruneAllBlocksBelow(pruningPointHash *externalapi.DomainHash) error
|
||||
}
|
||||
|
||||
@@ -8,20 +8,6 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// MineJSONBlockType indicates which type of blocks MineJSON mines
|
||||
type MineJSONBlockType int
|
||||
|
||||
const (
|
||||
// MineJSONBlockTypeUTXOValidBlock indicates for MineJSON to mine valid blocks.
|
||||
MineJSONBlockTypeUTXOValidBlock MineJSONBlockType = iota
|
||||
|
||||
// MineJSONBlockTypeUTXOInvalidBlock indicates for MineJSON to mine UTXO invalid blocks.
|
||||
MineJSONBlockTypeUTXOInvalidBlock
|
||||
|
||||
// MineJSONBlockTypeUTXOInvalidHeader indicates for MineJSON to mine UTXO invalid headers.
|
||||
MineJSONBlockTypeUTXOInvalidHeader
|
||||
)
|
||||
|
||||
// TestConsensus wraps the Consensus interface with some methods that are needed by tests only
|
||||
type TestConsensus interface {
|
||||
externalapi.Consensus
|
||||
@@ -47,7 +33,7 @@ type TestConsensus interface {
|
||||
AddUTXOInvalidBlock(parentHashes []*externalapi.DomainHash) (*externalapi.DomainHash,
|
||||
*externalapi.BlockInsertionResult, error)
|
||||
|
||||
MineJSON(r io.Reader, blockType MineJSONBlockType) (tips []*externalapi.DomainHash, err error)
|
||||
MineJSON(r io.Reader) (tips []*externalapi.DomainHash, err error)
|
||||
DiscardAllStores()
|
||||
|
||||
AcceptanceDataStore() model.AcceptanceDataStore
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package blockbuilder
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/pkg/errors"
|
||||
"sort"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
@@ -11,6 +9,7 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
)
|
||||
@@ -23,7 +22,6 @@ type blockBuilder struct {
|
||||
coinbaseManager model.CoinbaseManager
|
||||
consensusStateManager model.ConsensusStateManager
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
transactionValidator model.TransactionValidator
|
||||
|
||||
acceptanceDataStore model.AcceptanceDataStore
|
||||
blockRelationStore model.BlockRelationStore
|
||||
@@ -40,7 +38,6 @@ func New(
|
||||
coinbaseManager model.CoinbaseManager,
|
||||
consensusStateManager model.ConsensusStateManager,
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
transactionValidator model.TransactionValidator,
|
||||
|
||||
acceptanceDataStore model.AcceptanceDataStore,
|
||||
blockRelationStore model.BlockRelationStore,
|
||||
@@ -55,12 +52,10 @@ func New(
|
||||
coinbaseManager: coinbaseManager,
|
||||
consensusStateManager: consensusStateManager,
|
||||
ghostdagManager: ghostdagManager,
|
||||
transactionValidator: transactionValidator,
|
||||
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
multisetStore: multisetStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
acceptanceDataStore: acceptanceDataStore,
|
||||
blockRelationStore: blockRelationStore,
|
||||
multisetStore: multisetStore,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,11 +73,6 @@ func (bb *blockBuilder) BuildBlock(coinbaseData *externalapi.DomainCoinbaseData,
|
||||
func (bb *blockBuilder) buildBlock(coinbaseData *externalapi.DomainCoinbaseData,
|
||||
transactions []*externalapi.DomainTransaction) (*externalapi.DomainBlock, error) {
|
||||
|
||||
err := bb.validateTransactions(transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coinbase, err := bb.newBlockCoinbaseTransaction(coinbaseData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,53 +90,6 @@ func (bb *blockBuilder) buildBlock(coinbaseData *externalapi.DomainCoinbaseData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) validateTransactions(transactions []*externalapi.DomainTransaction) error {
|
||||
invalidTransactions := make([]ruleerrors.InvalidTransaction, 0)
|
||||
for _, transaction := range transactions {
|
||||
err := bb.validateTransaction(transaction)
|
||||
if err != nil {
|
||||
if !errors.As(err, &ruleerrors.RuleError{}) {
|
||||
return err
|
||||
}
|
||||
invalidTransactions = append(invalidTransactions,
|
||||
ruleerrors.InvalidTransaction{Transaction: transaction, Error: err})
|
||||
}
|
||||
}
|
||||
|
||||
if len(invalidTransactions) > 0 {
|
||||
return ruleerrors.NewErrInvalidTransactionsInNewBlock(invalidTransactions)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) validateTransaction(transaction *externalapi.DomainTransaction) error {
|
||||
originalEntries := make([]externalapi.UTXOEntry, len(transaction.Inputs))
|
||||
for i, input := range transaction.Inputs {
|
||||
originalEntries[i] = input.UTXOEntry
|
||||
input.UTXOEntry = nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
for i, input := range transaction.Inputs {
|
||||
input.UTXOEntry = originalEntries[i]
|
||||
}
|
||||
}()
|
||||
|
||||
err := bb.consensusStateManager.PopulateTransactionWithUTXOEntries(transaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
virtualSelectedParentMedianTime, err := bb.pastMedianTimeManager.PastMedianTime(model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bb.transactionValidator.ValidateTransactionInContextAndPopulateMassAndFee(transaction,
|
||||
model.VirtualBlockHash, virtualSelectedParentMedianTime)
|
||||
}
|
||||
|
||||
func (bb *blockBuilder) newBlockCoinbaseTransaction(
|
||||
coinbaseData *externalapi.DomainCoinbaseData) (*externalapi.DomainTransaction, error) {
|
||||
|
||||
@@ -254,7 +197,7 @@ func (bb *blockBuilder) calculateAcceptedIDMerkleRoot(acceptanceData externalapi
|
||||
sort.Slice(acceptedTransactions, func(i, j int) bool {
|
||||
acceptedTransactionIID := consensushashing.TransactionID(acceptedTransactions[i])
|
||||
acceptedTransactionJID := consensushashing.TransactionID(acceptedTransactions[j])
|
||||
return acceptedTransactionIID.Less(acceptedTransactionJID)
|
||||
return transactionid.Less(acceptedTransactionIID, acceptedTransactionJID)
|
||||
})
|
||||
|
||||
return merkle.CalculateIDMerkleRoot(acceptedTransactions), nil
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package blockbuilder_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
|
||||
@@ -24,14 +22,12 @@ func TestBuildBlockErrorCases(t *testing.T) {
|
||||
}
|
||||
defer teardown(false)
|
||||
|
||||
type testData struct {
|
||||
name string
|
||||
coinbaseData *externalapi.DomainCoinbaseData
|
||||
transactions []*externalapi.DomainTransaction
|
||||
testFunc func(test testData, err error) error
|
||||
}
|
||||
|
||||
tests := []testData{
|
||||
tests := []struct {
|
||||
name string
|
||||
coinbaseData *externalapi.DomainCoinbaseData
|
||||
transactions []*externalapi.DomainTransaction
|
||||
expectedErrorType error
|
||||
}{
|
||||
{
|
||||
"scriptPublicKey too long",
|
||||
&externalapi.DomainCoinbaseData{
|
||||
@@ -42,78 +38,7 @@ func TestBuildBlockErrorCases(t *testing.T) {
|
||||
ExtraData: nil,
|
||||
},
|
||||
nil,
|
||||
func(_ testData, err error) error {
|
||||
if !errors.Is(err, ruleerrors.ErrBadCoinbasePayloadLen) {
|
||||
return errors.Errorf("Unexpected error: %+v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
"missing UTXO transactions",
|
||||
&externalapi.DomainCoinbaseData{
|
||||
ScriptPublicKey: &externalapi.ScriptPublicKey{
|
||||
Script: nil,
|
||||
Version: 0,
|
||||
},
|
||||
ExtraData: nil,
|
||||
},
|
||||
[]*externalapi.DomainTransaction{
|
||||
{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{
|
||||
{
|
||||
PreviousOutpoint: externalapi.DomainOutpoint{
|
||||
TransactionID: externalapi.DomainTransactionID{}, Index: 0},
|
||||
},
|
||||
},
|
||||
Outputs: nil,
|
||||
LockTime: 0,
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
Gas: 0,
|
||||
PayloadHash: externalapi.DomainHash{},
|
||||
Payload: []byte{0},
|
||||
},
|
||||
{
|
||||
Version: constants.MaxTransactionVersion,
|
||||
Inputs: []*externalapi.DomainTransactionInput{
|
||||
{
|
||||
PreviousOutpoint: externalapi.DomainOutpoint{
|
||||
TransactionID: externalapi.DomainTransactionID{}, Index: 0},
|
||||
},
|
||||
},
|
||||
Outputs: nil,
|
||||
LockTime: 0,
|
||||
SubnetworkID: subnetworks.SubnetworkIDNative,
|
||||
Gas: 0,
|
||||
PayloadHash: externalapi.DomainHash{},
|
||||
Payload: []byte{1},
|
||||
},
|
||||
},
|
||||
|
||||
func(test testData, err error) error {
|
||||
errInvalidTransactionsInNewBlock := ruleerrors.ErrInvalidTransactionsInNewBlock{}
|
||||
if !errors.As(err, &errInvalidTransactionsInNewBlock) {
|
||||
return errors.Errorf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
if len(errInvalidTransactionsInNewBlock.InvalidTransactions) != len(test.transactions) {
|
||||
return errors.Errorf("Expected %d transaction but got %d",
|
||||
len(test.transactions), len(errInvalidTransactionsInNewBlock.InvalidTransactions))
|
||||
}
|
||||
|
||||
for i, invalidTx := range errInvalidTransactionsInNewBlock.InvalidTransactions {
|
||||
if !invalidTx.Transaction.Equal(test.transactions[i]) {
|
||||
return errors.Errorf("Expected transaction %d to be equal to its corresponding "+
|
||||
"test transaction", i)
|
||||
}
|
||||
|
||||
if !errors.As(invalidTx.Error, &ruleerrors.ErrMissingTxOut{}) {
|
||||
return errors.Errorf("Unexpected error for transaction %d: %+v", i, invalidTx.Error)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ruleerrors.ErrBadCoinbasePayloadLen,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -123,11 +48,8 @@ func TestBuildBlockErrorCases(t *testing.T) {
|
||||
t.Errorf("%s: No error from BuildBlock", test.name)
|
||||
return
|
||||
}
|
||||
|
||||
err := test.testFunc(test, err)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %s", test.name, err)
|
||||
return
|
||||
if test.expectedErrorType != nil && !errors.Is(err, test.expectedErrorType) {
|
||||
t.Errorf("%s: Expected error '%s', but got '%s'", test.name, test.expectedErrorType, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ package blockprocessor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/blockprocessor/blocklogger"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
@@ -144,8 +143,6 @@ func (bp *blockProcessor) validateAndInsertBlock(block *externalapi.DomainBlock,
|
||||
return nil, logClosureErr
|
||||
}
|
||||
|
||||
blocklogger.LogBlock(block)
|
||||
|
||||
return &externalapi.BlockInsertionResult{
|
||||
VirtualSelectedParentChainChanges: selectedParentChainChanges,
|
||||
}, nil
|
||||
@@ -293,9 +290,6 @@ func (bp *blockProcessor) discardAllChanges() {
|
||||
}
|
||||
|
||||
func (bp *blockProcessor) commitAllChanges() error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "commitAllChanges")
|
||||
defer onEnd()
|
||||
|
||||
dbTx, err := bp.databaseContext.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -126,8 +126,7 @@ func TestValidateAndInsertErrors(t *testing.T) {
|
||||
tipHash, emptyCoinbase, tx1 := initData(params)
|
||||
|
||||
// Tests all the error case on the function: "checkBlockStatus"(sub-function in function validateBlock)
|
||||
blockWithStatusInvalid, _, err := tc.BuildBlockWithParents([]*externalapi.DomainHash{params.GenesisHash},
|
||||
&emptyCoinbase, []*externalapi.DomainTransaction{tx1, tx1})
|
||||
blockWithStatusInvalid, err := tc.BuildBlock(&emptyCoinbase, []*externalapi.DomainTransaction{tx1, tx1})
|
||||
if err != nil {
|
||||
t.Fatalf("AddBlock: %+v", err)
|
||||
}
|
||||
|
||||
@@ -28,12 +28,6 @@ func (bp *blockProcessor) validateAndInsertImportedPruningPoint(newPruningPoint
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Deleting block data for all blocks in blockStore")
|
||||
err = bp.pruningManager.PruneAllBlocksBelow(newPruningPointHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Updating consensus state manager according to the new pruning point %s", newPruningPointHash)
|
||||
err = bp.consensusStateManager.ImportPruningPoint(newPruningPoint)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package blockvalidator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
@@ -31,21 +29,6 @@ func (v *blockValidator) ValidateHeaderInContext(blockHash *externalapi.DomainHa
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var logErr error
|
||||
log.Debug(logger.NewLogClosure(func() string {
|
||||
var ghostdagData *model.BlockGHOSTDAGData
|
||||
ghostdagData, logErr = v.ghostdagDataStore.Get(v.databaseContext, blockHash)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("block %s blue score is %d", blockHash, ghostdagData.BlueScore())
|
||||
}))
|
||||
|
||||
if logErr != nil {
|
||||
return logErr
|
||||
}
|
||||
}
|
||||
|
||||
err = v.validateMedianTime(header)
|
||||
|
||||
@@ -4,7 +4,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/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
@@ -16,10 +15,8 @@ import (
|
||||
func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *externalapi.DomainHash) (
|
||||
model.UTXODiff, externalapi.AcceptanceData, model.Multiset, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "CalculatePastUTXOAndAcceptanceData")
|
||||
defer onEnd()
|
||||
|
||||
log.Debugf("CalculatePastUTXOAndAcceptanceData start for block %s", blockHash)
|
||||
defer log.Debugf("CalculatePastUTXOAndAcceptanceData end for block %s", blockHash)
|
||||
|
||||
if blockHash.Equal(csm.genesisHash) {
|
||||
log.Debugf("Block %s is the genesis. By definition, "+
|
||||
@@ -38,9 +35,6 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
log.Debugf("Restored the past UTXO of block %s with selectedParent %s. "+
|
||||
"Diff toAdd length: %d, toRemove length: %d", blockHash, blockGHOSTDAGData.SelectedParent(),
|
||||
selectedParentPastUTXO.ToAdd().Len(), selectedParentPastUTXO.ToRemove().Len())
|
||||
|
||||
log.Debugf("Applying blue blocks to the selected parent past UTXO of block %s", blockHash)
|
||||
acceptanceData, utxoDiff, err := csm.applyMergeSetBlocks(blockHash, selectedParentPastUTXO, blockGHOSTDAGData)
|
||||
@@ -59,10 +53,8 @@ func (csm *consensusStateManager) CalculatePastUTXOAndAcceptanceData(blockHash *
|
||||
}
|
||||
|
||||
func (csm *consensusStateManager) restorePastUTXO(blockHash *externalapi.DomainHash) (model.MutableUTXODiff, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "restorePastUTXO")
|
||||
defer onEnd()
|
||||
|
||||
log.Debugf("restorePastUTXO start for block %s", blockHash)
|
||||
defer log.Debugf("restorePastUTXO end for block %s", blockHash)
|
||||
|
||||
var err error
|
||||
|
||||
@@ -119,7 +111,7 @@ func (csm *consensusStateManager) applyMergeSetBlocks(blockHash *externalapi.Dom
|
||||
externalapi.AcceptanceData, model.MutableUTXODiff, error) {
|
||||
|
||||
log.Debugf("applyMergeSetBlocks start for block %s", blockHash)
|
||||
defer log.Debugf("applyMergeSetBlocks end for block %s", blockHash)
|
||||
defer log.Tracef("applyMergeSetBlocks end for block %s", blockHash)
|
||||
|
||||
mergeSetHashes := ghostdagData.MergeSet()
|
||||
log.Debugf("Merge set for block %s is %v", blockHash, mergeSetHashes)
|
||||
@@ -271,9 +263,6 @@ func (csm *consensusStateManager) checkTransactionMass(
|
||||
func (csm *consensusStateManager) RestorePastUTXOSetIterator(blockHash *externalapi.DomainHash) (
|
||||
model.ReadOnlyUTXOSetIterator, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "RestorePastUTXOSetIterator")
|
||||
defer onEnd()
|
||||
|
||||
blockStatus, err := csm.resolveBlockStatus(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -287,7 +276,7 @@ func (csm *consensusStateManager) RestorePastUTXOSetIterator(blockHash *external
|
||||
log.Tracef("RestorePastUTXOSetIterator start for block %s", blockHash)
|
||||
defer log.Tracef("RestorePastUTXOSetIterator end for block %s", blockHash)
|
||||
|
||||
log.Debugf("Calculating UTXO diff for block %s", blockHash)
|
||||
log.Tracef("Calculating UTXO diff for block %s", blockHash)
|
||||
blockDiff, err := csm.restorePastUTXO(blockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -61,7 +61,7 @@ func (csm *consensusStateManager) importPruningPoint(newPruningPoint *externalap
|
||||
}
|
||||
log.Debugf("The new pruning point UTXO commitment validation passed")
|
||||
|
||||
log.Debugf("Staging the pruning point as the only DAG tip")
|
||||
log.Debugf("Staging the the pruning point as the only DAG tip")
|
||||
newTips := []*externalapi.DomainHash{newPruningPointHash}
|
||||
csm.consensusStateStore.StageTips(newTips)
|
||||
|
||||
@@ -86,7 +86,7 @@ func (csm *consensusStateManager) importPruningPoint(newPruningPoint *externalap
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Staging the new pruning point %s", newPruningPointHash)
|
||||
log.Debugf("Staging the new pruning point")
|
||||
csm.pruningStore.StagePruningPoint(newPruningPointHash)
|
||||
|
||||
log.Debugf("Populating the pruning point with UTXO entries")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package consensusstatemanager
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
@@ -10,10 +9,8 @@ import (
|
||||
)
|
||||
|
||||
func (csm *consensusStateManager) pickVirtualParents(tips []*externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "pickVirtualParents")
|
||||
defer onEnd()
|
||||
|
||||
log.Debugf("pickVirtualParents start for tips len: %d", len(tips))
|
||||
defer log.Debugf("pickVirtualParents end for tips len: %d", len(tips))
|
||||
|
||||
log.Debugf("Pushing all tips into a DownHeap")
|
||||
candidatesHeap := csm.dagTraversalManager.NewDownHeap()
|
||||
@@ -87,8 +84,8 @@ func (csm *consensusStateManager) pickVirtualParents(tips []*externalapi.DomainH
|
||||
func (csm *consensusStateManager) selectVirtualSelectedParent(
|
||||
candidatesHeap model.BlockHeap) (*externalapi.DomainHash, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "selectVirtualSelectedParent")
|
||||
defer onEnd()
|
||||
log.Tracef("selectVirtualSelectedParent start")
|
||||
defer log.Tracef("selectVirtualSelectedParent end")
|
||||
|
||||
disqualifiedCandidates := hashset.New()
|
||||
|
||||
@@ -156,8 +153,8 @@ func (csm *consensusStateManager) selectVirtualSelectedParent(
|
||||
func (csm *consensusStateManager) mergeSetIncrease(
|
||||
candidate *externalapi.DomainHash, selectedVirtualParents hashset.HashSet) (uint64, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "mergeSetIncrease")
|
||||
defer onEnd()
|
||||
log.Tracef("mergeSetIncrease start")
|
||||
defer log.Tracef("mergeSetIncrease end")
|
||||
|
||||
visited := hashset.New()
|
||||
queue := csm.dagTraversalManager.NewDownHeap()
|
||||
@@ -207,10 +204,8 @@ func (csm *consensusStateManager) mergeSetIncrease(
|
||||
func (csm *consensusStateManager) boundedMergeBreakingParents(
|
||||
parents []*externalapi.DomainHash) (hashset.HashSet, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "boundedMergeBreakingParents")
|
||||
defer onEnd()
|
||||
|
||||
log.Tracef("boundedMergeBreakingParents start for parents: %s", parents)
|
||||
defer log.Tracef("boundedMergeBreakingParents end for parents: %s", parents)
|
||||
|
||||
log.Debug("Temporarily setting virtual to all parents, so that we can run ghostdag on it")
|
||||
err := csm.dagTopologyManager.SetParents(model.VirtualBlockHash, parents)
|
||||
|
||||
@@ -56,7 +56,7 @@ func (csm *consensusStateManager) resolveBlockStatus(blockHash *externalapi.Doma
|
||||
|
||||
csm.blockStatusStore.Stage(unverifiedBlockHash, blockStatus)
|
||||
selectedParentStatus = blockStatus
|
||||
log.Debugf("Block %s status resolved to `%s`, finished %d/%d of unverified blocks", unverifiedBlockHash, blockStatus, len(unverifiedBlocks)-i, len(unverifiedBlocks))
|
||||
log.Debugf("Block %s status resolved to `%s`", unverifiedBlockHash, blockStatus)
|
||||
}
|
||||
|
||||
return blockStatus, nil
|
||||
@@ -122,7 +122,7 @@ func (csm *consensusStateManager) getUnverifiedChainBlocks(
|
||||
|
||||
func (csm *consensusStateManager) resolveSingleBlockStatus(blockHash *externalapi.DomainHash) (externalapi.BlockStatus, error) {
|
||||
log.Debugf("resolveSingleBlockStatus start for block %s", blockHash)
|
||||
defer log.Debugf("resolveSingleBlockStatus end for block %s", blockHash)
|
||||
defer log.Tracef("resolveSingleBlockStatus end for block %s", blockHash)
|
||||
|
||||
log.Tracef("Calculating pastUTXO and acceptance data and multiset for block %s", blockHash)
|
||||
pastUTXODiff, acceptanceData, multiset, err := csm.CalculatePastUTXOAndAcceptanceData(blockHash)
|
||||
|
||||
@@ -3,16 +3,13 @@ package consensusstatemanager
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/infrastructure/logger"
|
||||
)
|
||||
|
||||
func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.DomainHash,
|
||||
tips []*externalapi.DomainHash) (*externalapi.SelectedChainPath, error) {
|
||||
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "updateVirtual")
|
||||
defer onEnd()
|
||||
|
||||
log.Debugf("updateVirtual start for block %s", newBlockHash)
|
||||
defer log.Debugf("updateVirtual end for block %s", newBlockHash)
|
||||
|
||||
log.Debugf("Saving a reference to the GHOSTDAG data of the old virtual")
|
||||
var oldVirtualSelectedParent *externalapi.DomainHash
|
||||
@@ -47,9 +44,6 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Calculated the past UTXO of the new virtual. "+
|
||||
"Diff toAdd length: %d, toRemove length: %d",
|
||||
virtualUTXODiff.ToAdd().Len(), virtualUTXODiff.ToRemove().Len())
|
||||
|
||||
log.Debugf("Staging new acceptance data for the virtual block")
|
||||
csm.acceptanceDataStore.Stage(model.VirtualBlockHash, virtualAcceptanceData)
|
||||
@@ -79,8 +73,6 @@ func (csm *consensusStateManager) updateVirtual(newBlockHash *externalapi.Domain
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Selected parent chain changes: %d blocks were removed and %d blocks were added",
|
||||
len(selectedParentChainChanges.Removed), len(selectedParentChainChanges.Added))
|
||||
}
|
||||
|
||||
return selectedParentChainChanges, nil
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionid"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/merkle"
|
||||
@@ -140,7 +142,8 @@ func calculateAcceptedIDMerkleRoot(multiblockAcceptanceData externalapi.Acceptan
|
||||
}
|
||||
}
|
||||
sort.Slice(acceptedTransactions, func(i, j int) bool {
|
||||
return consensushashing.TransactionID(acceptedTransactions[i]).Less(
|
||||
return transactionid.Less(
|
||||
consensushashing.TransactionID(acceptedTransactions[i]),
|
||||
consensushashing.TransactionID(acceptedTransactions[j]))
|
||||
})
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
|
||||
t.Fatalf("Consensus failed building a block: %v", err)
|
||||
}
|
||||
blockParents := block.Header.ParentHashes()
|
||||
sort.Sort(testutils.NewTestGhostDAGSorter(virtualRelations.Parents, tc, t))
|
||||
sort.Sort(testutils.NewTestGhostDAGSorter(blockParents, tc, t))
|
||||
sort.Sort(consensus.NewTestGhostDAGSorter(virtualRelations.Parents, tc, t))
|
||||
sort.Sort(consensus.NewTestGhostDAGSorter(blockParents, tc, t))
|
||||
if !externalapi.HashesEqual(virtualRelations.Parents, blockParents) {
|
||||
t.Fatalf("Block relations and BuildBlock return different parents for virtual, %s != %s", virtualRelations.Parents, blockParents)
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
|
||||
}
|
||||
|
||||
virtualParents := getSortedVirtualParents(tc)
|
||||
sort.Sort(testutils.NewTestGhostDAGSorter(parents, tc, t))
|
||||
sort.Sort(consensus.NewTestGhostDAGSorter(parents, tc, t))
|
||||
|
||||
// Make sure the first half of the blocks are with highest blueWork
|
||||
// we use (max+1)/2 because the first "half" is rounded up, so `(dividend + (divisor - 1)) / divisor` = `(max + (2-1))/2` = `(max+1)/2`
|
||||
@@ -102,7 +102,7 @@ func TestConsensusStateManager_pickVirtualParents(t *testing.T) {
|
||||
parents = append(parents, block)
|
||||
}
|
||||
|
||||
sort.Sort(testutils.NewTestGhostDAGSorter(parents, tc, t))
|
||||
sort.Sort(consensus.NewTestGhostDAGSorter(parents, tc, t))
|
||||
virtualParents = getSortedVirtualParents(tc)
|
||||
if !externalapi.HashesEqual(virtualParents, parents) {
|
||||
t.Fatalf("Expected VirtualParents and parents to be equal, instead: %s != %s", virtualParents, parents)
|
||||
|
||||
@@ -71,6 +71,11 @@ func (dtm *dagTopologyManager) IsAncestorOf(blockHashA *externalapi.DomainHash,
|
||||
return dtm.reachabilityManager.IsDAGAncestorOf(blockHashA, blockHashB)
|
||||
}
|
||||
|
||||
// IsDescendantOf returns true if blockHashA is a DAG descendant of blockHashB
|
||||
func (dtm *dagTopologyManager) IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) {
|
||||
return dtm.reachabilityManager.IsDAGAncestorOf(blockHashB, blockHashA)
|
||||
}
|
||||
|
||||
// IsAncestorOfAny returns true if `blockHash` is an ancestor of at least one of `potentialDescendants`
|
||||
func (dtm *dagTopologyManager) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) {
|
||||
for _, potentialDescendant := range potentialDescendants {
|
||||
|
||||
@@ -5,12 +5,10 @@ import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/hashset"
|
||||
)
|
||||
|
||||
func (dtm *dagTraversalManager) Anticone(blockHash *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
// AnticoneFromContext returns blocks in (context.Past ⋂ block.Anticone)
|
||||
func (dtm *dagTraversalManager) AnticoneFromContext(context, block *externalapi.DomainHash) ([]*externalapi.DomainHash, error) {
|
||||
anticone := []*externalapi.DomainHash{}
|
||||
queue, err := dtm.consensusStateStore.Tips(dtm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queue := []*externalapi.DomainHash{context}
|
||||
visited := hashset.New()
|
||||
|
||||
for len(queue) > 0 {
|
||||
@@ -23,7 +21,7 @@ func (dtm *dagTraversalManager) Anticone(blockHash *externalapi.DomainHash) ([]*
|
||||
|
||||
visited.Add(current)
|
||||
|
||||
currentIsAncestorOfBlock, err := dtm.dagTopologyManager.IsAncestorOf(current, blockHash)
|
||||
currentIsAncestorOfBlock, err := dtm.dagTopologyManager.IsAncestorOf(current, block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -32,7 +30,7 @@ func (dtm *dagTraversalManager) Anticone(blockHash *externalapi.DomainHash) ([]*
|
||||
continue
|
||||
}
|
||||
|
||||
blockIsAncestorOfCurrent, err := dtm.dagTopologyManager.IsAncestorOf(blockHash, current)
|
||||
blockIsAncestorOfCurrent, err := dtm.dagTopologyManager.IsAncestorOf(block, current)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -109,16 +109,6 @@ func (bh *blockHeap) Push(blockHash *externalapi.DomainHash) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bh *blockHeap) PushSlice(blockHashes []*externalapi.DomainHash) error {
|
||||
for _, blockHash := range blockHashes {
|
||||
err := bh.Push(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Len returns the length of this heap
|
||||
func (bh *blockHeap) Len() int {
|
||||
return bh.impl.Len()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dagtraversalmanager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/pkg/errors"
|
||||
@@ -15,7 +16,29 @@ type dagTraversalManager struct {
|
||||
ghostdagManager model.GHOSTDAGManager
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
consensusStateStore model.ConsensusStateStore
|
||||
}
|
||||
|
||||
// selectedParentIterator implements the `model.BlockIterator` API
|
||||
type selectedParentIterator struct {
|
||||
databaseContext model.DBReader
|
||||
ghostdagDataStore model.GHOSTDAGDataStore
|
||||
current *externalapi.DomainHash
|
||||
}
|
||||
|
||||
func (spi *selectedParentIterator) Next() bool {
|
||||
if spi.current == nil {
|
||||
return false
|
||||
}
|
||||
ghostdagData, err := spi.ghostdagDataStore.Get(spi.databaseContext, spi.current)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ghostdagDataStore is missing ghostdagData for: %v. '%s' ", spi.current, err))
|
||||
}
|
||||
spi.current = ghostdagData.SelectedParent()
|
||||
return spi.current != nil
|
||||
}
|
||||
|
||||
func (spi *selectedParentIterator) Get() *externalapi.DomainHash {
|
||||
return spi.current
|
||||
}
|
||||
|
||||
// New instantiates a new DAGTraversalManager
|
||||
@@ -24,15 +47,23 @@ func New(
|
||||
dagTopologyManager model.DAGTopologyManager,
|
||||
ghostdagDataStore model.GHOSTDAGDataStore,
|
||||
reachabilityDataStore model.ReachabilityDataStore,
|
||||
ghostdagManager model.GHOSTDAGManager,
|
||||
conssensusStateStore model.ConsensusStateStore) model.DAGTraversalManager {
|
||||
ghostdagManager model.GHOSTDAGManager) model.DAGTraversalManager {
|
||||
return &dagTraversalManager{
|
||||
databaseContext: databaseContext,
|
||||
dagTopologyManager: dagTopologyManager,
|
||||
ghostdagDataStore: ghostdagDataStore,
|
||||
reachabilityDataStore: reachabilityDataStore,
|
||||
ghostdagManager: ghostdagManager,
|
||||
consensusStateStore: conssensusStateStore,
|
||||
}
|
||||
}
|
||||
|
||||
// SelectedParentIterator creates an iterator over the selected
|
||||
// parent chain of the given highHash
|
||||
func (dtm *dagTraversalManager) SelectedParentIterator(highHash *externalapi.DomainHash) model.BlockIterator {
|
||||
return &selectedParentIterator{
|
||||
databaseContext: dtm.databaseContext,
|
||||
ghostdagDataStore: dtm.ghostdagDataStore,
|
||||
current: highHash,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,200 +3,11 @@ package dagtraversalmanager_test
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/domain/consensus"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const commonChainSize = 5
|
||||
const depth uint64 = 2
|
||||
|
||||
//TestBlockAtDepthOnChainDag compares the result of BlockAtDepth to the result of looping over the SelectedChain on a single chain DAG.
|
||||
func TestBlockAtDepthOnChainDag(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, tearDown, err := factory.NewTestConsensus(params, false,
|
||||
"TestBlockAtDepthOnChainDag")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating a NewTestConsensus: %s", err)
|
||||
}
|
||||
defer tearDown(false)
|
||||
|
||||
highHash, err := createAChainDAG(params.GenesisHash, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating a Chain DAG In BlockAtDepthTEST: %+v", err)
|
||||
}
|
||||
currentBlockHash := highHash
|
||||
currentBlockData, err := tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), currentBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed getting GHOSTDAGData for block with hash %s: %+v", currentBlockHash.String(), err)
|
||||
}
|
||||
|
||||
for i := uint64(0); i <= depth; i++ {
|
||||
if currentBlockData.SelectedParent() == nil {
|
||||
break
|
||||
}
|
||||
currentBlockHash = currentBlockData.SelectedParent()
|
||||
currentBlockData, err = tc.GHOSTDAGDataStore().Get(tc.DatabaseContext(), currentBlockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed getting GHOSTDAGData for block with hash %s: %+v", currentBlockHash.String(), err)
|
||||
}
|
||||
}
|
||||
expectedBlockHash := currentBlockHash
|
||||
actualBlockHash, err := tc.DAGTraversalManager().BlockAtDepth(highHash, depth)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed on BlockAtDepth: %+v", err)
|
||||
}
|
||||
if !actualBlockHash.Equal(expectedBlockHash) {
|
||||
t.Fatalf("Expected block %s but got %s", expectedBlockHash, actualBlockHash)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func createAChainDAG(genesisHash *externalapi.DomainHash, tc testapi.TestConsensus) (*externalapi.DomainHash, error) {
|
||||
block := genesisHash
|
||||
var err error
|
||||
for i := 0; i < commonChainSize; i++ {
|
||||
block, _, err = tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// TestBlockAtDepthOnDAGWhereTwoBlocksHaveSameSelectedParent compares the results of BlockAtDepth
|
||||
// of 2 children that have the same selectedParent.
|
||||
func TestBlockAtDepthOnDAGWhereTwoBlocksHaveSameSelectedParent(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, tearDown, err := factory.NewTestConsensus(params, false,
|
||||
"TestBlockAtDepthOnDAGWhereTwoBlocksHaveSameSelectedParent")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating a NewTestConsensus: %s", err)
|
||||
}
|
||||
defer tearDown(false)
|
||||
|
||||
firstChild, secondChild, err := createADAGTwoChildrenWithSameSelectedParent(params.GenesisHash, tc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating a DAG where two blocks have same selected parent: %+v", err)
|
||||
}
|
||||
actualBlockHash, err := tc.DAGTraversalManager().BlockAtDepth(firstChild, depth)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed at BlockAtDepth: %+v", err)
|
||||
}
|
||||
expectedSameHash, err := tc.DAGTraversalManager().BlockAtDepth(secondChild, depth)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed in BlockAtDepth: %+v", err)
|
||||
}
|
||||
if !actualBlockHash.Equal(expectedSameHash) {
|
||||
t.Fatalf("Expected block %s but got %s", expectedSameHash, actualBlockHash)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func createADAGTwoChildrenWithSameSelectedParent(genesisHash *externalapi.DomainHash,
|
||||
tc testapi.TestConsensus) (*externalapi.DomainHash, *externalapi.DomainHash, error) {
|
||||
|
||||
block := genesisHash
|
||||
var err error
|
||||
for i := 0; i < commonChainSize; i++ {
|
||||
block, _, err = tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
firstChild, _, err := tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
secondChild, _, err := tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return firstChild, secondChild, nil
|
||||
}
|
||||
|
||||
// TestBlockAtDepthOnDAGWithTwoDifferentChains compares results of BlockAtDepth on two different chains,
|
||||
// on the same DAG, and validates they merge at the correct point.
|
||||
func TestBlockAtDepthOnDAGWithTwoDifferentChains(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
factory := consensus.NewFactory()
|
||||
tc, tearDown, err := factory.NewTestConsensus(params, false,
|
||||
"TestBlockAtDepthOnDAGWithTwoDifferentChains")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating a NewTestConsensus: %s", err)
|
||||
}
|
||||
defer tearDown(false)
|
||||
|
||||
const sizeOfTheFirstChildSubChainDAG = 3
|
||||
const sizeOfTheSecondChildSubChainDAG = 2
|
||||
|
||||
firstChild, secondChild, err := createADAGWithTwoDifferentChains(params.GenesisHash, tc, sizeOfTheFirstChildSubChainDAG,
|
||||
sizeOfTheSecondChildSubChainDAG)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed creating a DAG with two different chains in BlockAtDepthTEST: %+v", err)
|
||||
}
|
||||
|
||||
actualBlockHash, err := tc.DAGTraversalManager().BlockAtDepth(firstChild, sizeOfTheFirstChildSubChainDAG)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed in BlockAtDepth: %+v", err)
|
||||
}
|
||||
expectedSameHash, err := tc.DAGTraversalManager().BlockAtDepth(secondChild, sizeOfTheSecondChildSubChainDAG)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed in BlockAtDepth: %+v", err)
|
||||
}
|
||||
|
||||
if !actualBlockHash.Equal(expectedSameHash) {
|
||||
t.Fatalf("Expected block %s but got %s", expectedSameHash, actualBlockHash)
|
||||
}
|
||||
expectedDiffHash, err := tc.DAGTraversalManager().BlockAtDepth(secondChild, sizeOfTheSecondChildSubChainDAG-1)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed in BlockAtDepth: %+v", err)
|
||||
}
|
||||
if actualBlockHash.Equal(expectedDiffHash) {
|
||||
t.Fatalf("Expected to a differente block")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func createADAGWithTwoDifferentChains(genesisHash *externalapi.DomainHash, tc testapi.TestConsensus,
|
||||
sizeOfTheFirstChildSubChainDAG int, sizeOfTheSecondChildSubChainDAG int) (*externalapi.DomainHash, *externalapi.DomainHash, error) {
|
||||
|
||||
block := genesisHash
|
||||
var err error
|
||||
for i := 0; i < commonChainSize; i++ {
|
||||
block, _, err = tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
firstChainTipHash, _, err := tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
secondChainTipHash, _, err := tc.AddBlock([]*externalapi.DomainHash{block}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := 0; i < sizeOfTheFirstChildSubChainDAG; i++ {
|
||||
firstChainTipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{firstChainTipHash}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < sizeOfTheSecondChildSubChainDAG; i++ {
|
||||
secondChainTipHash, _, err = tc.AddBlock([]*externalapi.DomainHash{secondChainTipHash}, nil, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
return firstChainTipHash, secondChainTipHash, nil
|
||||
}
|
||||
|
||||
func TestLowestChainBlockAboveOrEqualToBlueScore(t *testing.T) {
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
params.FinalityDuration = 10 * params.TargetTimePerBlock
|
||||
@@ -207,6 +18,7 @@ func TestLowestChainBlockAboveOrEqualToBlueScore(t *testing.T) {
|
||||
t.Fatalf("NewTestConsensus: %s", err)
|
||||
}
|
||||
defer tearDown(false)
|
||||
|
||||
checkExpectedBlock := func(highHash *externalapi.DomainHash, blueScore uint64, expected *externalapi.DomainHash) {
|
||||
blockHash, err := tc.DAGTraversalManager().LowestChainBlockAboveOrEqualToBlueScore(highHash, blueScore)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,34 +11,20 @@ type selectedChildIterator struct {
|
||||
dagTopologyManager model.DAGTopologyManager
|
||||
|
||||
reachabilityDataStore model.ReachabilityDataStore
|
||||
highHash, lowHash *externalapi.DomainHash
|
||||
highHash *externalapi.DomainHash
|
||||
current *externalapi.DomainHash
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *selectedChildIterator) First() bool {
|
||||
s.current = s.lowHash
|
||||
return s.Next()
|
||||
}
|
||||
|
||||
func (s *selectedChildIterator) Next() bool {
|
||||
if s.err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
data, err := s.reachabilityDataStore.ReachabilityData(s.databaseContext, s.current)
|
||||
if err != nil {
|
||||
s.current = nil
|
||||
s.err = err
|
||||
return true
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, child := range data.Children() {
|
||||
isChildInSelectedParentChainOfHighHash, err := s.dagTopologyManager.IsInSelectedParentChainOf(child, s.highHash)
|
||||
if err != nil {
|
||||
s.current = nil
|
||||
s.err = err
|
||||
return true
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if isChildInSelectedParentChainOfHighHash {
|
||||
@@ -49,12 +35,10 @@ func (s *selectedChildIterator) Next() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *selectedChildIterator) Get() (*externalapi.DomainHash, error) {
|
||||
return s.current, s.err
|
||||
func (s *selectedChildIterator) Get() *externalapi.DomainHash {
|
||||
return s.current
|
||||
}
|
||||
|
||||
// SelectedChildIterator returns a BlockIterator that iterates from lowHash (exclusive) to highHash (inclusive) over
|
||||
// highHash's selected parent chain
|
||||
func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externalapi.DomainHash) (model.BlockIterator, error) {
|
||||
isLowHashInSelectedParentChainOfHighHash, err := dtm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash)
|
||||
if err != nil {
|
||||
@@ -69,7 +53,6 @@ func (dtm *dagTraversalManager) SelectedChildIterator(highHash, lowHash *externa
|
||||
dagTopologyManager: dtm.dagTopologyManager,
|
||||
reachabilityDataStore: dtm.reachabilityDataStore,
|
||||
highHash: highHash,
|
||||
lowHash: lowHash,
|
||||
current: lowHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestBlueBlockWindow(t *testing.T) {
|
||||
id string //id is a virtual entity that is used only for tests so we can define relations between blocks without knowing their hash
|
||||
expectedWindowWithGenesisPadding []string
|
||||
}{
|
||||
dagconfig.MainnetParams.Name: {
|
||||
"kaspa-mainnet": {
|
||||
{
|
||||
parents: []string{"A"},
|
||||
id: "B",
|
||||
@@ -91,7 +91,7 @@ func TestBlueBlockWindow(t *testing.T) {
|
||||
expectedWindowWithGenesisPadding: []string{"N", "M", "L", "K", "J", "I", "F", "C", "D", "H"},
|
||||
},
|
||||
},
|
||||
dagconfig.TestnetParams.Name: {
|
||||
"kaspa-testnet": {
|
||||
{
|
||||
parents: []string{"A"},
|
||||
id: "B",
|
||||
@@ -163,7 +163,7 @@ func TestBlueBlockWindow(t *testing.T) {
|
||||
expectedWindowWithGenesisPadding: []string{"N", "M", "L", "K", "J", "I", "F", "H", "C", "D"},
|
||||
},
|
||||
},
|
||||
dagconfig.DevnetParams.Name: {
|
||||
"kaspa-devnet": {
|
||||
{
|
||||
parents: []string{"A"},
|
||||
id: "B",
|
||||
@@ -235,7 +235,7 @@ func TestBlueBlockWindow(t *testing.T) {
|
||||
expectedWindowWithGenesisPadding: []string{"N", "M", "L", "K", "J", "I", "F", "H", "C", "D"},
|
||||
},
|
||||
},
|
||||
dagconfig.SimnetParams.Name: {
|
||||
"kaspa-simnet": {
|
||||
{
|
||||
parents: []string{"A"},
|
||||
id: "B",
|
||||
@@ -344,7 +344,7 @@ func TestBlueBlockWindow(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("BlueWindow: %s", err)
|
||||
}
|
||||
sort.Sort(testutils.NewTestGhostDAGSorter(window, tc, t))
|
||||
sort.Sort(consensus.NewTestGhostDAGSorter(window, tc, t))
|
||||
if err := checkWindowIDs(window, blockData.expectedWindowWithGenesisPadding, idByBlockMap); err != nil {
|
||||
t.Errorf("Unexpected values for window for block %s: %s", blockData.id, err)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package difficultymanager_test
|
||||
|
||||
import (
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/mstime"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
||||
@@ -133,9 +132,9 @@ func TestDifficulty(t *testing.T) {
|
||||
|
||||
var expectedBits uint32
|
||||
switch params.Name {
|
||||
case dagconfig.TestnetParams.Name, dagconfig.DevnetParams.Name:
|
||||
case "kaspa-testnet", "kaspa-devnet":
|
||||
expectedBits = uint32(0x1e7f83df)
|
||||
case dagconfig.MainnetParams.Name:
|
||||
case "kaspa-mainnet":
|
||||
expectedBits = uint32(0x207f83df)
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ func (gm *ghostdagManager) Less(blockHashA *externalapi.DomainHash, ghostdagData
|
||||
case 1:
|
||||
return false
|
||||
case 0:
|
||||
return blockHashA.Less(blockHashB)
|
||||
return externalapi.Less(blockHashA, blockHashB)
|
||||
default:
|
||||
panic("big.Int.Cmp is defined to always return -1/1/0 and nothing else")
|
||||
}
|
||||
|
||||
@@ -2,17 +2,15 @@ package ghostdagmanager_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/blockheader"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/processes/ghostdag2"
|
||||
@@ -179,101 +177,6 @@ func TestGHOSTDAG(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestBlueWork tests if GHOSTDAG picks as selected parent the parent
|
||||
// with the most blue work, even if its blue score is not the greatest.
|
||||
// To do that it creates one chain of 3 blocks over genesis, and another
|
||||
// chain of 2 blocks with more blue work than the 3 blocks chain, and
|
||||
// checks that a block that points to both chain tips will have the
|
||||
// 2 blocks chain tip as its selected parent.
|
||||
func TestBlueWork(t *testing.T) {
|
||||
dagTopology := &DAGTopologyManagerImpl{
|
||||
parentsMap: make(map[externalapi.DomainHash][]*externalapi.DomainHash),
|
||||
}
|
||||
|
||||
ghostdagDataStore := &GHOSTDAGDataStoreImpl{
|
||||
dagMap: make(map[externalapi.DomainHash]*model.BlockGHOSTDAGData),
|
||||
}
|
||||
|
||||
blockHeadersStore := &blockHeadersStore{
|
||||
dagMap: make(map[externalapi.DomainHash]externalapi.BlockHeader),
|
||||
}
|
||||
|
||||
fakeGenesisHash := externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{0})
|
||||
longestChainBlock1Hash := externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{1})
|
||||
longestChainBlock2Hash := externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{2})
|
||||
longestChainBlock3Hash := externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{3})
|
||||
heaviestChainBlock1Hash := externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{4})
|
||||
heaviestChainBlock2Hash := externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{5})
|
||||
tipHash := externalapi.NewDomainHashFromByteArray(&[externalapi.DomainHashSize]byte{6})
|
||||
|
||||
lowDifficultyHeader := blockheader.NewImmutableBlockHeader(
|
||||
0,
|
||||
nil,
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
|
||||
dagTopology.parentsMap[*fakeGenesisHash] = nil
|
||||
ghostdagDataStore.dagMap[*fakeGenesisHash] = model.NewBlockGHOSTDAGData(0, new(big.Int), nil, nil, nil, nil)
|
||||
blockHeadersStore.dagMap[*fakeGenesisHash] = lowDifficultyHeader
|
||||
|
||||
dagTopology.parentsMap[*longestChainBlock1Hash] = []*externalapi.DomainHash{fakeGenesisHash}
|
||||
blockHeadersStore.dagMap[*longestChainBlock1Hash] = lowDifficultyHeader
|
||||
|
||||
dagTopology.parentsMap[*longestChainBlock2Hash] = []*externalapi.DomainHash{longestChainBlock1Hash}
|
||||
blockHeadersStore.dagMap[*longestChainBlock2Hash] = lowDifficultyHeader
|
||||
|
||||
dagTopology.parentsMap[*longestChainBlock3Hash] = []*externalapi.DomainHash{longestChainBlock2Hash}
|
||||
blockHeadersStore.dagMap[*longestChainBlock3Hash] = lowDifficultyHeader
|
||||
|
||||
dagTopology.parentsMap[*heaviestChainBlock1Hash] = []*externalapi.DomainHash{fakeGenesisHash}
|
||||
blockHeadersStore.dagMap[*heaviestChainBlock1Hash] = blockheader.NewImmutableBlockHeader(
|
||||
0,
|
||||
nil,
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
&externalapi.DomainHash{},
|
||||
0,
|
||||
math.MaxUint32, // Put a very high difficulty so the chain that contains this block will have a very high blue work
|
||||
0,
|
||||
)
|
||||
|
||||
dagTopology.parentsMap[*heaviestChainBlock2Hash] = []*externalapi.DomainHash{heaviestChainBlock1Hash}
|
||||
blockHeadersStore.dagMap[*heaviestChainBlock2Hash] = lowDifficultyHeader
|
||||
|
||||
dagTopology.parentsMap[*tipHash] = []*externalapi.DomainHash{heaviestChainBlock2Hash, longestChainBlock3Hash}
|
||||
blockHeadersStore.dagMap[*tipHash] = lowDifficultyHeader
|
||||
|
||||
manager := ghostdagmanager.New(nil, dagTopology, ghostdagDataStore, blockHeadersStore, 18)
|
||||
blocksForGHOSTDAG := []*externalapi.DomainHash{
|
||||
longestChainBlock1Hash,
|
||||
longestChainBlock2Hash,
|
||||
longestChainBlock3Hash,
|
||||
heaviestChainBlock1Hash,
|
||||
heaviestChainBlock2Hash,
|
||||
tipHash,
|
||||
}
|
||||
|
||||
for _, blockHash := range blocksForGHOSTDAG {
|
||||
err := manager.GHOSTDAG(blockHash)
|
||||
if err != nil {
|
||||
t.Fatalf("GHOSTDAG: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ghostdagDataStore.dagMap[*longestChainBlock3Hash].BlueScore() <= ghostdagDataStore.dagMap[*heaviestChainBlock2Hash].BlueScore() {
|
||||
t.Fatalf("Expected longestChainBlock3Hash to have greater blue score than heaviestChainBlock2Hash")
|
||||
}
|
||||
|
||||
if !ghostdagDataStore.dagMap[*tipHash].SelectedParent().Equal(heaviestChainBlock2Hash) {
|
||||
t.Fatalf("Expected the block with the most blue work to be the selected parent of the tip")
|
||||
}
|
||||
}
|
||||
|
||||
func hashesToStrings(arr []*externalapi.DomainHash) []string {
|
||||
var strArr = make([]string, len(arr))
|
||||
for i, hash := range arr {
|
||||
@@ -387,6 +290,10 @@ func (dt *DAGTopologyManagerImpl) IsAncestorOf(hashBlockA *externalapi.DomainHas
|
||||
|
||||
}
|
||||
|
||||
func (dt *DAGTopologyManagerImpl) IsDescendantOf(blockHashA *externalapi.DomainHash, blockHashB *externalapi.DomainHash) (bool, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (dt *DAGTopologyManagerImpl) IsAncestorOfAny(blockHash *externalapi.DomainHash, potentialDescendants []*externalapi.DomainHash) (bool, error) {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
@@ -27,16 +27,16 @@ type testJSON struct {
|
||||
func TestPruning(t *testing.T) {
|
||||
expectedPruningPointByNet := map[string]map[string]string{
|
||||
"chain-for-test-pruning.json": {
|
||||
dagconfig.MainnetParams.Name: "1582",
|
||||
dagconfig.TestnetParams.Name: "1582",
|
||||
dagconfig.DevnetParams.Name: "1582",
|
||||
dagconfig.SimnetParams.Name: "1582",
|
||||
"kaspa-mainnet": "1582",
|
||||
"kaspa-simnet": "1582",
|
||||
"kaspa-devnet": "1582",
|
||||
"kaspa-testnet": "1582",
|
||||
},
|
||||
"dag-for-test-pruning.json": {
|
||||
dagconfig.MainnetParams.Name: "503",
|
||||
dagconfig.TestnetParams.Name: "503",
|
||||
dagconfig.DevnetParams.Name: "503",
|
||||
dagconfig.SimnetParams.Name: "502",
|
||||
"kaspa-mainnet": "503",
|
||||
"kaspa-simnet": "502",
|
||||
"kaspa-devnet": "503",
|
||||
"kaspa-testnet": "503",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -151,11 +151,8 @@ func (pm *pruningManager) UpdatePruningPointByVirtual() error {
|
||||
|
||||
newPruningPoint := currentPruningPoint
|
||||
newPruningPointGHOSTDAGData := currentPruningPointGHOSTDAGData
|
||||
for ok := iterator.First(); ok; ok = iterator.Next() {
|
||||
selectedChild, err := iterator.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for iterator.Next() {
|
||||
selectedChild := iterator.Get()
|
||||
selectedChildGHOSTDAGData, err := pm.ghostdagDataStore.Get(pm.databaseContext, selectedChild)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -224,47 +221,47 @@ func (pm *pruningManager) deletePastBlocks(pruningPoint *externalapi.DomainHash)
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.deletePastBlocks")
|
||||
defer onEnd()
|
||||
|
||||
// Go over all pruningPoint.Past and pruningPoint.Anticone that's not in virtual.Past
|
||||
// Go over all P.Past and P.AC that's not in V.Past
|
||||
queue := pm.dagTraversalManager.NewDownHeap()
|
||||
|
||||
// Find P.AC that's not in V.Past
|
||||
dagTips, err := pm.consensusStateStore.Tips(pm.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newTips := make([]*externalapi.DomainHash, 0, len(dagTips))
|
||||
virtualParents, err := pm.dagTopologyManager.Parents(model.VirtualBlockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start queue with all tips that are below the pruning point (and on the way remove them from list of tips)
|
||||
prunedTips, err := pm.pruneTips(pruningPoint, virtualParents)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, tip := range dagTips {
|
||||
isInPruningFutureOrInVirtualPast, err := pm.isInPruningFutureOrInVirtualPast(tip, pruningPoint, virtualParents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isInPruningFutureOrInVirtualPast {
|
||||
// Add them to the queue so they and their past will be pruned
|
||||
err := queue.Push(tip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
newTips = append(newTips, tip)
|
||||
}
|
||||
}
|
||||
err = queue.PushSlice(prunedTips)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add pruningPoint.Parents to queue
|
||||
pm.consensusStateStore.StageTips(newTips)
|
||||
// Add P.Parents
|
||||
parents, err := pm.dagTopologyManager.Parents(pruningPoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = queue.PushSlice(parents)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, parent := range parents {
|
||||
err = queue.Push(parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = pm.deleteBlocksDownward(queue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = pm.pruneVirtualDiffParents(pruningPoint, virtualParents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) deleteBlocksDownward(queue model.BlockHeap) error {
|
||||
visited := map[externalapi.DomainHash]struct{}{}
|
||||
// Prune everything in the queue including its past
|
||||
for queue.Len() > 0 {
|
||||
@@ -283,16 +280,16 @@ func (pm *pruningManager) deleteBlocksDownward(queue model.BlockHeap) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = queue.PushSlice(parents)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, parent := range parents {
|
||||
err = queue.Push(parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) pruneVirtualDiffParents(pruningPoint *externalapi.DomainHash, virtualParents []*externalapi.DomainHash) error {
|
||||
// Delete virtual diff parents that are in PruningPoint's Anticone and not in Virtual's Past
|
||||
virtualDiffParents, err := pm.consensusStateStore.VirtualDiffParents(pm.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -312,31 +309,6 @@ func (pm *pruningManager) pruneVirtualDiffParents(pruningPoint *externalapi.Doma
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) pruneTips(pruningPoint *externalapi.DomainHash, virtualParents []*externalapi.DomainHash) (
|
||||
prunedTips []*externalapi.DomainHash, err error) {
|
||||
|
||||
// Find P.AC that's not in V.Past
|
||||
dagTips, err := pm.consensusStateStore.Tips(pm.databaseContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newTips := make([]*externalapi.DomainHash, 0, len(dagTips))
|
||||
for _, tip := range dagTips {
|
||||
isInPruningFutureOrInVirtualPast, err := pm.isInPruningFutureOrInVirtualPast(tip, pruningPoint, virtualParents)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isInPruningFutureOrInVirtualPast {
|
||||
prunedTips = append(prunedTips, tip)
|
||||
} else {
|
||||
newTips = append(newTips, tip)
|
||||
}
|
||||
}
|
||||
pm.consensusStateStore.StageTips(newTips)
|
||||
|
||||
return prunedTips, nil
|
||||
}
|
||||
|
||||
func (pm *pruningManager) savePruningPoint(pruningPointHash *externalapi.DomainHash) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "pruningManager.savePruningPoint")
|
||||
defer onEnd()
|
||||
@@ -579,33 +551,3 @@ func (pm *pruningManager) updatePruningPointUTXOSet() error {
|
||||
log.Debugf("Finishing updating the pruning point UTXO set")
|
||||
return pm.pruningStore.FinishUpdatingPruningPointUTXOSet(pm.databaseContext)
|
||||
}
|
||||
|
||||
func (pm *pruningManager) PruneAllBlocksBelow(pruningPointHash *externalapi.DomainHash) error {
|
||||
onEnd := logger.LogAndMeasureExecutionTime(log, "PruneAllBlocksBelow")
|
||||
defer onEnd()
|
||||
|
||||
iterator, err := pm.blocksStore.AllBlockHashesIterator(pm.databaseContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for ok := iterator.First(); ok; ok = iterator.Next() {
|
||||
blockHash, err := iterator.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isInPastOfPruningPoint, err := pm.dagTopologyManager.IsAncestorOf(pruningPointHash, blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isInPastOfPruningPoint {
|
||||
continue
|
||||
}
|
||||
_, err = pm.deleteBlock(blockHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func buildJsonDAG(t *testing.T, tc testapi.TestConsensus, attackJson bool) (tips
|
||||
}
|
||||
defer gzipReader.Close()
|
||||
|
||||
tips, err = tc.MineJSON(gzipReader, testapi.MineJSONBlockTypeUTXOInvalidHeader)
|
||||
tips, err = tc.MineJSON(gzipReader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -337,36 +337,30 @@ func (rt *reachabilityManager) updateReindexRoot(selectedTip *externalapi.Domain
|
||||
|
||||
rc := newReindexContext(rt)
|
||||
|
||||
if !newReindexRoot.Equal(reindexRootAncestor) {
|
||||
log.Debugf("Concentrating the intervals towards the new reindex root")
|
||||
// Iterate from reindexRootAncestor towards newReindexRoot
|
||||
for {
|
||||
chosenChild, err := rt.FindNextAncestor(selectedTip, reindexRootAncestor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isFinalReindexRoot := chosenChild.Equal(newReindexRoot)
|
||||
|
||||
// Concentrate interval from current ancestor to its chosen child
|
||||
err = rc.concentrateInterval(reindexRootAncestor, chosenChild, isFinalReindexRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isFinalReindexRoot {
|
||||
break
|
||||
}
|
||||
|
||||
reindexRootAncestor = chosenChild
|
||||
// Iterate from reindexRootAncestor towards newReindexRoot
|
||||
for {
|
||||
chosenChild, err := rt.FindNextAncestor(selectedTip, reindexRootAncestor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Debugf("newReindexRoot is the same as reindexRootAncestor. Skipping concentration...")
|
||||
|
||||
isFinalReindexRoot := chosenChild.Equal(newReindexRoot)
|
||||
|
||||
// Concentrate interval from current ancestor to it's chosen child
|
||||
err = rc.concentrateInterval(reindexRootAncestor, chosenChild, isFinalReindexRoot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isFinalReindexRoot {
|
||||
break
|
||||
}
|
||||
|
||||
reindexRootAncestor = chosenChild
|
||||
}
|
||||
|
||||
// Update reindex root data store
|
||||
rt.stageReindexRoot(newReindexRoot)
|
||||
log.Debugf("Updated the reindex root to %s", newReindexRoot)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -399,12 +393,8 @@ func (rt *reachabilityManager) findNextReindexRoot(currentReindexRoot, selectedT
|
||||
// We have reindex root out of selected tip chain, however we switch chains only after a sufficient
|
||||
// threshold of reindexSlack score in order to address possible alternating reorg attacks.
|
||||
// The reindexSlack constant is used as an heuristic for a large enough constant on the one hand, but
|
||||
// one which will not harm performance on the other hand - given the available slack at the chain split point.
|
||||
//
|
||||
// Note: In some cases the blue score selected tip can be lower than the current reindex root blue score.
|
||||
// If that's the case we keep the reindex root unchanged.
|
||||
if selectedTipGHOSTDAGData.BlueScore() < currentRootGHOSTDAGData.BlueScore() ||
|
||||
selectedTipGHOSTDAGData.BlueScore()-currentRootGHOSTDAGData.BlueScore() < rt.reindexSlack {
|
||||
// one which will not harm performance on the other hand - given the available slack at the chain split point
|
||||
if selectedTipGHOSTDAGData.BlueScore()-currentRootGHOSTDAGData.BlueScore() < rt.reindexSlack {
|
||||
// Return current - this indicates no change
|
||||
return currentReindexRoot, currentReindexRoot, nil
|
||||
}
|
||||
@@ -432,11 +422,6 @@ func (rt *reachabilityManager) findNextReindexRoot(currentReindexRoot, selectedT
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if selectedTipGHOSTDAGData.BlueScore() < chosenChildGHOSTDAGData.BlueScore() {
|
||||
return nil, nil, errors.Errorf("chosen child %s has blue score greater "+
|
||||
"than %s although it's in its selected parent chain", chosenChild, selectedTip)
|
||||
}
|
||||
|
||||
if selectedTipGHOSTDAGData.BlueScore()-chosenChildGHOSTDAGData.BlueScore() < rt.reindexWindow {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -12,26 +12,6 @@ import (
|
||||
func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.DomainHash,
|
||||
maxBlueScoreDifference uint64) ([]*externalapi.DomainHash, error) {
|
||||
|
||||
// If lowHash is not in the selectedParentChain of highHash - SelectedChildIterator will fail.
|
||||
// Therefore, we traverse down lowHash's selectedParentChain until we reach a block that is in
|
||||
// highHash's selectedParentChain.
|
||||
// We keep originalLowHash to filter out blocks in it's past later down the road
|
||||
originalLowHash := lowHash
|
||||
for {
|
||||
isInSelectedParentChain, err := sm.dagTopologyManager.IsInSelectedParentChainOf(lowHash, highHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isInSelectedParentChain {
|
||||
break
|
||||
}
|
||||
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lowHash = lowBlockGHOSTDAGData.SelectedParent()
|
||||
}
|
||||
|
||||
lowBlockGHOSTDAGData, err := sm.ghostdagDataStore.Get(sm.databaseContext, lowHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -59,11 +39,8 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for ok := iterator.First(); ok; ok = iterator.Next() {
|
||||
highHash, err = iterator.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for iterator.Next() {
|
||||
highHash = iterator.Get()
|
||||
highBlockGHOSTDAGData, err = sm.ghostdagDataStore.Get(sm.databaseContext, highHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -103,17 +80,10 @@ func (sm *syncManager) antiPastHashesBetween(lowHash, highHash *externalapi.Doma
|
||||
if isCurrentAncestorOfLowHash {
|
||||
continue
|
||||
}
|
||||
// Push current to hashesUpHeap if it's not in the past of originalLowHash
|
||||
isInPastOfOriginalLowHash, err := sm.dagTopologyManager.IsAncestorOf(current, originalLowHash)
|
||||
err = hashesUpHeap.Push(current)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isInPastOfOriginalLowHash {
|
||||
err = hashesUpHeap.Push(current)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
parents, err := sm.dagTopologyManager.Parents(current)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -142,11 +112,8 @@ func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash)
|
||||
|
||||
lowHash := pruningPoint
|
||||
foundHeaderOnlyBlock := false
|
||||
for ok := selectedChildIterator.First(); ok; ok = selectedChildIterator.Next() {
|
||||
selectedChild, err := selectedChildIterator.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for selectedChildIterator.Next() {
|
||||
selectedChild := selectedChildIterator.Get()
|
||||
hasBlock, err := sm.blockStore.HasBlock(sm.databaseContext, selectedChild)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -159,14 +126,8 @@ func (sm *syncManager) missingBlockBodyHashes(highHash *externalapi.DomainHash)
|
||||
lowHash = selectedChild
|
||||
}
|
||||
if !foundHeaderOnlyBlock {
|
||||
if lowHash == highHash {
|
||||
// Blocks can be inserted inside the DAG during IBD if those were requested before IBD started.
|
||||
// In rare cases, all the IBD blocks might be already inserted by the time we reach this point.
|
||||
// In these cases - return an empty list of blocks to sync
|
||||
return []*externalapi.DomainHash{}, nil
|
||||
}
|
||||
// TODO: Once block children are fixed (https://github.com/kaspanet/kaspad/issues/1499),
|
||||
// this error should be returned rather the logged
|
||||
// TODO: Once block children are fixed, this error
|
||||
// should be returned instead of simply logged
|
||||
log.Errorf("no header-only blocks between %s and %s",
|
||||
lowHash, highHash)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
|
||||
"github.com/kaspanet/kaspad/domain/dagconfig"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPruningDepth(t *testing.T) {
|
||||
expectedResult := map[string]uint64{
|
||||
dagconfig.MainnetParams.Name: 244838,
|
||||
dagconfig.TestnetParams.Name: 244838,
|
||||
dagconfig.DevnetParams.Name: 244838,
|
||||
dagconfig.SimnetParams.Name: 192038,
|
||||
"kaspa-mainnet": 244838,
|
||||
"kaspa-testnet": 244838,
|
||||
"kaspa-devnet": 244838,
|
||||
"kaspa-simnet": 192038,
|
||||
}
|
||||
testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
|
||||
expected, found := expectedResult[params.Name]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user