dxs-stas-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/jest.config.js +6 -0
  2. package/package.json +38 -0
  3. package/rollup.config.ts +26 -0
  4. package/src/base.ts +4 -0
  5. package/src/bitcoin/address.ts +42 -0
  6. package/src/bitcoin/destination.ts +7 -0
  7. package/src/bitcoin/index.ts +15 -0
  8. package/src/bitcoin/mnemonic.ts +40 -0
  9. package/src/bitcoin/network.ts +11 -0
  10. package/src/bitcoin/op-codes.ts +143 -0
  11. package/src/bitcoin/out-point.ts +61 -0
  12. package/src/bitcoin/payment.ts +8 -0
  13. package/src/bitcoin/private-key.ts +57 -0
  14. package/src/bitcoin/script-type.ts +9 -0
  15. package/src/bitcoin/sig-hash-type.ts +7 -0
  16. package/src/bitcoin/token-scheme.ts +28 -0
  17. package/src/bitcoin/transaction-input.ts +33 -0
  18. package/src/bitcoin/transaction-output.ts +105 -0
  19. package/src/bitcoin/transaction.ts +31 -0
  20. package/src/bitcoin/wallet.ts +59 -0
  21. package/src/buffer/buffer-reader.ts +88 -0
  22. package/src/buffer/buffer-utils.ts +116 -0
  23. package/src/buffer/buffer-writer.ts +74 -0
  24. package/src/buffer/index.ts +3 -0
  25. package/src/hashes.ts +13 -0
  26. package/src/index.ts +8 -0
  27. package/src/script/build/null-data-builder.ts +21 -0
  28. package/src/script/build/p2pkh-builder.ts +34 -0
  29. package/src/script/build/p2stas-builder.ts +38 -0
  30. package/src/script/build/script-builder.ts +128 -0
  31. package/src/script/index.ts +10 -0
  32. package/src/script/read/script-reader.ts +79 -0
  33. package/src/script/script-samples.ts +27 -0
  34. package/src/script/script-token.ts +66 -0
  35. package/src/script/script-utils.ts +12 -0
  36. package/src/stas-bundle-factory.ts +367 -0
  37. package/src/transaction/build/input-builder.ts +270 -0
  38. package/src/transaction/build/output-builder.ts +25 -0
  39. package/src/transaction/build/transaction-builder.ts +163 -0
  40. package/src/transaction/index.ts +4 -0
  41. package/src/transaction/read/transaction-reader.ts +54 -0
  42. package/src/transaction-factory.ts +221 -0
  43. package/tests/script-build.test.ts +41 -0
  44. package/tests/script-read.test.ts +34 -0
  45. package/tests/stas-transactios.ts +41 -0
  46. package/tests/transaction-build.test.ts +394 -0
  47. package/tests/transaction-reader.test.ts +50 -0
  48. package/tsconfig.json +110 -0
  49. package/tslint.json +3 -0
@@ -0,0 +1,59 @@
1
+ import { mnemonicToSeedSync } from "@scure/bip39";
2
+ import { toBuffer } from "../buffer/buffer-utils";
3
+ import { HDKey, Versions } from "@scure/bip32";
4
+ import { PrivateKey } from "./private-key";
5
+
6
+ interface WalletOpt {
7
+ versions: Versions;
8
+ depth?: number;
9
+ index?: number;
10
+ parentFingerprint?: number;
11
+ chainCode: Uint8Array;
12
+ privateKey?: Uint8Array | bigint;
13
+ }
14
+
15
+ export class Wallet extends HDKey {
16
+ static fromMnemonic = (mnemonic: string) => {
17
+ const seed = mnemonicToSeedSync(mnemonic);
18
+ return Wallet.fromHdKey(Wallet.fromMasterSeed(seed));
19
+ };
20
+
21
+ static fromHdKey = ({
22
+ versions,
23
+ depth,
24
+ index,
25
+ parentFingerprint,
26
+ chainCode,
27
+ privateKey,
28
+ }: HDKey) =>
29
+ new Wallet({
30
+ versions,
31
+ depth,
32
+ index,
33
+ parentFingerprint,
34
+ chainCode: chainCode!,
35
+ privateKey: privateKey!,
36
+ });
37
+
38
+ private _pk: PrivateKey;
39
+
40
+ constructor(opt: WalletOpt) {
41
+ super(opt);
42
+
43
+ this._pk = new PrivateKey(toBuffer(this.privateKey!));
44
+ }
45
+
46
+ get Address() {
47
+ return this._pk.Address;
48
+ }
49
+
50
+ get PublicKey() {
51
+ return this._pk.PublicKey;
52
+ }
53
+
54
+ derive(path: string): Wallet {
55
+ return Wallet.fromHdKey(super.derive(path));
56
+ }
57
+
58
+ sign = (message: Uint8Array) => this._pk.sign(message);
59
+ }
@@ -0,0 +1,88 @@
1
+ import { ensureUInt, slice } from "./buffer-utils";
2
+
3
+ export class BufferReader {
4
+ buffer: Buffer;
5
+ offset: number;
6
+
7
+ constructor(buffer: Buffer, offset = 0) {
8
+ this.buffer = buffer;
9
+ this.offset = offset;
10
+ }
11
+
12
+ readUInt8() {
13
+ const result = this.buffer.readUInt8(this.offset);
14
+ this.offset++;
15
+
16
+ return result;
17
+ }
18
+
19
+ readUInt16() {
20
+ const result = this.buffer.readUInt16LE(this.offset);
21
+ this.offset += 2;
22
+
23
+ return result;
24
+ }
25
+
26
+ readInt32() {
27
+ const result = this.buffer.readInt32LE(this.offset);
28
+ this.offset += 4;
29
+
30
+ return result;
31
+ }
32
+
33
+ readUInt32() {
34
+ const result = this.buffer.readUInt32LE(this.offset);
35
+ this.offset += 4;
36
+
37
+ return result;
38
+ }
39
+
40
+ readUInt64() {
41
+ const a = this.buffer.readUInt32LE(this.offset);
42
+ let b = this.buffer.readUInt32LE(this.offset + 4);
43
+ b *= 0x100000000;
44
+
45
+ const result = b + a;
46
+ ensureUInt(result, 0x001fffffffffffff);
47
+
48
+ this.offset += 8;
49
+
50
+ return result;
51
+ }
52
+
53
+ readVarInt() {
54
+ var first = this.readUInt8();
55
+
56
+ // 8 bit
57
+ if (first < 0xfd) {
58
+ return first;
59
+ }
60
+
61
+ // 16 bit
62
+ if (first === 0xfd) {
63
+ return this.readUInt16();
64
+ }
65
+
66
+ // 32 bit
67
+ if (first === 0xfe) {
68
+ return this.readUInt32();
69
+ }
70
+
71
+ // 64 bit
72
+ return this.readUInt64();
73
+ }
74
+
75
+ readChunk(n: number) {
76
+ if (this.buffer.length < this.offset + n) {
77
+ throw new Error("Cannot read chunk out of bounds");
78
+ }
79
+ const result = slice(this.buffer, this.offset, this.offset + n);
80
+ this.offset += n;
81
+
82
+ return result;
83
+ }
84
+
85
+ readVarChunk() {
86
+ return this.readChunk(this.readVarInt());
87
+ }
88
+ }
@@ -0,0 +1,116 @@
1
+ import toBufferExternal from "typedarray-to-buffer";
2
+ import { OpCode } from "../bitcoin/op-codes";
3
+
4
+ export const OP_INT_BASE = OpCode.OP_RESERVED;
5
+
6
+ export const asMinimalOP = (buffer: Buffer) => {
7
+ if (buffer.length === 0) return OpCode.OP_0;
8
+ if (buffer.length !== 1) return;
9
+ if (buffer[0] >= 1 && buffer[0] <= 16) return OP_INT_BASE + buffer[0];
10
+ if (buffer[0] === 0x81) return OpCode.OP_1NEGATE;
11
+ };
12
+
13
+ export const ensureUInt = (value: number, max: number) => {
14
+ if (value < 0)
15
+ throw new Error("specified a negative value for writing an unsigned value");
16
+
17
+ if (value > max) throw new Error("RangeError: value out of range");
18
+
19
+ if (Math.floor(value) !== value)
20
+ throw new Error(`value has a fractional component: ${value}`);
21
+ };
22
+
23
+ export const toBuffer = toBufferExternal;
24
+
25
+ export const toUtf8Buffer = (value: string) => Buffer.from(value, "utf8");
26
+ export const toHexBuffer = (value: string) => Buffer.from(value, "hex");
27
+
28
+ export const slice = (buffer: Buffer, offset: number, length: number) =>
29
+ toBuffer(Uint8Array.prototype.slice.call(buffer, offset, length));
30
+
31
+ export const reverseBuffer = (buffer: Buffer) => {
32
+ let j = buffer.length - 1;
33
+ let tmp = 0;
34
+
35
+ for (let i = 0; i < buffer.length / 2; i++) {
36
+ tmp = buffer[i];
37
+ buffer[i] = buffer[j];
38
+ buffer[j] = tmp;
39
+ j--;
40
+ }
41
+
42
+ return buffer;
43
+ };
44
+
45
+ export const cloneBuffer = (
46
+ source: Buffer,
47
+ targetStart: number = 0,
48
+ sourceStart?: number | undefined,
49
+ sourceEnd?: number | undefined
50
+ ) => {
51
+ sourceStart = sourceStart ?? 0;
52
+ sourceEnd = sourceEnd ?? source.length;
53
+
54
+ const clone = Buffer.allocUnsafe(sourceEnd - sourceStart);
55
+ source.copy(clone, targetStart, sourceStart, sourceEnd);
56
+
57
+ return clone;
58
+ };
59
+
60
+ export const splitBuffer = (source: Buffer, splitBy: Buffer): Buffer[] => {
61
+ let search = -1;
62
+ let move = 0;
63
+ const segments: Buffer[] = [];
64
+
65
+ while ((search = source.indexOf(splitBy)) > -1) {
66
+ const segment = slice(source, 0, search + move);
67
+ if (segment.length > 0) segments.push(segment);
68
+
69
+ source = slice(source, search + splitBy.length, source.length);
70
+ }
71
+
72
+ if (source.length > 0) segments.push(source);
73
+
74
+ return segments;
75
+ };
76
+
77
+ export const getVarIntLength = (value: number): number =>
78
+ value < 0xfd ? 1 : value <= 0xffff ? 3 : value <= 0xffffffff ? 5 : 9;
79
+
80
+ export const getNumberSize = (data: number): number =>
81
+ data > 0 && data <= 16
82
+ ? 1
83
+ : getVarIntLength(getMinimumRequiredByte(data)) +
84
+ getMinimumRequiredByte(data);
85
+
86
+ export const getMinimumRequiredByte = (value: number): number =>
87
+ value >= -128 && value <= 127
88
+ ? 1
89
+ : value >= -32768 && value <= 32767
90
+ ? 2
91
+ : value >= -8388608 && value <= 8388607
92
+ ? 3
93
+ : value >= -2147483648 && value <= 2147483647
94
+ ? 4
95
+ : value >= -549755813888 && value <= 549755813887
96
+ ? 5
97
+ : value >= -140737488355328 && value <= 140737488355327
98
+ ? 6
99
+ : value >= -36028797018963968 && value <= 36028797018963967 // TODO it's a bug MAX_SAFE_INTEGER = 9007199254740991
100
+ ? 7
101
+ : 8;
102
+
103
+ export const getNumberBuffer = (value: number): Buffer => {
104
+ const size = getMinimumRequiredByte(value);
105
+ const buffer = Buffer.alloc(size);
106
+
107
+ buffer.writeIntLE(value, 0, size);
108
+
109
+ return buffer;
110
+ };
111
+
112
+ export const estimateChunkSize = (bufferSize: number) =>
113
+ getVarIntLength(bufferSize) + bufferSize;
114
+
115
+ export const getChunkSize = (buffer: Buffer) =>
116
+ estimateChunkSize(buffer.length);
@@ -0,0 +1,74 @@
1
+ import { ensureUInt } from "./buffer-utils";
2
+
3
+ export class BufferWriter {
4
+ buffer: Buffer;
5
+ offset: number;
6
+
7
+ constructor(buffer: Buffer, offset = 0) {
8
+ this.buffer = buffer;
9
+ this.offset = offset;
10
+ }
11
+
12
+ static fromSize = (size: number) =>
13
+ new BufferWriter(Buffer.allocUnsafe(size));
14
+
15
+ writeUInt8(value: number) {
16
+ this.offset = this.buffer.writeUInt8(value, this.offset);
17
+ }
18
+
19
+ writeUInt16(value: number) {
20
+ this.offset = this.buffer.writeUInt16LE(value, this.offset);
21
+ }
22
+
23
+ writeUInt32(value: number) {
24
+ this.offset = this.buffer.writeUInt32LE(value, this.offset);
25
+ }
26
+
27
+ writeUInt64(value: number) {
28
+ ensureUInt(value, 0x001fffffffffffff);
29
+
30
+ this.buffer.writeInt32LE(value & -1, this.offset);
31
+ this.buffer.writeUInt32LE(Math.floor(value / 0x100000000), this.offset + 4);
32
+ this.offset += 8;
33
+ }
34
+
35
+ writeVarInt(value: number) {
36
+ // 8 bit
37
+ if (value <= 0xfc) {
38
+ this.writeUInt8(value);
39
+
40
+ // 16 bit
41
+ } else if (value <= 0xffff) {
42
+ this.writeUInt8(0xfd);
43
+ this.writeUInt16(value);
44
+
45
+ // 32 bit
46
+ } else if (value <= 0xffffffff) {
47
+ this.writeUInt8(0xfe);
48
+ this.writeUInt32(value);
49
+
50
+ // 64 bit
51
+ } else {
52
+ this.writeUInt8(0xff);
53
+ this.writeUInt64(value);
54
+ }
55
+ }
56
+
57
+ writeChunk(chunk: Buffer) {
58
+ if (this.buffer.length < this.offset + chunk.length)
59
+ throw new Error(
60
+ `Cannot writte chunk out of bounds; total size: ${
61
+ this.buffer.length
62
+ }; position: ${this.offset}; excess: ${
63
+ this.offset + chunk.length - this.buffer.length
64
+ }`
65
+ );
66
+
67
+ this.offset += chunk.copy(this.buffer, this.offset);
68
+ }
69
+
70
+ writeVarChunk(chunk: Buffer) {
71
+ this.writeVarInt(chunk.length);
72
+ this.writeChunk(chunk);
73
+ }
74
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./buffer-reader";
2
+ export * from "./buffer-utils";
3
+ export * from "./buffer-writer";
package/src/hashes.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { sha256 as nobleHash256 } from "@noble/hashes/sha256";
2
+ import { ripemd160 as nobleRipemd160 } from "@noble/hashes/ripemd160";
3
+ import { toBuffer } from "./buffer/buffer-utils";
4
+
5
+ export const sha256 = (message: Buffer): Buffer =>
6
+ toBuffer(nobleHash256(message));
7
+
8
+ export const ripemd160 = (message: Buffer): Buffer =>
9
+ toBuffer(nobleRipemd160(message));
10
+
11
+ export const hash160 = (buffer: Buffer) => ripemd160(sha256(buffer));
12
+
13
+ export const hash256 = (buffer: Buffer) => sha256(sha256(buffer));
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ export * from "./bitcoin";
2
+ export * from "./buffer";
3
+ export * from "./script";
4
+ export * from "./stas-bundle-factory";
5
+ export * from "./transaction";
6
+ export * from "./transaction-factory";
7
+ export * from "./base";
8
+ export * from "./hashes";
@@ -0,0 +1,21 @@
1
+ import { OpCode } from "../../bitcoin/op-codes";
2
+ import { ScriptType } from "../../bitcoin/script-type";
3
+ import { nullDataTokens } from "../script-samples";
4
+ import { ScriptToken } from "../script-token";
5
+ import { ScriptBuilder } from "./script-builder";
6
+
7
+ export class NullDataBuilder extends ScriptBuilder {
8
+ constructor(data: Buffer[]) {
9
+ super(ScriptType.nullData);
10
+
11
+ for (var token of nullDataTokens) {
12
+ this._tokens.push(ScriptToken.fromScriptToken(token));
13
+ }
14
+
15
+ this.addOpCode(OpCode.OP_RETURN);
16
+
17
+ for (var segment of data) {
18
+ this.addData(segment);
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,34 @@
1
+ import { Address } from "../../bitcoin/address";
2
+ import { OpCode } from "../../bitcoin/op-codes";
3
+ import { ScriptType } from "../../bitcoin/script-type";
4
+ import { p2phkTokens } from "../script-samples";
5
+ import { ScriptToken } from "../script-token";
6
+ import { ScriptBuilder } from "./script-builder";
7
+
8
+ export class P2pkhBuilder extends ScriptBuilder {
9
+ private _isOpReturnAdded: boolean = false;
10
+
11
+ constructor(address: Address) {
12
+ super(ScriptType.p2pkh, address);
13
+
14
+ for (var token of p2phkTokens) {
15
+ if (token.IsReceiverId) {
16
+ const receiver = ScriptToken.fromBuffer(address.Hash160);
17
+ receiver.IsReceiverId = true;
18
+
19
+ this._tokens.push(receiver);
20
+ } else {
21
+ this._tokens.push(ScriptToken.fromScriptToken(token));
22
+ }
23
+ }
24
+ }
25
+
26
+ addReturnData(data: Buffer) {
27
+ if (!this._isOpReturnAdded) {
28
+ this.addOpCode(OpCode.OP_RETURN);
29
+ this._isOpReturnAdded = true;
30
+ }
31
+
32
+ this.addData(data);
33
+ }
34
+ }
@@ -0,0 +1,38 @@
1
+ import { Address } from "../../bitcoin/address";
2
+ import { OpCode } from "../../bitcoin/op-codes";
3
+ import { ScriptType } from "../../bitcoin/script-type";
4
+ import { getP2stasTokens } from "../script-samples";
5
+ import { ScriptToken } from "../script-token";
6
+ import { ScriptBuilder } from "./script-builder";
7
+
8
+ export class P2stasBuilder extends ScriptBuilder {
9
+ constructor(
10
+ address: Address,
11
+ tokenId: string,
12
+ symbol: string,
13
+ data: Buffer[] = []
14
+ ) {
15
+ super(ScriptType.p2stas, address);
16
+
17
+ const stasTokens = getP2stasTokens();
18
+
19
+ for (var token of stasTokens) {
20
+ if (token.IsReceiverId) {
21
+ const receiver = ScriptToken.fromBuffer(address.Hash160);
22
+ receiver.IsReceiverId = true;
23
+
24
+ this._tokens.push(receiver);
25
+ } else {
26
+ this._tokens.push(ScriptToken.fromScriptToken(token));
27
+ }
28
+ }
29
+
30
+ this.addOpCode(OpCode.OP_RETURN);
31
+ this.addData(Buffer.from(tokenId, "hex"));
32
+ this.addData(Buffer.from(symbol, "utf8"));
33
+
34
+ for (const d of data) {
35
+ this.addData(d);
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,128 @@
1
+ import { getNumberBuffer } from "../../buffer/buffer-utils";
2
+ import { BufferWriter } from "../../buffer/buffer-writer";
3
+ import { Address } from "../../bitcoin/address";
4
+ import { OpCode } from "../../bitcoin/op-codes";
5
+ import { ScriptType } from "../../bitcoin/script-type";
6
+ import { ScriptToken } from "../script-token";
7
+
8
+ export class ScriptBuilder {
9
+ _tokens: ScriptToken[] = [];
10
+
11
+ ScriptType: ScriptType;
12
+ ToAddress?: Address;
13
+
14
+ constructor(scriptType: ScriptType, toAddress?: Address) {
15
+ this.ScriptType = scriptType;
16
+ this.ToAddress = toAddress;
17
+ }
18
+
19
+ static fromTokens = (tokens: ScriptToken[], scriptType: ScriptType) => {
20
+ const builder = new ScriptBuilder(scriptType);
21
+ builder._tokens = tokens;
22
+
23
+ return builder;
24
+ };
25
+
26
+ size = () => {
27
+ let size = 0;
28
+
29
+ for (const token of this._tokens) {
30
+ size += this.tokenSize(token);
31
+ }
32
+
33
+ return size;
34
+ };
35
+
36
+ tokenSize = (token: ScriptToken) => {
37
+ let size = 1;
38
+
39
+ const opcodeNum = token.OpCodeNum;
40
+ const dataLength = token.DataLength;
41
+ const add =
42
+ opcodeNum > 0 && opcodeNum < OpCode.OP_PUSHDATA1
43
+ ? dataLength
44
+ : opcodeNum === OpCode.OP_PUSHDATA1
45
+ ? dataLength + 1
46
+ : opcodeNum === OpCode.OP_PUSHDATA2
47
+ ? dataLength + 2
48
+ : opcodeNum === OpCode.OP_PUSHDATA4
49
+ ? dataLength + 4
50
+ : 0;
51
+
52
+ return size + add;
53
+ };
54
+
55
+ toBuffer = () => {
56
+ const buffer = Buffer.alloc(this.size());
57
+ const bufferWriter = new BufferWriter(buffer);
58
+
59
+ for (var token of this._tokens) {
60
+ bufferWriter.writeUInt8(token.OpCodeNum);
61
+
62
+ if (token.OpCodeNum > 0 && token.OpCodeNum < OpCode.OP_PUSHDATA1) {
63
+ bufferWriter.writeChunk(token.Data!);
64
+ } else if (token.OpCodeNum === OpCode.OP_PUSHDATA1) {
65
+ bufferWriter.writeUInt8(token.DataLength);
66
+ bufferWriter.writeChunk(token.Data!);
67
+ } else if (token.OpCodeNum === OpCode.OP_PUSHDATA2) {
68
+ bufferWriter.writeUInt16(token.DataLength);
69
+ bufferWriter.writeChunk(token.Data!);
70
+ } else if (token.OpCodeNum === OpCode.OP_PUSHDATA4) {
71
+ bufferWriter.writeUInt32(token.DataLength);
72
+ bufferWriter.writeChunk(token.Data!);
73
+ }
74
+ }
75
+
76
+ return buffer;
77
+ };
78
+
79
+ toHex = () => this.toBuffer().toString("hex");
80
+
81
+ addToken = (token: ScriptToken) => {
82
+ this._tokens.push(token);
83
+
84
+ return this;
85
+ };
86
+
87
+ addOpCode = (opCode: OpCode) => {
88
+ this._tokens.push(new ScriptToken(opCode, opCode));
89
+
90
+ return this;
91
+ };
92
+
93
+ addData = (data: Buffer) => {
94
+ this._tokens.push(ScriptToken.fromBuffer(data));
95
+
96
+ return this;
97
+ };
98
+
99
+ addDatas = (data: Buffer[]) => {
100
+ for (const chunk of data) this._tokens.push(ScriptToken.fromBuffer(chunk));
101
+
102
+ return this;
103
+ };
104
+
105
+ addNumber = (data: number) => {
106
+ if (data === 0) this.addOpCode(OpCode.OP_0);
107
+ else if (data <= 16) this.addOpCode(0x50 + data);
108
+ else this.addData(getNumberBuffer(data));
109
+
110
+ return this;
111
+ };
112
+
113
+ toAsm = () => {
114
+ const opCodeKeys = Object.keys(OpCode);
115
+ const opCodeValues = Object.values(OpCode);
116
+
117
+ let result = "";
118
+
119
+ for (const token of this._tokens) {
120
+ if (result.length > 0) result += " ";
121
+
122
+ if (token.Data) result += token.Data!.toString("hex");
123
+ else result += opCodeKeys[opCodeValues.indexOf(token.OpCodeNum)];
124
+ }
125
+
126
+ return result;
127
+ };
128
+ }
@@ -0,0 +1,10 @@
1
+ export * from "./build/null-data-builder";
2
+ export * from "./build/p2pkh-builder";
3
+ export * from "./build/p2stas-builder";
4
+ export * from "./build/script-builder";
5
+
6
+ export * from "./read/script-reader";
7
+
8
+ export * from "./script-samples";
9
+ export * from "./script-token";
10
+ export * from "./script-utils";
@@ -0,0 +1,79 @@
1
+ import { asMinimalOP, slice } from "../../buffer/buffer-utils";
2
+ import { OpCode } from "../../bitcoin/op-codes";
3
+ import { ScriptToken } from "../script-token";
4
+
5
+ export class ScriptReader {
6
+ static read = (source: Buffer) => {
7
+ const result = [];
8
+
9
+ let i = 0;
10
+
11
+ while (i < source.length) {
12
+ const byte = source[i];
13
+
14
+ // data chunk
15
+ if (byte > OpCode.OP_0 && byte <= OpCode.OP_PUSHDATA4) {
16
+ const d = ScriptReader.decode(source, i);
17
+
18
+ // did reading a pushDataInt fail?
19
+ if (d === null) return [];
20
+
21
+ i += d.size;
22
+ // attempt to read too much data?
23
+
24
+ if (i + d.number > source.length) return [];
25
+
26
+ const data = slice(source, i, i + d.number);
27
+ i += d.number;
28
+
29
+ // decompile minimally
30
+ const op = asMinimalOP(data);
31
+
32
+ if (op !== undefined) {
33
+ result.push(new ScriptToken(byte, op));
34
+ } else {
35
+ result.push(ScriptToken.fromBuffer(data));
36
+ }
37
+ // opcode
38
+ } else {
39
+ result.push(new ScriptToken(byte, byte));
40
+
41
+ i += 1;
42
+ }
43
+ }
44
+
45
+ return result;
46
+ };
47
+
48
+ static decode = (buffer: Buffer, offset: number) => {
49
+ const opcode = buffer.readUInt8(offset);
50
+ let num;
51
+ let size;
52
+ // ~6 bit
53
+ if (opcode < OpCode.OP_PUSHDATA1) {
54
+ num = opcode;
55
+ size = 1;
56
+ // 8 bit
57
+ } else if (opcode === OpCode.OP_PUSHDATA1) {
58
+ if (offset + 2 > buffer.length) return null;
59
+ num = buffer.readUInt8(offset + 1);
60
+ size = 2;
61
+ // 16 bit
62
+ } else if (opcode === OpCode.OP_PUSHDATA2) {
63
+ if (offset + 3 > buffer.length) return null;
64
+ num = buffer.readUInt16LE(offset + 1);
65
+ size = 3;
66
+ // 32 bit
67
+ } else {
68
+ if (offset + 5 > buffer.length) return null;
69
+ if (opcode !== OpCode.OP_PUSHDATA4) throw new Error("Unexpected opcode");
70
+ num = buffer.readUInt32LE(offset + 1);
71
+ size = 5;
72
+ }
73
+ return {
74
+ opcode,
75
+ number: num,
76
+ size,
77
+ };
78
+ };
79
+ }