1 | 'use strict';
|
2 |
|
3 | var _ = require('lodash');
|
4 | var BlockHeader = require('./blockheader');
|
5 | var BN = require('../crypto/bn');
|
6 | var BufferUtil = require('../util/buffer');
|
7 | var BufferReader = require('../encoding/bufferreader');
|
8 | var BufferWriter = require('../encoding/bufferwriter');
|
9 | var Hash = require('../crypto/hash');
|
10 | var Transaction = require('../transaction');
|
11 | var $ = require('../util/preconditions');
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | function 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 |
|
30 | Block.MAX_BLOCK_SIZE = 1000000;
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | Block._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 |
|
52 |
|
53 |
|
54 |
|
55 | Block._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 |
|
73 |
|
74 |
|
75 | Block.fromObject = function fromObject(obj) {
|
76 | var info = Block._fromObject(obj);
|
77 | return new Block(info);
|
78 | };
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 | Block._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 |
|
99 |
|
100 |
|
101 | Block.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 |
|
109 |
|
110 |
|
111 | Block.fromBuffer = function fromBuffer(buf) {
|
112 | return Block.fromBufferReader(new BufferReader(buf));
|
113 | };
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 | Block.fromString = function fromString(str) {
|
120 | var buf = Buffer.from(str, 'hex');
|
121 | return Block.fromBuffer(buf);
|
122 | };
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 | Block.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 |
|
140 |
|
141 | Block.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 |
|
154 |
|
155 | Block.prototype.toBuffer = function toBuffer() {
|
156 | return this.toBufferWriter().concat();
|
157 | };
|
158 |
|
159 |
|
160 |
|
161 |
|
162 | Block.prototype.toString = function toString() {
|
163 | return this.toBuffer().toString('hex');
|
164 | };
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | Block.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 |
|
184 |
|
185 |
|
186 | Block.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 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 | Block.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 |
|
222 |
|
223 |
|
224 | Block.prototype.getMerkleRoot = function getMerkleRoot() {
|
225 | var tree = this.getMerkleTree();
|
226 | return tree[tree.length - 1];
|
227 | };
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 | Block.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 |
|
247 |
|
248 | Block.prototype._getHash = function() {
|
249 | return this.header._getHash();
|
250 | };
|
251 |
|
252 | var idProperty = {
|
253 | configurable: false,
|
254 | enumerable: true,
|
255 | |
256 |
|
257 |
|
258 | get: function() {
|
259 | if (!this._id) {
|
260 | this._id = this.header.id;
|
261 | }
|
262 | return this._id;
|
263 | },
|
264 | set: _.noop
|
265 | };
|
266 | Object.defineProperty(Block.prototype, 'id', idProperty);
|
267 | Object.defineProperty(Block.prototype, 'hash', idProperty);
|
268 |
|
269 | /**
|
270 | * @returns {string} - A string formatted for the console
|
271 | */
|
272 | Block.prototype.inspect = function inspect() {
|
273 | return '<Block ' + this.id + '>';
|
274 | };
|
275 |
|
276 | Block.Values = {
|
277 | START_OF_BLOCK: 8,
|
278 | NULL_HASH: Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
|
279 | };
|
280 |
|
281 | module.exports = Block;
|