/
blockwindow.go
103 lines (90 loc) · 2.98 KB
/
blockwindow.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package difficultymanager
import (
"math"
"math/big"
"github.com/BTCGhostdag/BTCD/domain/consensus/model"
"github.com/BTCGhostdag/BTCD/domain/consensus/model/externalapi"
"github.com/BTCGhostdag/BTCD/util/difficulty"
)
type difficultyBlock struct {
timeInMilliseconds int64
Bits uint32
hash *externalapi.DomainHash
blueWork *big.Int
}
type blockWindow []difficultyBlock
func (dm *difficultyManager) getDifficultyBlock(
stagingArea *model.StagingArea, blockHash *externalapi.DomainHash) (difficultyBlock, error) {
header, err := dm.headerStore.BlockHeader(dm.databaseContext, stagingArea, blockHash)
if err != nil {
return difficultyBlock{}, err
}
return difficultyBlock{
timeInMilliseconds: header.TimeInMilliseconds(),
Bits: header.Bits(),
hash: blockHash,
blueWork: header.BlueWork(),
}, nil
}
// blockWindow returns a blockWindow of the given size that contains the
// blocks in the past of startingNode, the sorting is unspecified.
// If the number of blocks in the past of startingNode is less then windowSize,
// the window will be padded by genesis blocks to achieve a size of windowSize.
func (dm *difficultyManager) blockWindow(stagingArea *model.StagingArea, startingNode *externalapi.DomainHash, windowSize int) (blockWindow,
[]*externalapi.DomainHash, error) {
window := make(blockWindow, 0, windowSize)
windowHashes, err := dm.dagTraversalManager.BlockWindow(stagingArea, startingNode, windowSize)
if err != nil {
return nil, nil, err
}
for _, hash := range windowHashes {
block, err := dm.getDifficultyBlock(stagingArea, hash)
if err != nil {
return nil, nil, err
}
window = append(window, block)
}
return window, windowHashes, nil
}
func ghostdagLess(blockA *difficultyBlock, blockB *difficultyBlock) bool {
switch blockA.blueWork.Cmp(blockB.blueWork) {
case -1:
return true
case 1:
return false
case 0:
return blockA.hash.Less(blockB.hash)
default:
panic("big.Int.Cmp is defined to always return -1/1/0 and nothing else")
}
}
func (window blockWindow) minMaxTimestamps() (min, max int64, minIndex int) {
min = math.MaxInt64
minIndex = 0
max = 0
for i, block := range window {
// If timestamps are equal we ghostdag compare in order to reach consensus on `minIndex`
if block.timeInMilliseconds < min ||
(block.timeInMilliseconds == min && ghostdagLess(&block, &window[minIndex])) {
min = block.timeInMilliseconds
minIndex = i
}
if block.timeInMilliseconds > max {
max = block.timeInMilliseconds
}
}
return
}
func (window *blockWindow) remove(n int) {
(*window)[n] = (*window)[len(*window)-1]
*window = (*window)[:len(*window)-1]
}
func (window blockWindow) averageTarget() *big.Int {
averageTarget := new(big.Int)
targetTmp := new(big.Int)
for _, block := range window {
difficulty.CompactToBigWithDestination(block.Bits, targetTmp)
averageTarget.Add(averageTarget, targetTmp)
}
return averageTarget.Div(averageTarget, big.NewInt(int64(len(window))))
}