UNPKG

7.81 kBJavaScriptView Raw
1'use strict';
2Object.defineProperty(exports, '__esModule', { value: true });
3const bufferutils_1 = require('./bufferutils');
4const bcrypto = require('./crypto');
5const transaction_1 = require('./transaction');
6const types = require('./types');
7const fastMerkleRoot = require('merkle-lib/fastRoot');
8const typeforce = require('typeforce');
9const varuint = require('varuint-bitcoin');
10const errorMerkleNoTxes = new TypeError(
11 'Cannot compute merkle root for zero transactions',
12);
13const errorWitnessNotSegwit = new TypeError(
14 'Cannot compute witness commit for non-segwit block',
15);
16class 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 // This Block contains a witness commit
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 // The merkle root for the witness data is in an OP_RETURN output.
104 // There is no rule for the index of the output, so use filter to find it.
105 // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
106 // If multiple commits are found, the output with highest index is assumed.
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 // Use the commit with the highest output (should only be one though)
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); // epoch
146 date.setUTCSeconds(this.timestamp);
147 return date;
148 }
149 // TODO: buffer, offset compatibility
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(); // TODO: extract from toBuffer?
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 // If the Block has segwit transactions but no witness commit,
186 // there's no way it can be valid, so fail the check.
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}
214exports.Block = Block;
215function 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}
227function 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}