UNPKG

7.61 kBJavaScriptView Raw
1'use strict';
2Object.defineProperty(exports, '__esModule', { value: true });
3exports.Block = void 0;
4const bufferutils_1 = require('./bufferutils');
5const bcrypto = require('./crypto');
6const merkle_1 = require('./merkle');
7const transaction_1 = require('./transaction');
8const types = require('./types');
9const { typeforce } = types;
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 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 // This Block contains a witness commit
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 // The merkle root for the witness data is in an OP_RETURN output.
85 // There is no rule for the index of the output, so use filter to find it.
86 // The root is prepended with 0xaa21a9ed so check for 0x6a24aa21a9ed
87 // If multiple commits are found, the output with highest index is assumed.
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 // Use the commit with the highest output (should only be one though)
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); // epoch
132 date.setUTCSeconds(this.timestamp);
133 return date;
134 }
135 // TODO: buffer, offset compatibility
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(); // TODO: extract from toBuffer?
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 // If the Block has segwit transactions but no witness commit,
164 // there's no way it can be valid, so fail the check.
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}
192exports.Block = Block;
193function 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}
205function 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}