1 | const utils = require('ethereumjs-util')
|
2 | const params = require('ethereum-common')
|
3 | const BN = utils.BN
|
4 | |
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | var BlockHeader = module.exports = function (data) {
|
24 | var fields = [{
|
25 | name: 'parentHash',
|
26 | length: 32,
|
27 | default: utils.zeros(32)
|
28 | }, {
|
29 | name: 'uncleHash',
|
30 | default: utils.SHA3_RLP_ARRAY
|
31 | }, {
|
32 | name: 'coinbase',
|
33 | length: 20,
|
34 | default: utils.zeros(20)
|
35 | }, {
|
36 | name: 'stateRoot',
|
37 | length: 32,
|
38 | default: utils.zeros(32)
|
39 | }, {
|
40 | name: 'transactionsTrie',
|
41 | length: 32,
|
42 | default: utils.SHA3_RLP
|
43 | }, {
|
44 | name: 'receiptTrie',
|
45 | length: 32,
|
46 | default: utils.SHA3_RLP
|
47 | }, {
|
48 | name: 'bloom',
|
49 | default: utils.zeros(256)
|
50 | }, {
|
51 | name: 'difficulty',
|
52 | default: new Buffer([])
|
53 | }, {
|
54 | name: 'number',
|
55 | default: utils.intToBuffer(params.homeSteadForkNumber.v)
|
56 | }, {
|
57 | name: 'gasLimit',
|
58 | default: new Buffer('ffffffffffffff', 'hex')
|
59 | }, {
|
60 | name: 'gasUsed',
|
61 | empty: true,
|
62 | default: new Buffer([])
|
63 | }, {
|
64 | name: 'timestamp',
|
65 | default: new Buffer([])
|
66 | }, {
|
67 | name: 'extraData',
|
68 | allowZero: true,
|
69 | empty: true,
|
70 | default: new Buffer([])
|
71 | }, {
|
72 | name: 'mixHash',
|
73 | default: utils.zeros(32)
|
74 |
|
75 | }, {
|
76 | name: 'nonce',
|
77 | default: new Buffer([])
|
78 | }]
|
79 | utils.defineProperties(this, fields, data)
|
80 | }
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | BlockHeader.prototype.canonicalDifficulty = function (parentBlock) {
|
89 | const blockTs = new BN(this.timestamp)
|
90 | const parentTs = new BN(parentBlock.header.timestamp)
|
91 | const parentDif = new BN(parentBlock.header.difficulty)
|
92 | const minimumDifficulty = new BN(params.minimumDifficulty.v)
|
93 | var offset = parentDif.div(new BN(params.difficultyBoundDivisor.v))
|
94 | var dif
|
95 |
|
96 | if (this.isHomestead()) {
|
97 |
|
98 |
|
99 | var a = blockTs.sub(parentTs).divn(10).neg().addn(1)
|
100 | var cutoff = new BN(-99)
|
101 |
|
102 | if (cutoff.cmp(a) === 1) {
|
103 | a = cutoff
|
104 | }
|
105 | dif = parentDif.add(offset.mul(a))
|
106 | } else {
|
107 |
|
108 | if (parentTs.addn(params.durationLimit.v).cmp(blockTs) === 1) {
|
109 | dif = offset.add(parentDif)
|
110 | } else {
|
111 | dif = parentDif.sub(offset)
|
112 | }
|
113 | }
|
114 |
|
115 | var exp = new BN(this.number).divn(100000).subn(2)
|
116 | if (!exp.isNeg()) {
|
117 | dif.iadd(new BN(2).pow(exp))
|
118 | }
|
119 |
|
120 | if (dif.cmp(minimumDifficulty) === -1) {
|
121 | dif = minimumDifficulty
|
122 | }
|
123 |
|
124 | return dif
|
125 | }
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 | BlockHeader.prototype.validateDifficulty = function (parentBlock) {
|
134 | const dif = this.canonicalDifficulty(parentBlock)
|
135 | return dif.cmp(new BN(this.difficulty)) === 0
|
136 | }
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | BlockHeader.prototype.validateGasLimit = function (parentBlock) {
|
145 | const pGasLimit = utils.bufferToInt(parentBlock.header.gasLimit)
|
146 | const gasLimit = utils.bufferToInt(this.gasLimit)
|
147 | const a = Math.floor(pGasLimit / params.gasLimitBoundDivisor.v)
|
148 | const maxGasLimit = pGasLimit + a
|
149 | const minGasLimit = pGasLimit - a
|
150 |
|
151 | return maxGasLimit > gasLimit && minGasLimit < gasLimit && params.minGasLimit.v <= gasLimit
|
152 | }
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | BlockHeader.prototype.validate = function (blockchain, height, cb) {
|
162 | var self = this
|
163 | if (arguments.length === 2) {
|
164 | cb = height
|
165 | height = false
|
166 | }
|
167 |
|
168 | if (this.isGenesis()) {
|
169 | return cb()
|
170 | }
|
171 |
|
172 |
|
173 | blockchain.getBlock(self.parentHash, function (err, parentBlock) {
|
174 | if (err) {
|
175 | return cb('could not find parent block')
|
176 | }
|
177 |
|
178 | self.parentBlock = parentBlock
|
179 |
|
180 | var number = new BN(self.number)
|
181 | if (number.cmp(new BN(parentBlock.header.number).addn(1)) !== 0) {
|
182 | return cb('invalid number')
|
183 | }
|
184 |
|
185 | if (height) {
|
186 | var dif = height.sub(new BN(parentBlock.header.number))
|
187 | if (!(dif.cmpn(8) === -1 && dif.cmpn(1) === 1)) {
|
188 | return cb('uncle block has a parent that is too old or to young')
|
189 | }
|
190 | }
|
191 |
|
192 | if (!self.validateDifficulty(parentBlock)) {
|
193 | return cb('invalid Difficulty')
|
194 | }
|
195 |
|
196 | if (!self.validateGasLimit(parentBlock)) {
|
197 | return cb('invalid gas limit')
|
198 | }
|
199 |
|
200 | if (utils.bufferToInt(parentBlock.header.number) + 1 !== utils.bufferToInt(self.number)) {
|
201 | return cb('invalid heigth')
|
202 | }
|
203 |
|
204 | if (utils.bufferToInt(self.timestamp) <= utils.bufferToInt(parentBlock.header.timestamp)) {
|
205 | return cb('invalid timestamp')
|
206 | }
|
207 |
|
208 | if (self.extraData.length > params.maximumExtraDataSize.v) {
|
209 | return cb('invalid amount of extra data')
|
210 | }
|
211 |
|
212 | cb()
|
213 | })
|
214 | }
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | BlockHeader.prototype.hash = function () {
|
222 | return utils.rlphash(this.raw)
|
223 | }
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 | BlockHeader.prototype.isGenesis = function () {
|
231 | return this.number.toString('hex') === ''
|
232 | }
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 | BlockHeader.prototype.isHomestead = function () {
|
240 | return utils.bufferToInt(this.number) >= params.homeSteadForkNumber.v
|
241 | }
|