From 5f226328363b3273e02469b5432d0cc91fc8862d Mon Sep 17 00:00:00 2001 From: Ori Newman Date: Tue, 29 Dec 2020 09:28:02 +0200 Subject: [PATCH] Use sync rate for getBlockTemplate's isSynced (#1311) * Use sync rate for getBlockTemplate's isSynced * Fix a typo Co-authored-by: Mike Zak --- app/protocol/flowcontext/blocks.go | 2 + app/protocol/flowcontext/flow_context.go | 7 ++ app/protocol/flowcontext/orphans.go | 1 + app/protocol/flowcontext/sync_rate.go | 78 +++++++++++++++++++++++ app/protocol/manager.go | 7 +- app/rpc/rpchandlers/get_block_template.go | 5 +- 6 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 app/protocol/flowcontext/sync_rate.go diff --git a/app/protocol/flowcontext/blocks.go b/app/protocol/flowcontext/blocks.go index 4458b1a9d..6a9a8d6b1 100644 --- a/app/protocol/flowcontext/blocks.go +++ b/app/protocol/flowcontext/blocks.go @@ -23,6 +23,8 @@ func (f *FlowContext) OnNewBlock(block *externalapi.DomainBlock, hash := consensushashing.BlockHash(block) log.Debugf("OnNewBlock start for block %s", hash) defer log.Debugf("OnNewBlock end for block %s", hash) + + f.updateRecentBlockAddedTimesWithLastBlock() unorphaningResults, err := f.UnorphanBlocks(block) if err != nil { return err diff --git a/app/protocol/flowcontext/flow_context.go b/app/protocol/flowcontext/flow_context.go index 08406621e..1846148fd 100644 --- a/app/protocol/flowcontext/flow_context.go +++ b/app/protocol/flowcontext/flow_context.go @@ -1,6 +1,7 @@ package flowcontext import ( + "github.com/kaspanet/kaspad/util/mstime" "sync" "time" @@ -35,6 +36,11 @@ type FlowContext struct { addressManager *addressmanager.AddressManager connectionManager *connmanager.ConnectionManager + recentBlockAddedTimes []int64 + recentBlockAddedTimesMutex sync.Mutex + + timeStarted int64 + onBlockAddedToDAGHandler OnBlockAddedToDAGHandler onTransactionAddedToMempoolHandler OnTransactionAddedToMempoolHandler @@ -69,6 +75,7 @@ func New(cfg *config.Config, domain domain.Domain, addressManager *addressmanage peers: make(map[id.ID]*peerpkg.Peer), transactionsToRebroadcast: make(map[externalapi.DomainTransactionID]*externalapi.DomainTransaction), orphans: make(map[externalapi.DomainHash]*externalapi.DomainBlock), + timeStarted: mstime.Now().UnixMilliseconds(), } } diff --git a/app/protocol/flowcontext/orphans.go b/app/protocol/flowcontext/orphans.go index 247fe333d..620748cf7 100644 --- a/app/protocol/flowcontext/orphans.go +++ b/app/protocol/flowcontext/orphans.go @@ -156,6 +156,7 @@ func (f *FlowContext) unorphanBlock(orphanHash externalapi.DomainHash) (*externa } return nil, false, err } + f.updateRecentBlockAddedTimesWithLastBlock() log.Infof("Unorphaned block %s", orphanHash) return blockInsertionResult, true, nil diff --git a/app/protocol/flowcontext/sync_rate.go b/app/protocol/flowcontext/sync_rate.go new file mode 100644 index 000000000..f581c520a --- /dev/null +++ b/app/protocol/flowcontext/sync_rate.go @@ -0,0 +1,78 @@ +package flowcontext + +import "github.com/kaspanet/kaspad/util/mstime" + +const ( + syncRateWindowInMilliSeconds = 60_000 + syncRateMaxDeviation = 0.05 + maxSelectedParentTimeDiffToAllowMiningInMilliSeconds = 300_000 +) + +func (f *FlowContext) updateRecentBlockAddedTimesWithLastBlock() { + f.recentBlockAddedTimesMutex.Lock() + defer f.recentBlockAddedTimesMutex.Unlock() + + f.removeOldBlockTimes() + f.recentBlockAddedTimes = append(f.recentBlockAddedTimes, mstime.Now().UnixMilliseconds()) +} + +// removeOldBlockTimes removes from recentBlockAddedTimes block times +// older than syncRateWindowInMilliSeconds. +// This function is not safe for concurrent use. +func (f *FlowContext) removeOldBlockTimes() { + now := mstime.Now().UnixMilliseconds() + mostRecentBlockToKeep := 0 + for i, blockAddedTime := range f.recentBlockAddedTimes { + if now-syncRateWindowInMilliSeconds < blockAddedTime { + mostRecentBlockToKeep = i + break + } + } + f.recentBlockAddedTimes = f.recentBlockAddedTimes[mostRecentBlockToKeep:] +} + +func (f *FlowContext) isSyncRateBelowMinimum() bool { + f.recentBlockAddedTimesMutex.Lock() + defer f.recentBlockAddedTimesMutex.Unlock() + + f.removeOldBlockTimes() + + now := mstime.Now().UnixMilliseconds() + timeSinceStart := now - f.timeStarted + if timeSinceStart <= syncRateWindowInMilliSeconds { + return false + } + + expectedBlocks := float64(syncRateWindowInMilliSeconds) / float64(f.cfg.NetParams().TargetTimePerBlock.Milliseconds()) + return 1-float64(len(f.recentBlockAddedTimes))/expectedBlocks > syncRateMaxDeviation +} + +// ShouldMine returns whether it's ok to use block template from this node +// for mining purposes. +func (f *FlowContext) ShouldMine() (bool, error) { + if f.isSyncRateBelowMinimum() { + log.Debugf("The sync rate is below the minimum, so ShouldMine returns true") + return true, nil + } + + if f.IsIBDRunning() { + log.Debugf("IBD is running, so ShouldMine returns false") + return false, nil + } + + virtualSelectedParent, err := f.domain.Consensus().GetVirtualSelectedParent() + if err != nil { + return false, err + } + + now := mstime.Now().UnixMilliseconds() + if now-virtualSelectedParent.Header.TimeInMilliseconds < maxSelectedParentTimeDiffToAllowMiningInMilliSeconds { + log.Debugf("The selected tip timestamp is recent (%d), so ShouldMine returns true", + virtualSelectedParent.Header.TimeInMilliseconds) + return true, nil + } + + log.Debugf("The selected tip timestamp is old (%d), so ShouldMine returns false", + virtualSelectedParent.Header.TimeInMilliseconds) + return false, nil +} diff --git a/app/protocol/manager.go b/app/protocol/manager.go index 0fe0f3a01..59a9b5267 100644 --- a/app/protocol/manager.go +++ b/app/protocol/manager.go @@ -68,7 +68,8 @@ func (m *Manager) SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMemp m.context.SetOnTransactionAddedToMempoolHandler(onTransactionAddedToMempoolHandler) } -// IsIBDRunning returns true if IBD is currently running -func (m *Manager) IsIBDRunning() bool { - return m.context.IsIBDRunning() +// ShouldMine returns whether it's ok to use block template from this node +// for mining purposes. +func (m *Manager) ShouldMine() (bool, error) { + return m.context.ShouldMine() } diff --git a/app/rpc/rpchandlers/get_block_template.go b/app/rpc/rpchandlers/get_block_template.go index f9936e8ad..c1fc0354a 100644 --- a/app/rpc/rpchandlers/get_block_template.go +++ b/app/rpc/rpchandlers/get_block_template.go @@ -33,7 +33,10 @@ func HandleGetBlockTemplate(context *rpccontext.Context, _ *router.Router, reque } msgBlock := appmessage.DomainBlockToMsgBlock(templateBlock) - isSynced := !context.ProtocolManager.IsIBDRunning() + isSynced, err := context.ProtocolManager.ShouldMine() + if err != nil { + return nil, err + } return appmessage.NewGetBlockTemplateResponseMessage(msgBlock, isSynced), nil }