dcr-ts 0.2.0 → 0.3.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/CHANGELOG.md +218 -0
- package/README.md +8 -3
- package/dist/index.cjs +121 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -10
- package/dist/index.d.ts +56 -10
- package/dist/index.js +121 -19
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -367,7 +367,16 @@ declare function pubKeyHashEd25519Address(hash: Uint8Array, network: Network): s
|
|
|
367
367
|
declare function pubKeyHashSchnorrAddress(hash: Uint8Array, network: Network): string;
|
|
368
368
|
/** Encode a compressed public key as a pay-to-pubkey (secp256k1 ECDSA) address. */
|
|
369
369
|
declare function pubKeyAddress(compressedPubKey: Uint8Array, network: Network): string;
|
|
370
|
-
/**
|
|
370
|
+
/**
|
|
371
|
+
* Encode a 32-byte Ed25519 public key as a pay-to-pubkey address.
|
|
372
|
+
*
|
|
373
|
+
* This is the one address kind where a decoded string is not a stable identity.
|
|
374
|
+
* Identifier bytes `0x01` and `0x81` name the same address — same key, same
|
|
375
|
+
* pkScript — and only the `0x01` form is emitted here, so `encode(decode(x))`
|
|
376
|
+
* does not always give back `x`. dcrd behaves identically; see
|
|
377
|
+
* {@link decodeAddress}. Compare the key bytes, or re-encode first, rather than
|
|
378
|
+
* comparing address strings.
|
|
379
|
+
*/
|
|
371
380
|
declare function pubKeyEd25519Address(pubKey: Uint8Array, network: Network): string;
|
|
372
381
|
/** Encode a compressed public key as a pay-to-pubkey (secp256k1 Schnorr) address. */
|
|
373
382
|
declare function pubKeySchnorrAddress(compressedPubKey: Uint8Array, network: Network): string;
|
|
@@ -392,6 +401,17 @@ declare function addressFromScript(redeemScript: Uint8Array, network: Network):
|
|
|
392
401
|
/**
|
|
393
402
|
* Decode and validate an address. When `network` is given the address must
|
|
394
403
|
* belong to it; otherwise the network is inferred from the version prefix.
|
|
404
|
+
*
|
|
405
|
+
* "Given" means a `Network`: omit the argument to infer, but anything else
|
|
406
|
+
* supplied for it — including a partial network missing its prefixes — is
|
|
407
|
+
* rejected with `invalid-argument` rather than treated as an omission. A network
|
|
408
|
+
* is matched by comparing its five address version prefixes rather than by object
|
|
409
|
+
* identity, so a second copy of this package, a `structuredClone` or a
|
|
410
|
+
* config-file round trip all work.
|
|
411
|
+
*
|
|
412
|
+
* One decoded string is not canonical: for the Ed25519 pay-to-pubkey kind the
|
|
413
|
+
* identifier bytes `0x01` and `0x81` decode to the same key and the same
|
|
414
|
+
* pkScript, and re-encoding always emits the `0x01` form. dcrd does the same.
|
|
395
415
|
*/
|
|
396
416
|
declare function decodeAddress(address: string, network?: Network): DecodedAddress;
|
|
397
417
|
/** True when `address` is a well-formed address (optionally for `network`). */
|
|
@@ -671,7 +691,21 @@ type Wordlist = readonly string[];
|
|
|
671
691
|
declare const englishWordlist: Wordlist;
|
|
672
692
|
/** Generate a new mnemonic. `strength` is entropy bits (128–256, default 128). */
|
|
673
693
|
declare function generateMnemonic(strength?: number, wordlist?: Wordlist): string;
|
|
674
|
-
/**
|
|
694
|
+
/**
|
|
695
|
+
* Validate a mnemonic's checksum and wordlist membership.
|
|
696
|
+
*
|
|
697
|
+
* Answers `false` for a non-string rather than throwing, which is what a
|
|
698
|
+
* predicate is for and what {@link mnemonicToMasterKey} already relies on. The
|
|
699
|
+
* check is written out here so that answer is a decision rather than an
|
|
700
|
+
* accident: `@scure` reaches the same `false` by catching its own `TypeError`
|
|
701
|
+
* out of `nfkd`, which would stop being true if it ever reordered those checks.
|
|
702
|
+
*
|
|
703
|
+
* The wordlist is the exception, and throws as it does at every other entry in
|
|
704
|
+
* this module. It is the caller's configuration rather than the subject being
|
|
705
|
+
* predicated, so answering `false` for a malformed one would report a bad phrase
|
|
706
|
+
* for what is a bad argument — the confusion `assertMnemonicString` exists to
|
|
707
|
+
* avoid on the other parameter.
|
|
708
|
+
*/
|
|
675
709
|
declare function validateMnemonic(mnemonic: string, wordlist?: Wordlist): boolean;
|
|
676
710
|
/** Recover the raw entropy behind a mnemonic. */
|
|
677
711
|
declare function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): Uint8Array;
|
|
@@ -686,14 +720,21 @@ declare function entropyToMnemonic(entropy: Uint8Array, wordlist?: Wordlist): st
|
|
|
686
720
|
* checksum verified first. The word count *is* enforced: BIP39 is defined only
|
|
687
721
|
* for 12, 15, 18, 21 or 24 words.
|
|
688
722
|
*
|
|
689
|
-
* Both failures are checked here rather than caught from `@scure`, and
|
|
690
|
-
* checks are exhaustive: `mnemonicToSeedSync` reaches
|
|
691
|
-
* through `nfkd`, which rejects a non-string, and `normalize`, which
|
|
692
|
-
* word count outside the five. Its
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
723
|
+
* Both mnemonic failures are checked here rather than caught from `@scure`, and
|
|
724
|
+
* the two checks are exhaustive for that argument: `mnemonicToSeedSync` reaches
|
|
725
|
+
* it only through `nfkd`, which rejects a non-string, and `normalize`, which
|
|
726
|
+
* rejects a word count outside the five. Its PBKDF2 parameters are constants, so
|
|
727
|
+
* nothing else in it can throw. Catching instead would report `invalid-mnemonic`
|
|
728
|
+
* for a future `@scure` failure that has nothing to do with the mnemonic, which
|
|
729
|
+
* is the one direction that wastes the most debugging time.
|
|
730
|
+
*
|
|
731
|
+
* The passphrase is checked here for the opposite reason: `@scure` never rejects
|
|
732
|
+
* it. Its salt is `"mnemonic" + passphrase`, a concatenation that stringifies
|
|
733
|
+
* whatever it is given, so `null` silently meant the passphrase `"null"` — and
|
|
734
|
+
* through {@link mnemonicToMasterKey}, a different wallet with nothing raised.
|
|
735
|
+
* `passphrase ?? null` is ordinary JavaScript, and a database column that is
|
|
736
|
+
* NULL for "no passphrase" is the ordinary way to arrive at it. Only literal
|
|
737
|
+
* `undefined` reaches the default.
|
|
697
738
|
*/
|
|
698
739
|
declare function mnemonicToSeed(mnemonic: string, passphrase?: string): Uint8Array;
|
|
699
740
|
/**
|
|
@@ -931,6 +972,11 @@ interface P2PKHInputToSign {
|
|
|
931
972
|
* 50 inputs and 26x at 500; end to end the gain is smaller (1.7x at 250) because
|
|
932
973
|
* ECDSA dominates.
|
|
933
974
|
*
|
|
975
|
+
* Every `idx` in `toSign` must be distinct. A repeat is rejected with
|
|
976
|
+
* `invalid-argument` before anything is written, because letting the later entry
|
|
977
|
+
* win silently dropped a signing the caller asked for; see the comment on the
|
|
978
|
+
* check itself.
|
|
979
|
+
*
|
|
934
980
|
* The prefix hash is taken **before** any signature script is assigned, which is
|
|
935
981
|
* also why this is correct: the prefix commits to no witness data, so writing
|
|
936
982
|
* signature scripts cannot invalidate it. For other hash types the cache is
|
package/dist/index.d.ts
CHANGED
|
@@ -367,7 +367,16 @@ declare function pubKeyHashEd25519Address(hash: Uint8Array, network: Network): s
|
|
|
367
367
|
declare function pubKeyHashSchnorrAddress(hash: Uint8Array, network: Network): string;
|
|
368
368
|
/** Encode a compressed public key as a pay-to-pubkey (secp256k1 ECDSA) address. */
|
|
369
369
|
declare function pubKeyAddress(compressedPubKey: Uint8Array, network: Network): string;
|
|
370
|
-
/**
|
|
370
|
+
/**
|
|
371
|
+
* Encode a 32-byte Ed25519 public key as a pay-to-pubkey address.
|
|
372
|
+
*
|
|
373
|
+
* This is the one address kind where a decoded string is not a stable identity.
|
|
374
|
+
* Identifier bytes `0x01` and `0x81` name the same address — same key, same
|
|
375
|
+
* pkScript — and only the `0x01` form is emitted here, so `encode(decode(x))`
|
|
376
|
+
* does not always give back `x`. dcrd behaves identically; see
|
|
377
|
+
* {@link decodeAddress}. Compare the key bytes, or re-encode first, rather than
|
|
378
|
+
* comparing address strings.
|
|
379
|
+
*/
|
|
371
380
|
declare function pubKeyEd25519Address(pubKey: Uint8Array, network: Network): string;
|
|
372
381
|
/** Encode a compressed public key as a pay-to-pubkey (secp256k1 Schnorr) address. */
|
|
373
382
|
declare function pubKeySchnorrAddress(compressedPubKey: Uint8Array, network: Network): string;
|
|
@@ -392,6 +401,17 @@ declare function addressFromScript(redeemScript: Uint8Array, network: Network):
|
|
|
392
401
|
/**
|
|
393
402
|
* Decode and validate an address. When `network` is given the address must
|
|
394
403
|
* belong to it; otherwise the network is inferred from the version prefix.
|
|
404
|
+
*
|
|
405
|
+
* "Given" means a `Network`: omit the argument to infer, but anything else
|
|
406
|
+
* supplied for it — including a partial network missing its prefixes — is
|
|
407
|
+
* rejected with `invalid-argument` rather than treated as an omission. A network
|
|
408
|
+
* is matched by comparing its five address version prefixes rather than by object
|
|
409
|
+
* identity, so a second copy of this package, a `structuredClone` or a
|
|
410
|
+
* config-file round trip all work.
|
|
411
|
+
*
|
|
412
|
+
* One decoded string is not canonical: for the Ed25519 pay-to-pubkey kind the
|
|
413
|
+
* identifier bytes `0x01` and `0x81` decode to the same key and the same
|
|
414
|
+
* pkScript, and re-encoding always emits the `0x01` form. dcrd does the same.
|
|
395
415
|
*/
|
|
396
416
|
declare function decodeAddress(address: string, network?: Network): DecodedAddress;
|
|
397
417
|
/** True when `address` is a well-formed address (optionally for `network`). */
|
|
@@ -671,7 +691,21 @@ type Wordlist = readonly string[];
|
|
|
671
691
|
declare const englishWordlist: Wordlist;
|
|
672
692
|
/** Generate a new mnemonic. `strength` is entropy bits (128–256, default 128). */
|
|
673
693
|
declare function generateMnemonic(strength?: number, wordlist?: Wordlist): string;
|
|
674
|
-
/**
|
|
694
|
+
/**
|
|
695
|
+
* Validate a mnemonic's checksum and wordlist membership.
|
|
696
|
+
*
|
|
697
|
+
* Answers `false` for a non-string rather than throwing, which is what a
|
|
698
|
+
* predicate is for and what {@link mnemonicToMasterKey} already relies on. The
|
|
699
|
+
* check is written out here so that answer is a decision rather than an
|
|
700
|
+
* accident: `@scure` reaches the same `false` by catching its own `TypeError`
|
|
701
|
+
* out of `nfkd`, which would stop being true if it ever reordered those checks.
|
|
702
|
+
*
|
|
703
|
+
* The wordlist is the exception, and throws as it does at every other entry in
|
|
704
|
+
* this module. It is the caller's configuration rather than the subject being
|
|
705
|
+
* predicated, so answering `false` for a malformed one would report a bad phrase
|
|
706
|
+
* for what is a bad argument — the confusion `assertMnemonicString` exists to
|
|
707
|
+
* avoid on the other parameter.
|
|
708
|
+
*/
|
|
675
709
|
declare function validateMnemonic(mnemonic: string, wordlist?: Wordlist): boolean;
|
|
676
710
|
/** Recover the raw entropy behind a mnemonic. */
|
|
677
711
|
declare function mnemonicToEntropy(mnemonic: string, wordlist?: Wordlist): Uint8Array;
|
|
@@ -686,14 +720,21 @@ declare function entropyToMnemonic(entropy: Uint8Array, wordlist?: Wordlist): st
|
|
|
686
720
|
* checksum verified first. The word count *is* enforced: BIP39 is defined only
|
|
687
721
|
* for 12, 15, 18, 21 or 24 words.
|
|
688
722
|
*
|
|
689
|
-
* Both failures are checked here rather than caught from `@scure`, and
|
|
690
|
-
* checks are exhaustive: `mnemonicToSeedSync` reaches
|
|
691
|
-
* through `nfkd`, which rejects a non-string, and `normalize`, which
|
|
692
|
-
* word count outside the five. Its
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
723
|
+
* Both mnemonic failures are checked here rather than caught from `@scure`, and
|
|
724
|
+
* the two checks are exhaustive for that argument: `mnemonicToSeedSync` reaches
|
|
725
|
+
* it only through `nfkd`, which rejects a non-string, and `normalize`, which
|
|
726
|
+
* rejects a word count outside the five. Its PBKDF2 parameters are constants, so
|
|
727
|
+
* nothing else in it can throw. Catching instead would report `invalid-mnemonic`
|
|
728
|
+
* for a future `@scure` failure that has nothing to do with the mnemonic, which
|
|
729
|
+
* is the one direction that wastes the most debugging time.
|
|
730
|
+
*
|
|
731
|
+
* The passphrase is checked here for the opposite reason: `@scure` never rejects
|
|
732
|
+
* it. Its salt is `"mnemonic" + passphrase`, a concatenation that stringifies
|
|
733
|
+
* whatever it is given, so `null` silently meant the passphrase `"null"` — and
|
|
734
|
+
* through {@link mnemonicToMasterKey}, a different wallet with nothing raised.
|
|
735
|
+
* `passphrase ?? null` is ordinary JavaScript, and a database column that is
|
|
736
|
+
* NULL for "no passphrase" is the ordinary way to arrive at it. Only literal
|
|
737
|
+
* `undefined` reaches the default.
|
|
697
738
|
*/
|
|
698
739
|
declare function mnemonicToSeed(mnemonic: string, passphrase?: string): Uint8Array;
|
|
699
740
|
/**
|
|
@@ -931,6 +972,11 @@ interface P2PKHInputToSign {
|
|
|
931
972
|
* 50 inputs and 26x at 500; end to end the gain is smaller (1.7x at 250) because
|
|
932
973
|
* ECDSA dominates.
|
|
933
974
|
*
|
|
975
|
+
* Every `idx` in `toSign` must be distinct. A repeat is rejected with
|
|
976
|
+
* `invalid-argument` before anything is written, because letting the later entry
|
|
977
|
+
* win silently dropped a signing the caller asked for; see the comment on the
|
|
978
|
+
* check itself.
|
|
979
|
+
*
|
|
934
980
|
* The prefix hash is taken **before** any signature script is assigned, which is
|
|
935
981
|
* also why this is correct: the prefix commits to no witness data, so writing
|
|
936
982
|
* signature scripts cannot invalidate it. For other hash types the cache is
|
package/dist/index.js
CHANGED
|
@@ -29,6 +29,9 @@ function err(code, who, message) {
|
|
|
29
29
|
|
|
30
30
|
// src/bytes.ts
|
|
31
31
|
function copyOf(src, off, n) {
|
|
32
|
+
if (!isBytes(src)) {
|
|
33
|
+
throw err("invalid-argument", "copyOf", `source must be a Uint8Array, got ${typeName(src)}`);
|
|
34
|
+
}
|
|
32
35
|
if (!Number.isInteger(off) || !Number.isInteger(n) || off < 0 || n < 0) {
|
|
33
36
|
throw err(
|
|
34
37
|
"not-an-integer",
|
|
@@ -116,6 +119,7 @@ var Writer = class {
|
|
|
116
119
|
// steps. Every input amount and output value in a transaction crosses one of
|
|
117
120
|
// these, and the loop version measured ~33x slower on the primitive.
|
|
118
121
|
u64(v) {
|
|
122
|
+
if (typeof v !== "bigint") throw err("invalid-argument", "Writer.u64", `value must be a bigint, got ${typeName(v)}`);
|
|
119
123
|
if (v < 0n || v > 0xffffffffffffffffn) throw err("out-of-range", "Writer.u64", "value must fit in an unsigned 64-bit integer");
|
|
120
124
|
this.ensure(8);
|
|
121
125
|
this.view.setBigUint64(this.len, v, true);
|
|
@@ -124,6 +128,7 @@ var Writer = class {
|
|
|
124
128
|
}
|
|
125
129
|
/** Signed 64-bit little-endian (two's complement). Used for atom amounts. */
|
|
126
130
|
i64(v) {
|
|
131
|
+
if (typeof v !== "bigint") throw err("invalid-argument", "Writer.i64", `value must be a bigint, got ${typeName(v)}`);
|
|
127
132
|
if (v < -(1n << 63n) || v >= 1n << 63n) throw err("out-of-range", "Writer.i64", "value must fit in a signed 64-bit integer");
|
|
128
133
|
this.ensure(8);
|
|
129
134
|
this.view.setBigInt64(this.len, v, true);
|
|
@@ -131,6 +136,7 @@ var Writer = class {
|
|
|
131
136
|
return this;
|
|
132
137
|
}
|
|
133
138
|
bytes(b) {
|
|
139
|
+
if (!isBytes(b)) throw err("invalid-argument", "Writer.bytes", `value must be a Uint8Array, got ${typeName(b)}`);
|
|
134
140
|
this.ensure(b.length);
|
|
135
141
|
this.buf.set(b, this.len);
|
|
136
142
|
this.len += b.length;
|
|
@@ -773,6 +779,9 @@ for (let i = 0; i < ALPHABET.length; i++) {
|
|
|
773
779
|
INDEX[ALPHABET.charCodeAt(i)] = i;
|
|
774
780
|
}
|
|
775
781
|
function base58Encode(bytes) {
|
|
782
|
+
if (!isBytes(bytes)) {
|
|
783
|
+
throw err("invalid-argument", "base58Encode", `input must be a Uint8Array, got ${typeName(bytes)}`);
|
|
784
|
+
}
|
|
776
785
|
let zeros = 0;
|
|
777
786
|
while (zeros < bytes.length && bytes[zeros] === 0) zeros++;
|
|
778
787
|
let num = 0n;
|
|
@@ -908,6 +917,7 @@ function publicKeyFromPrivate(privateKey, compressed = true) {
|
|
|
908
917
|
return secp256k1.getPublicKey(privateKey, compressed);
|
|
909
918
|
}
|
|
910
919
|
function isValidPublicKey(key) {
|
|
920
|
+
if (!isBytes(key)) return false;
|
|
911
921
|
try {
|
|
912
922
|
secp256k1.ProjectivePoint.fromHex(key);
|
|
913
923
|
return true;
|
|
@@ -1202,6 +1212,32 @@ for (const network of Object.values(networks)) {
|
|
|
1202
1212
|
{ network, kind: "pubkey-ecdsa", prefix: network.pubKeyAddrId }
|
|
1203
1213
|
);
|
|
1204
1214
|
}
|
|
1215
|
+
function eq2(a, b) {
|
|
1216
|
+
return a[0] === b[0] && a[1] === b[1];
|
|
1217
|
+
}
|
|
1218
|
+
var ADDRESS_PREFIX_FIELDS = [
|
|
1219
|
+
"pubKeyAddrId",
|
|
1220
|
+
"pubKeyHashAddrId",
|
|
1221
|
+
"pubKeyHashEdwardsAddrId",
|
|
1222
|
+
"pubKeyHashSchnorrAddrId",
|
|
1223
|
+
"scriptHashAddrId"
|
|
1224
|
+
];
|
|
1225
|
+
function assertNetwork(network, who) {
|
|
1226
|
+
const shaped = typeof network === "object" && network !== null && typeof network.name === "string" && ADDRESS_PREFIX_FIELDS.every((f) => {
|
|
1227
|
+
const p = network[f];
|
|
1228
|
+
return Array.isArray(p) && p.length === 2 && typeof p[0] === "number" && typeof p[1] === "number";
|
|
1229
|
+
});
|
|
1230
|
+
if (!shaped) {
|
|
1231
|
+
throw err(
|
|
1232
|
+
"invalid-argument",
|
|
1233
|
+
who,
|
|
1234
|
+
`network must be a Network with its address prefixes, got ${typeName(network)} (pass mainnet, testnet3, simnet or regnet)`
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
function sameNetwork(a, b) {
|
|
1239
|
+
return a === b || eq2(a.pubKeyAddrId, b.pubKeyAddrId) && eq2(a.pubKeyHashAddrId, b.pubKeyHashAddrId) && eq2(a.pubKeyHashEdwardsAddrId, b.pubKeyHashEdwardsAddrId) && eq2(a.pubKeyHashSchnorrAddrId, b.pubKeyHashSchnorrAddrId) && eq2(a.scriptHashAddrId, b.scriptHashAddrId);
|
|
1240
|
+
}
|
|
1205
1241
|
function encode(prefix, payload) {
|
|
1206
1242
|
const data = new Uint8Array(2 + payload.length);
|
|
1207
1243
|
data[0] = prefix[0];
|
|
@@ -1209,19 +1245,28 @@ function encode(prefix, payload) {
|
|
|
1209
1245
|
data.set(payload, 2);
|
|
1210
1246
|
return checkEncode(data);
|
|
1211
1247
|
}
|
|
1248
|
+
function assertHash20(hash, who) {
|
|
1249
|
+
if (!isBytes(hash)) {
|
|
1250
|
+
throw err("invalid-argument", who, `hash must be a Uint8Array, got ${typeName(hash)}`);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1212
1253
|
function pubKeyHashAddress(hash, network) {
|
|
1254
|
+
assertHash20(hash, "pubKeyHashAddress");
|
|
1213
1255
|
if (hash.length !== 20) throw err("bad-length", "pubKeyHashAddress", `hash must be 20 bytes, got ${hash.length}`);
|
|
1214
1256
|
return encode(network.pubKeyHashAddrId, hash);
|
|
1215
1257
|
}
|
|
1216
1258
|
function scriptHashAddress(hash, network) {
|
|
1259
|
+
assertHash20(hash, "scriptHashAddress");
|
|
1217
1260
|
if (hash.length !== 20) throw err("bad-length", "scriptHashAddress", `hash must be 20 bytes, got ${hash.length}`);
|
|
1218
1261
|
return encode(network.scriptHashAddrId, hash);
|
|
1219
1262
|
}
|
|
1220
1263
|
function pubKeyHashEd25519Address(hash, network) {
|
|
1264
|
+
assertHash20(hash, "pubKeyHashEd25519Address");
|
|
1221
1265
|
if (hash.length !== 20) throw err("bad-length", "pubKeyHashEd25519Address", `hash must be 20 bytes, got ${hash.length}`);
|
|
1222
1266
|
return encode(network.pubKeyHashEdwardsAddrId, hash);
|
|
1223
1267
|
}
|
|
1224
1268
|
function pubKeyHashSchnorrAddress(hash, network) {
|
|
1269
|
+
assertHash20(hash, "pubKeyHashSchnorrAddress");
|
|
1225
1270
|
if (hash.length !== 20) throw err("bad-length", "pubKeyHashSchnorrAddress", `hash must be 20 bytes, got ${hash.length}`);
|
|
1226
1271
|
return encode(network.pubKeyHashSchnorrAddrId, hash);
|
|
1227
1272
|
}
|
|
@@ -1291,6 +1336,7 @@ function decodeAddress(address, network) {
|
|
|
1291
1336
|
if (typeof address !== "string") {
|
|
1292
1337
|
throw err("invalid-argument", "decodeAddress", `address must be a string, got ${typeName(address)}`);
|
|
1293
1338
|
}
|
|
1339
|
+
if (network !== void 0) assertNetwork(network, "decodeAddress");
|
|
1294
1340
|
if (address.length > MAX_ADDRESS_LENGTH) {
|
|
1295
1341
|
throw err(
|
|
1296
1342
|
"input-too-long",
|
|
@@ -1302,21 +1348,18 @@ function decodeAddress(address, network) {
|
|
|
1302
1348
|
if (data.length < 3) throw err("bad-length", "decodeAddress", `payload is ${data.length} bytes, too short to hold a prefix and data`);
|
|
1303
1349
|
const prefix = [data[0], data[1]];
|
|
1304
1350
|
const payload = data.subarray(2);
|
|
1305
|
-
const match = PREFIXES.find(
|
|
1306
|
-
(e) => e.prefix[0] === prefix[0] && e.prefix[1] === prefix[1] && (!network || e.network === network)
|
|
1307
|
-
);
|
|
1351
|
+
const match = PREFIXES.find((e) => e.prefix[0] === prefix[0] && e.prefix[1] === prefix[1]);
|
|
1308
1352
|
if (!match) {
|
|
1309
1353
|
const hex = `0x${prefix[0].toString(16).padStart(2, "0")}${prefix[1].toString(16).padStart(2, "0")}`;
|
|
1310
|
-
const onAnotherNetwork = network && PREFIXES.find((e) => e.prefix[0] === prefix[0] && e.prefix[1] === prefix[1]);
|
|
1311
|
-
if (onAnotherNetwork) {
|
|
1312
|
-
throw err(
|
|
1313
|
-
"wrong-network",
|
|
1314
|
-
"decodeAddress",
|
|
1315
|
-
`address is a ${onAnotherNetwork.kind} address for ${onAnotherNetwork.network.name}, not ${network.name}`
|
|
1316
|
-
);
|
|
1317
|
-
}
|
|
1318
1354
|
throw err("unknown-prefix", "decodeAddress", `unknown address prefix ${hex}`);
|
|
1319
1355
|
}
|
|
1356
|
+
if (network && !sameNetwork(match.network, network)) {
|
|
1357
|
+
throw err(
|
|
1358
|
+
"wrong-network",
|
|
1359
|
+
"decodeAddress",
|
|
1360
|
+
`address is a ${match.kind} address for ${match.network.name}, not ${network.name}`
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1320
1363
|
if (match.kind === "pubkey-ecdsa") {
|
|
1321
1364
|
if (payload.length !== 33) throw err("bad-length", "decodeAddress", `pay-to-pubkey payload must be 33 bytes, got ${payload.length}`);
|
|
1322
1365
|
const { kind, pubKey } = decodePubKeyData(payload);
|
|
@@ -1339,13 +1382,7 @@ function isValidAddress(address, network) {
|
|
|
1339
1382
|
}
|
|
1340
1383
|
}
|
|
1341
1384
|
function addressToScript(address, network) {
|
|
1342
|
-
|
|
1343
|
-
throw err(
|
|
1344
|
-
"invalid-argument",
|
|
1345
|
-
"addressToScript",
|
|
1346
|
-
"a network is required (pass mainnet, testnet3, simnet or regnet)"
|
|
1347
|
-
);
|
|
1348
|
-
}
|
|
1385
|
+
assertNetwork(network, "addressToScript");
|
|
1349
1386
|
const d = decodeAddress(address, network);
|
|
1350
1387
|
switch (d.kind) {
|
|
1351
1388
|
case "pubkeyhash-ecdsa":
|
|
@@ -1388,7 +1425,7 @@ function encodeWif(privateKey, network, signatureType = 0 /* Ecdsa */) {
|
|
|
1388
1425
|
if (!isBytes(privateKey)) throw err("invalid-argument", "encodeWif", "private key must be a Uint8Array");
|
|
1389
1426
|
if (privateKey.length !== 32) throw err("bad-length", "encodeWif", `private key must be 32 bytes, got ${privateKey.length}`);
|
|
1390
1427
|
if (!Number.isInteger(signatureType) || SignatureType[signatureType] === void 0) {
|
|
1391
|
-
throw err("unsupported-signature-type", "encodeWif", `unknown signature type ${signatureType}`);
|
|
1428
|
+
throw err("unsupported-signature-type", "encodeWif", `unknown signature type ${shown(signatureType)}`);
|
|
1392
1429
|
}
|
|
1393
1430
|
assertWifScalar(privateKey, signatureType, "encodeWif");
|
|
1394
1431
|
const payload = new Uint8Array(3 + 32);
|
|
@@ -1488,6 +1525,9 @@ var ExtendedKey = class _ExtendedKey {
|
|
|
1488
1525
|
cachedPoint = void 0;
|
|
1489
1526
|
/** Derive a master key from a BIP32 seed (16–64 bytes). */
|
|
1490
1527
|
static fromSeed(seed, network) {
|
|
1528
|
+
if (!isBytes(seed)) {
|
|
1529
|
+
throw err("invalid-argument", "ExtendedKey.fromSeed", `seed must be a Uint8Array, got ${typeName(seed)}`);
|
|
1530
|
+
}
|
|
1491
1531
|
if (seed.length < 16 || seed.length > 64) {
|
|
1492
1532
|
throw err("out-of-range", "ExtendedKey.fromSeed", `seed must be 16..64 bytes, got ${seed.length}`);
|
|
1493
1533
|
}
|
|
@@ -1648,6 +1688,9 @@ var ExtendedKey = class _ExtendedKey {
|
|
|
1648
1688
|
return this.derivePathInner(path, true);
|
|
1649
1689
|
}
|
|
1650
1690
|
derivePathInner(path, strictBip32) {
|
|
1691
|
+
if (typeof path !== "string") {
|
|
1692
|
+
throw err("invalid-path", "ExtendedKey.derivePath", `path must be a string, got ${typeName(path)}`);
|
|
1693
|
+
}
|
|
1651
1694
|
const parts = path.trim().split("/");
|
|
1652
1695
|
if (parts[0] === "m" || parts[0] === "M") parts.shift();
|
|
1653
1696
|
let key = this;
|
|
@@ -1736,6 +1779,9 @@ var ExtendedKey = class _ExtendedKey {
|
|
|
1736
1779
|
* key it just parsed. See {@link copyOf}.
|
|
1737
1780
|
*/
|
|
1738
1781
|
static fromSerialized(data) {
|
|
1782
|
+
if (!isBytes(data)) {
|
|
1783
|
+
throw err("invalid-argument", "ExtendedKey.fromSerialized", `serialization must be a Uint8Array, got ${typeName(data)}`);
|
|
1784
|
+
}
|
|
1739
1785
|
if (data.length !== SERIALIZED_LENGTH) throw err("bad-length", "ExtendedKey.fromSerialized", `expected ${SERIALIZED_LENGTH} bytes, got ${data.length}`);
|
|
1740
1786
|
const version = [data[0], data[1], data[2], data[3]];
|
|
1741
1787
|
const depth = data[4];
|
|
@@ -1796,7 +1842,15 @@ function assertMnemonicString(mnemonic, who) {
|
|
|
1796
1842
|
throw err("invalid-argument", who, `mnemonic must be a string, got ${typeof mnemonic}`);
|
|
1797
1843
|
}
|
|
1798
1844
|
}
|
|
1845
|
+
function assertPassphrase(passphrase, who) {
|
|
1846
|
+
if (typeof passphrase !== "string") {
|
|
1847
|
+
throw err("invalid-argument", who, `passphrase must be a string, got ${typeName(passphrase)}`);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1799
1850
|
function assertWordlist(wordlist, who) {
|
|
1851
|
+
if (!Array.isArray(wordlist)) {
|
|
1852
|
+
throw err("invalid-argument", who, `wordlist must be an array of 2048 words, got ${typeName(wordlist)}`);
|
|
1853
|
+
}
|
|
1800
1854
|
if (wordlist.length !== 2048) {
|
|
1801
1855
|
throw err("invalid-argument", who, `wordlist must hold 2048 words, got ${wordlist.length}`);
|
|
1802
1856
|
}
|
|
@@ -1813,6 +1867,8 @@ function generateMnemonic(strength = 128, wordlist$1 = wordlist) {
|
|
|
1813
1867
|
return generateMnemonic$1(wordlist$1, strength);
|
|
1814
1868
|
}
|
|
1815
1869
|
function validateMnemonic(mnemonic, wordlist$1 = wordlist) {
|
|
1870
|
+
assertWordlist(wordlist$1, "validateMnemonic");
|
|
1871
|
+
if (typeof mnemonic !== "string") return false;
|
|
1816
1872
|
return validateMnemonic$1(mnemonic, wordlist$1);
|
|
1817
1873
|
}
|
|
1818
1874
|
function mnemonicToEntropy(mnemonic, wordlist$1 = wordlist) {
|
|
@@ -1841,6 +1897,7 @@ function entropyToMnemonic(entropy, wordlist$1 = wordlist) {
|
|
|
1841
1897
|
}
|
|
1842
1898
|
function mnemonicToSeed(mnemonic, passphrase = "") {
|
|
1843
1899
|
assertMnemonicString(mnemonic, "mnemonicToSeed");
|
|
1900
|
+
assertPassphrase(passphrase, "mnemonicToSeed");
|
|
1844
1901
|
const words = mnemonic.normalize("NFKD").split(" ").length;
|
|
1845
1902
|
if (!MNEMONIC_WORD_COUNTS.includes(words)) {
|
|
1846
1903
|
throw err(
|
|
@@ -1853,6 +1910,7 @@ function mnemonicToSeed(mnemonic, passphrase = "") {
|
|
|
1853
1910
|
}
|
|
1854
1911
|
function mnemonicToMasterKey(mnemonic, network, passphrase = "", wordlist$1 = wordlist) {
|
|
1855
1912
|
assertMnemonicString(mnemonic, "mnemonicToMasterKey");
|
|
1913
|
+
assertPassphrase(passphrase, "mnemonicToMasterKey");
|
|
1856
1914
|
assertWordlist(wordlist$1, "mnemonicToMasterKey");
|
|
1857
1915
|
if (!validateMnemonic(mnemonic, wordlist$1)) {
|
|
1858
1916
|
throw err(
|
|
@@ -1952,6 +2010,20 @@ var Transaction = class _Transaction {
|
|
|
1952
2010
|
`outpoint tree must be ${0 /* Regular */} (regular) or ${1 /* Stake */} (stake), got ${shown(previousOutPoint.tree)}`
|
|
1953
2011
|
);
|
|
1954
2012
|
}
|
|
2013
|
+
if (opts.valueIn != null && typeof opts.valueIn !== "bigint") {
|
|
2014
|
+
throw err(
|
|
2015
|
+
"invalid-argument",
|
|
2016
|
+
"tx.addInput",
|
|
2017
|
+
`valueIn must be a bigint, got ${typeName(opts.valueIn)}`
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
2020
|
+
if (opts.signatureScript && !isBytes(opts.signatureScript)) {
|
|
2021
|
+
throw err(
|
|
2022
|
+
"invalid-argument",
|
|
2023
|
+
"tx.addInput",
|
|
2024
|
+
`signatureScript must be a Uint8Array, got ${typeName(opts.signatureScript)}`
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
1955
2027
|
this.inputs.push({
|
|
1956
2028
|
previousOutPoint: {
|
|
1957
2029
|
hash: copyOf(previousOutPoint.hash, 0, 32),
|
|
@@ -1968,6 +2040,13 @@ var Transaction = class _Transaction {
|
|
|
1968
2040
|
}
|
|
1969
2041
|
/** Add an output. The script is copied; see {@link addInput}. */
|
|
1970
2042
|
addOutput(value, pkScript, version = 0) {
|
|
2043
|
+
if (typeof value !== "bigint") {
|
|
2044
|
+
throw err(
|
|
2045
|
+
"invalid-argument",
|
|
2046
|
+
"tx.addOutput",
|
|
2047
|
+
`value must be a bigint, got ${typeName(value)}`
|
|
2048
|
+
);
|
|
2049
|
+
}
|
|
1971
2050
|
if (!isBytes(pkScript)) {
|
|
1972
2051
|
throw err(
|
|
1973
2052
|
"invalid-argument",
|
|
@@ -2178,6 +2257,9 @@ function calcSignatureHash(subScript, hashType, tx, idx, cachedPrefix) {
|
|
|
2178
2257
|
const prefixIsInputIndependent = masked === 1 /* All */ && !anyoneCanPay;
|
|
2179
2258
|
let prefixHash;
|
|
2180
2259
|
if (cachedPrefix !== void 0 && prefixIsInputIndependent) {
|
|
2260
|
+
if (!isBytes(cachedPrefix)) {
|
|
2261
|
+
throw err("invalid-argument", "sighash", `cachedPrefix must be a Uint8Array, got ${typeName(cachedPrefix)}`);
|
|
2262
|
+
}
|
|
2181
2263
|
if (cachedPrefix.length !== 32) {
|
|
2182
2264
|
throw err("bad-length", "sighash", `cachedPrefix must be 32 bytes, got ${cachedPrefix.length}`);
|
|
2183
2265
|
}
|
|
@@ -2239,6 +2321,9 @@ function signHash(hash, privateKey) {
|
|
|
2239
2321
|
}
|
|
2240
2322
|
function verifyHash(hash, derSignature, publicKey) {
|
|
2241
2323
|
assertHash32(hash, "verifyHash");
|
|
2324
|
+
if (!isBytes(publicKey)) {
|
|
2325
|
+
throw err("invalid-argument", "verifyHash", `public key must be a Uint8Array, got ${typeName(publicKey)}`);
|
|
2326
|
+
}
|
|
2242
2327
|
try {
|
|
2243
2328
|
const sig = secp256k1.Signature.fromDER(derSignature);
|
|
2244
2329
|
const canonical = sig.toDERRawBytes();
|
|
@@ -2284,6 +2369,17 @@ function signP2PKHInput(tx, idx, subScript, privateKey, hashType = 1 /* All */,
|
|
|
2284
2369
|
}
|
|
2285
2370
|
function signP2PKHInputs(tx, toSign, hashType = 1 /* All */) {
|
|
2286
2371
|
assertSignableSigHashType(hashType);
|
|
2372
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2373
|
+
for (const { idx } of toSign) {
|
|
2374
|
+
if (seen.has(idx)) {
|
|
2375
|
+
throw err(
|
|
2376
|
+
"invalid-argument",
|
|
2377
|
+
"signP2PKHInputs",
|
|
2378
|
+
`input index ${idx} is listed more than once`
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2381
|
+
seen.add(idx);
|
|
2382
|
+
}
|
|
2287
2383
|
const cachedPrefix = sigHashPrefixAll(tx);
|
|
2288
2384
|
for (const { idx, subScript, privateKey, compressed = true } of toSign) {
|
|
2289
2385
|
tx.inputs[idx].signatureScript = signatureScript(
|
|
@@ -2303,6 +2399,9 @@ function signP2PKHInputs(tx, toSign, hashType = 1 /* All */) {
|
|
|
2303
2399
|
var ATOMS_PER_COIN = 100000000n;
|
|
2304
2400
|
var COIN_DECIMALS = 8;
|
|
2305
2401
|
function dcrToAtoms(dcr) {
|
|
2402
|
+
if (typeof dcr !== "string") {
|
|
2403
|
+
throw err("invalid-amount", "dcrToAtoms", `amount must be a string, got ${typeName(dcr)}`);
|
|
2404
|
+
}
|
|
2306
2405
|
const s = dcr.trim();
|
|
2307
2406
|
if (!/^-?\d+(\.\d+)?$/.test(s)) throw err("invalid-amount", "dcrToAtoms", `cannot parse ${shown(dcr)}`);
|
|
2308
2407
|
const negative = s.startsWith("-");
|
|
@@ -2316,6 +2415,9 @@ function dcrToAtoms(dcr) {
|
|
|
2316
2415
|
return negative ? -atoms : atoms;
|
|
2317
2416
|
}
|
|
2318
2417
|
function atomsToDcr(atoms) {
|
|
2418
|
+
if (typeof atoms !== "bigint") {
|
|
2419
|
+
throw err("invalid-amount", "atomsToDcr", `atoms must be a bigint, got ${typeName(atoms)}`);
|
|
2420
|
+
}
|
|
2319
2421
|
const negative = atoms < 0n;
|
|
2320
2422
|
const a = negative ? -atoms : atoms;
|
|
2321
2423
|
const intPart = a / ATOMS_PER_COIN;
|