1 | 'use strict';
|
2 | Object.defineProperty(exports, '__esModule', { value: true });
|
3 | const bufferutils_1 = require('./bufferutils');
|
4 | const bcrypto = require('./crypto');
|
5 | const transaction_1 = require('./transaction');
|
6 | const types = require('./types');
|
7 | const fastMerkleRoot = require('merkle-lib/fastRoot');
|
8 | const typeforce = require('typeforce');
|
9 | const varuint = require('varuint-bitcoin');
|
10 | const errorMerkleNoTxes = new TypeError(
|
11 | 'Cannot compute merkle root for zero transactions',
|
12 | );
|
13 | const errorWitnessNotSegwit = new TypeError(
|
14 | 'Cannot compute witness commit for non-segwit block',
|
15 | );
|
16 | class Block {
|
17 | constructor() {
|
18 | this.version = 1;
|
19 | this.prevHash = undefined;
|
20 | this.merkleRoot = undefined;
|
21 | this.timestamp = 0;
|
22 | this.witnessCommit = undefined;
|
23 | this.bits = 0;
|
24 | this.nonce = 0;
|
25 | this.transactions = undefined;
|
26 | }
|
27 | static fromBuffer(buffer) {
|
28 | if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)');
|
29 | let offset = 0;
|
30 | const readSlice = n => {
|
31 | offset += n;
|
32 | return buffer.slice(offset - n, offset);
|
33 | };
|
34 | const readUInt32 = () => {
|
35 | const i = buffer.readUInt32LE(offset);
|
36 | offset += 4;
|
37 | return i;
|
38 | };
|
39 | const readInt32 = () => {
|
40 | const i = buffer.readInt32LE(offset);
|
41 | offset += 4;
|
42 | return i;
|
43 | };
|
44 | const block = new Block();
|
45 | block.version = readInt32();
|
46 | block.prevHash = readSlice(32);
|
47 | block.merkleRoot = readSlice(32);
|
48 | block.timestamp = readUInt32();
|
49 | block.bits = readUInt32();
|
50 | block.nonce = readUInt32();
|
51 | if (buffer.length === 80) return block;
|
52 | const readVarInt = () => {
|
53 | const vi = varuint.decode(buffer, offset);
|
54 | offset += varuint.decode.bytes;
|
55 | return vi;
|
56 | };
|
57 | const readTransaction = () => {
|
58 | const tx = transaction_1.Transaction.fromBuffer(
|
59 | buffer.slice(offset),
|
60 | true,
|
61 | );
|
62 | offset += tx.byteLength();
|
63 | return tx;
|
64 | };
|
65 | const nTransactions = readVarInt();
|
66 | block.transactions = [];
|
67 | for (let i = 0; i < nTransactions; ++i) {
|
68 | const tx = readTransaction();
|
69 | block.transactions.push(tx);
|
70 | }
|
71 | const witnessCommit = block.getWitnessCommit();
|
72 |
|
73 | if (witnessCommit) block.witnessCommit = witnessCommit;
|
74 | return block;
|
75 | }
|
76 | static fromHex(hex) {
|
77 | return Block.fromBuffer(Buffer.from(hex, 'hex'));
|
78 | }
|
79 | static calculateTarget(bits) {
|
80 | const exponent = ((bits & 0xff000000) >> 24) - 3;
|
81 | const mantissa = bits & 0x007fffff;
|
82 | const target = Buffer.alloc(32, 0);
|
83 | target.writeUIntBE(mantissa, 29 - exponent, 3);
|
84 | return target;
|
85 | }
|
86 | static calculateMerkleRoot(transactions, forWitness) {
|
87 | typeforce([{ getHash: types.Function }], transactions);
|
88 | if (transactions.length === 0) throw errorMerkleNoTxes;
|
89 | if (forWitness && !txesHaveWitnessCommit(transactions))
|
90 | throw errorWitnessNotSegwit;
|
91 | const hashes = transactions.map(transaction =>
|
92 | transaction.getHash(forWitness),
|
93 | );
|
94 | const rootHash = fastMerkleRoot(hashes, bcrypto.hash256);
|
95 | return forWitness
|
96 | ? bcrypto.hash256(
|
97 | Buffer.concat([rootHash, transactions[0].ins[0].witness[0]]),
|
98 | )
|
99 | : rootHash;
|
100 | }
|
101 | getWitnessCommit() {
|
102 | if (!txesHaveWitnessCommit(this.transactions)) return null;
|
103 |
|
104 |
|
105 |
|
106 |
|
107 | const witnessCommits = this.transactions[0].outs
|
108 | .filter(out =>
|
109 | out.script.slice(0, 6).equals(Buffer.from('6a24aa21a9ed', 'hex')),
|
110 | )
|
111 | .map(out => out.script.slice(6, 38));
|
112 | if (witnessCommits.length === 0) return null;
|
113 |
|
114 | const result = witnessCommits[witnessCommits.length - 1];
|
115 | if (!(result instanceof Buffer && result.length === 32)) return null;
|
116 | return result;
|
117 | }
|
118 | hasWitnessCommit() {
|
119 | if (
|
120 | this.witnessCommit instanceof Buffer &&
|
121 | this.witnessCommit.length === 32
|
122 | )
|
123 | return true;
|
124 | if (this.getWitnessCommit() !== null) return true;
|
125 | return false;
|
126 | }
|
127 | hasWitness() {
|
128 | return anyTxHasWitness(this.transactions);
|
129 | }
|
130 | byteLength(headersOnly) {
|
131 | if (headersOnly || !this.transactions) return 80;
|
132 | return (
|
133 | 80 +
|
134 | varuint.encodingLength(this.transactions.length) +
|
135 | this.transactions.reduce((a, x) => a + x.byteLength(), 0)
|
136 | );
|
137 | }
|
138 | getHash() {
|
139 | return bcrypto.hash256(this.toBuffer(true));
|
140 | }
|
141 | getId() {
|
142 | return bufferutils_1.reverseBuffer(this.getHash()).toString('hex');
|
143 | }
|
144 | getUTCDate() {
|
145 | const date = new Date(0);
|
146 | date.setUTCSeconds(this.timestamp);
|
147 | return date;
|
148 | }
|
149 |
|
150 | toBuffer(headersOnly) {
|
151 | const buffer = Buffer.allocUnsafe(this.byteLength(headersOnly));
|
152 | let offset = 0;
|
153 | const writeSlice = slice => {
|
154 | slice.copy(buffer, offset);
|
155 | offset += slice.length;
|
156 | };
|
157 | const writeInt32 = i => {
|
158 | buffer.writeInt32LE(i, offset);
|
159 | offset += 4;
|
160 | };
|
161 | const writeUInt32 = i => {
|
162 | buffer.writeUInt32LE(i, offset);
|
163 | offset += 4;
|
164 | };
|
165 | writeInt32(this.version);
|
166 | writeSlice(this.prevHash);
|
167 | writeSlice(this.merkleRoot);
|
168 | writeUInt32(this.timestamp);
|
169 | writeUInt32(this.bits);
|
170 | writeUInt32(this.nonce);
|
171 | if (headersOnly || !this.transactions) return buffer;
|
172 | varuint.encode(this.transactions.length, buffer, offset);
|
173 | offset += varuint.encode.bytes;
|
174 | this.transactions.forEach(tx => {
|
175 | const txSize = tx.byteLength();
|
176 | tx.toBuffer(buffer, offset);
|
177 | offset += txSize;
|
178 | });
|
179 | return buffer;
|
180 | }
|
181 | toHex(headersOnly) {
|
182 | return this.toBuffer(headersOnly).toString('hex');
|
183 | }
|
184 | checkTxRoots() {
|
185 |
|
186 |
|
187 | const hasWitnessCommit = this.hasWitnessCommit();
|
188 | if (!hasWitnessCommit && this.hasWitness()) return false;
|
189 | return (
|
190 | this.__checkMerkleRoot() &&
|
191 | (hasWitnessCommit ? this.__checkWitnessCommit() : true)
|
192 | );
|
193 | }
|
194 | checkProofOfWork() {
|
195 | const hash = bufferutils_1.reverseBuffer(this.getHash());
|
196 | const target = Block.calculateTarget(this.bits);
|
197 | return hash.compare(target) <= 0;
|
198 | }
|
199 | __checkMerkleRoot() {
|
200 | if (!this.transactions) throw errorMerkleNoTxes;
|
201 | const actualMerkleRoot = Block.calculateMerkleRoot(this.transactions);
|
202 | return this.merkleRoot.compare(actualMerkleRoot) === 0;
|
203 | }
|
204 | __checkWitnessCommit() {
|
205 | if (!this.transactions) throw errorMerkleNoTxes;
|
206 | if (!this.hasWitnessCommit()) throw errorWitnessNotSegwit;
|
207 | const actualWitnessCommit = Block.calculateMerkleRoot(
|
208 | this.transactions,
|
209 | true,
|
210 | );
|
211 | return this.witnessCommit.compare(actualWitnessCommit) === 0;
|
212 | }
|
213 | }
|
214 | exports.Block = Block;
|
215 | function txesHaveWitnessCommit(transactions) {
|
216 | return (
|
217 | transactions instanceof Array &&
|
218 | transactions[0] &&
|
219 | transactions[0].ins &&
|
220 | transactions[0].ins instanceof Array &&
|
221 | transactions[0].ins[0] &&
|
222 | transactions[0].ins[0].witness &&
|
223 | transactions[0].ins[0].witness instanceof Array &&
|
224 | transactions[0].ins[0].witness.length > 0
|
225 | );
|
226 | }
|
227 | function anyTxHasWitness(transactions) {
|
228 | return (
|
229 | transactions instanceof Array &&
|
230 | transactions.some(
|
231 | tx =>
|
232 | typeof tx === 'object' &&
|
233 | tx.ins instanceof Array &&
|
234 | tx.ins.some(
|
235 | input =>
|
236 | typeof input === 'object' &&
|
237 | input.witness instanceof Array &&
|
238 | input.witness.length > 0,
|
239 | ),
|
240 | )
|
241 | );
|
242 | }
|