UNPKG

11.4 kBJavaScriptView Raw
1var bip66 = require('bip66')
2var bufferutils = require('./bufferutils')
3var typeforce = require('typeforce')
4var types = require('./types')
5
6var OPS = require('./opcodes.json')
7var REVERSE_OPS = (function () {
8 var result = {}
9 for (var op in OPS) {
10 var code = OPS[op]
11 result[code] = op
12 }
13 return result
14})()
15
16var OP_INT_BASE = OPS.OP_RESERVED // OP_1 - 1
17
18function compile (chunks) {
19 // TODO: remove me
20 if (Buffer.isBuffer(chunks)) return chunks
21
22 typeforce(types.Array, chunks)
23
24 var bufferSize = chunks.reduce(function (accum, chunk) {
25 // data chunk
26 if (Buffer.isBuffer(chunk)) {
27 // adhere to BIP62.3, minimal push policy
28 if (chunk.length === 1 && chunk[0] >= 1 && chunk[0] <= 16) {
29 return accum + 1
30 }
31
32 return accum + bufferutils.pushDataSize(chunk.length) + chunk.length
33 }
34
35 // opcode
36 return accum + 1
37 }, 0.0)
38
39 var buffer = new Buffer(bufferSize)
40 var offset = 0
41
42 chunks.forEach(function (chunk) {
43 // data chunk
44 if (Buffer.isBuffer(chunk)) {
45 // adhere to BIP62.3, minimal push policy
46 if (chunk.length === 1 && chunk[0] >= 1 && chunk[0] <= 16) {
47 var opcode = OP_INT_BASE + chunk[0]
48 buffer.writeUInt8(opcode, offset)
49 offset += 1
50 return
51 }
52
53 offset += bufferutils.writePushDataInt(buffer, chunk.length, offset)
54
55 chunk.copy(buffer, offset)
56 offset += chunk.length
57
58 // opcode
59 } else {
60 buffer.writeUInt8(chunk, offset)
61 offset += 1
62 }
63 })
64
65 if (offset !== buffer.length) throw new Error('Could not decode chunks')
66 return buffer
67}
68
69function decompile (buffer) {
70 // TODO: remove me
71 if (types.Array(buffer)) return buffer
72
73 typeforce(types.Buffer, buffer)
74
75 var chunks = []
76 var i = 0
77
78 while (i < buffer.length) {
79 var opcode = buffer[i]
80
81 // data chunk
82 if ((opcode > OPS.OP_0) && (opcode <= OPS.OP_PUSHDATA4)) {
83 var d = bufferutils.readPushDataInt(buffer, i)
84
85 // did reading a pushDataInt fail? empty script
86 if (d === null) return []
87 i += d.size
88
89 // attempt to read too much data? empty script
90 if (i + d.number > buffer.length) return []
91
92 var data = buffer.slice(i, i + d.number)
93 i += d.number
94
95 chunks.push(data)
96
97 // opcode
98 } else {
99 chunks.push(opcode)
100
101 i += 1
102 }
103 }
104
105 return chunks
106}
107
108function toASM (chunks) {
109 if (Buffer.isBuffer(chunks)) {
110 chunks = decompile(chunks)
111 }
112
113 return chunks.map(function (chunk) {
114 // data?
115 if (Buffer.isBuffer(chunk)) return chunk.toString('hex')
116
117 // opcode!
118 return REVERSE_OPS[chunk]
119 }).join(' ')
120}
121
122function fromASM (asm) {
123 typeforce(types.String, asm)
124
125 return compile(asm.split(' ').map(function (chunkStr) {
126 // opcode?
127 if (OPS[chunkStr] !== undefined) return OPS[chunkStr]
128
129 // data!
130 return new Buffer(chunkStr, 'hex')
131 }))
132}
133
134function isCanonicalPubKey (buffer) {
135 if (!Buffer.isBuffer(buffer)) return false
136 if (buffer.length < 33) return false
137
138 switch (buffer[0]) {
139 case 0x02:
140 case 0x03:
141 return buffer.length === 33
142 case 0x04:
143 return buffer.length === 65
144 }
145
146 return false
147}
148
149function isDefinedHashType (hashType) {
150 var hashTypeMod = hashType & ~0x80
151
152// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
153 return hashTypeMod > 0x00 && hashTypeMod < 0x04
154}
155
156function isCanonicalSignature (buffer) {
157 if (!Buffer.isBuffer(buffer)) return false
158 if (!isDefinedHashType(buffer[buffer.length - 1])) return false
159
160 return bip66.check(buffer.slice(0, -1))
161}
162
163function isPubKeyHashInput (script) {
164 var chunks = decompile(script)
165
166 return chunks.length === 2 &&
167 isCanonicalSignature(chunks[0]) &&
168 isCanonicalPubKey(chunks[1])
169}
170
171function isPubKeyHashOutput (script) {
172 var buffer = compile(script)
173
174 return buffer.length === 25 &&
175 buffer[0] === OPS.OP_DUP &&
176 buffer[1] === OPS.OP_HASH160 &&
177 buffer[2] === 0x14 &&
178 buffer[23] === OPS.OP_EQUALVERIFY &&
179 buffer[24] === OPS.OP_CHECKSIG
180}
181
182function isPubKeyInput (script) {
183 var chunks = decompile(script)
184
185 return chunks.length === 1 &&
186 isCanonicalSignature(chunks[0])
187}
188
189function isPubKeyOutput (script) {
190 var chunks = decompile(script)
191
192 return chunks.length === 2 &&
193 isCanonicalPubKey(chunks[0]) &&
194 chunks[1] === OPS.OP_CHECKSIG
195}
196
197function isScriptHashInput (script, allowIncomplete) {
198 var chunks = decompile(script)
199 if (chunks.length < 2) return false
200
201 var lastChunk = chunks[chunks.length - 1]
202 if (!Buffer.isBuffer(lastChunk)) return false
203
204 var scriptSigChunks = chunks.slice(0, -1)
205 var redeemScriptChunks = decompile(lastChunk)
206
207 // is redeemScript a valid script?
208 if (redeemScriptChunks.length === 0) return false
209
210 return classifyInput(scriptSigChunks, allowIncomplete) === classifyOutput(redeemScriptChunks)
211}
212
213function isScriptHashOutput (script) {
214 var buffer = compile(script)
215
216 return buffer.length === 23 &&
217 buffer[0] === OPS.OP_HASH160 &&
218 buffer[1] === 0x14 &&
219 buffer[22] === OPS.OP_EQUAL
220}
221
222function isWitnessPubKeyHashOutput (script) {
223 var buffer = compile(script)
224
225 return buffer.length === 22 &&
226 buffer[0] === OPS.OP_0 &&
227 buffer[1] === 0x14
228}
229
230function isWitnessScriptHashOutput (script) {
231 var buffer = compile(script)
232
233 return buffer.length === 34 &&
234 buffer[0] === OPS.OP_0 &&
235 buffer[1] === 0x20
236}
237
238// allowIncomplete is to account for combining signatures
239// See https://github.com/bitcoin/bitcoin/blob/f425050546644a36b0b8e0eb2f6934a3e0f6f80f/src/script/sign.cpp#L195-L197
240function isMultisigInput (script, allowIncomplete) {
241 var chunks = decompile(script)
242 if (chunks.length < 2) return false
243 if (chunks[0] !== OPS.OP_0) return false
244
245 if (allowIncomplete) {
246 return chunks.slice(1).every(function (chunk) {
247 return chunk === OPS.OP_0 || isCanonicalSignature(chunk)
248 })
249 }
250
251 return chunks.slice(1).every(isCanonicalSignature)
252}
253
254function isMultisigOutput (script) {
255 var chunks = decompile(script)
256 if (chunks.length < 4) return false
257 if (chunks[chunks.length - 1] !== OPS.OP_CHECKMULTISIG) return false
258
259 var mOp = chunks[0]
260 var nOp = chunks[chunks.length - 2]
261
262 if (!types.Number(mOp)) return false
263 if (!types.Number(nOp)) return false
264
265 var m = mOp - OP_INT_BASE
266 var n = nOp - OP_INT_BASE
267
268 // 0 < m <= n <= 16
269 if (m <= 0) return false
270 if (m > n) return false
271 if (n > 16) return false
272 if (n !== chunks.length - 3) return false
273
274 return chunks.slice(1, -2).every(isCanonicalPubKey)
275}
276
277function isNullDataOutput (script) {
278 var chunks = decompile(script)
279 return chunks[0] === OPS.OP_RETURN
280}
281
282function classifyOutput (script) {
283 var chunks = decompile(script)
284
285 if (isWitnessPubKeyHashOutput(chunks)) {
286 return 'witnesspubkeyhash'
287 } else if (isWitnessScriptHashOutput(chunks)) {
288 return 'witnessscripthash'
289 } else if (isPubKeyHashOutput(chunks)) {
290 return 'pubkeyhash'
291 } else if (isScriptHashOutput(chunks)) {
292 return 'scripthash'
293 } else if (isMultisigOutput(chunks)) {
294 return 'multisig'
295 } else if (isPubKeyOutput(chunks)) {
296 return 'pubkey'
297 } else if (isNullDataOutput(chunks)) {
298 return 'nulldata'
299 }
300
301 return 'nonstandard'
302}
303
304function classifyInput (script, allowIncomplete) {
305 var chunks = decompile(script)
306
307 if (isPubKeyHashInput(chunks)) {
308 return 'pubkeyhash'
309 } else if (isMultisigInput(chunks, allowIncomplete)) {
310 return 'multisig'
311 } else if (isScriptHashInput(chunks, allowIncomplete)) {
312 return 'scripthash'
313 } else if (isPubKeyInput(chunks)) {
314 return 'pubkey'
315 }
316
317 return 'nonstandard'
318}
319
320// Standard Script Templates
321// {pubKey} OP_CHECKSIG
322function pubKeyOutput (pubKey) {
323 return compile([pubKey, OPS.OP_CHECKSIG])
324}
325
326// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
327function pubKeyHashOutput (pubKeyHash) {
328 typeforce(types.Hash160bit, pubKeyHash)
329
330 return compile([OPS.OP_DUP, OPS.OP_HASH160, pubKeyHash, OPS.OP_EQUALVERIFY, OPS.OP_CHECKSIG])
331}
332
333// OP_HASH160 {scriptHash} OP_EQUAL
334function scriptHashOutput (scriptHash) {
335 typeforce(types.Hash160bit, scriptHash)
336
337 return compile([OPS.OP_HASH160, scriptHash, OPS.OP_EQUAL])
338}
339
340// m [pubKeys ...] n OP_CHECKMULTISIG
341function multisigOutput (m, pubKeys) {
342 typeforce(types.tuple(types.Number, [types.Buffer]), arguments)
343
344 var n = pubKeys.length
345 if (n < m) throw new Error('Not enough pubKeys provided')
346
347 return compile([].concat(
348 OP_INT_BASE + m,
349 pubKeys,
350 OP_INT_BASE + n,
351 OPS.OP_CHECKMULTISIG
352 ))
353}
354
355// OP_0 {pubKeyHash}
356function witnessPubKeyHashOutput (pubKeyHash) {
357 typeforce(types.Hash160bit, pubKeyHash)
358
359 return compile([OPS.OP_0, pubKeyHash])
360}
361
362// OP_0 {scriptHash}
363function witnessScriptHashOutput (scriptHash) {
364 typeforce(types.Hash256bit, scriptHash)
365
366 return compile([OPS.OP_0, scriptHash])
367}
368
369// {signature}
370function pubKeyInput (signature) {
371 typeforce(types.Buffer, signature)
372
373 return compile([signature])
374}
375
376// {signature} {pubKey}
377function pubKeyHashInput (signature, pubKey) {
378 typeforce(types.tuple(types.Buffer, types.Buffer), arguments)
379
380 return compile([signature, pubKey])
381}
382
383// <scriptSig> {serialized scriptPubKey script}
384function scriptHashInput (scriptSig, scriptPubKey) {
385 var scriptSigChunks = decompile(scriptSig)
386 var serializedScriptPubKey = compile(scriptPubKey)
387
388 return compile([].concat(
389 scriptSigChunks,
390 serializedScriptPubKey
391 ))
392}
393
394// <scriptSig> {serialized scriptPubKey script}
395function witnessScriptHashInput (scriptSig, scriptPubKey) {
396 return scriptHashInput(scriptSig, scriptPubKey)
397}
398
399// OP_0 [signatures ...]
400function multisigInput (signatures, scriptPubKey) {
401 if (scriptPubKey) {
402 var chunks = decompile(scriptPubKey)
403 if (!isMultisigOutput(chunks)) throw new Error('Expected multisig scriptPubKey')
404
405 var mOp = chunks[0]
406 var nOp = chunks[chunks.length - 2]
407 var m = mOp - OP_INT_BASE
408 var n = nOp - OP_INT_BASE
409
410 if (signatures.length < m) throw new Error('Not enough signatures provided')
411 if (signatures.length > n) throw new Error('Too many signatures provided')
412 }
413
414 return compile([].concat(OPS.OP_0, signatures))
415}
416
417function nullDataOutput (data) {
418 return compile([OPS.OP_RETURN, data])
419}
420
421module.exports = {
422 compile: compile,
423 decompile: decompile,
424 fromASM: fromASM,
425 toASM: toASM,
426
427 number: require('./script_number'),
428
429 isCanonicalPubKey: isCanonicalPubKey,
430 isCanonicalSignature: isCanonicalSignature,
431 isDefinedHashType: isDefinedHashType,
432 isPubKeyHashInput: isPubKeyHashInput,
433 isPubKeyHashOutput: isPubKeyHashOutput,
434 isPubKeyInput: isPubKeyInput,
435 isPubKeyOutput: isPubKeyOutput,
436 isScriptHashInput: isScriptHashInput,
437 isScriptHashOutput: isScriptHashOutput,
438 isWitnessPubKeyHashOutput: isWitnessPubKeyHashOutput,
439 isWitnessScriptHashOutput: isWitnessScriptHashOutput,
440 isMultisigInput: isMultisigInput,
441 isMultisigOutput: isMultisigOutput,
442 isNullDataOutput: isNullDataOutput,
443
444 classifyOutput: classifyOutput,
445 classifyInput: classifyInput,
446 pubKeyOutput: pubKeyOutput,
447 pubKeyHashOutput: pubKeyHashOutput,
448 scriptHashOutput: scriptHashOutput,
449 witnessPubKeyHashOutput: witnessPubKeyHashOutput,
450 witnessScriptHashInput: witnessScriptHashInput,
451 witnessScriptHashOutput: witnessScriptHashOutput,
452
453 multisigOutput: multisigOutput,
454 pubKeyInput: pubKeyInput,
455 pubKeyHashInput: pubKeyHashInput,
456 scriptHashInput: scriptHashInput,
457 multisigInput: multisigInput,
458 nullDataOutput: nullDataOutput
459}