UNPKG

8.24 kBJavaScriptView Raw
1'use strict';
2
3var _ = require('lodash');
4var BN = require('../crypto/bn');
5var BufferUtil = require('../util/buffer');
6var BufferReader = require('../encoding/bufferreader');
7var BufferWriter = require('../encoding/bufferwriter');
8var Hash = require('../crypto/hash');
9var JSUtil = require('../util/js');
10var $ = require('../util/preconditions');
11
12var GENESIS_BITS = 0x1d00ffff;
13
14/**
15 * Instantiate a BlockHeader from a Buffer, JSON object, or Object with
16 * the properties of the BlockHeader
17 *
18 * @param {*} - A Buffer, JSON string, or Object
19 * @returns {BlockHeader} - An instance of block header
20 * @constructor
21 */
22var 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 * @param {*} - A Buffer, JSON string or Object
47 * @returns {Object} - An object representing block header data
48 * @throws {TypeError} - If the argument was not recognized
49 * @private
50 */
51BlockHeader._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 * @param {Object} - A JSON string
65 * @returns {Object} - An object representing block header data
66 * @private
67 */
68BlockHeader._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 * @param {Object} - A plain JavaScript object
93 * @returns {BlockHeader} - An instance of block header
94 */
95BlockHeader.fromObject = function fromObject(obj) {
96 var info = BlockHeader._fromObject(obj);
97 return new BlockHeader(info);
98};
99
100/**
101 * @param {Binary} - Raw block binary data or buffer
102 * @returns {BlockHeader} - An instance of block header
103 */
104BlockHeader.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 * @param {Buffer} - A buffer of the block header
116 * @returns {BlockHeader} - An instance of block header
117 */
118BlockHeader.fromBuffer = function fromBuffer(buf) {
119 var info = BlockHeader._fromBufferReader(BufferReader(buf));
120 return new BlockHeader(info);
121};
122
123/**
124 * @param {string} - A hex encoded buffer of the block header
125 * @returns {BlockHeader} - An instance of block header
126 */
127BlockHeader.fromString = function fromString(str) {
128 var buf = Buffer.from(str, 'hex');
129 return BlockHeader.fromBuffer(buf);
130};
131
132/**
133 * @param {BufferReader} - A BufferReader of the block header
134 * @returns {Object} - An object representing block header data
135 * @private
136 */
137BlockHeader._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 * @param {BufferReader} - A BufferReader of the block header
150 * @returns {BlockHeader} - An instance of block header
151 */
152BlockHeader.fromBufferReader = function fromBufferReader(br) {
153 var info = BlockHeader._fromBufferReader(br);
154 return new BlockHeader(info);
155};
156
157/**
158 * @returns {Object} - A plain object of the BlockHeader
159 */
160BlockHeader.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 * @returns {Buffer} - A Buffer of the BlockHeader
174 */
175BlockHeader.prototype.toBuffer = function toBuffer() {
176 return this.toBufferWriter().concat();
177};
178
179/**
180 * @returns {string} - A hex encoded string of the BlockHeader
181 */
182BlockHeader.prototype.toString = function toString() {
183 return this.toBuffer().toString('hex');
184};
185
186/**
187 * @param {BufferWriter} - An existing instance BufferWriter
188 * @returns {BufferWriter} - An instance of BufferWriter representation of the BlockHeader
189 */
190BlockHeader.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 * Returns the target difficulty for this block
205 * @param {Number} bits
206 * @returns {BN} An instance of BN with the decoded difficulty bits
207 */
208BlockHeader.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 * @link https://en.bitcoin.it/wiki/Difficulty
221 * @return {Number}
222 */
223BlockHeader.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 * @returns {Buffer} - The little endian hash buffer of the header
236 */
237BlockHeader.prototype._getHash = function hash() {
238 var buf = this.toBuffer();
239 return Hash.sha256sha256(buf);
240};
241
242var idProperty = {
243 configurable: false,
244 enumerable: true,
245 /**
246 * @returns {string} - The big endian hash buffer of the header
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};
256Object.defineProperty(BlockHeader.prototype, 'id', idProperty);
257Object.defineProperty(BlockHeader.prototype, 'hash', idProperty);
258
259/**
260 * @returns {Boolean} - If timestamp is not too far in the future
261 */
262BlockHeader.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 * @returns {Boolean} - If the proof-of-work hash satisfies the target difficulty
272 */
273BlockHeader.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 * @returns {string} - A string formatted for the console
285 */
286BlockHeader.prototype.inspect = function inspect() {
287 return '<BlockHeader ' + this.id + '>';
288};
289
290BlockHeader.Constants = {
291 START_OF_HEADER: 8, // Start buffer position in raw block data
292 MAX_TIME_OFFSET: 2 * 60 * 60, // The max a timestamp can be in the future
293 LARGEST_HASH: new BN('10000000000000000000000000000000000000000000000000000000000000000', 'hex')
294};
295
296module.exports = BlockHeader;