UNPKG

7.47 kBJavaScriptView Raw
1'use strict';
2
3var _ = require('lodash');
4var BlockHeader = require('./blockheader');
5var BN = require('../crypto/bn');
6var BufferUtil = require('../util/buffer');
7var BufferReader = require('../encoding/bufferreader');
8var BufferWriter = require('../encoding/bufferwriter');
9var Hash = require('../crypto/hash');
10var Transaction = require('../transaction');
11var $ = require('../util/preconditions');
12
13/**
14 * Instantiate a Block from a Buffer, JSON object, or Object with
15 * the properties of the Block
16 *
17 * @param {*} - A Buffer, JSON string, or Object
18 * @returns {Block}
19 * @constructor
20 */
21function Block(arg) {
22 if (!(this instanceof Block)) {
23 return new Block(arg);
24 }
25 _.extend(this, Block._from(arg));
26 return this;
27}
28
29// https://github.com/bitcoin/bitcoin/blob/b5fa132329f0377d787a4a21c1686609c2bfaece/src/primitives/block.h#L14
30Block.MAX_BLOCK_SIZE = 1000000;
31
32/**
33 * @param {*} - A Buffer, JSON string or Object
34 * @returns {Object} - An object representing block data
35 * @throws {TypeError} - If the argument was not recognized
36 * @private
37 */
38Block._from = function _from(arg) {
39 var info = {};
40 if (BufferUtil.isBuffer(arg)) {
41 info = Block._fromBufferReader(BufferReader(arg));
42 } else if (_.isObject(arg)) {
43 info = Block._fromObject(arg);
44 } else {
45 throw new TypeError('Unrecognized argument for Block');
46 }
47 return info;
48};
49
50/**
51 * @param {Object} - A plain JavaScript object
52 * @returns {Object} - An object representing block data
53 * @private
54 */
55Block._fromObject = function _fromObject(data) {
56 var transactions = [];
57 data.transactions.forEach(function(tx) {
58 if (tx instanceof Transaction) {
59 transactions.push(tx);
60 } else {
61 transactions.push(Transaction().fromObject(tx));
62 }
63 });
64 var info = {
65 header: BlockHeader.fromObject(data.header),
66 transactions: transactions
67 };
68 return info;
69};
70
71/**
72 * @param {Object} - A plain JavaScript object
73 * @returns {Block} - An instance of block
74 */
75Block.fromObject = function fromObject(obj) {
76 var info = Block._fromObject(obj);
77 return new Block(info);
78};
79
80/**
81 * @param {BufferReader} - Block data
82 * @returns {Object} - An object representing the block data
83 * @private
84 */
85Block._fromBufferReader = function _fromBufferReader(br) {
86 var info = {};
87 $.checkState(!br.finished(), 'No block data received');
88 info.header = BlockHeader.fromBufferReader(br);
89 var transactions = br.readVarintNum();
90 info.transactions = [];
91 for (var i = 0; i < transactions; i++) {
92 info.transactions.push(Transaction().fromBufferReader(br));
93 }
94 return info;
95};
96
97/**
98 * @param {BufferReader} - A buffer reader of the block
99 * @returns {Block} - An instance of block
100 */
101Block.fromBufferReader = function fromBufferReader(br) {
102 $.checkArgument(br, 'br is required');
103 var info = Block._fromBufferReader(br);
104 return new Block(info);
105};
106
107/**
108 * @param {Buffer} - A buffer of the block
109 * @returns {Block} - An instance of block
110 */
111Block.fromBuffer = function fromBuffer(buf) {
112 return Block.fromBufferReader(new BufferReader(buf));
113};
114
115/**
116 * @param {string} - str - A hex encoded string of the block
117 * @returns {Block} - A hex encoded string of the block
118 */
119Block.fromString = function fromString(str) {
120 var buf = Buffer.from(str, 'hex');
121 return Block.fromBuffer(buf);
122};
123
124/**
125 * @param {Binary} - Raw block binary data or buffer
126 * @returns {Block} - An instance of block
127 */
128Block.fromRawBlock = function fromRawBlock(data) {
129 if (!BufferUtil.isBuffer(data)) {
130 data = Buffer.from(data, 'binary');
131 }
132 var br = BufferReader(data);
133 br.pos = Block.Values.START_OF_BLOCK;
134 var info = Block._fromBufferReader(br);
135 return new Block(info);
136};
137
138/**
139 * @returns {Object} - A plain object with the block properties
140 */
141Block.prototype.toObject = Block.prototype.toJSON = function toObject() {
142 var transactions = [];
143 this.transactions.forEach(function(tx) {
144 transactions.push(tx.toObject());
145 });
146 return {
147 header: this.header.toObject(),
148 transactions: transactions
149 };
150};
151
152/**
153 * @returns {Buffer} - A buffer of the block
154 */
155Block.prototype.toBuffer = function toBuffer() {
156 return this.toBufferWriter().concat();
157};
158
159/**
160 * @returns {string} - A hex encoded string of the block
161 */
162Block.prototype.toString = function toString() {
163 return this.toBuffer().toString('hex');
164};
165
166/**
167 * @param {BufferWriter} - An existing instance of BufferWriter
168 * @returns {BufferWriter} - An instance of BufferWriter representation of the Block
169 */
170Block.prototype.toBufferWriter = function toBufferWriter(bw) {
171 if (!bw) {
172 bw = new BufferWriter();
173 }
174 bw.write(this.header.toBuffer());
175 bw.writeVarintNum(this.transactions.length);
176 for (var i = 0; i < this.transactions.length; i++) {
177 this.transactions[i].toBufferWriter(bw);
178 }
179 return bw;
180};
181
182/**
183 * Will iterate through each transaction and return an array of hashes
184 * @returns {Array} - An array with transaction hashes
185 */
186Block.prototype.getTransactionHashes = function getTransactionHashes() {
187 var hashes = [];
188 if (this.transactions.length === 0) {
189 return [Block.Values.NULL_HASH];
190 }
191 for (var t = 0; t < this.transactions.length; t++) {
192 hashes.push(this.transactions[t]._getHash());
193 }
194 return hashes;
195};
196
197/**
198 * Will build a merkle tree of all the transactions, ultimately arriving at
199 * a single point, the merkle root.
200 * @link https://en.bitcoin.it/wiki/Protocol_specification#Merkle_Trees
201 * @returns {Array} - An array with each level of the tree after the other.
202 */
203Block.prototype.getMerkleTree = function getMerkleTree() {
204
205 var tree = this.getTransactionHashes();
206
207 var j = 0;
208 for (var size = this.transactions.length; size > 1; size = Math.floor((size + 1) / 2)) {
209 for (var i = 0; i < size; i += 2) {
210 var i2 = Math.min(i + 1, size - 1);
211 var buf = Buffer.concat([tree[j + i], tree[j + i2]]);
212 tree.push(Hash.sha256sha256(buf));
213 }
214 j += size;
215 }
216
217 return tree;
218};
219
220/**
221 * Calculates the merkleRoot from the transactions.
222 * @returns {Buffer} - A buffer of the merkle root hash
223 */
224Block.prototype.getMerkleRoot = function getMerkleRoot() {
225 var tree = this.getMerkleTree();
226 return tree[tree.length - 1];
227};
228
229/**
230 * Verifies that the transactions in the block match the header merkle root
231 * @returns {Boolean} - If the merkle roots match
232 */
233Block.prototype.validMerkleRoot = function validMerkleRoot() {
234
235 var h = new BN(this.header.merkleRoot.toString('hex'), 'hex');
236 var c = new BN(this.getMerkleRoot().toString('hex'), 'hex');
237
238 if (h.cmp(c) !== 0) {
239 return false;
240 }
241
242 return true;
243};
244
245/**
246 * @returns {Buffer} - The little endian hash buffer of the header
247 */
248Block.prototype._getHash = function() {
249 return this.header._getHash();
250};
251
252var idProperty = {
253 configurable: false,
254 enumerable: true,
255 /**
256 * @returns {string} - The big endian hash buffer of the header
257 */
258 get: function() {
259 if (!this._id) {
260 this._id = this.header.id;
261 }
262 return this._id;
263 },
264 set: _.noop
265};
266Object.defineProperty(Block.prototype, 'id', idProperty);
267Object.defineProperty(Block.prototype, 'hash', idProperty);
268
269/**
270 * @returns {string} - A string formatted for the console
271 */
272Block.prototype.inspect = function inspect() {
273 return '<Block ' + this.id + '>';
274};
275
276Block.Values = {
277 START_OF_BLOCK: 8, // Start of block in raw block data
278 NULL_HASH: Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
279};
280
281module.exports = Block;