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