-
Notifications
You must be signed in to change notification settings - Fork 18
/
blockchain.go
178 lines (147 loc) · 4.81 KB
/
blockchain.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package blockchain
import (
"sync"
"time"
"github.com/energicryptocurrency/go-energi/energi/consensus/difficultysim/calculus"
"github.com/energicryptocurrency/go-energi/energi/consensus/difficultysim/params"
)
// Block represents each block in the blockchain
type Block struct {
Difficulty uint64 `json:"difficultyStart"`
Height uint64 `json:"height"`
Coinbase string `json:"coinbase"`
Time uint64 `json:"time"`
Nonce uint64 `json:"nonce"`
}
// Blockchain represents current state of the chain
type Blockchain struct {
Locker *sync.Mutex
Chain []Block
// time function
Now func() uint64
}
// CreateBlockchain creates initial blockchain state
func CreateBlockchain() *Blockchain {
// create blockchain parameters
chain := Blockchain{
Now: func() uint64 {
return uint64(time.Now().UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond)))
},
Locker: &sync.Mutex{},
}
// insert genesis block
chain.Chain = []Block{{Difficulty: params.InitialDifficulty, Height: 1,
Coinbase: "", Nonce: 0, Time: chain.Now()}}
return &chain
}
// GetBlock return height corresponding mined block
func (chain *Blockchain) GetBlock(height uint64) *Block {
chain.Locker.Lock()
defer chain.Locker.Unlock()
if len(chain.Chain) < int(height) || height <= 0 {
return nil
}
return &chain.Chain[height-1]
}
// LastBlock returns last mined block in chain (the blockchain has genesis block)
func (chain *Blockchain) LastBlock() *Block {
chain.Locker.Lock()
defer chain.Locker.Unlock()
return &chain.Chain[len(chain.Chain)-1]
}
// NewBlock appends new mined block into the chain
func (chain *Blockchain) NewBlock(block *Block) {
chain.Locker.Lock()
defer chain.Locker.Unlock()
if chain.Chain[len(chain.Chain)-1].Height+1 != block.Height {
return
}
chain.Chain = append(chain.Chain, *block)
}
/**
* Implements block time consensus
*
* POS-11: Block time restrictions
* POS-12: Block interval enforcement
*/
func (chain *Blockchain) CalcTimeTargetV1(parent *Block) *calculus.TimeTarget {
ret := new(calculus.TimeTarget)
now := chain.Now()
parentNumber := parent.Height
blockNumber := parentNumber + 1
// POS-11: Block time restrictions
ret.Max = now + params.MaxFutureGap
// POS-11: Block time restrictions
ret.Min = parent.Time + params.MinBlockGap
var targetBlockGap uint64
if params.BananaBlockTimeIsActive {
targetBlockGap = params.BananaTargetBlockGap
} else {
targetBlockGap = params.TargetBlockGap
}
ret.BlockTarget = parent.Time + targetBlockGap
ret.PeriodTarget = ret.BlockTarget
// POS-12: Block interval enforcement
// ---
if blockNumber >= params.AveragingWindow {
// TODO: LRU cache here for extra DoS mitigation
past := parent
// NOTE: we have to do this way as parent may be not part of canonical
// chain. As no mutex is held, we cannot do checks for canonical.
for i := params.AveragingWindow - 1; i > 0; i-- {
past = chain.GetBlock(past.Height - 1)
if past == nil {
return ret
}
}
ret.PeriodTarget = past.Time + params.TargetPeriodGap
periodMinTime := ret.PeriodTarget - params.MinBlockGap
if periodMinTime > ret.Min {
ret.Min = periodMinTime
}
}
return ret
}
// CalcTimeTargetV2 calculates asgard time target
func (chain *Blockchain) CalcTimeTargetV2(parent *Block) *calculus.TimeTarget {
ret := &calculus.TimeTarget{}
parentBlockTime := parent.Time // Defines the original parent block time.
// POS-11: Block time restrictions
ret.Max = chain.Now() + params.MaxFutureGap
var targetBlockGap, minBlockGap uint64
if params.BananaBlockTimeIsActive {
targetBlockGap = params.BananaTargetBlockGap
minBlockGap = params.BananaMinBlockGap
} else {
targetBlockGap = params.TargetBlockGap
minBlockGap = params.MinBlockGap
}
// POS-11: Block time restrictions
ret.Min = parentBlockTime + minBlockGap
ret.BlockTarget = parentBlockTime + targetBlockGap
ret.PeriodTarget = ret.BlockTarget
// Block interval enforcement
// TODO: LRU cache here for extra DoS mitigation
timeDiffs := make([]uint64, params.BlockTimeEMAPeriod)
// compute block time differences
// note that the most recent time difference will be the most
// weighted by the EMA, and the oldest time difference will be the least
for i := params.BlockTimeEMAPeriod; i > 0; i-- {
past := chain.GetBlock(parent.Height - 1)
if past == nil {
break
}
timeDiffs[i-1] = parent.Time - past.Time
parent = past
}
ema := calculus.CalculateBlockTimeEMA(timeDiffs, params.BlockTimeEMAPeriod, targetBlockGap)
ret.PeriodTarget = ema[len(ema)-1]
// set up the parameters for PID control (diffV2)
drift := calculus.CalculateBlockTimeDrift(ema, targetBlockGap)
integral := calculus.CalculateBlockTimeIntegral(drift)
derivative := calculus.CalculateBlockTimeDerivative(drift)
ret.Drift = drift[len(drift)-1]
ret.Integral = integral
ret.Derivative = derivative[len(derivative)-1]
return ret
}