1 | 'use strict';
|
2 |
|
3 | var _ = require('lodash');
|
4 | var BN = require('../crypto/bn');
|
5 | var BufferUtil = require('../util/buffer');
|
6 | var BufferReader = require('../encoding/bufferreader');
|
7 | var BufferWriter = require('../encoding/bufferwriter');
|
8 | var Hash = require('../crypto/hash');
|
9 | var JSUtil = require('../util/js');
|
10 | var $ = require('../util/preconditions');
|
11 |
|
12 | var GENESIS_BITS = 0x1d00ffff;
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | var BlockHeader = function BlockHeader(arg) {
|
23 | if (!(this instanceof BlockHeader)) {
|
24 | return new BlockHeader(arg);
|
25 | }
|
26 | var info = BlockHeader._from(arg);
|
27 | this.version = info.version;
|
28 | this.prevHash = info.prevHash;
|
29 | this.merkleRoot = info.merkleRoot;
|
30 | this.time = info.time;
|
31 | this.timestamp = info.time;
|
32 | this.bits = info.bits;
|
33 | this.nonce = info.nonce;
|
34 |
|
35 | if (info.hash) {
|
36 | $.checkState(
|
37 | this.hash === info.hash,
|
38 | 'Argument object hash property does not match block hash.'
|
39 | );
|
40 | }
|
41 |
|
42 | return this;
|
43 | };
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | BlockHeader._from = function _from(arg) {
|
52 | var info = {};
|
53 | if (BufferUtil.isBuffer(arg)) {
|
54 | info = BlockHeader._fromBufferReader(BufferReader(arg));
|
55 | } else if (_.isObject(arg)) {
|
56 | info = BlockHeader._fromObject(arg);
|
57 | } else {
|
58 | throw new TypeError('Unrecognized argument for BlockHeader');
|
59 | }
|
60 | return info;
|
61 | };
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | BlockHeader._fromObject = function _fromObject(data) {
|
69 | $.checkArgument(data, 'data is required');
|
70 | var prevHash = data.prevHash;
|
71 | var merkleRoot = data.merkleRoot;
|
72 | if (_.isString(data.prevHash)) {
|
73 | prevHash = BufferUtil.reverse(Buffer.from(data.prevHash, 'hex'));
|
74 | }
|
75 | if (_.isString(data.merkleRoot)) {
|
76 | merkleRoot = BufferUtil.reverse(Buffer.from(data.merkleRoot, 'hex'));
|
77 | }
|
78 | var info = {
|
79 | hash: data.hash,
|
80 | version: data.version,
|
81 | prevHash: prevHash,
|
82 | merkleRoot: merkleRoot,
|
83 | time: data.time,
|
84 | timestamp: data.time,
|
85 | bits: data.bits,
|
86 | nonce: data.nonce
|
87 | };
|
88 | return info;
|
89 | };
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 | BlockHeader.fromObject = function fromObject(obj) {
|
96 | var info = BlockHeader._fromObject(obj);
|
97 | return new BlockHeader(info);
|
98 | };
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | BlockHeader.fromRawBlock = function fromRawBlock(data) {
|
105 | if (!BufferUtil.isBuffer(data)) {
|
106 | data = Buffer.from(data, 'binary');
|
107 | }
|
108 | var br = BufferReader(data);
|
109 | br.pos = BlockHeader.Constants.START_OF_HEADER;
|
110 | var info = BlockHeader._fromBufferReader(br);
|
111 | return new BlockHeader(info);
|
112 | };
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 | BlockHeader.fromBuffer = function fromBuffer(buf) {
|
119 | var info = BlockHeader._fromBufferReader(BufferReader(buf));
|
120 | return new BlockHeader(info);
|
121 | };
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 | BlockHeader.fromString = function fromString(str) {
|
128 | var buf = Buffer.from(str, 'hex');
|
129 | return BlockHeader.fromBuffer(buf);
|
130 | };
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 |
|
137 | BlockHeader._fromBufferReader = function _fromBufferReader(br) {
|
138 | var info = {};
|
139 | info.version = br.readInt32LE();
|
140 | info.prevHash = br.read(32);
|
141 | info.merkleRoot = br.read(32);
|
142 | info.time = br.readUInt32LE();
|
143 | info.bits = br.readUInt32LE();
|
144 | info.nonce = br.readUInt32LE();
|
145 | return info;
|
146 | };
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 | BlockHeader.fromBufferReader = function fromBufferReader(br) {
|
153 | var info = BlockHeader._fromBufferReader(br);
|
154 | return new BlockHeader(info);
|
155 | };
|
156 |
|
157 |
|
158 |
|
159 |
|
160 | BlockHeader.prototype.toObject = BlockHeader.prototype.toJSON = function toObject() {
|
161 | return {
|
162 | hash: this.hash,
|
163 | version: this.version,
|
164 | prevHash: BufferUtil.reverse(this.prevHash).toString('hex'),
|
165 | merkleRoot: BufferUtil.reverse(this.merkleRoot).toString('hex'),
|
166 | time: this.time,
|
167 | bits: this.bits,
|
168 | nonce: this.nonce
|
169 | };
|
170 | };
|
171 |
|
172 |
|
173 |
|
174 |
|
175 | BlockHeader.prototype.toBuffer = function toBuffer() {
|
176 | return this.toBufferWriter().concat();
|
177 | };
|
178 |
|
179 |
|
180 |
|
181 |
|
182 | BlockHeader.prototype.toString = function toString() {
|
183 | return this.toBuffer().toString('hex');
|
184 | };
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 | BlockHeader.prototype.toBufferWriter = function toBufferWriter(bw) {
|
191 | if (!bw) {
|
192 | bw = new BufferWriter();
|
193 | }
|
194 | bw.writeInt32LE(this.version);
|
195 | bw.write(this.prevHash);
|
196 | bw.write(this.merkleRoot);
|
197 | bw.writeUInt32LE(this.time);
|
198 | bw.writeUInt32LE(this.bits);
|
199 | bw.writeUInt32LE(this.nonce);
|
200 | return bw;
|
201 | };
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 | BlockHeader.prototype.getTargetDifficulty = function getTargetDifficulty(bits) {
|
209 | bits = bits || this.bits;
|
210 |
|
211 | var target = new BN(bits & 0xffffff);
|
212 | var mov = 8 * ((bits >>> 24) - 3);
|
213 | while (mov-- > 0) {
|
214 | target = target.mul(new BN(2));
|
215 | }
|
216 | return target;
|
217 | };
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 | BlockHeader.prototype.getDifficulty = function getDifficulty() {
|
224 | var difficulty1TargetBN = this.getTargetDifficulty(GENESIS_BITS).mul(new BN(Math.pow(10, 8)));
|
225 | var currentTargetBN = this.getTargetDifficulty();
|
226 |
|
227 | var difficultyString = difficulty1TargetBN.div(currentTargetBN).toString(10);
|
228 | var decimalPos = difficultyString.length - 8;
|
229 | difficultyString = difficultyString.slice(0, decimalPos) + '.' + difficultyString.slice(decimalPos);
|
230 |
|
231 | return parseFloat(difficultyString);
|
232 | };
|
233 |
|
234 |
|
235 |
|
236 |
|
237 | BlockHeader.prototype._getHash = function hash() {
|
238 | var buf = this.toBuffer();
|
239 | return Hash.sha256sha256(buf);
|
240 | };
|
241 |
|
242 | var idProperty = {
|
243 | configurable: false,
|
244 | enumerable: true,
|
245 | |
246 |
|
247 |
|
248 | get: function() {
|
249 | if (!this._id) {
|
250 | this._id = BufferReader(this._getHash()).readReverse().toString('hex');
|
251 | }
|
252 | return this._id;
|
253 | },
|
254 | set: _.noop
|
255 | };
|
256 | Object.defineProperty(BlockHeader.prototype, 'id', idProperty);
|
257 | Object.defineProperty(BlockHeader.prototype, 'hash', idProperty);
|
258 |
|
259 | /**
|
260 | * @returns {Boolean} - If timestamp is not too far in the future
|
261 | */
|
262 | BlockHeader.prototype.validTimestamp = function validTimestamp() {
|
263 | var currentTime = Math.round(new Date().getTime() / 1000);
|
264 | if (this.time > currentTime + BlockHeader.Constants.MAX_TIME_OFFSET) {
|
265 | return false;
|
266 | }
|
267 | return true;
|
268 | };
|
269 |
|
270 |
|
271 |
|
272 |
|
273 | BlockHeader.prototype.validProofOfWork = function validProofOfWork() {
|
274 | var pow = new BN(this.id, 'hex');
|
275 | var target = this.getTargetDifficulty();
|
276 |
|
277 | if (pow.cmp(target) > 0) {
|
278 | return false;
|
279 | }
|
280 | return true;
|
281 | };
|
282 |
|
283 |
|
284 |
|
285 |
|
286 | BlockHeader.prototype.inspect = function inspect() {
|
287 | return '<BlockHeader ' + this.id + '>';
|
288 | };
|
289 |
|
290 | BlockHeader.Constants = {
|
291 | START_OF_HEADER: 8,
|
292 | MAX_TIME_OFFSET: 2 * 60 * 60,
|
293 | LARGEST_HASH: new BN('10000000000000000000000000000000000000000000000000000000000000000', 'hex')
|
294 | };
|
295 |
|
296 | module.exports = BlockHeader;
|