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.
- package/jest.config.js +6 -0
- package/package.json +38 -0
- package/rollup.config.ts +26 -0
- package/src/base.ts +4 -0
- package/src/bitcoin/address.ts +42 -0
- package/src/bitcoin/destination.ts +7 -0
- package/src/bitcoin/index.ts +15 -0
- package/src/bitcoin/mnemonic.ts +40 -0
- package/src/bitcoin/network.ts +11 -0
- package/src/bitcoin/op-codes.ts +143 -0
- package/src/bitcoin/out-point.ts +61 -0
- package/src/bitcoin/payment.ts +8 -0
- package/src/bitcoin/private-key.ts +57 -0
- package/src/bitcoin/script-type.ts +9 -0
- package/src/bitcoin/sig-hash-type.ts +7 -0
- package/src/bitcoin/token-scheme.ts +28 -0
- package/src/bitcoin/transaction-input.ts +33 -0
- package/src/bitcoin/transaction-output.ts +105 -0
- package/src/bitcoin/transaction.ts +31 -0
- package/src/bitcoin/wallet.ts +59 -0
- package/src/buffer/buffer-reader.ts +88 -0
- package/src/buffer/buffer-utils.ts +116 -0
- package/src/buffer/buffer-writer.ts +74 -0
- package/src/buffer/index.ts +3 -0
- package/src/hashes.ts +13 -0
- package/src/index.ts +8 -0
- package/src/script/build/null-data-builder.ts +21 -0
- package/src/script/build/p2pkh-builder.ts +34 -0
- package/src/script/build/p2stas-builder.ts +38 -0
- package/src/script/build/script-builder.ts +128 -0
- package/src/script/index.ts +10 -0
- package/src/script/read/script-reader.ts +79 -0
- package/src/script/script-samples.ts +27 -0
- package/src/script/script-token.ts +66 -0
- package/src/script/script-utils.ts +12 -0
- package/src/stas-bundle-factory.ts +367 -0
- package/src/transaction/build/input-builder.ts +270 -0
- package/src/transaction/build/output-builder.ts +25 -0
- package/src/transaction/build/transaction-builder.ts +163 -0
- package/src/transaction/index.ts +4 -0
- package/src/transaction/read/transaction-reader.ts +54 -0
- package/src/transaction-factory.ts +221 -0
- package/tests/script-build.test.ts +41 -0
- package/tests/script-read.test.ts +34 -0
- package/tests/stas-transactios.ts +41 -0
- package/tests/transaction-build.test.ts +394 -0
- package/tests/transaction-reader.test.ts +50 -0
- package/tsconfig.json +110 -0
- package/tslint.json +3 -0
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dxs-stas-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dxs.stas.sdk.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"start": "tsnd ./src/index.ts",
|
|
9
|
+
"publish": "rollup -c rollup.config.ts -w --configPlugin typescript"
|
|
10
|
+
},
|
|
11
|
+
"directories": {
|
|
12
|
+
"test": "tests"
|
|
13
|
+
},
|
|
14
|
+
"author": "oleg@dxs.app",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@rollup/plugin-commonjs": "^24.0.0",
|
|
18
|
+
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
19
|
+
"@rollup/plugin-typescript": "^11.0.0",
|
|
20
|
+
"@types/jest": "^29.2.5",
|
|
21
|
+
"@types/typedarray-to-buffer": "^4.0.0",
|
|
22
|
+
"jest": "^29.3.1",
|
|
23
|
+
"rollup": "^3.10.0",
|
|
24
|
+
"ts-jest": "^29.0.5",
|
|
25
|
+
"ts-node-dev": "^2.0.0",
|
|
26
|
+
"tslib": "^2.4.1",
|
|
27
|
+
"tslint-config-prettier": "^1.18.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@noble/hashes": "^1.2.0",
|
|
31
|
+
"@noble/secp256k1": "^1.7.1",
|
|
32
|
+
"@scure/base": "^1.1.1",
|
|
33
|
+
"@scure/bip32": "^1.1.5",
|
|
34
|
+
"@scure/bip39": "^1.1.1",
|
|
35
|
+
"buffer": "^6.0.3",
|
|
36
|
+
"typedarray-to-buffer": "^4.0.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
package/rollup.config.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import resolve from "@rollup/plugin-node-resolve";
|
|
2
|
+
import commonjs from "@rollup/plugin-commonjs";
|
|
3
|
+
import typescript from "@rollup/plugin-typescript";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
input: `src/index.ts`,
|
|
7
|
+
output: [
|
|
8
|
+
{
|
|
9
|
+
file: "./dist/dxs.stas.sdk.js",
|
|
10
|
+
format: "cjs",
|
|
11
|
+
treeshake: true,
|
|
12
|
+
sourcemap: true,
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
watch: {
|
|
16
|
+
include: "src/**",
|
|
17
|
+
},
|
|
18
|
+
plugins: [
|
|
19
|
+
commonjs(),
|
|
20
|
+
typescript(),
|
|
21
|
+
resolve({
|
|
22
|
+
preferBuiltins: true,
|
|
23
|
+
extensions: [".mjs", ".js", ".json", ".node", ".ts"],
|
|
24
|
+
}),
|
|
25
|
+
],
|
|
26
|
+
};
|
package/src/base.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { bs58check } from "../base";
|
|
2
|
+
import { toBuffer } from "../buffer/buffer-utils";
|
|
3
|
+
import { hash160 } from "../hashes";
|
|
4
|
+
import { Network, Networks } from "./network";
|
|
5
|
+
|
|
6
|
+
export class Address {
|
|
7
|
+
Value: string;
|
|
8
|
+
Hash160: Buffer;
|
|
9
|
+
Network: Network = Networks.Mainnet;
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
Only p2pkh and mainnet supports now
|
|
13
|
+
*/
|
|
14
|
+
constructor(hash160: Buffer) {
|
|
15
|
+
if (hash160.length !== 20) throw new Error("Invalid hash160");
|
|
16
|
+
|
|
17
|
+
const buffer = Buffer.allocUnsafe(21);
|
|
18
|
+
|
|
19
|
+
buffer.writeUInt8(this.Network.pubKeyHash, 0);
|
|
20
|
+
hash160.copy(buffer, 1);
|
|
21
|
+
|
|
22
|
+
this.Value = bs58check.encode(buffer);
|
|
23
|
+
this.Hash160 = hash160;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static fromBase58 = (address: string) => {
|
|
27
|
+
const buffer = toBuffer(bs58check.decode(address));
|
|
28
|
+
|
|
29
|
+
if (buffer[0] !== Networks.Mainnet.pubKeyHash)
|
|
30
|
+
throw new Error("Only mainnet supported");
|
|
31
|
+
|
|
32
|
+
const hash160 = Buffer.allocUnsafe(20);
|
|
33
|
+
buffer.copy(hash160, 0, 1);
|
|
34
|
+
|
|
35
|
+
return new Address(hash160);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
static fromPublicKey = (publicKey: Buffer) => new Address(hash160(publicKey));
|
|
39
|
+
|
|
40
|
+
static fromHash160Hex = (hash160: string) =>
|
|
41
|
+
new Address(Buffer.from(hash160, "hex"));
|
|
42
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from "./address";
|
|
2
|
+
export * from "./destination";
|
|
3
|
+
export * from "./mnemonic";
|
|
4
|
+
export * from "./network";
|
|
5
|
+
export * from "./op-codes";
|
|
6
|
+
export * from "./out-point";
|
|
7
|
+
export * from "./payment";
|
|
8
|
+
export * from "./private-key";
|
|
9
|
+
export * from "./script-type";
|
|
10
|
+
export * from "./sig-hash-type";
|
|
11
|
+
export * from "./token-scheme";
|
|
12
|
+
export * from "./transaction-input";
|
|
13
|
+
export * from "./transaction-output";
|
|
14
|
+
export * from "./transaction";
|
|
15
|
+
export * from "./wallet";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { generateMnemonic, validateMnemonic } from "@scure/bip39";
|
|
2
|
+
import { wordlist } from "@scure/bip39/wordlists/english";
|
|
3
|
+
|
|
4
|
+
export { wordlist } from "@scure/bip39/wordlists/english";
|
|
5
|
+
|
|
6
|
+
export type TWords = { [idxs: string]: string };
|
|
7
|
+
|
|
8
|
+
export class Mnemonic {
|
|
9
|
+
constructor(public phrase: string, public words: TWords) {}
|
|
10
|
+
|
|
11
|
+
public static generate = (): Mnemonic =>
|
|
12
|
+
Mnemonic.fromPhrase(generateMnemonic(wordlist, 128));
|
|
13
|
+
|
|
14
|
+
public static fromWords = (words: TWords): Mnemonic => {
|
|
15
|
+
var phrase = Object.values(words).join(" ");
|
|
16
|
+
|
|
17
|
+
return new Mnemonic(phrase, words);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
public static fromPhrase = (phrase: string): Mnemonic => {
|
|
21
|
+
var words = phrase.split(" ").reduce<TWords>((a, v, i) => {
|
|
22
|
+
a[`${i}`] = v;
|
|
23
|
+
|
|
24
|
+
return a;
|
|
25
|
+
}, {});
|
|
26
|
+
|
|
27
|
+
return new Mnemonic(phrase, words);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
public static fromRandomText = (text: string): Mnemonic | undefined => {
|
|
31
|
+
const sanitized = text
|
|
32
|
+
.replace(/\r?\n|\r/g, " ")
|
|
33
|
+
.replace(/\s{2,}/g, " ")
|
|
34
|
+
.replace(/^\s+/, "")
|
|
35
|
+
.replace(/\s+$/, "");
|
|
36
|
+
|
|
37
|
+
if (validateMnemonic(sanitized, wordlist))
|
|
38
|
+
return Mnemonic.fromPhrase(sanitized);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
export enum OpCode {
|
|
2
|
+
// push value
|
|
3
|
+
OP_0 = 0x00,
|
|
4
|
+
OP_PUSHDATA1 = 0x4c,
|
|
5
|
+
OP_PUSHDATA2 = 0x4d,
|
|
6
|
+
OP_PUSHDATA4 = 0x4e,
|
|
7
|
+
OP_1NEGATE = 0x4f,
|
|
8
|
+
OP_RESERVED = 0x50,
|
|
9
|
+
OP_TRUE = 0x51,
|
|
10
|
+
OP_1 = 0x51,
|
|
11
|
+
OP_2 = 0x52,
|
|
12
|
+
OP_3 = 0x53,
|
|
13
|
+
OP_4 = 0x54,
|
|
14
|
+
OP_5 = 0x55,
|
|
15
|
+
OP_6 = 0x56,
|
|
16
|
+
OP_7 = 0x57,
|
|
17
|
+
OP_8 = 0x58,
|
|
18
|
+
OP_9 = 0x59,
|
|
19
|
+
OP_10 = 0x5a,
|
|
20
|
+
OP_11 = 0x5b,
|
|
21
|
+
OP_12 = 0x5c,
|
|
22
|
+
OP_13 = 0x5d,
|
|
23
|
+
OP_14 = 0x5e,
|
|
24
|
+
OP_15 = 0x5f,
|
|
25
|
+
OP_16 = 0x60,
|
|
26
|
+
|
|
27
|
+
// control
|
|
28
|
+
OP_NOP = 0x61,
|
|
29
|
+
OP_VER = 0x62,
|
|
30
|
+
OP_IF = 0x63,
|
|
31
|
+
OP_NOTIF = 0x64,
|
|
32
|
+
OP_VERIF = 0x65,
|
|
33
|
+
OP_VERNOTIF = 0x66,
|
|
34
|
+
OP_ELSE = 0x67,
|
|
35
|
+
OP_ENDIF = 0x68,
|
|
36
|
+
OP_VERIFY = 0x69,
|
|
37
|
+
OP_RETURN = 0x6a,
|
|
38
|
+
|
|
39
|
+
// stack ops
|
|
40
|
+
OP_TOALTSTACK = 0x6b,
|
|
41
|
+
OP_FROMALTSTACK = 0x6c,
|
|
42
|
+
OP_2DROP = 0x6d,
|
|
43
|
+
OP_2DUP = 0x6e,
|
|
44
|
+
OP_3DUP = 0x6f,
|
|
45
|
+
OP_2OVER = 0x70,
|
|
46
|
+
OP_2ROT = 0x71,
|
|
47
|
+
OP_2SWAP = 0x72,
|
|
48
|
+
OP_IFDUP = 0x73,
|
|
49
|
+
OP_DEPTH = 0x74,
|
|
50
|
+
OP_DROP = 0x75,
|
|
51
|
+
OP_DUP = 0x76,
|
|
52
|
+
OP_NIP = 0x77,
|
|
53
|
+
OP_OVER = 0x78,
|
|
54
|
+
OP_PICK = 0x79,
|
|
55
|
+
OP_ROLL = 0x7a,
|
|
56
|
+
OP_ROT = 0x7b,
|
|
57
|
+
OP_SWAP = 0x7c,
|
|
58
|
+
OP_TUCK = 0x7d,
|
|
59
|
+
|
|
60
|
+
// data manipulation ops
|
|
61
|
+
OP_CAT = 0x7e,
|
|
62
|
+
OP_SPLIT = 0x7f,
|
|
63
|
+
OP_NUM2BIN = 0x80,
|
|
64
|
+
OP_BIN2NUM = 0x81,
|
|
65
|
+
OP_SIZE = 0x82,
|
|
66
|
+
|
|
67
|
+
// bit logic
|
|
68
|
+
OP_INVERT = 0x83,
|
|
69
|
+
OP_AND = 0x84,
|
|
70
|
+
OP_OR = 0x85,
|
|
71
|
+
OP_XOR = 0x86,
|
|
72
|
+
OP_EQUAL = 0x87,
|
|
73
|
+
OP_EQUALVERIFY = 0x88,
|
|
74
|
+
OP_RESERVED1 = 0x89,
|
|
75
|
+
OP_RESERVED2 = 0x8a,
|
|
76
|
+
|
|
77
|
+
// numeric
|
|
78
|
+
OP_1ADD = 0x8b,
|
|
79
|
+
OP_1SUB = 0x8c,
|
|
80
|
+
OP_2MUL = 0x8d,
|
|
81
|
+
OP_2DIV = 0x8e,
|
|
82
|
+
OP_NEGATE = 0x8f,
|
|
83
|
+
OP_ABS = 0x90,
|
|
84
|
+
OP_NOT = 0x91,
|
|
85
|
+
OP_0NOTEQUAL = 0x92,
|
|
86
|
+
|
|
87
|
+
OP_ADD = 0x93,
|
|
88
|
+
OP_SUB = 0x94,
|
|
89
|
+
OP_MUL = 0x95,
|
|
90
|
+
OP_DIV = 0x96,
|
|
91
|
+
OP_MOD = 0x97,
|
|
92
|
+
OP_LSHIFT = 0x98,
|
|
93
|
+
OP_RSHIFT = 0x99,
|
|
94
|
+
|
|
95
|
+
OP_BOOLAND = 0x9a,
|
|
96
|
+
OP_BOOLOR = 0x9b,
|
|
97
|
+
OP_NUMEQUAL = 0x9c,
|
|
98
|
+
OP_NUMEQUALVERIFY = 0x9d,
|
|
99
|
+
OP_NUMNOTEQUAL = 0x9e,
|
|
100
|
+
OP_LESSTHAN = 0x9f,
|
|
101
|
+
OP_GREATERTHAN = 0xa0,
|
|
102
|
+
OP_LESSTHANOREQUAL = 0xa1,
|
|
103
|
+
OP_GREATERTHANOREQUAL = 0xa2,
|
|
104
|
+
OP_MIN = 0xa3,
|
|
105
|
+
OP_MAX = 0xa4,
|
|
106
|
+
|
|
107
|
+
OP_WITHIN = 0xa5,
|
|
108
|
+
|
|
109
|
+
// crypto
|
|
110
|
+
OP_RIPEMD160 = 0xa6,
|
|
111
|
+
OP_SHA1 = 0xa7,
|
|
112
|
+
OP_SHA256 = 0xa8,
|
|
113
|
+
OP_HASH160 = 0xa9,
|
|
114
|
+
OP_HASH256 = 0xaa,
|
|
115
|
+
OP_CODESEPARATOR = 0xab,
|
|
116
|
+
OP_CHECKSIG = 0xac,
|
|
117
|
+
OP_CHECKSIGVERIFY = 0xad,
|
|
118
|
+
OP_CHECKMULTISIG = 0xae,
|
|
119
|
+
OP_CHECKMULTISIGVERIFY = 0xaf,
|
|
120
|
+
|
|
121
|
+
// expansion
|
|
122
|
+
OP_NOP1 = 0xb0,
|
|
123
|
+
OP_NOP2 = 0xb1,
|
|
124
|
+
OP_CHECKLOCKTIMEVERIFY = 0xb1,
|
|
125
|
+
OP_NOP3 = 0xb2,
|
|
126
|
+
OP_CHECKSEQUENCEVERIFY = 0xb2,
|
|
127
|
+
OP_NOP4 = 0xb3,
|
|
128
|
+
OP_NOP5 = 0xb4,
|
|
129
|
+
OP_NOP6 = 0xb5,
|
|
130
|
+
OP_NOP7 = 0xb6,
|
|
131
|
+
OP_NOP8 = 0xb7,
|
|
132
|
+
OP_NOP9 = 0xb8,
|
|
133
|
+
OP_NOP10 = 0xb9,
|
|
134
|
+
|
|
135
|
+
// template matching params
|
|
136
|
+
OP_SMALLDATA = 0xf9,
|
|
137
|
+
OP_SMALLINTEGER = 0xfa,
|
|
138
|
+
OP_PUBKEYS = 0xfb,
|
|
139
|
+
OP_PUBKEYHASH = 0xfd,
|
|
140
|
+
OP_PUBKEY = 0xfe,
|
|
141
|
+
|
|
142
|
+
OP_INVALIDOPCODE = 0xff,
|
|
143
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { TransactionReader } from "../transaction";
|
|
2
|
+
import { Address } from "./address";
|
|
3
|
+
import { ScriptType } from "./script-type";
|
|
4
|
+
import { Transaction } from "./transaction";
|
|
5
|
+
|
|
6
|
+
export class OutPoint {
|
|
7
|
+
TxId: string;
|
|
8
|
+
Vout: number;
|
|
9
|
+
LockignScript: Buffer;
|
|
10
|
+
Satoshis: number;
|
|
11
|
+
Address: Address;
|
|
12
|
+
ScriptType: ScriptType;
|
|
13
|
+
Transaction?: Transaction;
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
txId: string,
|
|
17
|
+
vout: number,
|
|
18
|
+
lockignScript: Buffer,
|
|
19
|
+
satoshis: number,
|
|
20
|
+
address: Address,
|
|
21
|
+
scriptType: ScriptType
|
|
22
|
+
) {
|
|
23
|
+
this.TxId = txId;
|
|
24
|
+
this.Vout = vout;
|
|
25
|
+
this.LockignScript = lockignScript;
|
|
26
|
+
this.Satoshis = satoshis;
|
|
27
|
+
this.Address = address;
|
|
28
|
+
this.ScriptType = scriptType;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static fromTransaction = (transaction: Transaction, vout: number) =>
|
|
32
|
+
new OutPointFull(transaction, vout);
|
|
33
|
+
|
|
34
|
+
static fromHex = (hex: string, vout: number) =>
|
|
35
|
+
new OutPointFull(TransactionReader.readHex(hex), vout);
|
|
36
|
+
|
|
37
|
+
toString = () => `${this.TxId}:${this.Vout}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class OutPointFull extends OutPoint {
|
|
41
|
+
constructor(transaction: Transaction, vout: number) {
|
|
42
|
+
const output = transaction.Outputs[vout];
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
output.ScriptType !== ScriptType.p2pkh &&
|
|
46
|
+
output.ScriptType !== ScriptType.p2stas
|
|
47
|
+
)
|
|
48
|
+
throw new Error("p2pkh or p2stat output must be provided");
|
|
49
|
+
|
|
50
|
+
super(
|
|
51
|
+
transaction.Id,
|
|
52
|
+
vout,
|
|
53
|
+
output.LockignScript,
|
|
54
|
+
output.Satoshis,
|
|
55
|
+
output.Address!,
|
|
56
|
+
output.ScriptType
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
this.Transaction = transaction;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getPublicKey,
|
|
3
|
+
Signature,
|
|
4
|
+
signSync,
|
|
5
|
+
utils,
|
|
6
|
+
verify,
|
|
7
|
+
} from "@noble/secp256k1";
|
|
8
|
+
import { hmac } from "@noble/hashes/hmac";
|
|
9
|
+
import { sha256 } from "@noble/hashes/sha256";
|
|
10
|
+
import { getChunkSize, toBuffer } from "../buffer/buffer-utils";
|
|
11
|
+
import { Address } from "./address";
|
|
12
|
+
import { BufferWriter } from "../buffer";
|
|
13
|
+
import { hash256 } from "../hashes";
|
|
14
|
+
|
|
15
|
+
export { verify } from "@noble/secp256k1";
|
|
16
|
+
|
|
17
|
+
utils.hmacSha256Sync = (key, ...msgs) =>
|
|
18
|
+
hmac(sha256, key, utils.concatBytes(...msgs));
|
|
19
|
+
|
|
20
|
+
utils.sha256Sync = (...msgs) => sha256(utils.concatBytes(...msgs));
|
|
21
|
+
|
|
22
|
+
export class PrivateKey {
|
|
23
|
+
private _pk: Buffer;
|
|
24
|
+
|
|
25
|
+
Address: Address;
|
|
26
|
+
PublicKey: Buffer;
|
|
27
|
+
|
|
28
|
+
constructor(pk: Buffer) {
|
|
29
|
+
this._pk = pk;
|
|
30
|
+
this.PublicKey = toBuffer(getPublicKey(this._pk, true));
|
|
31
|
+
this.Address = Address.fromPublicKey(toBuffer(this.PublicKey));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
sign = (message: Uint8Array) =>
|
|
35
|
+
toBuffer(signSync(message, this._pk, { der: true }));
|
|
36
|
+
|
|
37
|
+
verify = (signature: Buffer, message: Buffer) =>
|
|
38
|
+
verify(signature, message, this.PublicKey);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const verifyBitcoinSignedMessage = (
|
|
42
|
+
message: Buffer,
|
|
43
|
+
publicKey: Buffer,
|
|
44
|
+
signature: Buffer
|
|
45
|
+
) => {
|
|
46
|
+
const prefix = Buffer.from("Bitcoin Signed Message:\n");
|
|
47
|
+
const writer = BufferWriter.fromSize(
|
|
48
|
+
getChunkSize(prefix) + getChunkSize(message)
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
writer.writeVarChunk(prefix);
|
|
52
|
+
writer.writeVarChunk(message);
|
|
53
|
+
|
|
54
|
+
const sig = Signature.fromCompact(signature);
|
|
55
|
+
|
|
56
|
+
return verify(sig, hash256(writer.buffer), publicKey);
|
|
57
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export class TokenScheme {
|
|
2
|
+
Name: string;
|
|
3
|
+
TokenId: string;
|
|
4
|
+
Symbol: string;
|
|
5
|
+
SatoshisPerToken: number;
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
name: string,
|
|
9
|
+
tokenId: string,
|
|
10
|
+
symbol: string,
|
|
11
|
+
satoshisPerToken: number
|
|
12
|
+
) {
|
|
13
|
+
this.Name = name;
|
|
14
|
+
this.TokenId = tokenId;
|
|
15
|
+
this.Symbol = symbol;
|
|
16
|
+
this.SatoshisPerToken = satoshisPerToken;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
toJson = () =>
|
|
20
|
+
JSON.stringify({
|
|
21
|
+
name: this.Name,
|
|
22
|
+
tokenId: this.TokenId,
|
|
23
|
+
symbol: this.Symbol,
|
|
24
|
+
satoshisPerToken: this.SatoshisPerToken,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
toBuffer = () => Buffer.from(this.toJson(), "utf8");
|
|
28
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ScriptReader } from "../script/read/script-reader";
|
|
2
|
+
import { Address } from "./address";
|
|
3
|
+
|
|
4
|
+
export class TransactionInput {
|
|
5
|
+
TxId: string;
|
|
6
|
+
Vout: number;
|
|
7
|
+
UnlockingScript: Buffer;
|
|
8
|
+
Sequence: number;
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
txId: string,
|
|
12
|
+
vout: number,
|
|
13
|
+
unlockingScript: Buffer,
|
|
14
|
+
sequence: number
|
|
15
|
+
) {
|
|
16
|
+
this.TxId = txId;
|
|
17
|
+
this.Vout = vout;
|
|
18
|
+
this.UnlockingScript = unlockingScript;
|
|
19
|
+
this.Sequence = sequence;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
tryGetAddress = () => {
|
|
23
|
+
const scriptTokens = ScriptReader.read(this.UnlockingScript);
|
|
24
|
+
const lastToken = scriptTokens[scriptTokens.length - 1];
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
lastToken.DataLength === 33 &&
|
|
28
|
+
(lastToken.Data![0] === 2 || lastToken.Data![0] === 3)
|
|
29
|
+
) {
|
|
30
|
+
return Address.fromPublicKey(lastToken.Data!);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { ScriptReader } from "../script/read/script-reader";
|
|
2
|
+
import { p2phkTokens, getP2stasTokens } from "../script/script-samples";
|
|
3
|
+
import { ScriptToken } from "../script/script-token";
|
|
4
|
+
import { Address } from "./address";
|
|
5
|
+
import { OpCode } from "./op-codes";
|
|
6
|
+
import { ScriptType } from "./script-type";
|
|
7
|
+
|
|
8
|
+
export class TransactionOutput {
|
|
9
|
+
Satoshis: number;
|
|
10
|
+
LockignScript: Buffer;
|
|
11
|
+
ScriptType: ScriptType = ScriptType.unknown;
|
|
12
|
+
Address?: Address;
|
|
13
|
+
TokenId?: string;
|
|
14
|
+
Symbol?: string;
|
|
15
|
+
data: Buffer[] = [];
|
|
16
|
+
|
|
17
|
+
constructor(satoshis: number, lockignScript: Buffer) {
|
|
18
|
+
this.Satoshis = satoshis;
|
|
19
|
+
this.LockignScript = lockignScript;
|
|
20
|
+
|
|
21
|
+
const scriptTokens = ScriptReader.read(this.LockignScript);
|
|
22
|
+
|
|
23
|
+
if (!this._isNulData(scriptTokens)) {
|
|
24
|
+
if (!this._isP2pkh(scriptTokens)) {
|
|
25
|
+
this._isP2stas(scriptTokens);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private _isNulData = (scriptTokens: ScriptToken[]): boolean => {
|
|
31
|
+
if (
|
|
32
|
+
scriptTokens.length > 2 &&
|
|
33
|
+
scriptTokens[0].OpCodeNum === OpCode.OP_0 &&
|
|
34
|
+
scriptTokens[1].OpCodeNum === OpCode.OP_RETURN
|
|
35
|
+
) {
|
|
36
|
+
for (let i = 2; i < scriptTokens.length; i++)
|
|
37
|
+
this.data.push(scriptTokens[i].Data!);
|
|
38
|
+
|
|
39
|
+
this.ScriptType = ScriptType.nullData;
|
|
40
|
+
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return false;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
private _isP2pkh = (scriptTokens: ScriptToken[]): boolean => {
|
|
48
|
+
if (scriptTokens.length > p2phkTokens.length) return false;
|
|
49
|
+
|
|
50
|
+
let opReturnReached = false;
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < scriptTokens.length; i++) {
|
|
53
|
+
const token = scriptTokens[i];
|
|
54
|
+
|
|
55
|
+
if (!opReturnReached) {
|
|
56
|
+
if (token.OpCodeNum === OpCode.OP_RETURN) {
|
|
57
|
+
opReturnReached = true;
|
|
58
|
+
} else {
|
|
59
|
+
if (token.OpCodeNum !== p2phkTokens[i].OpCodeNum) return false;
|
|
60
|
+
|
|
61
|
+
if (p2phkTokens[i].IsReceiverId)
|
|
62
|
+
this.Address = new Address(token.Data!);
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
this.data.push(token.Data!);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.ScriptType = ScriptType.p2pkh;
|
|
70
|
+
|
|
71
|
+
return true;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
private _isP2stas = (scriptTokens: ScriptToken[]): boolean => {
|
|
75
|
+
const p2stasTokens = getP2stasTokens();
|
|
76
|
+
|
|
77
|
+
if (scriptTokens.length < p2stasTokens.length) return false;
|
|
78
|
+
|
|
79
|
+
let opReturnIdx = -1;
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < scriptTokens.length; i++) {
|
|
82
|
+
const token = scriptTokens[i];
|
|
83
|
+
|
|
84
|
+
if (opReturnIdx === -1) {
|
|
85
|
+
if (token.OpCodeNum === OpCode.OP_RETURN) {
|
|
86
|
+
opReturnIdx = i;
|
|
87
|
+
} else {
|
|
88
|
+
if (token.OpCodeNum !== p2stasTokens[i].OpCodeNum) return false;
|
|
89
|
+
|
|
90
|
+
if (p2stasTokens[i].IsReceiverId)
|
|
91
|
+
this.Address = new Address(token.Data!);
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
if (i === opReturnIdx + 1) this.TokenId = token.Data!.toString("hex");
|
|
95
|
+
else if (i === opReturnIdx + 2)
|
|
96
|
+
this.Symbol = token.Data!.toString("utf8");
|
|
97
|
+
else this.data.push(token.Data!);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.ScriptType = ScriptType.p2stas;
|
|
102
|
+
|
|
103
|
+
return true;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { reverseBuffer } from "../buffer/buffer-utils";
|
|
2
|
+
import { hash256 } from "../hashes";
|
|
3
|
+
import { TransactionInput } from "./transaction-input";
|
|
4
|
+
import { TransactionOutput } from "./transaction-output";
|
|
5
|
+
|
|
6
|
+
export class Transaction {
|
|
7
|
+
Inputs: TransactionInput[];
|
|
8
|
+
Outputs: TransactionOutput[];
|
|
9
|
+
Version: number;
|
|
10
|
+
LockTime: number;
|
|
11
|
+
Raw: Buffer;
|
|
12
|
+
Hex: string;
|
|
13
|
+
Id: string;
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
raw: Buffer,
|
|
17
|
+
inputs: TransactionInput[],
|
|
18
|
+
outputs: TransactionOutput[],
|
|
19
|
+
version: number,
|
|
20
|
+
lockTime: number
|
|
21
|
+
) {
|
|
22
|
+
this.Inputs = inputs;
|
|
23
|
+
this.Outputs = outputs;
|
|
24
|
+
this.Version = version;
|
|
25
|
+
this.LockTime = lockTime;
|
|
26
|
+
|
|
27
|
+
this.Raw = raw;
|
|
28
|
+
this.Hex = raw.toString("hex");
|
|
29
|
+
this.Id = reverseBuffer(hash256(raw)).toString("hex");
|
|
30
|
+
}
|
|
31
|
+
}
|