kaspad/domain/blockdag/delayed_blocks.go
Elichai Turkel c82a951a24
[NOD-1316] Refactor TestGHOSTDAG to enable arbitrary DAGs (#899)
* Add VirtualBlueHashes to BlockDAG

* Refactor TestGHOSTDAG to read DAGs from json files

* Added a new DAG for the ghostdag test suite

* Pass BehaviorFlags to delayed blocks
2020-08-25 14:00:43 +03:00

98 lines
3.3 KiB
Go

package blockdag
import (
"github.com/kaspanet/kaspad/util"
"github.com/kaspanet/kaspad/util/daghash"
"github.com/kaspanet/kaspad/util/mstime"
"github.com/pkg/errors"
"time"
)
// delayedBlock represents a block which has a delayed timestamp and will be processed at processTime
type delayedBlock struct {
block *util.Block
processTime mstime.Time
flags BehaviorFlags
}
func (dag *BlockDAG) isKnownDelayedBlock(hash *daghash.Hash) bool {
_, exists := dag.delayedBlocks[*hash]
return exists
}
func (dag *BlockDAG) addDelayedBlock(block *util.Block, flags BehaviorFlags, delay time.Duration) error {
processTime := dag.Now().Add(delay)
log.Debugf("Adding block to delayed blocks queue (block hash: %s, process time: %s)", block.Hash().String(), processTime)
delayedBlock := &delayedBlock{
block: block,
processTime: processTime,
flags: flags,
}
dag.delayedBlocks[*block.Hash()] = delayedBlock
dag.delayedBlocksQueue.Push(delayedBlock)
return dag.processDelayedBlocks()
}
// processDelayedBlocks loops over all delayed blocks and processes blocks which are due.
// This method is invoked after processing a block (ProcessBlock method).
func (dag *BlockDAG) processDelayedBlocks() error {
// Check if the delayed block with the earliest process time should be processed
for dag.delayedBlocksQueue.Len() > 0 {
earliestDelayedBlockProcessTime := dag.peekDelayedBlock().processTime
if earliestDelayedBlockProcessTime.After(dag.Now()) {
break
}
delayedBlock := dag.popDelayedBlock()
_, _, err := dag.processBlockNoLock(delayedBlock.block, delayedBlock.flags|BFAfterDelay)
if err != nil {
log.Errorf("Error while processing delayed block (block %s): %s", delayedBlock.block.Hash().String(), err)
// Rule errors should not be propagated as they refer only to the delayed block,
// while this function runs in the context of another block
if !errors.As(err, &RuleError{}) {
return err
}
}
log.Debugf("Processed delayed block (block %s)", delayedBlock.block.Hash().String())
}
return nil
}
// popDelayedBlock removes the topmost (delayed block with earliest process time) of the queue and returns it.
func (dag *BlockDAG) popDelayedBlock() *delayedBlock {
delayedBlock := dag.delayedBlocksQueue.pop()
delete(dag.delayedBlocks, *delayedBlock.block.Hash())
return delayedBlock
}
func (dag *BlockDAG) peekDelayedBlock() *delayedBlock {
return dag.delayedBlocksQueue.peek()
}
// maxDelayOfParents returns the maximum delay of the given block hashes.
// Note that delay could be 0, but isDelayed will return true. This is the case where the parent process time is due.
func (dag *BlockDAG) maxDelayOfParents(parentHashes []*daghash.Hash) (delay time.Duration, isDelayed bool) {
for _, parentHash := range parentHashes {
if delayedParent, exists := dag.delayedBlocks[*parentHash]; exists {
isDelayed = true
parentDelay := delayedParent.processTime.Sub(dag.Now())
if parentDelay > delay {
delay = parentDelay
}
}
}
return delay, isDelayed
}
func (dag *BlockDAG) shouldBlockBeDelayed(block *util.Block) (delay time.Duration, isDelayed bool) {
header := &block.MsgBlock().Header
maxTimestamp := dag.Now().Add(time.Duration(dag.TimestampDeviationTolerance) * dag.Params.TargetTimePerBlock)
if header.Timestamp.After(maxTimestamp) {
return header.Timestamp.Sub(maxTimestamp), true
}
return 0, false
}