ecash-lib 1.2.2-rc9 → 1.4.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/README.md +2 -1
- package/dist/ecc.d.ts +12 -0
- package/dist/ecc.d.ts.map +1 -1
- package/dist/ecc.js +9 -47
- package/dist/ecc.js.map +1 -1
- package/dist/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
- package/dist/ffi/ecash_lib_wasm_bg_browser.wasm.d.ts +18 -4
- package/dist/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
- package/dist/ffi/ecash_lib_wasm_bg_nodejs.wasm.d.ts +18 -4
- package/dist/ffi/ecash_lib_wasm_browser.d.ts +95 -4
- package/dist/ffi/ecash_lib_wasm_browser.js +245 -14
- package/dist/ffi/ecash_lib_wasm_nodejs.d.ts +77 -0
- package/dist/ffi/ecash_lib_wasm_nodejs.js +247 -14
- package/dist/hash.d.ts +21 -1
- package/dist/hash.d.ts.map +1 -1
- package/dist/hash.js +16 -10
- package/dist/hash.js.map +1 -1
- package/dist/hdwallet.d.ts +33 -0
- package/dist/hdwallet.d.ts.map +1 -0
- package/dist/hdwallet.js +148 -0
- package/dist/hdwallet.js.map +1 -0
- package/dist/hmac.d.ts +13 -0
- package/dist/hmac.d.ts.map +1 -0
- package/dist/hmac.js +63 -0
- package/dist/hmac.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/initBrowser.d.ts.map +1 -1
- package/dist/initBrowser.js +3 -0
- package/dist/initBrowser.js.map +1 -1
- package/dist/initNodeJs.d.ts.map +1 -1
- package/dist/initNodeJs.js +3 -0
- package/dist/initNodeJs.js.map +1 -1
- package/dist/io/bytes.d.ts +5 -3
- package/dist/io/bytes.d.ts.map +1 -1
- package/dist/io/bytes.js +15 -7
- package/dist/io/bytes.js.map +1 -1
- package/dist/io/writer.d.ts +4 -3
- package/dist/io/writer.d.ts.map +1 -1
- package/dist/io/writerbytes.d.ts +4 -3
- package/dist/io/writerbytes.d.ts.map +1 -1
- package/dist/io/writerbytes.js +7 -6
- package/dist/io/writerbytes.js.map +1 -1
- package/dist/io/writerlength.d.ts +4 -3
- package/dist/io/writerlength.d.ts.map +1 -1
- package/dist/io/writerlength.js +3 -3
- package/dist/io/writerlength.js.map +1 -1
- package/dist/mnemonic.d.ts +14 -0
- package/dist/mnemonic.d.ts.map +1 -0
- package/dist/mnemonic.js +123 -0
- package/dist/mnemonic.js.map +1 -0
- package/dist/pbkdf2.d.ts +11 -0
- package/dist/pbkdf2.d.ts.map +1 -0
- package/dist/pbkdf2.js +36 -0
- package/dist/pbkdf2.js.map +1 -0
- package/dist/script.d.ts +4 -0
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +6 -0
- package/dist/script.js.map +1 -1
- package/package.json +1 -1
- package/src/address/address.ts +346 -0
- package/src/address/legacyaddr.ts +129 -0
- package/src/consts.ts +8 -0
- package/src/ecc.ts +61 -0
- package/src/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
- package/src/ffi/ecash_lib_wasm_bg_browser.wasm.d.ts +32 -0
- package/src/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
- package/src/ffi/ecash_lib_wasm_bg_nodejs.wasm.d.ts +32 -0
- package/src/ffi/ecash_lib_wasm_browser.d.ts +183 -0
- package/src/ffi/ecash_lib_wasm_browser.js +571 -0
- package/src/ffi/ecash_lib_wasm_nodejs.d.ts +127 -0
- package/src/ffi/ecash_lib_wasm_nodejs.js +498 -0
- package/src/hash.ts +46 -0
- package/src/hdwallet.ts +181 -0
- package/src/hmac.ts +74 -0
- package/src/index.ts +29 -0
- package/src/indexBrowser.ts +6 -0
- package/src/indexNodeJs.ts +6 -0
- package/src/initBrowser.ts +21 -0
- package/src/initNodeJs.ts +20 -0
- package/src/io/bytes.ts +80 -0
- package/src/io/hex.ts +69 -0
- package/src/io/int.ts +6 -0
- package/src/io/str.ts +16 -0
- package/src/io/varsize.ts +49 -0
- package/src/io/writer.ts +20 -0
- package/src/io/writerbytes.ts +87 -0
- package/src/io/writerlength.ts +44 -0
- package/src/mnemonic.ts +153 -0
- package/src/op.ts +162 -0
- package/src/opcode.ts +154 -0
- package/src/pbkdf2.ts +52 -0
- package/src/script.ts +195 -0
- package/src/sigHashType.ts +190 -0
- package/src/test/testRunner.ts +209 -0
- package/src/token/alp.ts +146 -0
- package/src/token/common.ts +32 -0
- package/src/token/empp.ts +29 -0
- package/src/token/slp.ts +212 -0
- package/src/tx.ts +180 -0
- package/src/txBuilder.ts +262 -0
- package/src/unsignedTx.ts +359 -0
- package/tsconfig.json +2 -1
- package/wordlists/english.json +2053 -0
- package/.nyc_output/0fc40ca6-d52c-45eb-b31b-2601ce70b887.json +0 -1
- package/.nyc_output/ac5be6db-4e40-41f8-8b84-7598d4747e57.json +0 -1
- package/.nyc_output/b316d46f-5ea0-4e98-884a-bfbf9cc1d0f8.json +0 -1
- package/.nyc_output/f965566b-9422-4874-b45e-9eefda9c769c.json +0 -1
- package/.nyc_output/processinfo/0fc40ca6-d52c-45eb-b31b-2601ce70b887.json +0 -1
- package/.nyc_output/processinfo/ac5be6db-4e40-41f8-8b84-7598d4747e57.json +0 -1
- package/.nyc_output/processinfo/b316d46f-5ea0-4e98-884a-bfbf9cc1d0f8.json +0 -1
- package/.nyc_output/processinfo/f965566b-9422-4874-b45e-9eefda9c769c.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/dist/address/cashaddr.d.ts +0 -78
- package/dist/address/cashaddr.d.ts.map +0 -1
- package/dist/address/cashaddr.js +0 -543
- package/dist/address/cashaddr.js.map +0 -1
- package/dist/cashaddr/cashaddr.d.ts +0 -23
- package/dist/cashaddr/cashaddr.d.ts.map +0 -1
- package/dist/cashaddr/cashaddr.js +0 -325
- package/dist/cashaddr/cashaddr.js.map +0 -1
- package/global.d.ts +0 -64
- package/test.log +0 -82
package/src/hdwallet.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Copyright (c) 2025 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
import { Ecc } from './ecc.js';
|
|
6
|
+
import { hmacSha512 } from './hmac.js';
|
|
7
|
+
import { shaRmd160 } from './hash.js';
|
|
8
|
+
import { Bytes } from './io/bytes.js';
|
|
9
|
+
import { strToBytes } from './io/str.js';
|
|
10
|
+
import { WriterBytes } from './io/writerbytes.js';
|
|
11
|
+
|
|
12
|
+
const HIGHEST_BIT = 0x80000000;
|
|
13
|
+
|
|
14
|
+
export class HdNode {
|
|
15
|
+
private _ecc: Ecc;
|
|
16
|
+
private _seckey: Uint8Array | undefined;
|
|
17
|
+
private _pubkey: Uint8Array;
|
|
18
|
+
private _chainCode: Uint8Array;
|
|
19
|
+
private _depth: number;
|
|
20
|
+
private _index: number;
|
|
21
|
+
private _parentFingerprint: number;
|
|
22
|
+
|
|
23
|
+
public constructor(params: {
|
|
24
|
+
ecc: Ecc;
|
|
25
|
+
seckey: Uint8Array | undefined;
|
|
26
|
+
pubkey: Uint8Array;
|
|
27
|
+
chainCode: Uint8Array;
|
|
28
|
+
depth: number;
|
|
29
|
+
index: number;
|
|
30
|
+
parentFingerprint: number;
|
|
31
|
+
}) {
|
|
32
|
+
this._ecc = params.ecc;
|
|
33
|
+
this._seckey = params.seckey;
|
|
34
|
+
this._pubkey = params.pubkey;
|
|
35
|
+
this._chainCode = params.chainCode;
|
|
36
|
+
this._depth = params.depth;
|
|
37
|
+
this._index = params.index;
|
|
38
|
+
this._parentFingerprint = params.parentFingerprint;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public seckey(): Uint8Array | undefined {
|
|
42
|
+
return this._seckey;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public pubkey(): Uint8Array {
|
|
46
|
+
return this._pubkey;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public pkh(): Uint8Array {
|
|
50
|
+
return shaRmd160(this._pubkey);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public fingerprint(): Uint8Array {
|
|
54
|
+
return this.pkh().slice(0, 4);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public index(): number {
|
|
58
|
+
return this._index;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public depth(): number {
|
|
62
|
+
return this._depth;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public parentFingerprint(): number {
|
|
66
|
+
return this._parentFingerprint;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public chainCode(): Uint8Array {
|
|
70
|
+
return this._chainCode;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public derive(index: number): HdNode {
|
|
74
|
+
const isHardened = index >= HIGHEST_BIT;
|
|
75
|
+
const data = new WriterBytes(1 + 32 + 4);
|
|
76
|
+
if (isHardened) {
|
|
77
|
+
if (this._seckey === undefined) {
|
|
78
|
+
throw new Error('Missing private key for hardened child key');
|
|
79
|
+
}
|
|
80
|
+
data.putU8(0);
|
|
81
|
+
data.putBytes(this._seckey);
|
|
82
|
+
} else {
|
|
83
|
+
data.putBytes(this._pubkey);
|
|
84
|
+
}
|
|
85
|
+
data.putU32(index, 'BE');
|
|
86
|
+
const hashed = hmacSha512(this._chainCode, data.data);
|
|
87
|
+
const hashedLeft = hashed.slice(0, 32);
|
|
88
|
+
const hashedRight = hashed.slice(32);
|
|
89
|
+
|
|
90
|
+
// In case the secret key doesn't lie on the curve, we proceed with the
|
|
91
|
+
// next index. This is astronomically unlikely but part of the specification.
|
|
92
|
+
if (!this._ecc.isValidSeckey(hashedLeft)) {
|
|
93
|
+
return this.derive(index + 1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let seckey: Uint8Array | undefined;
|
|
97
|
+
let pubkey: Uint8Array;
|
|
98
|
+
if (this._seckey !== undefined) {
|
|
99
|
+
try {
|
|
100
|
+
seckey = this._ecc.seckeyAdd(this._seckey, hashedLeft);
|
|
101
|
+
} catch (ex) {
|
|
102
|
+
console.log('Skipping index', index, ':', ex);
|
|
103
|
+
return this.derive(index + 1);
|
|
104
|
+
}
|
|
105
|
+
pubkey = this._ecc.derivePubkey(seckey);
|
|
106
|
+
} else {
|
|
107
|
+
try {
|
|
108
|
+
pubkey = this._ecc.pubkeyAdd(this._pubkey, hashedLeft);
|
|
109
|
+
} catch (ex) {
|
|
110
|
+
console.log('Skipping index', index, ':', ex);
|
|
111
|
+
return this.derive(index + 1);
|
|
112
|
+
}
|
|
113
|
+
seckey = undefined;
|
|
114
|
+
}
|
|
115
|
+
return new HdNode({
|
|
116
|
+
ecc: this._ecc,
|
|
117
|
+
seckey: seckey,
|
|
118
|
+
pubkey: pubkey,
|
|
119
|
+
chainCode: hashedRight,
|
|
120
|
+
depth: this._depth + 1,
|
|
121
|
+
index,
|
|
122
|
+
parentFingerprint: new Bytes(this.fingerprint()).readU32('BE'),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public deriveHardened(index: number): HdNode {
|
|
127
|
+
if (index < 0 || index >= HIGHEST_BIT) {
|
|
128
|
+
throw new TypeError(
|
|
129
|
+
`index must be between 0 and ${HIGHEST_BIT}, got ${index}`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return this.derive(index + HIGHEST_BIT);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public derivePath(path: string): HdNode {
|
|
136
|
+
let splitPath = path.split('/');
|
|
137
|
+
if (splitPath[0] === 'm') {
|
|
138
|
+
if (this._parentFingerprint) {
|
|
139
|
+
throw new TypeError('Expected master, got child');
|
|
140
|
+
}
|
|
141
|
+
splitPath = splitPath.slice(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let hd: HdNode = this;
|
|
145
|
+
for (const step of splitPath) {
|
|
146
|
+
if (step.slice(-1) === `'`) {
|
|
147
|
+
hd = hd.deriveHardened(parseInt(step.slice(0, -1), 10));
|
|
148
|
+
} else {
|
|
149
|
+
hd = hd.derive(parseInt(step, 10));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return hd;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public static fromPrivateKey(
|
|
157
|
+
ecc: Ecc,
|
|
158
|
+
seckey: Uint8Array,
|
|
159
|
+
chainCode: Uint8Array,
|
|
160
|
+
): HdNode {
|
|
161
|
+
return new HdNode({
|
|
162
|
+
ecc,
|
|
163
|
+
seckey: seckey,
|
|
164
|
+
pubkey: ecc.derivePubkey(seckey),
|
|
165
|
+
chainCode,
|
|
166
|
+
depth: 0,
|
|
167
|
+
index: 0,
|
|
168
|
+
parentFingerprint: 0,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public static fromSeed(ecc: Ecc, seed: Uint8Array): HdNode {
|
|
173
|
+
if (seed.length < 16 || seed.length > 64) {
|
|
174
|
+
throw new TypeError('Seed must be between 16 and 64 bytes long');
|
|
175
|
+
}
|
|
176
|
+
const hashed = hmacSha512(strToBytes('Bitcoin seed'), seed);
|
|
177
|
+
const hashedLeft = hashed.slice(0, 32);
|
|
178
|
+
const hashedRight = hashed.slice(32);
|
|
179
|
+
return HdNode.fromPrivateKey(ecc, hashedLeft, hashedRight);
|
|
180
|
+
}
|
|
181
|
+
}
|
package/src/hmac.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Copyright (c) 2025 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
import { Hasher, sha256Hasher, sha512Hasher } from './hash';
|
|
6
|
+
|
|
7
|
+
export class Hmac {
|
|
8
|
+
oHash: Hasher;
|
|
9
|
+
iHash: Hasher;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
hashFactory: () => Hasher,
|
|
13
|
+
blockLength: number,
|
|
14
|
+
key: Uint8Array,
|
|
15
|
+
) {
|
|
16
|
+
this.oHash = hashFactory();
|
|
17
|
+
this.iHash = hashFactory();
|
|
18
|
+
const pad = new Uint8Array(blockLength);
|
|
19
|
+
if (key.length > blockLength) {
|
|
20
|
+
const hasher = hashFactory();
|
|
21
|
+
hasher.update(key);
|
|
22
|
+
key = hasher.finalize();
|
|
23
|
+
}
|
|
24
|
+
pad.set(key, 0);
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < pad.length; i++) {
|
|
27
|
+
pad[i] ^= 0x36;
|
|
28
|
+
}
|
|
29
|
+
this.iHash.update(pad);
|
|
30
|
+
|
|
31
|
+
for (let i = 0; i < pad.length; i++) {
|
|
32
|
+
pad[i] ^= 0x36 ^ 0x5c;
|
|
33
|
+
}
|
|
34
|
+
this.oHash.update(pad);
|
|
35
|
+
|
|
36
|
+
pad.fill(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
update(data: Uint8Array) {
|
|
40
|
+
this.iHash.update(data);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
digest(): Uint8Array {
|
|
44
|
+
this.oHash.update(this.iHash.finalize());
|
|
45
|
+
const hash = this.oHash.finalize();
|
|
46
|
+
this.iHash.free();
|
|
47
|
+
this.oHash.free();
|
|
48
|
+
return hash;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
clone(): Hmac {
|
|
52
|
+
const clone: Hmac = Object.create(Object.getPrototypeOf(this), {});
|
|
53
|
+
clone.oHash = this.oHash.clone();
|
|
54
|
+
clone.iHash = this.iHash.clone();
|
|
55
|
+
return clone;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
free() {
|
|
59
|
+
this.iHash.free();
|
|
60
|
+
this.oHash.free();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array {
|
|
65
|
+
const hmac = new Hmac(sha256Hasher, 64, key);
|
|
66
|
+
hmac.update(data);
|
|
67
|
+
return hmac.digest();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function hmacSha512(key: Uint8Array, data: Uint8Array): Uint8Array {
|
|
71
|
+
const hmac = new Hmac(sha512Hasher, 128, key);
|
|
72
|
+
hmac.update(data);
|
|
73
|
+
return hmac.digest();
|
|
74
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
export * from './consts.js';
|
|
6
|
+
export * from './ecc.js';
|
|
7
|
+
export * from './hash.js';
|
|
8
|
+
export * from './op.js';
|
|
9
|
+
export * from './opcode.js';
|
|
10
|
+
export * from './script.js';
|
|
11
|
+
export * from './mnemonic.js';
|
|
12
|
+
export * from './hdwallet.js';
|
|
13
|
+
export * from './address/address.js';
|
|
14
|
+
export * from './sigHashType.js';
|
|
15
|
+
export * from './tx.js';
|
|
16
|
+
export * from './txBuilder.js';
|
|
17
|
+
export * from './unsignedTx.js';
|
|
18
|
+
export * from './io/bytes.js';
|
|
19
|
+
export * from './io/hex.js';
|
|
20
|
+
export * from './io/int.js';
|
|
21
|
+
export * from './io/str.js';
|
|
22
|
+
export * from './io/varsize.js';
|
|
23
|
+
export * from './io/writer.js';
|
|
24
|
+
export * from './io/writerbytes.js';
|
|
25
|
+
export * from './io/writerlength.js';
|
|
26
|
+
export * from './token/alp.js';
|
|
27
|
+
export * from './token/common.js';
|
|
28
|
+
export * from './token/empp.js';
|
|
29
|
+
export * from './token/slp.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
import { __setEcc } from './ecc.js';
|
|
6
|
+
import __wbg_init, * as ffi from './ffi/ecash_lib_wasm_browser.js';
|
|
7
|
+
import { __setHashes } from './hash.js';
|
|
8
|
+
|
|
9
|
+
/** Load and initialize the WASM module for Web */
|
|
10
|
+
export async function initWasm() {
|
|
11
|
+
await __wbg_init();
|
|
12
|
+
__setEcc(ffi.Ecc);
|
|
13
|
+
__setHashes({
|
|
14
|
+
sha256: ffi.sha256,
|
|
15
|
+
sha256d: ffi.sha256d,
|
|
16
|
+
shaRmd160: ffi.shaRmd160,
|
|
17
|
+
sha512: ffi.sha512,
|
|
18
|
+
Sha256H: ffi.Sha256H,
|
|
19
|
+
Sha512H: ffi.Sha512H,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
import { __setEcc } from './ecc.js';
|
|
6
|
+
import * as ffi from './ffi/ecash_lib_wasm_nodejs.js';
|
|
7
|
+
import { __setHashes } from './hash.js';
|
|
8
|
+
|
|
9
|
+
/** Load and initialize the WASM module for NodeJS */
|
|
10
|
+
export async function initWasm() {
|
|
11
|
+
__setEcc(ffi.Ecc);
|
|
12
|
+
__setHashes({
|
|
13
|
+
sha256: ffi.sha256,
|
|
14
|
+
sha256d: ffi.sha256d,
|
|
15
|
+
shaRmd160: ffi.shaRmd160,
|
|
16
|
+
sha512: ffi.sha512,
|
|
17
|
+
Sha256H: ffi.Sha256H,
|
|
18
|
+
Sha512H: ffi.Sha512H,
|
|
19
|
+
});
|
|
20
|
+
}
|
package/src/io/bytes.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
export type Endian = 'LE' | 'BE';
|
|
6
|
+
|
|
7
|
+
export function endianToBool(endian?: Endian): boolean {
|
|
8
|
+
if (!endian) {
|
|
9
|
+
// By default, little endian
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return endian === 'LE';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Reads ints/bytes from a Uint8Array. All integers are little-endian. */
|
|
16
|
+
export class Bytes {
|
|
17
|
+
public data: Uint8Array;
|
|
18
|
+
public view: DataView;
|
|
19
|
+
public idx: number;
|
|
20
|
+
|
|
21
|
+
/** Create a new Bytes that reads from the given data */
|
|
22
|
+
public constructor(data: Uint8Array) {
|
|
23
|
+
this.data = data;
|
|
24
|
+
this.view = new DataView(
|
|
25
|
+
this.data.buffer,
|
|
26
|
+
this.data.byteOffset,
|
|
27
|
+
this.data.byteLength,
|
|
28
|
+
);
|
|
29
|
+
this.idx = 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Read a single byte */
|
|
33
|
+
public readU8(): number {
|
|
34
|
+
this.ensureSize(1);
|
|
35
|
+
const result = this.data[this.idx];
|
|
36
|
+
this.idx++;
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Read 2-byte little-endian integer (uint16_t) */
|
|
41
|
+
public readU16(endian?: Endian): number {
|
|
42
|
+
this.ensureSize(2);
|
|
43
|
+
const result = this.view.getUint16(this.idx, endianToBool(endian));
|
|
44
|
+
this.idx += 2;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Read 4-byte little-endian integer (uint32_t) */
|
|
49
|
+
public readU32(endian?: Endian): number {
|
|
50
|
+
this.ensureSize(4);
|
|
51
|
+
const result = this.view.getUint32(this.idx, endianToBool(endian));
|
|
52
|
+
this.idx += 4;
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Read 8-byte little-endian integer (uint64_t) */
|
|
57
|
+
public readU64(endian?: Endian): bigint {
|
|
58
|
+
this.ensureSize(8);
|
|
59
|
+
const result = this.view.getBigUint64(this.idx, endianToBool(endian));
|
|
60
|
+
this.idx += 8;
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Read the given number of bytes as array */
|
|
65
|
+
public readBytes(numBytes: number): Uint8Array {
|
|
66
|
+
this.ensureSize(numBytes);
|
|
67
|
+
const result = this.data.slice(this.idx, this.idx + numBytes);
|
|
68
|
+
this.idx += numBytes;
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private ensureSize(extraBytes: number) {
|
|
73
|
+
if (this.data.length < this.idx + extraBytes) {
|
|
74
|
+
throw (
|
|
75
|
+
`Not enough bytes: Tried reading ${extraBytes} byte(s), but ` +
|
|
76
|
+
`there are only ${this.data.length - this.idx} byte(s) left`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/io/hex.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright (c) 2023-2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
const LUT_HEX_4b = [
|
|
6
|
+
'0',
|
|
7
|
+
'1',
|
|
8
|
+
'2',
|
|
9
|
+
'3',
|
|
10
|
+
'4',
|
|
11
|
+
'5',
|
|
12
|
+
'6',
|
|
13
|
+
'7',
|
|
14
|
+
'8',
|
|
15
|
+
'9',
|
|
16
|
+
'a',
|
|
17
|
+
'b',
|
|
18
|
+
'c',
|
|
19
|
+
'd',
|
|
20
|
+
'e',
|
|
21
|
+
'f',
|
|
22
|
+
];
|
|
23
|
+
const LUT_HEX_8b = new Array(0x100);
|
|
24
|
+
const LUT_BIN_8b: { [key: string]: number } = {};
|
|
25
|
+
for (let n = 0; n < 0x100; n++) {
|
|
26
|
+
const hex = `${LUT_HEX_4b[(n >>> 4) & 0xf]}${LUT_HEX_4b[n & 0xf]}`;
|
|
27
|
+
LUT_HEX_8b[n] = hex;
|
|
28
|
+
LUT_BIN_8b[hex] = n;
|
|
29
|
+
}
|
|
30
|
+
// End Pre-Init
|
|
31
|
+
|
|
32
|
+
export function toHex(buffer: Uint8Array): string {
|
|
33
|
+
let out = '';
|
|
34
|
+
for (let idx = 0, edx = buffer.length; idx < edx; ++idx) {
|
|
35
|
+
out += LUT_HEX_8b[buffer[idx]];
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function toHexRev(buffer: Uint8Array): string {
|
|
41
|
+
let out = '';
|
|
42
|
+
for (let idx = buffer.length - 1; idx >= 0; --idx) {
|
|
43
|
+
out += LUT_HEX_8b[buffer[idx]];
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function fromHex(str: string): Uint8Array {
|
|
49
|
+
if ((str.length & 1) != 0) {
|
|
50
|
+
throw new Error(`Odd hex length: ${str}`);
|
|
51
|
+
}
|
|
52
|
+
const nBytes = str.length >> 1;
|
|
53
|
+
const array = new Uint8Array(nBytes);
|
|
54
|
+
for (let idx = 0; idx < str.length; idx += 2) {
|
|
55
|
+
const pair = str.substring(idx, idx + 2);
|
|
56
|
+
const byte = LUT_BIN_8b[pair];
|
|
57
|
+
if (byte === undefined) {
|
|
58
|
+
throw new Error(`Invalid hex pair: ${pair}, at index ${idx}`);
|
|
59
|
+
}
|
|
60
|
+
array[idx >> 1] = byte;
|
|
61
|
+
}
|
|
62
|
+
return array;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function fromHexRev(str: string): Uint8Array {
|
|
66
|
+
const array = fromHex(str);
|
|
67
|
+
array.reverse();
|
|
68
|
+
return array;
|
|
69
|
+
}
|
package/src/io/int.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
/** Union type which can either be a normal JS Number or a BigInt. */
|
|
6
|
+
export type Int = number | bigint;
|
package/src/io/str.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
const decoder = new TextDecoder();
|
|
7
|
+
|
|
8
|
+
/** Turn a UTF-8 encoded string into a Uint8Array */
|
|
9
|
+
export function strToBytes(str: string): Uint8Array {
|
|
10
|
+
return encoder.encode(str);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Turn a Uint8Array into a UTF-8 encoded string */
|
|
14
|
+
export function bytesToStr(bytes: Uint8Array): string {
|
|
15
|
+
return decoder.decode(bytes);
|
|
16
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
import { Bytes } from './bytes.js';
|
|
6
|
+
import { Writer } from './writer.js';
|
|
7
|
+
import { Int } from './int.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Read a VARINT, which encodes a size in the Bitcoin protocol, see:
|
|
11
|
+
* https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
|
|
12
|
+
*/
|
|
13
|
+
export function readVarSize(bytes: Bytes): Int {
|
|
14
|
+
const firstByte = bytes.readU8();
|
|
15
|
+
if (firstByte <= 0xfc) {
|
|
16
|
+
return firstByte;
|
|
17
|
+
} else if (firstByte == 0xfd) {
|
|
18
|
+
return bytes.readU16();
|
|
19
|
+
} else if (firstByte == 0xfe) {
|
|
20
|
+
return bytes.readU32();
|
|
21
|
+
} else if (firstByte == 0xff) {
|
|
22
|
+
return bytes.readU64();
|
|
23
|
+
} else {
|
|
24
|
+
throw 'Unreachable';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Write a VARINT, which encodes a size in the Bitcoin protocol, see:
|
|
30
|
+
* https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer
|
|
31
|
+
* @param size Size integer to write
|
|
32
|
+
* @param writer Writer to write the size to
|
|
33
|
+
*/
|
|
34
|
+
export function writeVarSize(size: Int, writer: Writer) {
|
|
35
|
+
if (size <= 0xfc) {
|
|
36
|
+
writer.putU8(size);
|
|
37
|
+
} else if (size <= 0xffff) {
|
|
38
|
+
writer.putU8(0xfd);
|
|
39
|
+
writer.putU16(size);
|
|
40
|
+
} else if (size <= 0xffffffff) {
|
|
41
|
+
writer.putU8(0xfe);
|
|
42
|
+
writer.putU32(size);
|
|
43
|
+
} else if (size <= 0xffffffffffffffffn) {
|
|
44
|
+
writer.putU8(0xff);
|
|
45
|
+
writer.putU64(size);
|
|
46
|
+
} else {
|
|
47
|
+
throw 'Integer too big for VarSize';
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/io/writer.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
import { Endian } from './bytes.js';
|
|
6
|
+
import { Int } from './int.js';
|
|
7
|
+
|
|
8
|
+
/** Writer interface to abstract over writing Bitcoin objects */
|
|
9
|
+
export interface Writer {
|
|
10
|
+
/** Write a single byte */
|
|
11
|
+
putU8(value: Int): void;
|
|
12
|
+
/** Write a 2-byte little-endian integer (uint16_t) */
|
|
13
|
+
putU16(value: Int, endian?: Endian): void;
|
|
14
|
+
/** Write a 4-byte little-endian integer (uint32_t) */
|
|
15
|
+
putU32(value: Int, endian?: Endian): void;
|
|
16
|
+
/** Write an 8-byte little-endian integer (uint64_t) */
|
|
17
|
+
putU64(value: Int, endian?: Endian): void;
|
|
18
|
+
/** Write the given bytes */
|
|
19
|
+
putBytes(bytes: Uint8Array): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Copyright (c) 2024 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
import { Endian, endianToBool } from './bytes.js';
|
|
6
|
+
import { Int } from './int.js';
|
|
7
|
+
import { Writer } from './writer.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Implementation of `Writer` which writes to an array of pre-allocated size.
|
|
11
|
+
* It's intended to be used in unison with `WriterLength`, which first finds
|
|
12
|
+
* out the length of the serialized object and then the actual data is written
|
|
13
|
+
* using this class.
|
|
14
|
+
**/
|
|
15
|
+
export class WriterBytes implements Writer {
|
|
16
|
+
public data: Uint8Array;
|
|
17
|
+
public view: DataView;
|
|
18
|
+
public idx: number;
|
|
19
|
+
|
|
20
|
+
/** Create a new WriterBytes with the given pre-allocated size */
|
|
21
|
+
public constructor(length: number) {
|
|
22
|
+
this.data = new Uint8Array(length);
|
|
23
|
+
this.view = new DataView(
|
|
24
|
+
this.data.buffer,
|
|
25
|
+
this.data.byteOffset,
|
|
26
|
+
this.data.byteLength,
|
|
27
|
+
);
|
|
28
|
+
this.idx = 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Write a single byte */
|
|
32
|
+
public putU8(value: Int): void {
|
|
33
|
+
if (value < 0 || value > 0xff) {
|
|
34
|
+
throw new Error(`Cannot fit ${value} into a u8`);
|
|
35
|
+
}
|
|
36
|
+
this.ensureSize(1);
|
|
37
|
+
this.data[this.idx] = Number(value);
|
|
38
|
+
this.idx++;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Write a 2-byte little-endian integer (uint16_t) */
|
|
42
|
+
public putU16(value: Int, endian?: Endian): void {
|
|
43
|
+
if (value < 0 || value > 0xffff) {
|
|
44
|
+
throw new Error(`Cannot fit ${value} into a u16`);
|
|
45
|
+
}
|
|
46
|
+
this.ensureSize(2);
|
|
47
|
+
this.view.setUint16(this.idx, Number(value), endianToBool(endian));
|
|
48
|
+
this.idx += 2;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Write a 4-byte little-endian integer (uint32_t) */
|
|
52
|
+
public putU32(value: Int, endian?: Endian): void {
|
|
53
|
+
if (value < 0 || value > 0xffffffff) {
|
|
54
|
+
throw new Error(`Cannot fit ${value} into a u32`);
|
|
55
|
+
}
|
|
56
|
+
this.ensureSize(4);
|
|
57
|
+
this.view.setUint32(this.idx, Number(value), endianToBool(endian));
|
|
58
|
+
this.idx += 4;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Write an 8-byte little-endian integer (uint64_t) */
|
|
62
|
+
public putU64(value: Int, endian?: Endian): void {
|
|
63
|
+
if (value < 0 || value > 0xffffffffffffffffn) {
|
|
64
|
+
throw new Error(`Cannot fit ${value} into a u64`);
|
|
65
|
+
}
|
|
66
|
+
this.ensureSize(8);
|
|
67
|
+
this.view.setBigUint64(this.idx, BigInt(value), endianToBool(endian));
|
|
68
|
+
this.idx += 8;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Write the given bytes */
|
|
72
|
+
public putBytes(bytes: Uint8Array): void {
|
|
73
|
+
this.ensureSize(bytes.length);
|
|
74
|
+
this.data.set(bytes, this.idx);
|
|
75
|
+
this.idx += bytes.length;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private ensureSize(extraBytes: number) {
|
|
79
|
+
if (this.data.length < this.idx + extraBytes) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Not enough bytes: Tried writing ${extraBytes} byte(s), but ` +
|
|
82
|
+
`only ${this.data.length - this.idx} byte(s) have been ` +
|
|
83
|
+
`pre-allocated`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|