UNPKG

5.33 kBJavaScriptView Raw
1'use strict';
2Object.defineProperty(exports, '__esModule', { value: true });
3const scriptNumber = require('./script_number');
4const scriptSignature = require('./script_signature');
5const types = require('./types');
6const bip66 = require('bip66');
7const ecc = require('tiny-secp256k1');
8const pushdata = require('pushdata-bitcoin');
9const typeforce = require('typeforce');
10exports.OPS = require('bitcoin-ops');
11const REVERSE_OPS = require('bitcoin-ops/map');
12const OP_INT_BASE = exports.OPS.OP_RESERVED; // OP_1 - 1
13function isOPInt(value) {
14 return (
15 types.Number(value) &&
16 (value === exports.OPS.OP_0 ||
17 (value >= exports.OPS.OP_1 && value <= exports.OPS.OP_16) ||
18 value === exports.OPS.OP_1NEGATE)
19 );
20}
21function isPushOnlyChunk(value) {
22 return types.Buffer(value) || isOPInt(value);
23}
24function isPushOnly(value) {
25 return types.Array(value) && value.every(isPushOnlyChunk);
26}
27exports.isPushOnly = isPushOnly;
28function asMinimalOP(buffer) {
29 if (buffer.length === 0) return exports.OPS.OP_0;
30 if (buffer.length !== 1) return;
31 if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
32 if (buffer[0] === 0x81) return exports.OPS.OP_1NEGATE;
33}
34function chunksIsBuffer(buf) {
35 return Buffer.isBuffer(buf);
36}
37function chunksIsArray(buf) {
38 return types.Array(buf);
39}
40function singleChunkIsBuffer(buf) {
41 return Buffer.isBuffer(buf);
42}
43function compile(chunks) {
44 // TODO: remove me
45 if (chunksIsBuffer(chunks)) return chunks;
46 typeforce(types.Array, chunks);
47 const bufferSize = chunks.reduce((accum, chunk) => {
48 // data chunk
49 if (singleChunkIsBuffer(chunk)) {
50 // adhere to BIP62.3, minimal push policy
51 if (chunk.length === 1 && asMinimalOP(chunk) !== undefined) {
52 return accum + 1;
53 }
54 return accum + pushdata.encodingLength(chunk.length) + chunk.length;
55 }
56 // opcode
57 return accum + 1;
58 }, 0.0);
59 const buffer = Buffer.allocUnsafe(bufferSize);
60 let offset = 0;
61 chunks.forEach(chunk => {
62 // data chunk
63 if (singleChunkIsBuffer(chunk)) {
64 // adhere to BIP62.3, minimal push policy
65 const opcode = asMinimalOP(chunk);
66 if (opcode !== undefined) {
67 buffer.writeUInt8(opcode, offset);
68 offset += 1;
69 return;
70 }
71 offset += pushdata.encode(buffer, chunk.length, offset);
72 chunk.copy(buffer, offset);
73 offset += chunk.length;
74 // opcode
75 } else {
76 buffer.writeUInt8(chunk, offset);
77 offset += 1;
78 }
79 });
80 if (offset !== buffer.length) throw new Error('Could not decode chunks');
81 return buffer;
82}
83exports.compile = compile;
84function decompile(buffer) {
85 // TODO: remove me
86 if (chunksIsArray(buffer)) return buffer;
87 typeforce(types.Buffer, buffer);
88 const chunks = [];
89 let i = 0;
90 while (i < buffer.length) {
91 const opcode = buffer[i];
92 // data chunk
93 if (opcode > exports.OPS.OP_0 && opcode <= exports.OPS.OP_PUSHDATA4) {
94 const d = pushdata.decode(buffer, i);
95 // did reading a pushDataInt fail?
96 if (d === null) return null;
97 i += d.size;
98 // attempt to read too much data?
99 if (i + d.number > buffer.length) return null;
100 const data = buffer.slice(i, i + d.number);
101 i += d.number;
102 // decompile minimally
103 const op = asMinimalOP(data);
104 if (op !== undefined) {
105 chunks.push(op);
106 } else {
107 chunks.push(data);
108 }
109 // opcode
110 } else {
111 chunks.push(opcode);
112 i += 1;
113 }
114 }
115 return chunks;
116}
117exports.decompile = decompile;
118function toASM(chunks) {
119 if (chunksIsBuffer(chunks)) {
120 chunks = decompile(chunks);
121 }
122 return chunks
123 .map(chunk => {
124 // data?
125 if (singleChunkIsBuffer(chunk)) {
126 const op = asMinimalOP(chunk);
127 if (op === undefined) return chunk.toString('hex');
128 chunk = op;
129 }
130 // opcode!
131 return REVERSE_OPS[chunk];
132 })
133 .join(' ');
134}
135exports.toASM = toASM;
136function fromASM(asm) {
137 typeforce(types.String, asm);
138 return compile(
139 asm.split(' ').map(chunkStr => {
140 // opcode?
141 if (exports.OPS[chunkStr] !== undefined) return exports.OPS[chunkStr];
142 typeforce(types.Hex, chunkStr);
143 // data!
144 return Buffer.from(chunkStr, 'hex');
145 }),
146 );
147}
148exports.fromASM = fromASM;
149function toStack(chunks) {
150 chunks = decompile(chunks);
151 typeforce(isPushOnly, chunks);
152 return chunks.map(op => {
153 if (singleChunkIsBuffer(op)) return op;
154 if (op === exports.OPS.OP_0) return Buffer.allocUnsafe(0);
155 return scriptNumber.encode(op - OP_INT_BASE);
156 });
157}
158exports.toStack = toStack;
159function isCanonicalPubKey(buffer) {
160 return ecc.isPoint(buffer);
161}
162exports.isCanonicalPubKey = isCanonicalPubKey;
163function isDefinedHashType(hashType) {
164 const hashTypeMod = hashType & ~0x80;
165 // return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
166 return hashTypeMod > 0x00 && hashTypeMod < 0x04;
167}
168exports.isDefinedHashType = isDefinedHashType;
169function isCanonicalScriptSignature(buffer) {
170 if (!Buffer.isBuffer(buffer)) return false;
171 if (!isDefinedHashType(buffer[buffer.length - 1])) return false;
172 return bip66.check(buffer.slice(0, -1));
173}
174exports.isCanonicalScriptSignature = isCanonicalScriptSignature;
175// tslint:disable-next-line variable-name
176exports.number = scriptNumber;
177exports.signature = scriptSignature;