@notabene/verify-proof 1.12.0-next.4 → 1.12.0-next.7

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.
@@ -8,6 +8,79 @@ import { secp256k1 } from '@noble/curves/secp256k1';
8
8
  import { Verifier } from 'bip322-js';
9
9
 
10
10
  if (typeof globalThis !== 'undefined' && !globalThis.Buffer) { globalThis.Buffer = Buffer; }
11
+ var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
12
+ var GENERATOR = [
13
+ BigInt("0x98f2bc8e61"),
14
+ BigInt("0x79b76d99e2"),
15
+ BigInt("0xf33e5fb3c4"),
16
+ BigInt("0xae2eabe2a8"),
17
+ BigInt("0x1e4f43e470")
18
+ ];
19
+ function polymod(values) {
20
+ let c = BigInt(1);
21
+ for (const d of values) {
22
+ const c0 = c >> BigInt(35);
23
+ c = (c & BigInt("0x07ffffffff")) << BigInt(5) ^ BigInt(d);
24
+ for (let i = 0; i < 5; i++) {
25
+ if (c0 >> BigInt(i) & BigInt(1)) {
26
+ c ^= GENERATOR[i];
27
+ }
28
+ }
29
+ }
30
+ return c ^ BigInt(1);
31
+ }
32
+ function prefixExpand(prefix) {
33
+ const result = [];
34
+ for (let i = 0; i < prefix.length; i++) {
35
+ result.push(prefix.charCodeAt(i) & 31);
36
+ }
37
+ result.push(0);
38
+ return result;
39
+ }
40
+ function decodeCashAddr(address, defaultPrefix = "bitcoincash") {
41
+ let prefix;
42
+ const colonIndex = address.indexOf(":");
43
+ if (colonIndex !== -1) {
44
+ prefix = address.slice(0, colonIndex).toLowerCase();
45
+ } else {
46
+ prefix = defaultPrefix;
47
+ }
48
+ const rawPayload = colonIndex !== -1 ? address.slice(colonIndex + 1) : address;
49
+ if (/[A-Z]/.test(rawPayload) && /[a-z]/.test(rawPayload)) {
50
+ throw new Error("Invalid CashAddr: mixed case");
51
+ }
52
+ const payload = rawPayload.toLowerCase();
53
+ const words = [];
54
+ for (const ch of payload) {
55
+ const idx = CHARSET.indexOf(ch);
56
+ if (idx === -1) throw new Error("Invalid CashAddr character: " + ch);
57
+ words.push(idx);
58
+ }
59
+ if (words.length < 8) throw new Error("CashAddr too short");
60
+ const checksumData = [...prefixExpand(prefix), ...words];
61
+ if (polymod(checksumData) !== BigInt(0)) {
62
+ throw new Error("Invalid CashAddr checksum");
63
+ }
64
+ const dataWords = words.slice(0, -8);
65
+ const payloadBytes = bech32.fromWords(dataWords);
66
+ if (payloadBytes.length === 0) {
67
+ throw new Error("Invalid CashAddr padding");
68
+ }
69
+ const versionByte = payloadBytes[0];
70
+ const type = versionByte >> 3 & 15;
71
+ const hashSizeBits = versionByte & 7;
72
+ const expectedSizes = [20, 24, 28, 32, 40, 48, 56, 64];
73
+ const expectedSize = expectedSizes[hashSizeBits];
74
+ const hashBytes = payloadBytes.slice(1);
75
+ if (hashBytes.length !== expectedSize) {
76
+ throw new Error(
77
+ `Hash length mismatch: expected ${expectedSize}, got ${hashBytes.length}`
78
+ );
79
+ }
80
+ return { prefix, type, hash: Uint8Array.from(hashBytes) };
81
+ }
82
+
83
+ // src/bitcoin.ts
11
84
  var CHAIN_CONFIGS = {
12
85
  bitcoin: {
13
86
  messagePrefix: "Bitcoin Signed Message:\n",
@@ -70,7 +143,9 @@ var CHAIN_CONFIGS = {
70
143
  }
71
144
  };
72
145
  async function verifyBTCSignature(proof) {
73
- const [ns, , address] = proof.address.split(/:/);
146
+ const parts = proof.address.split(":");
147
+ const ns = parts[0];
148
+ const address = parts.slice(2).join(":");
74
149
  if (ns !== "bip122") return { ...proof, status: ProofStatus.FAILED };
75
150
  const chainConfig = getChainConfig(address);
76
151
  if (!chainConfig) return { ...proof, status: ProofStatus.FAILED };
@@ -79,7 +154,11 @@ async function verifyBTCSignature(proof) {
79
154
  return verifyBIP137(address, proof, chainConfig);
80
155
  }
81
156
  if (chainConfig.isTestnet) {
82
- return verifyBIP322(address, proof);
157
+ try {
158
+ return verifyBIP322(address, proof);
159
+ } catch {
160
+ return { ...proof, status: ProofStatus.FAILED };
161
+ }
83
162
  }
84
163
  const isTaproot = address.startsWith("bc1p") || address.startsWith("tb1p");
85
164
  if (isTaproot && proof.type === ProofTypes.BIP137) {
@@ -120,10 +199,10 @@ function getChainConfig(address) {
120
199
  if (address.startsWith("X") || address.startsWith("7")) {
121
200
  return CHAIN_CONFIGS["dash"];
122
201
  }
123
- if (address.startsWith("q")) {
202
+ if (address.startsWith("q") || address.startsWith("bitcoincash:")) {
124
203
  return CHAIN_CONFIGS["bitcoincash"];
125
204
  }
126
- if (address.startsWith("tb1")) {
205
+ if (address.startsWith("m") || address.startsWith("n") || address.startsWith("2") || address.startsWith("tb1")) {
127
206
  return CHAIN_CONFIGS["testnet"];
128
207
  }
129
208
  return CHAIN_CONFIGS["bitcoin"];
@@ -166,7 +245,7 @@ function getDerivationMode(address) {
166
245
  return "Legacy" /* LEGACY */;
167
246
  } else if (address.match("^(D).*")) {
168
247
  return "Dogecoin" /* DOGECOIN */;
169
- } else if (address.match("^(q).*")) {
248
+ } else if (address.match("^(q).*") || address.startsWith("bitcoincash:")) {
170
249
  return "Bitcoin Cash" /* BCH */;
171
250
  } else if (address.match("^(t1|t3).*")) {
172
251
  return "Legacy" /* LEGACY */;
@@ -205,19 +284,30 @@ function verify(attestation, address, proof, checkSegwitAlways, chainConfig) {
205
284
  const publicKey = signature.recoverPublicKey(hash);
206
285
  const publicKeyBytes = publicKey.toRawBytes(compressed);
207
286
  const publicKeyHash = hash160(publicKeyBytes);
208
- let actual = "";
209
- if (address.startsWith("q")) {
210
- actual = encodeBase58AddressFormat(
211
- chainConfig.pubKeyHashVersion,
212
- publicKeyHash
213
- );
214
- return actual.startsWith("1");
287
+ if (address.startsWith("q") || address.startsWith("bitcoincash:")) {
288
+ try {
289
+ const decoded = decodeCashAddr(address);
290
+ if (decoded.type !== 0) return false;
291
+ if (decoded.hash.length !== publicKeyHash.length) return false;
292
+ for (let i = 0; i < decoded.hash.length; i++) {
293
+ if (decoded.hash[i] !== publicKeyHash[i]) return false;
294
+ }
295
+ return true;
296
+ } catch {
297
+ return false;
298
+ }
215
299
  }
300
+ let actual = "";
216
301
  if (segwitType) {
217
302
  if (segwitType === "p2sh(p2wpkh)" /* P2SH_P2WPKH */) {
303
+ const redeemScript = new Uint8Array(22);
304
+ redeemScript[0] = 0;
305
+ redeemScript[1] = 20;
306
+ redeemScript.set(publicKeyHash, 2);
307
+ const redeemScriptHash = hash160(redeemScript);
218
308
  actual = encodeBase58AddressFormat(
219
309
  chainConfig.scriptHashVersion,
220
- publicKeyHash
310
+ redeemScriptHash
221
311
  );
222
312
  } else {
223
313
  if (chainConfig.bech32Prefix) {
@@ -308,5 +398,5 @@ function hash160(buffer) {
308
398
  }
309
399
 
310
400
  export { verifyBTCSignature };
311
- //# sourceMappingURL=bitcoin-QK53ILBF.js.map
312
- //# sourceMappingURL=bitcoin-QK53ILBF.js.map
401
+ //# sourceMappingURL=bitcoin-ISUCDKFR.js.map
402
+ //# sourceMappingURL=bitcoin-ISUCDKFR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cashaddr.ts","../src/bitcoin.ts"],"names":["encodeLength","bech32"],"mappings":"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","file":"bitcoin-ISUCDKFR.js","sourcesContent":["import { bech32 } from \"@scure/base\";\n\n/**\n * CashAddr base32 character set.\n * Each character maps to its 5-bit index value (0–31).\n * Spec: https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md\n */\nconst CHARSET = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\n\n/**\n * Generator polynomials for the CashAddr BCH checksum.\n * These five constants define the GF(2^32) polynomial used to compute\n * and validate the 40-bit checksum appended to every CashAddr string.\n */\nconst GENERATOR = [\n BigInt(\"0x98f2bc8e61\"),\n BigInt(\"0x79b76d99e2\"),\n BigInt(\"0xf33e5fb3c4\"),\n BigInt(\"0xae2eabe2a8\"),\n BigInt(\"0x1e4f43e470\"),\n];\n\n/**\n * Computes the CashAddr BCH checksum over an array of 5-bit values.\n *\n * The input should be the concatenation of:\n * - the prefix expansion (see {@link prefixExpand})\n * - all payload words including the 8 checksum words\n *\n * A valid address produces a result of `0n`; any other value indicates\n * a corrupt or invalid address.\n *\n * @param values - Array of 5-bit integers covering prefix + full payload\n * @returns The 40-bit checksum residue as a bigint (0n = valid)\n */\nfunction polymod(values: number[]): bigint {\n let c = BigInt(1);\n for (const d of values) {\n // Extract the top 5 bits before shifting\n const c0 = c >> BigInt(35);\n // Shift the accumulator left by 5 bits and XOR in the next data word\n c = ((c & BigInt(\"0x07ffffffff\")) << BigInt(5)) ^ BigInt(d);\n // Apply each generator polynomial for set bits in c0\n for (let i = 0; i < 5; i++) {\n if ((c0 >> BigInt(i)) & BigInt(1)) {\n c ^= GENERATOR[i];\n }\n }\n }\n // Final XOR with 1 so a valid address yields 0\n return c ^ BigInt(1);\n}\n\n/**\n * Expands a CashAddr prefix into the form required by the checksum algorithm.\n *\n * Each character of the prefix is reduced to its lower 5 bits, then a zero\n * byte is appended as a separator between the prefix and the payload words.\n *\n * @param prefix - The human-readable prefix (e.g. `\"bitcoincash\"`)\n * @returns Array of 5-bit integers representing the expanded prefix\n */\nfunction prefixExpand(prefix: string): number[] {\n const result: number[] = [];\n for (let i = 0; i < prefix.length; i++) {\n // Keep only the lower 5 bits of each character's ASCII code\n result.push(prefix.charCodeAt(i) & 0x1f);\n }\n // Zero separator between prefix and payload\n result.push(0);\n return result;\n}\n\n/**\n * Decodes a CashAddr string into its prefix, address type, and raw hash.\n *\n * Supports both prefixed (`bitcoincash:q...`) and prefix-less (`q...`) forms.\n * Validates the BCH checksum and rejects non-P2PKH types or malformed input.\n *\n * Address type values (upper nibble of the version byte):\n * - `0` — P2PKH (pay-to-public-key-hash)\n * - `1` — P2SH (pay-to-script-hash)\n *\n * @param address - The CashAddr string to decode\n * @param defaultPrefix - Prefix to assume when none is present in the address (default: `\"bitcoincash\"`)\n * @returns An object with:\n * - `prefix` — the decoded human-readable prefix\n * - `type` — the address type (0 = P2PKH, 1 = P2SH, …)\n * - `hash` — the raw public-key or script hash bytes\n * @throws If the address contains invalid characters, fails the checksum, has a mismatched hash length,\n * contains mixed upper/lower case, or if the payload contains invalid 5-to-8-bit padding\n * (thrown by {@link bech32.fromWords} from `@scure/base`)\n */\nexport function decodeCashAddr(\n address: string,\n defaultPrefix = \"bitcoincash\"\n): { prefix: string; type: number; hash: Uint8Array } {\n let prefix: string;\n\n // Split on \":\" to separate an explicit prefix from the payload\n const colonIndex = address.indexOf(\":\");\n if (colonIndex !== -1) {\n prefix = address.slice(0, colonIndex).toLowerCase();\n } else {\n prefix = defaultPrefix;\n }\n\n // CashAddr spec forbids mixed upper/lower case\n const rawPayload = colonIndex !== -1 ? address.slice(colonIndex + 1) : address;\n if (/[A-Z]/.test(rawPayload) && /[a-z]/.test(rawPayload)) {\n throw new Error(\"Invalid CashAddr: mixed case\");\n }\n const payload = rawPayload.toLowerCase();\n\n // Map each character to its 5-bit index in the CashAddr alphabet\n const words: number[] = [];\n for (const ch of payload) {\n const idx = CHARSET.indexOf(ch);\n if (idx === -1) throw new Error(\"Invalid CashAddr character: \" + ch);\n words.push(idx);\n }\n\n // Minimum length: at least 1 data word + 8 checksum words\n if (words.length < 8) throw new Error(\"CashAddr too short\");\n\n // Verify the BCH checksum over prefix expansion + all payload words\n const checksumData = [...prefixExpand(prefix), ...words];\n if (polymod(checksumData) !== BigInt(0)) {\n throw new Error(\"Invalid CashAddr checksum\");\n }\n\n // Strip the 8 trailing checksum words; the remainder encodes (version_byte || hash)\n const dataWords = words.slice(0, -8);\n // Convert from 5-bit words to 8-bit bytes using bech32.fromWords (equivalent to convertBits(data, 5, 8))\n const payloadBytes = bech32.fromWords(dataWords);\n if (payloadBytes.length === 0) {\n throw new Error(\"Invalid CashAddr padding\");\n }\n\n // The first byte is the version byte: upper nibble = type, lower 3 bits = hash size index\n const versionByte = payloadBytes[0];\n const type = (versionByte >> 3) & 0x0f;\n const hashSizeBits = versionByte & 0x07;\n\n // Look up the expected hash length in bytes from the size index\n const expectedSizes = [20, 24, 28, 32, 40, 48, 56, 64];\n const expectedSize = expectedSizes[hashSizeBits];\n\n const hashBytes = payloadBytes.slice(1);\n if (hashBytes.length !== expectedSize) {\n throw new Error(\n `Hash length mismatch: expected ${expectedSize}, got ${hashBytes.length}`\n );\n }\n\n return { prefix, type, hash: Uint8Array.from(hashBytes) };\n}\n","import {\n ProofStatus,\n ProofTypes,\n SignatureProof,\n} from \"@notabene/javascript-sdk\";\n\nimport { encode as encodeLength } from \"varuint-bitcoin\";\nimport { base64, bech32, createBase58check } from \"@scure/base\";\nimport { Hash } from \"ox\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { SignatureType } from \"@noble/curves/abstract/weierstrass\";\nimport { Verifier } from \"bip322-js\";\nimport { decodeCashAddr } from \"./cashaddr\";\n\nenum SEGWIT_TYPES {\n P2WPKH = \"p2wpkh\",\n P2SH_P2WPKH = \"p2sh(p2wpkh)\",\n}\n\ninterface ChainConfig {\n messagePrefix: string;\n pubKeyHashVersion: number | Uint8Array;\n scriptHashVersion: number | Uint8Array;\n bech32Prefix?: string;\n isTestnet?: boolean;\n}\n\nconst CHAIN_CONFIGS: Record<string, ChainConfig> = {\n bitcoin: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x00, // 1...\n scriptHashVersion: 0x05, // 3...\n bech32Prefix: \"bc\",\n isTestnet: false,\n },\n bitcoincash: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x00, // 1...\n scriptHashVersion: 0x05, // 3...\n bech32Prefix: \"bc\",\n isTestnet: false,\n },\n litecoin: {\n messagePrefix: \"\\u0019Litecoin Signed Message:\\n\",\n pubKeyHashVersion: 0x30, // L... or M...\n scriptHashVersion: 0x32, // 3... or M...\n bech32Prefix: \"ltc\",\n isTestnet: false,\n },\n dogecoin: {\n messagePrefix: \"\\u0019Dogecoin Signed Message:\\n\",\n pubKeyHashVersion: 0x1e, // D...\n scriptHashVersion: 0x16, // A...\n isTestnet: false,\n },\n dash: {\n messagePrefix: \"\\u0019DarkCoin Signed Message:\\n\",\n pubKeyHashVersion: 0x4c, // X...\n scriptHashVersion: 0x10, // 7...\n isTestnet: false,\n },\n zcash: {\n messagePrefix: \"\\u0018Zcash Signed Message:\\n\",\n pubKeyHashVersion: Uint8Array.from([0x1c, 0xb8]), // <-- FIXED\n scriptHashVersion: Uint8Array.from([0x1c, 0xbd]),\n isTestnet: false,\n },\n\n testnet: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x6f, // m or n\n scriptHashVersion: 0xc4, // 2\n bech32Prefix: \"tb\",\n isTestnet: true,\n },\n};\n\nenum DerivationMode {\n LEGACY = \"Legacy\",\n NATIVE = \"Native SegWit\",\n SEGWIT = \"SegWit\",\n P2SH_SEGWIT = \"p2sh\",\n BCH = \"Bitcoin Cash\",\n ETHEREUM = \"Ethereum\",\n DOGECOIN = \"Dogecoin\",\n UNKNOWN = \"Unknown\",\n}\n\nexport async function verifyBTCSignature(\n proof: SignatureProof\n): Promise<SignatureProof> {\n // Split on \":\" but rejoin from index 2 onwards so that a prefixed CashAddr\n // like \"bitcoincash:qr...\" in the third segment is preserved whole.\n // e.g. \"bip122:<chainId>:bitcoincash:qr...\" → address = \"bitcoincash:qr...\"\n const parts = proof.address.split(\":\");\n const ns = parts[0];\n const address = parts.slice(2).join(\":\");\n if (ns !== \"bip122\") return { ...proof, status: ProofStatus.FAILED };\n\n // Map chainId to our chain configuration\n const chainConfig = getChainConfig(address);\n if (!chainConfig) return { ...proof, status: ProofStatus.FAILED };\n\n const isZcash = address.startsWith(\"t1\") || address.startsWith(\"t3\");\n if (isZcash) {\n return verifyBIP137(address, proof, chainConfig);\n }\n\n // Use BIP322 for testnet addresses\n if (chainConfig.isTestnet) {\n try {\n return verifyBIP322(address, proof);\n } catch {\n return { ...proof, status: ProofStatus.FAILED };\n }\n }\n\n // Check if this is a Taproot address (bc1p or tb1p)\n const isTaproot = address.startsWith(\"bc1p\") || address.startsWith(\"tb1p\");\n\n // For Taproot addresses with BIP-137 proof type, use BIP-322 verification\n // since BIP-137 doesn't officially support Taproot\n if (isTaproot && proof.type === ProofTypes.BIP137) {\n return verifyBIP322(address, proof);\n }\n\n try {\n switch (proof.type) {\n case ProofTypes.BIP137:\n return verifyBIP137(address, proof, chainConfig);\n case ProofTypes.BIP322:\n return verifyBIP322(address, proof);\n default:\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n } catch {\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n}\n\nfunction getChainConfig(address: string): ChainConfig {\n if (\n address.startsWith(\"1\") ||\n address.startsWith(\"3\") ||\n address.startsWith(\"bc1\")\n ) {\n return CHAIN_CONFIGS[\"bitcoin\"];\n }\n if (address.startsWith(\"t1\") || address.startsWith(\"t3\")) {\n return CHAIN_CONFIGS[\"zcash\"];\n }\n if (\n address.startsWith(\"L\") ||\n address.startsWith(\"M\") ||\n address.startsWith(\"ltc1\")\n ) {\n return CHAIN_CONFIGS[\"litecoin\"];\n }\n if (address.startsWith(\"D\") || address.startsWith(\"A\")) {\n return CHAIN_CONFIGS[\"dogecoin\"];\n }\n if (address.startsWith(\"X\") || address.startsWith(\"7\")) {\n return CHAIN_CONFIGS[\"dash\"];\n }\n if (address.startsWith(\"q\") || address.startsWith(\"bitcoincash:\")) {\n return CHAIN_CONFIGS[\"bitcoincash\"];\n }\n // Bitcoin testnet addresses can start with \"m\", \"n\", \"2\", or \"tb1\"\n if (\n address.startsWith(\"m\") ||\n address.startsWith(\"n\") ||\n address.startsWith(\"2\") ||\n address.startsWith(\"tb1\")\n ) {\n return CHAIN_CONFIGS[\"testnet\"]\n }\n\n return CHAIN_CONFIGS[\"bitcoin\"];\n}\n\nfunction verifyBIP322(address: string, proof: SignatureProof) {\n const { attestation, proof: signatureProof } = proof;\n const verified = Verifier.verifySignature(\n address,\n attestation,\n signatureProof\n );\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n\nfunction verifyBIP137(\n address: string,\n proof: SignatureProof,\n chainConfig: ChainConfig\n) {\n const derivationMode = getDerivationMode(address);\n\n // For legacy addresses (starting with \"1\"), never use SegWit encoding\n // For P2SH addresses (starting with \"3\"), use SegWit encoding if they have bech32 support\n // For native SegWit addresses (bc1, tb1, ltc1), always use SegWit encoding\n const useSegwitEncoding = Boolean(\n chainConfig.bech32Prefix &&\n (derivationMode === DerivationMode.NATIVE ||\n (derivationMode === DerivationMode.SEGWIT && !address.startsWith(\"1\")))\n );\n\n const verified = verify(\n proof.attestation,\n address,\n proof.proof,\n useSegwitEncoding,\n chainConfig\n );\n\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n\nfunction getDerivationMode(address: string) {\n if (address.match(\"^(bc1|tb1|ltc1).*\")) {\n return DerivationMode.NATIVE;\n } else if (address.match(\"^[32M].*\")) {\n return DerivationMode.SEGWIT;\n } else if (address.match(\"^[1nmL].*\")) {\n return DerivationMode.LEGACY;\n } else if (address.match(\"^(D).*\")) {\n return DerivationMode.DOGECOIN;\n } else if (address.match(\"^(q).*\") || address.startsWith(\"bitcoincash:\")) {\n return DerivationMode.BCH;\n } else if (address.match(\"^(t1|t3).*\")) {\n return DerivationMode.LEGACY; // Zcash addresses\n } else if (address.match(\"^[X7].*\")) {\n return DerivationMode.LEGACY; // Dash addresses\n } else {\n throw new Error(\n \"INVALID ADDRESS: \"\n .concat(address)\n .concat(\" is not a valid or a supported address\")\n );\n }\n}\n\ntype DecodedSignature = {\n compressed: boolean;\n segwitType?: SEGWIT_TYPES;\n signature: SignatureType;\n};\n\nfunction decodeSignature(proof: string): DecodedSignature {\n const sigbytes = base64.decode(proof);\n if (sigbytes.length !== 65) throw new Error(\"Invalid signature length\");\n const flagByte = sigbytes[0] - 27;\n if (flagByte > 15 || flagByte < 0) {\n throw new Error(\"Invalid signature parameter\");\n }\n const compressed = !!(flagByte & 12); // Are there cases that aren't compressed?\n const recovery = flagByte & 3;\n const signature = secp256k1.Signature.fromCompact(sigbytes.slice(1));\n\n return {\n compressed,\n segwitType: !(flagByte & 8)\n ? undefined\n : !(flagByte & 4)\n ? SEGWIT_TYPES.P2SH_P2WPKH\n : SEGWIT_TYPES.P2WPKH,\n signature: signature.addRecoveryBit(recovery),\n };\n}\n\nfunction verify(\n attestation: string,\n address: string,\n proof: string,\n checkSegwitAlways: boolean,\n chainConfig: ChainConfig\n) {\n const { compressed, segwitType, signature } = decodeSignature(proof);\n if (checkSegwitAlways && !compressed) {\n throw new Error(\n \"checkSegwitAlways can only be used with a compressed pubkey signature flagbyte\"\n );\n }\n const hash = magicHash(attestation, chainConfig.messagePrefix);\n const publicKey = signature.recoverPublicKey(hash);\n const publicKeyBytes = publicKey.toRawBytes(compressed);\n const publicKeyHash = hash160(publicKeyBytes);\n // Special handling for Bitcoin Cash addresses\n if (address.startsWith(\"q\") || address.startsWith(\"bitcoincash:\")) {\n try {\n const decoded = decodeCashAddr(address);\n if (decoded.type !== 0) return false; // Must be P2PKH\n if (decoded.hash.length !== publicKeyHash.length) return false;\n for (let i = 0; i < decoded.hash.length; i++) {\n if (decoded.hash[i] !== publicKeyHash[i]) return false;\n }\n return true;\n } catch {\n return false;\n }\n }\n\n let actual: string = \"\";\n\n if (segwitType) {\n if (segwitType === SEGWIT_TYPES.P2SH_P2WPKH) {\n // P2SH-P2WPKH: address is hash160 of the witness redeem script, not the raw pubkey hash\n const redeemScript = new Uint8Array(22);\n redeemScript[0] = 0x00; // OP_0\n redeemScript[1] = 0x14; // push 20 bytes\n redeemScript.set(publicKeyHash, 2);\n const redeemScriptHash = hash160(redeemScript);\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n redeemScriptHash\n );\n } else {\n // parsed.segwitType === SEGWIT_TYPES.P2WPKH\n if (chainConfig.bech32Prefix) {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n } else {\n // Fallback to legacy if bech32 not supported\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n // base58 can be p2pkh or p2sh-p2wpkh\n }\n }\n } else {\n // For addresses starting with \"3\" (P2SH), try both P2SH-P2WPKH and legacy P2SH encodings if segwitType is undefined\n if (address.startsWith(\"3\") && !segwitType) {\n // P2SH-P2WPKH: script hash of the redeem script (OP_0 <pubkeyhash>)\n const redeemScript = new Uint8Array(22);\n redeemScript[0] = 0x00; // OP_0\n redeemScript[1] = 0x14; // push 20 bytes\n redeemScript.set(publicKeyHash, 2);\n const redeemScriptHash = hash160(redeemScript);\n const p2shP2wpkh = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n redeemScriptHash\n );\n // Legacy P2SH: script hash of the public key\n const legacyP2sh = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n if (address === p2shP2wpkh || address === legacyP2sh) {\n return true;\n }\n actual = legacyP2sh; // fallback for error reporting\n } else if (address.startsWith(\"bc1q\") || address.startsWith(\"tb1q\") || address.startsWith(\"ltc1q\")) {\n // For native SegWit P2WPKH addresses (bc1q/tb1q/ltc1q), always encode as bech32\n // This handles Ledger wallets that sign without segwit flags\n if (chainConfig.bech32Prefix) {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n } else {\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n }\n } else if (checkSegwitAlways && chainConfig.bech32Prefix) {\n try {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n // if address is bech32 it is not p2sh\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (e) {\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n // base58 can be p2pkh or p2sh-p2wpkh\n }\n } else {\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n }\n }\n\n return actual === address;\n}\n\nconst base58check = createBase58check(Hash.sha256);\n\nfunction encodeBase58AddressFormat(\n version: number | Uint8Array,\n publicKeyHash: Uint8Array\n) {\n const prefixBytes =\n typeof version === \"number\" ? Uint8Array.of(version) : version; // Accept raw Uint8Array for Zcash\n\n const payload = new Uint8Array(prefixBytes.length + publicKeyHash.length);\n payload.set(prefixBytes);\n payload.set(publicKeyHash, prefixBytes.length);\n return base58check.encode(payload);\n}\n\nfunction magicHash(attestation: string, messagePrefix: string) {\n const prefix = new TextEncoder().encode(messagePrefix);\n const message = new TextEncoder().encode(attestation);\n const length = encodeLength(message.length).buffer;\n const buffer = new Uint8Array(\n prefix.length + length.byteLength + message.length\n );\n buffer.set(prefix);\n buffer.set(new Uint8Array(length), prefix.length);\n buffer.set(message, prefix.length + length.byteLength);\n return hash256(buffer);\n}\n\nfunction encodeBech32Address(\n publicKeyHash: Uint8Array,\n prefix: string = \"bc\"\n): string {\n const bwords = bech32.toWords(publicKeyHash);\n bwords.unshift(0);\n return bech32.encode(prefix, bwords);\n}\n\nfunction hash256(buffer: Uint8Array): Uint8Array {\n return Hash.sha256(Hash.sha256(buffer));\n}\n\nfunction hash160(buffer: Uint8Array): Uint8Array {\n return Hash.ripemd160(Hash.sha256(buffer));\n}\n"]}
@@ -10,6 +10,79 @@ var secp256k1 = require('@noble/curves/secp256k1');
10
10
  var bip322Js = require('bip322-js');
11
11
 
12
12
  if (typeof globalThis !== 'undefined' && !globalThis.Buffer) { globalThis.Buffer = buffer.Buffer; }
13
+ var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
14
+ var GENERATOR = [
15
+ BigInt("0x98f2bc8e61"),
16
+ BigInt("0x79b76d99e2"),
17
+ BigInt("0xf33e5fb3c4"),
18
+ BigInt("0xae2eabe2a8"),
19
+ BigInt("0x1e4f43e470")
20
+ ];
21
+ function polymod(values) {
22
+ let c = BigInt(1);
23
+ for (const d of values) {
24
+ const c0 = c >> BigInt(35);
25
+ c = (c & BigInt("0x07ffffffff")) << BigInt(5) ^ BigInt(d);
26
+ for (let i = 0; i < 5; i++) {
27
+ if (c0 >> BigInt(i) & BigInt(1)) {
28
+ c ^= GENERATOR[i];
29
+ }
30
+ }
31
+ }
32
+ return c ^ BigInt(1);
33
+ }
34
+ function prefixExpand(prefix) {
35
+ const result = [];
36
+ for (let i = 0; i < prefix.length; i++) {
37
+ result.push(prefix.charCodeAt(i) & 31);
38
+ }
39
+ result.push(0);
40
+ return result;
41
+ }
42
+ function decodeCashAddr(address, defaultPrefix = "bitcoincash") {
43
+ let prefix;
44
+ const colonIndex = address.indexOf(":");
45
+ if (colonIndex !== -1) {
46
+ prefix = address.slice(0, colonIndex).toLowerCase();
47
+ } else {
48
+ prefix = defaultPrefix;
49
+ }
50
+ const rawPayload = colonIndex !== -1 ? address.slice(colonIndex + 1) : address;
51
+ if (/[A-Z]/.test(rawPayload) && /[a-z]/.test(rawPayload)) {
52
+ throw new Error("Invalid CashAddr: mixed case");
53
+ }
54
+ const payload = rawPayload.toLowerCase();
55
+ const words = [];
56
+ for (const ch of payload) {
57
+ const idx = CHARSET.indexOf(ch);
58
+ if (idx === -1) throw new Error("Invalid CashAddr character: " + ch);
59
+ words.push(idx);
60
+ }
61
+ if (words.length < 8) throw new Error("CashAddr too short");
62
+ const checksumData = [...prefixExpand(prefix), ...words];
63
+ if (polymod(checksumData) !== BigInt(0)) {
64
+ throw new Error("Invalid CashAddr checksum");
65
+ }
66
+ const dataWords = words.slice(0, -8);
67
+ const payloadBytes = base.bech32.fromWords(dataWords);
68
+ if (payloadBytes.length === 0) {
69
+ throw new Error("Invalid CashAddr padding");
70
+ }
71
+ const versionByte = payloadBytes[0];
72
+ const type = versionByte >> 3 & 15;
73
+ const hashSizeBits = versionByte & 7;
74
+ const expectedSizes = [20, 24, 28, 32, 40, 48, 56, 64];
75
+ const expectedSize = expectedSizes[hashSizeBits];
76
+ const hashBytes = payloadBytes.slice(1);
77
+ if (hashBytes.length !== expectedSize) {
78
+ throw new Error(
79
+ `Hash length mismatch: expected ${expectedSize}, got ${hashBytes.length}`
80
+ );
81
+ }
82
+ return { prefix, type, hash: Uint8Array.from(hashBytes) };
83
+ }
84
+
85
+ // src/bitcoin.ts
13
86
  var CHAIN_CONFIGS = {
14
87
  bitcoin: {
15
88
  messagePrefix: "Bitcoin Signed Message:\n",
@@ -72,7 +145,9 @@ var CHAIN_CONFIGS = {
72
145
  }
73
146
  };
74
147
  async function verifyBTCSignature(proof) {
75
- const [ns, , address] = proof.address.split(/:/);
148
+ const parts = proof.address.split(":");
149
+ const ns = parts[0];
150
+ const address = parts.slice(2).join(":");
76
151
  if (ns !== "bip122") return { ...proof, status: javascriptSdk.ProofStatus.FAILED };
77
152
  const chainConfig = getChainConfig(address);
78
153
  if (!chainConfig) return { ...proof, status: javascriptSdk.ProofStatus.FAILED };
@@ -81,7 +156,11 @@ async function verifyBTCSignature(proof) {
81
156
  return verifyBIP137(address, proof, chainConfig);
82
157
  }
83
158
  if (chainConfig.isTestnet) {
84
- return verifyBIP322(address, proof);
159
+ try {
160
+ return verifyBIP322(address, proof);
161
+ } catch {
162
+ return { ...proof, status: javascriptSdk.ProofStatus.FAILED };
163
+ }
85
164
  }
86
165
  const isTaproot = address.startsWith("bc1p") || address.startsWith("tb1p");
87
166
  if (isTaproot && proof.type === javascriptSdk.ProofTypes.BIP137) {
@@ -122,10 +201,10 @@ function getChainConfig(address) {
122
201
  if (address.startsWith("X") || address.startsWith("7")) {
123
202
  return CHAIN_CONFIGS["dash"];
124
203
  }
125
- if (address.startsWith("q")) {
204
+ if (address.startsWith("q") || address.startsWith("bitcoincash:")) {
126
205
  return CHAIN_CONFIGS["bitcoincash"];
127
206
  }
128
- if (address.startsWith("tb1")) {
207
+ if (address.startsWith("m") || address.startsWith("n") || address.startsWith("2") || address.startsWith("tb1")) {
129
208
  return CHAIN_CONFIGS["testnet"];
130
209
  }
131
210
  return CHAIN_CONFIGS["bitcoin"];
@@ -168,7 +247,7 @@ function getDerivationMode(address) {
168
247
  return "Legacy" /* LEGACY */;
169
248
  } else if (address.match("^(D).*")) {
170
249
  return "Dogecoin" /* DOGECOIN */;
171
- } else if (address.match("^(q).*")) {
250
+ } else if (address.match("^(q).*") || address.startsWith("bitcoincash:")) {
172
251
  return "Bitcoin Cash" /* BCH */;
173
252
  } else if (address.match("^(t1|t3).*")) {
174
253
  return "Legacy" /* LEGACY */;
@@ -207,19 +286,30 @@ function verify(attestation, address, proof, checkSegwitAlways, chainConfig) {
207
286
  const publicKey = signature.recoverPublicKey(hash);
208
287
  const publicKeyBytes = publicKey.toRawBytes(compressed);
209
288
  const publicKeyHash = hash160(publicKeyBytes);
210
- let actual = "";
211
- if (address.startsWith("q")) {
212
- actual = encodeBase58AddressFormat(
213
- chainConfig.pubKeyHashVersion,
214
- publicKeyHash
215
- );
216
- return actual.startsWith("1");
289
+ if (address.startsWith("q") || address.startsWith("bitcoincash:")) {
290
+ try {
291
+ const decoded = decodeCashAddr(address);
292
+ if (decoded.type !== 0) return false;
293
+ if (decoded.hash.length !== publicKeyHash.length) return false;
294
+ for (let i = 0; i < decoded.hash.length; i++) {
295
+ if (decoded.hash[i] !== publicKeyHash[i]) return false;
296
+ }
297
+ return true;
298
+ } catch {
299
+ return false;
300
+ }
217
301
  }
302
+ let actual = "";
218
303
  if (segwitType) {
219
304
  if (segwitType === "p2sh(p2wpkh)" /* P2SH_P2WPKH */) {
305
+ const redeemScript = new Uint8Array(22);
306
+ redeemScript[0] = 0;
307
+ redeemScript[1] = 20;
308
+ redeemScript.set(publicKeyHash, 2);
309
+ const redeemScriptHash = hash160(redeemScript);
220
310
  actual = encodeBase58AddressFormat(
221
311
  chainConfig.scriptHashVersion,
222
- publicKeyHash
312
+ redeemScriptHash
223
313
  );
224
314
  } else {
225
315
  if (chainConfig.bech32Prefix) {
@@ -310,5 +400,5 @@ function hash160(buffer) {
310
400
  }
311
401
 
312
402
  exports.verifyBTCSignature = verifyBTCSignature;
313
- //# sourceMappingURL=bitcoin-3CW4MNAW.cjs.map
314
- //# sourceMappingURL=bitcoin-3CW4MNAW.cjs.map
403
+ //# sourceMappingURL=bitcoin-J22QFAA6.cjs.map
404
+ //# sourceMappingURL=bitcoin-J22QFAA6.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cashaddr.ts","../src/bitcoin.ts"],"names":["bech32","ProofStatus","ProofTypes","Verifier","base64","secp256k1","createBase58check","Hash","encodeLength"],"mappings":"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","file":"bitcoin-J22QFAA6.cjs","sourcesContent":["import { bech32 } from \"@scure/base\";\n\n/**\n * CashAddr base32 character set.\n * Each character maps to its 5-bit index value (0–31).\n * Spec: https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md\n */\nconst CHARSET = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\n\n/**\n * Generator polynomials for the CashAddr BCH checksum.\n * These five constants define the GF(2^32) polynomial used to compute\n * and validate the 40-bit checksum appended to every CashAddr string.\n */\nconst GENERATOR = [\n BigInt(\"0x98f2bc8e61\"),\n BigInt(\"0x79b76d99e2\"),\n BigInt(\"0xf33e5fb3c4\"),\n BigInt(\"0xae2eabe2a8\"),\n BigInt(\"0x1e4f43e470\"),\n];\n\n/**\n * Computes the CashAddr BCH checksum over an array of 5-bit values.\n *\n * The input should be the concatenation of:\n * - the prefix expansion (see {@link prefixExpand})\n * - all payload words including the 8 checksum words\n *\n * A valid address produces a result of `0n`; any other value indicates\n * a corrupt or invalid address.\n *\n * @param values - Array of 5-bit integers covering prefix + full payload\n * @returns The 40-bit checksum residue as a bigint (0n = valid)\n */\nfunction polymod(values: number[]): bigint {\n let c = BigInt(1);\n for (const d of values) {\n // Extract the top 5 bits before shifting\n const c0 = c >> BigInt(35);\n // Shift the accumulator left by 5 bits and XOR in the next data word\n c = ((c & BigInt(\"0x07ffffffff\")) << BigInt(5)) ^ BigInt(d);\n // Apply each generator polynomial for set bits in c0\n for (let i = 0; i < 5; i++) {\n if ((c0 >> BigInt(i)) & BigInt(1)) {\n c ^= GENERATOR[i];\n }\n }\n }\n // Final XOR with 1 so a valid address yields 0\n return c ^ BigInt(1);\n}\n\n/**\n * Expands a CashAddr prefix into the form required by the checksum algorithm.\n *\n * Each character of the prefix is reduced to its lower 5 bits, then a zero\n * byte is appended as a separator between the prefix and the payload words.\n *\n * @param prefix - The human-readable prefix (e.g. `\"bitcoincash\"`)\n * @returns Array of 5-bit integers representing the expanded prefix\n */\nfunction prefixExpand(prefix: string): number[] {\n const result: number[] = [];\n for (let i = 0; i < prefix.length; i++) {\n // Keep only the lower 5 bits of each character's ASCII code\n result.push(prefix.charCodeAt(i) & 0x1f);\n }\n // Zero separator between prefix and payload\n result.push(0);\n return result;\n}\n\n/**\n * Decodes a CashAddr string into its prefix, address type, and raw hash.\n *\n * Supports both prefixed (`bitcoincash:q...`) and prefix-less (`q...`) forms.\n * Validates the BCH checksum and rejects non-P2PKH types or malformed input.\n *\n * Address type values (upper nibble of the version byte):\n * - `0` — P2PKH (pay-to-public-key-hash)\n * - `1` — P2SH (pay-to-script-hash)\n *\n * @param address - The CashAddr string to decode\n * @param defaultPrefix - Prefix to assume when none is present in the address (default: `\"bitcoincash\"`)\n * @returns An object with:\n * - `prefix` — the decoded human-readable prefix\n * - `type` — the address type (0 = P2PKH, 1 = P2SH, …)\n * - `hash` — the raw public-key or script hash bytes\n * @throws If the address contains invalid characters, fails the checksum, has a mismatched hash length,\n * contains mixed upper/lower case, or if the payload contains invalid 5-to-8-bit padding\n * (thrown by {@link bech32.fromWords} from `@scure/base`)\n */\nexport function decodeCashAddr(\n address: string,\n defaultPrefix = \"bitcoincash\"\n): { prefix: string; type: number; hash: Uint8Array } {\n let prefix: string;\n\n // Split on \":\" to separate an explicit prefix from the payload\n const colonIndex = address.indexOf(\":\");\n if (colonIndex !== -1) {\n prefix = address.slice(0, colonIndex).toLowerCase();\n } else {\n prefix = defaultPrefix;\n }\n\n // CashAddr spec forbids mixed upper/lower case\n const rawPayload = colonIndex !== -1 ? address.slice(colonIndex + 1) : address;\n if (/[A-Z]/.test(rawPayload) && /[a-z]/.test(rawPayload)) {\n throw new Error(\"Invalid CashAddr: mixed case\");\n }\n const payload = rawPayload.toLowerCase();\n\n // Map each character to its 5-bit index in the CashAddr alphabet\n const words: number[] = [];\n for (const ch of payload) {\n const idx = CHARSET.indexOf(ch);\n if (idx === -1) throw new Error(\"Invalid CashAddr character: \" + ch);\n words.push(idx);\n }\n\n // Minimum length: at least 1 data word + 8 checksum words\n if (words.length < 8) throw new Error(\"CashAddr too short\");\n\n // Verify the BCH checksum over prefix expansion + all payload words\n const checksumData = [...prefixExpand(prefix), ...words];\n if (polymod(checksumData) !== BigInt(0)) {\n throw new Error(\"Invalid CashAddr checksum\");\n }\n\n // Strip the 8 trailing checksum words; the remainder encodes (version_byte || hash)\n const dataWords = words.slice(0, -8);\n // Convert from 5-bit words to 8-bit bytes using bech32.fromWords (equivalent to convertBits(data, 5, 8))\n const payloadBytes = bech32.fromWords(dataWords);\n if (payloadBytes.length === 0) {\n throw new Error(\"Invalid CashAddr padding\");\n }\n\n // The first byte is the version byte: upper nibble = type, lower 3 bits = hash size index\n const versionByte = payloadBytes[0];\n const type = (versionByte >> 3) & 0x0f;\n const hashSizeBits = versionByte & 0x07;\n\n // Look up the expected hash length in bytes from the size index\n const expectedSizes = [20, 24, 28, 32, 40, 48, 56, 64];\n const expectedSize = expectedSizes[hashSizeBits];\n\n const hashBytes = payloadBytes.slice(1);\n if (hashBytes.length !== expectedSize) {\n throw new Error(\n `Hash length mismatch: expected ${expectedSize}, got ${hashBytes.length}`\n );\n }\n\n return { prefix, type, hash: Uint8Array.from(hashBytes) };\n}\n","import {\n ProofStatus,\n ProofTypes,\n SignatureProof,\n} from \"@notabene/javascript-sdk\";\n\nimport { encode as encodeLength } from \"varuint-bitcoin\";\nimport { base64, bech32, createBase58check } from \"@scure/base\";\nimport { Hash } from \"ox\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { SignatureType } from \"@noble/curves/abstract/weierstrass\";\nimport { Verifier } from \"bip322-js\";\nimport { decodeCashAddr } from \"./cashaddr\";\n\nenum SEGWIT_TYPES {\n P2WPKH = \"p2wpkh\",\n P2SH_P2WPKH = \"p2sh(p2wpkh)\",\n}\n\ninterface ChainConfig {\n messagePrefix: string;\n pubKeyHashVersion: number | Uint8Array;\n scriptHashVersion: number | Uint8Array;\n bech32Prefix?: string;\n isTestnet?: boolean;\n}\n\nconst CHAIN_CONFIGS: Record<string, ChainConfig> = {\n bitcoin: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x00, // 1...\n scriptHashVersion: 0x05, // 3...\n bech32Prefix: \"bc\",\n isTestnet: false,\n },\n bitcoincash: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x00, // 1...\n scriptHashVersion: 0x05, // 3...\n bech32Prefix: \"bc\",\n isTestnet: false,\n },\n litecoin: {\n messagePrefix: \"\\u0019Litecoin Signed Message:\\n\",\n pubKeyHashVersion: 0x30, // L... or M...\n scriptHashVersion: 0x32, // 3... or M...\n bech32Prefix: \"ltc\",\n isTestnet: false,\n },\n dogecoin: {\n messagePrefix: \"\\u0019Dogecoin Signed Message:\\n\",\n pubKeyHashVersion: 0x1e, // D...\n scriptHashVersion: 0x16, // A...\n isTestnet: false,\n },\n dash: {\n messagePrefix: \"\\u0019DarkCoin Signed Message:\\n\",\n pubKeyHashVersion: 0x4c, // X...\n scriptHashVersion: 0x10, // 7...\n isTestnet: false,\n },\n zcash: {\n messagePrefix: \"\\u0018Zcash Signed Message:\\n\",\n pubKeyHashVersion: Uint8Array.from([0x1c, 0xb8]), // <-- FIXED\n scriptHashVersion: Uint8Array.from([0x1c, 0xbd]),\n isTestnet: false,\n },\n\n testnet: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x6f, // m or n\n scriptHashVersion: 0xc4, // 2\n bech32Prefix: \"tb\",\n isTestnet: true,\n },\n};\n\nenum DerivationMode {\n LEGACY = \"Legacy\",\n NATIVE = \"Native SegWit\",\n SEGWIT = \"SegWit\",\n P2SH_SEGWIT = \"p2sh\",\n BCH = \"Bitcoin Cash\",\n ETHEREUM = \"Ethereum\",\n DOGECOIN = \"Dogecoin\",\n UNKNOWN = \"Unknown\",\n}\n\nexport async function verifyBTCSignature(\n proof: SignatureProof\n): Promise<SignatureProof> {\n // Split on \":\" but rejoin from index 2 onwards so that a prefixed CashAddr\n // like \"bitcoincash:qr...\" in the third segment is preserved whole.\n // e.g. \"bip122:<chainId>:bitcoincash:qr...\" → address = \"bitcoincash:qr...\"\n const parts = proof.address.split(\":\");\n const ns = parts[0];\n const address = parts.slice(2).join(\":\");\n if (ns !== \"bip122\") return { ...proof, status: ProofStatus.FAILED };\n\n // Map chainId to our chain configuration\n const chainConfig = getChainConfig(address);\n if (!chainConfig) return { ...proof, status: ProofStatus.FAILED };\n\n const isZcash = address.startsWith(\"t1\") || address.startsWith(\"t3\");\n if (isZcash) {\n return verifyBIP137(address, proof, chainConfig);\n }\n\n // Use BIP322 for testnet addresses\n if (chainConfig.isTestnet) {\n try {\n return verifyBIP322(address, proof);\n } catch {\n return { ...proof, status: ProofStatus.FAILED };\n }\n }\n\n // Check if this is a Taproot address (bc1p or tb1p)\n const isTaproot = address.startsWith(\"bc1p\") || address.startsWith(\"tb1p\");\n\n // For Taproot addresses with BIP-137 proof type, use BIP-322 verification\n // since BIP-137 doesn't officially support Taproot\n if (isTaproot && proof.type === ProofTypes.BIP137) {\n return verifyBIP322(address, proof);\n }\n\n try {\n switch (proof.type) {\n case ProofTypes.BIP137:\n return verifyBIP137(address, proof, chainConfig);\n case ProofTypes.BIP322:\n return verifyBIP322(address, proof);\n default:\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n } catch {\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n}\n\nfunction getChainConfig(address: string): ChainConfig {\n if (\n address.startsWith(\"1\") ||\n address.startsWith(\"3\") ||\n address.startsWith(\"bc1\")\n ) {\n return CHAIN_CONFIGS[\"bitcoin\"];\n }\n if (address.startsWith(\"t1\") || address.startsWith(\"t3\")) {\n return CHAIN_CONFIGS[\"zcash\"];\n }\n if (\n address.startsWith(\"L\") ||\n address.startsWith(\"M\") ||\n address.startsWith(\"ltc1\")\n ) {\n return CHAIN_CONFIGS[\"litecoin\"];\n }\n if (address.startsWith(\"D\") || address.startsWith(\"A\")) {\n return CHAIN_CONFIGS[\"dogecoin\"];\n }\n if (address.startsWith(\"X\") || address.startsWith(\"7\")) {\n return CHAIN_CONFIGS[\"dash\"];\n }\n if (address.startsWith(\"q\") || address.startsWith(\"bitcoincash:\")) {\n return CHAIN_CONFIGS[\"bitcoincash\"];\n }\n // Bitcoin testnet addresses can start with \"m\", \"n\", \"2\", or \"tb1\"\n if (\n address.startsWith(\"m\") ||\n address.startsWith(\"n\") ||\n address.startsWith(\"2\") ||\n address.startsWith(\"tb1\")\n ) {\n return CHAIN_CONFIGS[\"testnet\"]\n }\n\n return CHAIN_CONFIGS[\"bitcoin\"];\n}\n\nfunction verifyBIP322(address: string, proof: SignatureProof) {\n const { attestation, proof: signatureProof } = proof;\n const verified = Verifier.verifySignature(\n address,\n attestation,\n signatureProof\n );\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n\nfunction verifyBIP137(\n address: string,\n proof: SignatureProof,\n chainConfig: ChainConfig\n) {\n const derivationMode = getDerivationMode(address);\n\n // For legacy addresses (starting with \"1\"), never use SegWit encoding\n // For P2SH addresses (starting with \"3\"), use SegWit encoding if they have bech32 support\n // For native SegWit addresses (bc1, tb1, ltc1), always use SegWit encoding\n const useSegwitEncoding = Boolean(\n chainConfig.bech32Prefix &&\n (derivationMode === DerivationMode.NATIVE ||\n (derivationMode === DerivationMode.SEGWIT && !address.startsWith(\"1\")))\n );\n\n const verified = verify(\n proof.attestation,\n address,\n proof.proof,\n useSegwitEncoding,\n chainConfig\n );\n\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n\nfunction getDerivationMode(address: string) {\n if (address.match(\"^(bc1|tb1|ltc1).*\")) {\n return DerivationMode.NATIVE;\n } else if (address.match(\"^[32M].*\")) {\n return DerivationMode.SEGWIT;\n } else if (address.match(\"^[1nmL].*\")) {\n return DerivationMode.LEGACY;\n } else if (address.match(\"^(D).*\")) {\n return DerivationMode.DOGECOIN;\n } else if (address.match(\"^(q).*\") || address.startsWith(\"bitcoincash:\")) {\n return DerivationMode.BCH;\n } else if (address.match(\"^(t1|t3).*\")) {\n return DerivationMode.LEGACY; // Zcash addresses\n } else if (address.match(\"^[X7].*\")) {\n return DerivationMode.LEGACY; // Dash addresses\n } else {\n throw new Error(\n \"INVALID ADDRESS: \"\n .concat(address)\n .concat(\" is not a valid or a supported address\")\n );\n }\n}\n\ntype DecodedSignature = {\n compressed: boolean;\n segwitType?: SEGWIT_TYPES;\n signature: SignatureType;\n};\n\nfunction decodeSignature(proof: string): DecodedSignature {\n const sigbytes = base64.decode(proof);\n if (sigbytes.length !== 65) throw new Error(\"Invalid signature length\");\n const flagByte = sigbytes[0] - 27;\n if (flagByte > 15 || flagByte < 0) {\n throw new Error(\"Invalid signature parameter\");\n }\n const compressed = !!(flagByte & 12); // Are there cases that aren't compressed?\n const recovery = flagByte & 3;\n const signature = secp256k1.Signature.fromCompact(sigbytes.slice(1));\n\n return {\n compressed,\n segwitType: !(flagByte & 8)\n ? undefined\n : !(flagByte & 4)\n ? SEGWIT_TYPES.P2SH_P2WPKH\n : SEGWIT_TYPES.P2WPKH,\n signature: signature.addRecoveryBit(recovery),\n };\n}\n\nfunction verify(\n attestation: string,\n address: string,\n proof: string,\n checkSegwitAlways: boolean,\n chainConfig: ChainConfig\n) {\n const { compressed, segwitType, signature } = decodeSignature(proof);\n if (checkSegwitAlways && !compressed) {\n throw new Error(\n \"checkSegwitAlways can only be used with a compressed pubkey signature flagbyte\"\n );\n }\n const hash = magicHash(attestation, chainConfig.messagePrefix);\n const publicKey = signature.recoverPublicKey(hash);\n const publicKeyBytes = publicKey.toRawBytes(compressed);\n const publicKeyHash = hash160(publicKeyBytes);\n // Special handling for Bitcoin Cash addresses\n if (address.startsWith(\"q\") || address.startsWith(\"bitcoincash:\")) {\n try {\n const decoded = decodeCashAddr(address);\n if (decoded.type !== 0) return false; // Must be P2PKH\n if (decoded.hash.length !== publicKeyHash.length) return false;\n for (let i = 0; i < decoded.hash.length; i++) {\n if (decoded.hash[i] !== publicKeyHash[i]) return false;\n }\n return true;\n } catch {\n return false;\n }\n }\n\n let actual: string = \"\";\n\n if (segwitType) {\n if (segwitType === SEGWIT_TYPES.P2SH_P2WPKH) {\n // P2SH-P2WPKH: address is hash160 of the witness redeem script, not the raw pubkey hash\n const redeemScript = new Uint8Array(22);\n redeemScript[0] = 0x00; // OP_0\n redeemScript[1] = 0x14; // push 20 bytes\n redeemScript.set(publicKeyHash, 2);\n const redeemScriptHash = hash160(redeemScript);\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n redeemScriptHash\n );\n } else {\n // parsed.segwitType === SEGWIT_TYPES.P2WPKH\n if (chainConfig.bech32Prefix) {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n } else {\n // Fallback to legacy if bech32 not supported\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n // base58 can be p2pkh or p2sh-p2wpkh\n }\n }\n } else {\n // For addresses starting with \"3\" (P2SH), try both P2SH-P2WPKH and legacy P2SH encodings if segwitType is undefined\n if (address.startsWith(\"3\") && !segwitType) {\n // P2SH-P2WPKH: script hash of the redeem script (OP_0 <pubkeyhash>)\n const redeemScript = new Uint8Array(22);\n redeemScript[0] = 0x00; // OP_0\n redeemScript[1] = 0x14; // push 20 bytes\n redeemScript.set(publicKeyHash, 2);\n const redeemScriptHash = hash160(redeemScript);\n const p2shP2wpkh = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n redeemScriptHash\n );\n // Legacy P2SH: script hash of the public key\n const legacyP2sh = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n if (address === p2shP2wpkh || address === legacyP2sh) {\n return true;\n }\n actual = legacyP2sh; // fallback for error reporting\n } else if (address.startsWith(\"bc1q\") || address.startsWith(\"tb1q\") || address.startsWith(\"ltc1q\")) {\n // For native SegWit P2WPKH addresses (bc1q/tb1q/ltc1q), always encode as bech32\n // This handles Ledger wallets that sign without segwit flags\n if (chainConfig.bech32Prefix) {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n } else {\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n }\n } else if (checkSegwitAlways && chainConfig.bech32Prefix) {\n try {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n // if address is bech32 it is not p2sh\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (e) {\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n // base58 can be p2pkh or p2sh-p2wpkh\n }\n } else {\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n }\n }\n\n return actual === address;\n}\n\nconst base58check = createBase58check(Hash.sha256);\n\nfunction encodeBase58AddressFormat(\n version: number | Uint8Array,\n publicKeyHash: Uint8Array\n) {\n const prefixBytes =\n typeof version === \"number\" ? Uint8Array.of(version) : version; // Accept raw Uint8Array for Zcash\n\n const payload = new Uint8Array(prefixBytes.length + publicKeyHash.length);\n payload.set(prefixBytes);\n payload.set(publicKeyHash, prefixBytes.length);\n return base58check.encode(payload);\n}\n\nfunction magicHash(attestation: string, messagePrefix: string) {\n const prefix = new TextEncoder().encode(messagePrefix);\n const message = new TextEncoder().encode(attestation);\n const length = encodeLength(message.length).buffer;\n const buffer = new Uint8Array(\n prefix.length + length.byteLength + message.length\n );\n buffer.set(prefix);\n buffer.set(new Uint8Array(length), prefix.length);\n buffer.set(message, prefix.length + length.byteLength);\n return hash256(buffer);\n}\n\nfunction encodeBech32Address(\n publicKeyHash: Uint8Array,\n prefix: string = \"bc\"\n): string {\n const bwords = bech32.toWords(publicKeyHash);\n bwords.unshift(0);\n return bech32.encode(prefix, bwords);\n}\n\nfunction hash256(buffer: Uint8Array): Uint8Array {\n return Hash.sha256(Hash.sha256(buffer));\n}\n\nfunction hash160(buffer: Uint8Array): Uint8Array {\n return Hash.ripemd160(Hash.sha256(buffer));\n}\n"]}
package/dist/index.cjs CHANGED
@@ -52,7 +52,7 @@ async function verifyProof(proof, publicKey) {
52
52
  case javascriptSdk.ProofTypes.EIP712:
53
53
  case javascriptSdk.ProofTypes.BIP137:
54
54
  case javascriptSdk.ProofTypes.BIP322: {
55
- const { verifyBTCSignature } = await import('./bitcoin-3CW4MNAW.cjs');
55
+ const { verifyBTCSignature } = await import('./bitcoin-J22QFAA6.cjs');
56
56
  return verifyBTCSignature(proof);
57
57
  }
58
58
  case javascriptSdk.ProofTypes.TIP191: {
package/dist/index.js CHANGED
@@ -50,7 +50,7 @@ async function verifyProof(proof, publicKey) {
50
50
  case ProofTypes.EIP712:
51
51
  case ProofTypes.BIP137:
52
52
  case ProofTypes.BIP322: {
53
- const { verifyBTCSignature } = await import('./bitcoin-QK53ILBF.js');
53
+ const { verifyBTCSignature } = await import('./bitcoin-ISUCDKFR.js');
54
54
  return verifyBTCSignature(proof);
55
55
  }
56
56
  case ProofTypes.TIP191: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notabene/verify-proof",
3
- "version": "1.12.0-next.4",
3
+ "version": "1.12.0-next.7",
4
4
  "description": "Verify ownership proofs",
5
5
  "source": "src/index.ts",
6
6
  "type": "module",
package/src/bitcoin.ts CHANGED
@@ -10,6 +10,7 @@ import { Hash } from "ox";
10
10
  import { secp256k1 } from "@noble/curves/secp256k1";
11
11
  import { SignatureType } from "@noble/curves/abstract/weierstrass";
12
12
  import { Verifier } from "bip322-js";
13
+ import { decodeCashAddr } from "./cashaddr";
13
14
 
14
15
  enum SEGWIT_TYPES {
15
16
  P2WPKH = "p2wpkh",
@@ -88,7 +89,12 @@ enum DerivationMode {
88
89
  export async function verifyBTCSignature(
89
90
  proof: SignatureProof
90
91
  ): Promise<SignatureProof> {
91
- const [ns, , address] = proof.address.split(/:/);
92
+ // Split on ":" but rejoin from index 2 onwards so that a prefixed CashAddr
93
+ // like "bitcoincash:qr..." in the third segment is preserved whole.
94
+ // e.g. "bip122:<chainId>:bitcoincash:qr..." → address = "bitcoincash:qr..."
95
+ const parts = proof.address.split(":");
96
+ const ns = parts[0];
97
+ const address = parts.slice(2).join(":");
92
98
  if (ns !== "bip122") return { ...proof, status: ProofStatus.FAILED };
93
99
 
94
100
  // Map chainId to our chain configuration
@@ -102,7 +108,11 @@ export async function verifyBTCSignature(
102
108
 
103
109
  // Use BIP322 for testnet addresses
104
110
  if (chainConfig.isTestnet) {
105
- return verifyBIP322(address, proof);
111
+ try {
112
+ return verifyBIP322(address, proof);
113
+ } catch {
114
+ return { ...proof, status: ProofStatus.FAILED };
115
+ }
106
116
  }
107
117
 
108
118
  // Check if this is a Taproot address (bc1p or tb1p)
@@ -158,11 +168,17 @@ function getChainConfig(address: string): ChainConfig {
158
168
  if (address.startsWith("X") || address.startsWith("7")) {
159
169
  return CHAIN_CONFIGS["dash"];
160
170
  }
161
- if (address.startsWith("q")) {
171
+ if (address.startsWith("q") || address.startsWith("bitcoincash:")) {
162
172
  return CHAIN_CONFIGS["bitcoincash"];
163
173
  }
164
- if (address.startsWith("tb1")) {
165
- return CHAIN_CONFIGS["testnet"];
174
+ // Bitcoin testnet addresses can start with "m", "n", "2", or "tb1"
175
+ if (
176
+ address.startsWith("m") ||
177
+ address.startsWith("n") ||
178
+ address.startsWith("2") ||
179
+ address.startsWith("tb1")
180
+ ) {
181
+ return CHAIN_CONFIGS["testnet"]
166
182
  }
167
183
 
168
184
  return CHAIN_CONFIGS["bitcoin"];
@@ -220,7 +236,7 @@ function getDerivationMode(address: string) {
220
236
  return DerivationMode.LEGACY;
221
237
  } else if (address.match("^(D).*")) {
222
238
  return DerivationMode.DOGECOIN;
223
- } else if (address.match("^(q).*")) {
239
+ } else if (address.match("^(q).*") || address.startsWith("bitcoincash:")) {
224
240
  return DerivationMode.BCH;
225
241
  } else if (address.match("^(t1|t3).*")) {
226
242
  return DerivationMode.LEGACY; // Zcash addresses
@@ -280,26 +296,34 @@ function verify(
280
296
  const publicKey = signature.recoverPublicKey(hash);
281
297
  const publicKeyBytes = publicKey.toRawBytes(compressed);
282
298
  const publicKeyHash = hash160(publicKeyBytes);
283
- let actual: string = "";
284
-
285
299
  // Special handling for Bitcoin Cash addresses
286
- if (address.startsWith("q")) {
287
- // For BCH, we'll compare the public key hash directly since we're getting a CashAddr
288
- // Convert the CashAddr to legacy format for comparison
289
- actual = encodeBase58AddressFormat(
290
- chainConfig.pubKeyHashVersion,
291
- publicKeyHash
292
- );
293
- // Legacy P2PKH addresses in BCH start with '1' just like BTC
294
- // Source: https://reference.cash/protocol/blockchain/encoding/cashaddr#legacy-address-format
295
- return actual.startsWith("1");
300
+ if (address.startsWith("q") || address.startsWith("bitcoincash:")) {
301
+ try {
302
+ const decoded = decodeCashAddr(address);
303
+ if (decoded.type !== 0) return false; // Must be P2PKH
304
+ if (decoded.hash.length !== publicKeyHash.length) return false;
305
+ for (let i = 0; i < decoded.hash.length; i++) {
306
+ if (decoded.hash[i] !== publicKeyHash[i]) return false;
307
+ }
308
+ return true;
309
+ } catch {
310
+ return false;
311
+ }
296
312
  }
297
313
 
314
+ let actual: string = "";
315
+
298
316
  if (segwitType) {
299
317
  if (segwitType === SEGWIT_TYPES.P2SH_P2WPKH) {
318
+ // P2SH-P2WPKH: address is hash160 of the witness redeem script, not the raw pubkey hash
319
+ const redeemScript = new Uint8Array(22);
320
+ redeemScript[0] = 0x00; // OP_0
321
+ redeemScript[1] = 0x14; // push 20 bytes
322
+ redeemScript.set(publicKeyHash, 2);
323
+ const redeemScriptHash = hash160(redeemScript);
300
324
  actual = encodeBase58AddressFormat(
301
325
  chainConfig.scriptHashVersion,
302
- publicKeyHash
326
+ redeemScriptHash
303
327
  );
304
328
  } else {
305
329
  // parsed.segwitType === SEGWIT_TYPES.P2WPKH
@@ -0,0 +1,157 @@
1
+ import { bech32 } from "@scure/base";
2
+
3
+ /**
4
+ * CashAddr base32 character set.
5
+ * Each character maps to its 5-bit index value (0–31).
6
+ * Spec: https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md
7
+ */
8
+ const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
9
+
10
+ /**
11
+ * Generator polynomials for the CashAddr BCH checksum.
12
+ * These five constants define the GF(2^32) polynomial used to compute
13
+ * and validate the 40-bit checksum appended to every CashAddr string.
14
+ */
15
+ const GENERATOR = [
16
+ BigInt("0x98f2bc8e61"),
17
+ BigInt("0x79b76d99e2"),
18
+ BigInt("0xf33e5fb3c4"),
19
+ BigInt("0xae2eabe2a8"),
20
+ BigInt("0x1e4f43e470"),
21
+ ];
22
+
23
+ /**
24
+ * Computes the CashAddr BCH checksum over an array of 5-bit values.
25
+ *
26
+ * The input should be the concatenation of:
27
+ * - the prefix expansion (see {@link prefixExpand})
28
+ * - all payload words including the 8 checksum words
29
+ *
30
+ * A valid address produces a result of `0n`; any other value indicates
31
+ * a corrupt or invalid address.
32
+ *
33
+ * @param values - Array of 5-bit integers covering prefix + full payload
34
+ * @returns The 40-bit checksum residue as a bigint (0n = valid)
35
+ */
36
+ function polymod(values: number[]): bigint {
37
+ let c = BigInt(1);
38
+ for (const d of values) {
39
+ // Extract the top 5 bits before shifting
40
+ const c0 = c >> BigInt(35);
41
+ // Shift the accumulator left by 5 bits and XOR in the next data word
42
+ c = ((c & BigInt("0x07ffffffff")) << BigInt(5)) ^ BigInt(d);
43
+ // Apply each generator polynomial for set bits in c0
44
+ for (let i = 0; i < 5; i++) {
45
+ if ((c0 >> BigInt(i)) & BigInt(1)) {
46
+ c ^= GENERATOR[i];
47
+ }
48
+ }
49
+ }
50
+ // Final XOR with 1 so a valid address yields 0
51
+ return c ^ BigInt(1);
52
+ }
53
+
54
+ /**
55
+ * Expands a CashAddr prefix into the form required by the checksum algorithm.
56
+ *
57
+ * Each character of the prefix is reduced to its lower 5 bits, then a zero
58
+ * byte is appended as a separator between the prefix and the payload words.
59
+ *
60
+ * @param prefix - The human-readable prefix (e.g. `"bitcoincash"`)
61
+ * @returns Array of 5-bit integers representing the expanded prefix
62
+ */
63
+ function prefixExpand(prefix: string): number[] {
64
+ const result: number[] = [];
65
+ for (let i = 0; i < prefix.length; i++) {
66
+ // Keep only the lower 5 bits of each character's ASCII code
67
+ result.push(prefix.charCodeAt(i) & 0x1f);
68
+ }
69
+ // Zero separator between prefix and payload
70
+ result.push(0);
71
+ return result;
72
+ }
73
+
74
+ /**
75
+ * Decodes a CashAddr string into its prefix, address type, and raw hash.
76
+ *
77
+ * Supports both prefixed (`bitcoincash:q...`) and prefix-less (`q...`) forms.
78
+ * Validates the BCH checksum and rejects non-P2PKH types or malformed input.
79
+ *
80
+ * Address type values (upper nibble of the version byte):
81
+ * - `0` — P2PKH (pay-to-public-key-hash)
82
+ * - `1` — P2SH (pay-to-script-hash)
83
+ *
84
+ * @param address - The CashAddr string to decode
85
+ * @param defaultPrefix - Prefix to assume when none is present in the address (default: `"bitcoincash"`)
86
+ * @returns An object with:
87
+ * - `prefix` — the decoded human-readable prefix
88
+ * - `type` — the address type (0 = P2PKH, 1 = P2SH, …)
89
+ * - `hash` — the raw public-key or script hash bytes
90
+ * @throws If the address contains invalid characters, fails the checksum, has a mismatched hash length,
91
+ * contains mixed upper/lower case, or if the payload contains invalid 5-to-8-bit padding
92
+ * (thrown by {@link bech32.fromWords} from `@scure/base`)
93
+ */
94
+ export function decodeCashAddr(
95
+ address: string,
96
+ defaultPrefix = "bitcoincash"
97
+ ): { prefix: string; type: number; hash: Uint8Array } {
98
+ let prefix: string;
99
+
100
+ // Split on ":" to separate an explicit prefix from the payload
101
+ const colonIndex = address.indexOf(":");
102
+ if (colonIndex !== -1) {
103
+ prefix = address.slice(0, colonIndex).toLowerCase();
104
+ } else {
105
+ prefix = defaultPrefix;
106
+ }
107
+
108
+ // CashAddr spec forbids mixed upper/lower case
109
+ const rawPayload = colonIndex !== -1 ? address.slice(colonIndex + 1) : address;
110
+ if (/[A-Z]/.test(rawPayload) && /[a-z]/.test(rawPayload)) {
111
+ throw new Error("Invalid CashAddr: mixed case");
112
+ }
113
+ const payload = rawPayload.toLowerCase();
114
+
115
+ // Map each character to its 5-bit index in the CashAddr alphabet
116
+ const words: number[] = [];
117
+ for (const ch of payload) {
118
+ const idx = CHARSET.indexOf(ch);
119
+ if (idx === -1) throw new Error("Invalid CashAddr character: " + ch);
120
+ words.push(idx);
121
+ }
122
+
123
+ // Minimum length: at least 1 data word + 8 checksum words
124
+ if (words.length < 8) throw new Error("CashAddr too short");
125
+
126
+ // Verify the BCH checksum over prefix expansion + all payload words
127
+ const checksumData = [...prefixExpand(prefix), ...words];
128
+ if (polymod(checksumData) !== BigInt(0)) {
129
+ throw new Error("Invalid CashAddr checksum");
130
+ }
131
+
132
+ // Strip the 8 trailing checksum words; the remainder encodes (version_byte || hash)
133
+ const dataWords = words.slice(0, -8);
134
+ // Convert from 5-bit words to 8-bit bytes using bech32.fromWords (equivalent to convertBits(data, 5, 8))
135
+ const payloadBytes = bech32.fromWords(dataWords);
136
+ if (payloadBytes.length === 0) {
137
+ throw new Error("Invalid CashAddr padding");
138
+ }
139
+
140
+ // The first byte is the version byte: upper nibble = type, lower 3 bits = hash size index
141
+ const versionByte = payloadBytes[0];
142
+ const type = (versionByte >> 3) & 0x0f;
143
+ const hashSizeBits = versionByte & 0x07;
144
+
145
+ // Look up the expected hash length in bytes from the size index
146
+ const expectedSizes = [20, 24, 28, 32, 40, 48, 56, 64];
147
+ const expectedSize = expectedSizes[hashSizeBits];
148
+
149
+ const hashBytes = payloadBytes.slice(1);
150
+ if (hashBytes.length !== expectedSize) {
151
+ throw new Error(
152
+ `Hash length mismatch: expected ${expectedSize}, got ${hashBytes.length}`
153
+ );
154
+ }
155
+
156
+ return { prefix, type, hash: Uint8Array.from(hashBytes) };
157
+ }
@@ -336,6 +336,68 @@ describe("verifyBTCSignature", () => {
336
336
  expect(result).toEqual({ ...testnetProof, status: ProofStatus.VERIFIED });
337
337
  });
338
338
 
339
+ describe("Bitcoin testnet address prefixes", () => {
340
+ it("handles testnet P2PKH addresses starting with 'm'", async () => {
341
+ const testnetMProof: SignatureProof = {
342
+ type: ProofTypes.BIP137,
343
+ address:
344
+ "bip122:000000000933ea01ad0ee984209779ba:mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn",
345
+ did: "did:pkh:bip122:000000000933ea01ad0ee984209779ba:mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn",
346
+ attestation: "Testnet m-address verification",
347
+ proof:
348
+ "H796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
349
+ status: ProofStatus.PENDING,
350
+ wallet_provider: "Manual Wallet Signature",
351
+ };
352
+
353
+ const result = await verifyBTCSignature(testnetMProof);
354
+ // Should be recognized as testnet and processed without throwing
355
+ expect([ProofStatus.VERIFIED, ProofStatus.FAILED]).toContain(
356
+ result.status
357
+ );
358
+ });
359
+
360
+ it("handles testnet P2PKH addresses starting with 'n'", async () => {
361
+ const testnetNProof: SignatureProof = {
362
+ type: ProofTypes.BIP137,
363
+ address:
364
+ "bip122:000000000933ea01ad0ee984209779ba:n1bkBJpWfQEa3JGPf8MYwHLny62FPNwJC4",
365
+ did: "did:pkh:bip122:000000000933ea01ad0ee984209779ba:n1bkBJpWfQEa3JGPf8MYwHLny62FPNwJC4",
366
+ attestation: "Testnet n-address verification",
367
+ proof:
368
+ "H796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
369
+ status: ProofStatus.PENDING,
370
+ wallet_provider: "Manual Wallet Signature",
371
+ };
372
+
373
+ const result = await verifyBTCSignature(testnetNProof);
374
+ // Should be recognized as testnet and processed without throwing
375
+ expect([ProofStatus.VERIFIED, ProofStatus.FAILED]).toContain(
376
+ result.status
377
+ );
378
+ });
379
+
380
+ it("handles testnet P2SH addresses starting with '2'", async () => {
381
+ const testnet2Proof: SignatureProof = {
382
+ type: ProofTypes.BIP137,
383
+ address:
384
+ "bip122:000000000933ea01ad0ee984209779ba:2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc",
385
+ did: "did:pkh:bip122:000000000933ea01ad0ee984209779ba:2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc",
386
+ attestation: "Testnet 2-address verification",
387
+ proof:
388
+ "H796FDv8f8w3syiaMSGoL6SAwPLRf6t13S+fYNjYA9EnJy3T0jZOY1eHBaGTBufOuW78FVFSwXKyUnrEjYOT9EU=",
389
+ status: ProofStatus.PENDING,
390
+ wallet_provider: "Manual Wallet Signature",
391
+ };
392
+
393
+ const result = await verifyBTCSignature(testnet2Proof);
394
+ // Should be recognized as testnet and processed without throwing
395
+ expect([ProofStatus.VERIFIED, ProofStatus.FAILED]).toContain(
396
+ result.status
397
+ );
398
+ });
399
+ });
400
+
339
401
  it("handles legacy multisig addresses", async () => {
340
402
  const multisigProof: SignatureProof = {
341
403
  ...legacyProof,
@@ -403,6 +465,110 @@ describe("verifyBTCSignature", () => {
403
465
  expect(result).toEqual({ ...bchProof, status: ProofStatus.VERIFIED });
404
466
  });
405
467
 
468
+ it("fails for BCH address mismatch", async () => {
469
+ // Same valid signature but a different BCH address — should fail
470
+ const bchMismatchProof: SignatureProof = {
471
+ type: ProofTypes.BIP137,
472
+ address:
473
+ "bip122:00000000000000000019d6689c085ae165831e93:qz7utdl8glhzfnw3fesqmfsd8c9qlzh9vcqzsm06uj",
474
+ did: "did:pkh:bip122:00000000000000000019d6689c085ae165831e93:qz7utdl8glhzfnw3fesqmfsd8c9qlzh9vcqzsm06uj",
475
+ attestation: "This is an example of a signed message.",
476
+ proof:
477
+ "H0qNIXAv3YA5+Y6I4p/lD03v8VEHZzTw+JAzfeoSKHydXhKsXgtPDaftjr3h3Bs8R2FqS9C6GOBvk+RISJK5lcU=",
478
+ status: ProofStatus.PENDING,
479
+ wallet_provider: "Electron Cash BCH",
480
+ };
481
+
482
+ const result = await verifyBTCSignature(bchMismatchProof);
483
+ expect(result).toEqual({
484
+ ...bchMismatchProof,
485
+ status: ProofStatus.FAILED,
486
+ });
487
+ });
488
+
489
+ it("fails for BCH address with invalid checksum", async () => {
490
+ // Corrupted CashAddr (last char changed) — should fail
491
+ const bchBadChecksumProof: SignatureProof = {
492
+ type: ProofTypes.BIP137,
493
+ address:
494
+ "bip122:00000000000000000019d6689c085ae165831e93:qrxxxgvmsqa7qzhd6dsnne3udc7u34fg8g56pzx2hz",
495
+ did: "did:pkh:bip122:00000000000000000019d6689c085ae165831e93:qrxxxgvmsqa7qzhd6dsnne3udc7u34fg8g56pzx2hz",
496
+ attestation: "This is an example of a signed message.",
497
+ proof:
498
+ "H0qNIXAv3YA5+Y6I4p/lD03v8VEHZzTw+JAzfeoSKHydXhKsXgtPDaftjr3h3Bs8R2FqS9C6GOBvk+RISJK5lcU=",
499
+ status: ProofStatus.PENDING,
500
+ wallet_provider: "Electron Cash BCH",
501
+ };
502
+
503
+ const result = await verifyBTCSignature(bchBadChecksumProof);
504
+ expect(result).toEqual({
505
+ ...bchBadChecksumProof,
506
+ status: ProofStatus.FAILED,
507
+ });
508
+ });
509
+
510
+ it("handles prefixed CashAddr (bitcoincash:q...)", async () => {
511
+ // Same valid signature as the existing BCH test, but the address URI
512
+ // includes the "bitcoincash:" scheme prefix as a fourth colon-segment.
513
+ // parts.slice(2).join(':') must reconstruct "bitcoincash:qr..." whole.
514
+ const bchPrefixedProof: SignatureProof = {
515
+ type: ProofTypes.BIP137,
516
+ address:
517
+ "bip122:00000000000000000019d6689c085ae165831e93:bitcoincash:qrxxxgvmsqa7qzhd6dsnne3udc7u34fg8g56pzx2hy",
518
+ did: "did:pkh:bip122:00000000000000000019d6689c085ae165831e93:bitcoincash:qrxxxgvmsqa7qzhd6dsnne3udc7u34fg8g56pzx2hy",
519
+ attestation: "This is an example of a signed message.",
520
+ proof:
521
+ "H0qNIXAv3YA5+Y6I4p/lD03v8VEHZzTw+JAzfeoSKHydXhKsXgtPDaftjr3h3Bs8R2FqS9C6GOBvk+RISJK5lcU=",
522
+ status: ProofStatus.PENDING,
523
+ wallet_provider: "Electron Cash BCH",
524
+ };
525
+
526
+ const result = await verifyBTCSignature(bchPrefixedProof);
527
+ expect(result).toEqual({ ...bchPrefixedProof, status: ProofStatus.VERIFIED });
528
+ });
529
+
530
+ it("rejects P2SH CashAddr (p... address)", async () => {
531
+ // P2SH CashAddrs start with 'p' (version byte type=1).
532
+ // 'p...' does not match the 'q'-prefix BCH path, so getDerivationMode
533
+ // throws "INVALID ADDRESS", which the outer try/catch converts to FAILED.
534
+ // The type !== 0 guard inside the BCH block is defense-in-depth for
535
+ // any future path that might reach it with a valid 'p...' address.
536
+ const bchP2SHProof: SignatureProof = {
537
+ type: ProofTypes.BIP137,
538
+ address:
539
+ "bip122:00000000000000000019d6689c085ae165831e93:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq",
540
+ did: "did:pkh:bip122:00000000000000000019d6689c085ae165831e93:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq",
541
+ attestation: "This is an example of a signed message.",
542
+ proof:
543
+ "H0qNIXAv3YA5+Y6I4p/lD03v8VEHZzTw+JAzfeoSKHydXhKsXgtPDaftjr3h3Bs8R2FqS9C6GOBvk+RISJK5lcU=",
544
+ status: ProofStatus.PENDING,
545
+ wallet_provider: "Electron Cash BCH",
546
+ };
547
+
548
+ const result = await verifyBTCSignature(bchP2SHProof);
549
+ expect(result).toEqual({ ...bchP2SHProof, status: ProofStatus.FAILED });
550
+ });
551
+
552
+ it("fails for too-short BCH address", async () => {
553
+ // "qqqqqqq" has 7 valid CashAddr chars but fewer than the 8 words
554
+ // required for a checksum, so decodeCashAddr throws "CashAddr too short".
555
+ // The catch in verify's BCH block returns false → FAILED.
556
+ const bchTooShortProof: SignatureProof = {
557
+ type: ProofTypes.BIP137,
558
+ address:
559
+ "bip122:00000000000000000019d6689c085ae165831e93:qqqqqqq",
560
+ did: "did:pkh:bip122:00000000000000000019d6689c085ae165831e93:qqqqqqq",
561
+ attestation: "This is an example of a signed message.",
562
+ proof:
563
+ "H0qNIXAv3YA5+Y6I4p/lD03v8VEHZzTw+JAzfeoSKHydXhKsXgtPDaftjr3h3Bs8R2FqS9C6GOBvk+RISJK5lcU=",
564
+ status: ProofStatus.PENDING,
565
+ wallet_provider: "Electron Cash BCH",
566
+ };
567
+
568
+ const result = await verifyBTCSignature(bchTooShortProof);
569
+ expect(result).toEqual({ ...bchTooShortProof, status: ProofStatus.FAILED });
570
+ });
571
+
406
572
  it("handles unknown address formats", async () => {
407
573
  const unknownProof: SignatureProof = {
408
574
  type: ProofTypes.BIP137,
@@ -712,6 +878,32 @@ describe("verifyBTCSignature", () => {
712
878
  });
713
879
  });
714
880
 
881
+ describe("Trezor wallet quirks", () => {
882
+ it("handles Trezor P2SH-P2WPKH signature with segwit flag byte", async () => {
883
+ // Trezor sets the BIP-137 segwit flag (flag byte 35 = 27 + 8) for P2SH-P2WPKH addresses.
884
+ // The P2SH-P2WPKH branch must derive the address via hash160(redeemScript),
885
+ // not use the raw publicKeyHash directly.
886
+ const trezorP2shP2wpkhProof: SignatureProof = {
887
+ type: ProofTypes.BIP137,
888
+ address:
889
+ "bip122:000000000019d6689c085ae165831e93:3CNuGWYUR9osV6PRdkqHZ7cGBaum2XjYBQ",
890
+ did: "did:pkh:bip122:000000000019d6689c085ae165831e93:3CNuGWYUR9osV6PRdkqHZ7cGBaum2XjYBQ",
891
+ attestation:
892
+ "I certify that\n\nbip122:000000000019d6689c085ae165831e93 account 3CNuGWYUR9osV6PRdkqHZ7cGBaum2XjYBQ\n\nbelonged to undefined\n\non Tue, 17 Feb 2026 14:36:05 GMT",
893
+ proof:
894
+ "I74BHi+4QLDTQgxCJqOUvlWdB3BizwSC/tCPeqrCfvJfaF6pYbBZiOvMp4FWf5iswzpmoZ1MlB7G0HuXk7Anmn0=",
895
+ status: ProofStatus.PENDING,
896
+ wallet_provider: "Trezor",
897
+ };
898
+
899
+ const result = await verifyBTCSignature(trezorP2shP2wpkhProof);
900
+ expect(result).toEqual({
901
+ ...trezorP2shP2wpkhProof,
902
+ status: ProofStatus.VERIFIED,
903
+ });
904
+ });
905
+ });
906
+
715
907
  describe("Ledger wallet quirks", () => {
716
908
  it("handles Ledger bc1q signature without segwit flag", async () => {
717
909
  // Ledger signs bc1q addresses without setting segwit flag byte
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bitcoin.ts"],"names":["ProofStatus","ProofTypes","Verifier","base64","secp256k1","createBase58check","Hash","encodeLength","bech32"],"mappings":";;;;;;;;;;;;AA0BA,IAAM,aAAA,GAA6C;AAAA,EACjD,OAAA,EAAS;AAAA,IACP,aAAA,EAAe,4BAAA;AAAA,IACf,iBAAA,EAAmB,CAAA;AAAA;AAAA,IACnB,iBAAA,EAAmB,CAAA;AAAA;AAAA,IACnB,YAAA,EAAc,IAAA;AAAA,IACd,SAAA,EAAW;AAAA,GACb;AAAA,EACA,WAAA,EAAa;AAAA,IACX,aAAA,EAAe,4BAAA;AAAA,IACf,iBAAA,EAAmB,CAAA;AAAA;AAAA,IACnB,iBAAA,EAAmB,CAAA;AAAA;AAAA,IACnB,YAAA,EAAc,IAAA;AAAA,IACd,SAAA,EAAW;AAAA,GACb;AAAA,EACA,QAAA,EAAU;AAAA,IACR,aAAA,EAAe,6BAAA;AAAA,IACf,iBAAA,EAAmB,EAAA;AAAA;AAAA,IACnB,iBAAA,EAAmB,EAAA;AAAA;AAAA,IACnB,YAAA,EAAc,KAAA;AAAA,IACd,SAAA,EAAW;AAAA,GACb;AAAA,EACA,QAAA,EAAU;AAAA,IACR,aAAA,EAAe,6BAAA;AAAA,IACf,iBAAA,EAAmB,EAAA;AAAA;AAAA,IACnB,iBAAA,EAAmB,EAAA;AAAA;AAAA,IACnB,SAAA,EAAW;AAAA,GACb;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,aAAA,EAAe,6BAAA;AAAA,IACf,iBAAA,EAAmB,EAAA;AAAA;AAAA,IACnB,iBAAA,EAAmB,EAAA;AAAA;AAAA,IACnB,SAAA,EAAW;AAAA,GACb;AAAA,EACA,KAAA,EAAO;AAAA,IACL,aAAA,EAAe,0BAAA;AAAA,IACf,mBAAmB,UAAA,CAAW,IAAA,CAAK,CAAC,EAAA,EAAM,GAAI,CAAC,CAAA;AAAA;AAAA,IAC/C,mBAAmB,UAAA,CAAW,IAAA,CAAK,CAAC,EAAA,EAAM,GAAI,CAAC,CAAA;AAAA,IAC/C,SAAA,EAAW;AAAA,GACb;AAAA,EAEA,OAAA,EAAS;AAAA,IACP,aAAA,EAAe,4BAAA;AAAA,IACf,iBAAA,EAAmB,GAAA;AAAA;AAAA,IACnB,iBAAA,EAAmB,GAAA;AAAA;AAAA,IACnB,YAAA,EAAc,IAAA;AAAA,IACd,SAAA,EAAW;AAAA;AAEf,CAAA;AAaA,eAAsB,mBACpB,KAAA,EACyB;AACzB,EAAA,MAAM,CAAC,MAAM,OAAO,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,GAAG,CAAA;AAC/C,EAAA,IAAI,EAAA,KAAO,UAAU,OAAO,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQA,0BAAY,MAAA,EAAO;AAGnE,EAAA,MAAM,WAAA,GAAc,eAAe,OAAO,CAAA;AAC1C,EAAA,IAAI,CAAC,aAAa,OAAO,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQA,0BAAY,MAAA,EAAO;AAEhE,EAAA,MAAM,UAAU,OAAA,CAAQ,UAAA,CAAW,IAAI,CAAA,IAAK,OAAA,CAAQ,WAAW,IAAI,CAAA;AACnE,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO,YAAA,CAAa,OAAA,EAAS,KAAA,EAAO,WAAW,CAAA;AAAA,EACjD;AAGA,EAAA,IAAI,YAAY,SAAA,EAAW;AACzB,IAAA,OAAO,YAAA,CAAa,SAAS,KAAK,CAAA;AAAA,EACpC;AAGA,EAAA,MAAM,YAAY,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,IAAK,OAAA,CAAQ,WAAW,MAAM,CAAA;AAIzE,EAAA,IAAI,SAAA,IAAa,KAAA,CAAM,IAAA,KAASC,wBAAA,CAAW,MAAA,EAAQ;AACjD,IAAA,OAAO,YAAA,CAAa,SAAS,KAAK,CAAA;AAAA,EACpC;AAEA,EAAA,IAAI;AACF,IAAA,QAAQ,MAAM,IAAA;AAAM,MAClB,KAAKA,wBAAA,CAAW,MAAA;AACd,QAAA,OAAO,YAAA,CAAa,OAAA,EAAS,KAAA,EAAO,WAAW,CAAA;AAAA,MACjD,KAAKA,wBAAA,CAAW,MAAA;AACd,QAAA,OAAO,YAAA,CAAa,SAAS,KAAK,CAAA;AAAA,MACpC;AACE,QAAA,OAAO;AAAA,UACL,GAAG,KAAA;AAAA,UACH,QAAQD,yBAAA,CAAY;AAAA,SACtB;AAAA;AACJ,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO;AAAA,MACL,GAAG,KAAA;AAAA,MACH,QAAQA,yBAAA,CAAY;AAAA,KACtB;AAAA,EACF;AACF;AAEA,SAAS,eAAe,OAAA,EAA8B;AACpD,EAAA,IACE,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,IACtB,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,IACtB,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA,EACxB;AACA,IAAA,OAAO,cAAc,SAAS,CAAA;AAAA,EAChC;AACA,EAAA,IAAI,QAAQ,UAAA,CAAW,IAAI,KAAK,OAAA,CAAQ,UAAA,CAAW,IAAI,CAAA,EAAG;AACxD,IAAA,OAAO,cAAc,OAAO,CAAA;AAAA,EAC9B;AACA,EAAA,IACE,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,IACtB,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,IACtB,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,EACzB;AACA,IAAA,OAAO,cAAc,UAAU,CAAA;AAAA,EACjC;AACA,EAAA,IAAI,QAAQ,UAAA,CAAW,GAAG,KAAK,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AACtD,IAAA,OAAO,cAAc,UAAU,CAAA;AAAA,EACjC;AACA,EAAA,IAAI,QAAQ,UAAA,CAAW,GAAG,KAAK,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AACtD,IAAA,OAAO,cAAc,MAAM,CAAA;AAAA,EAC7B;AACA,EAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AAC3B,IAAA,OAAO,cAAc,aAAa,CAAA;AAAA,EACpC;AACA,EAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,KAAK,CAAA,EAAG;AAC7B,IAAA,OAAO,cAAc,SAAS,CAAA;AAAA,EAChC;AAEA,EAAA,OAAO,cAAc,SAAS,CAAA;AAChC;AAEA,SAAS,YAAA,CAAa,SAAiB,KAAA,EAAuB;AAC5D,EAAA,MAAM,EAAE,WAAA,EAAa,KAAA,EAAO,cAAA,EAAe,GAAI,KAAA;AAC/C,EAAA,MAAM,WAAWE,iBAAA,CAAS,eAAA;AAAA,IACxB,OAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO;AAAA,IACL,GAAG,KAAA;AAAA,IACH,MAAA,EAAQ,QAAA,GAAWF,yBAAA,CAAY,QAAA,GAAWA,yBAAA,CAAY;AAAA,GACxD;AACF;AAEA,SAAS,YAAA,CACP,OAAA,EACA,KAAA,EACA,WAAA,EACA;AACA,EAAA,MAAM,cAAA,GAAiB,kBAAkB,OAAO,CAAA;AAKhD,EAAA,MAAM,iBAAA,GAAoB,OAAA;AAAA,IACxB,WAAA,CAAY,iBACT,cAAA,KAAmB,eAAA,iBACjB,mBAAmB,QAAA,iBAAyB,CAAC,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA;AAAA,GAC1E;AAEA,EAAA,MAAM,QAAA,GAAW,MAAA;AAAA,IACf,KAAA,CAAM,WAAA;AAAA,IACN,OAAA;AAAA,IACA,KAAA,CAAM,KAAA;AAAA,IACN,iBAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO;AAAA,IACL,GAAG,KAAA;AAAA,IACH,MAAA,EAAQ,QAAA,GAAWA,yBAAA,CAAY,QAAA,GAAWA,yBAAA,CAAY;AAAA,GACxD;AACF;AAEA,SAAS,kBAAkB,OAAA,EAAiB;AAC1C,EAAA,IAAI,OAAA,CAAQ,KAAA,CAAM,mBAAmB,CAAA,EAAG;AACtC,IAAA,OAAO,eAAA;AAAA,EACT,CAAA,MAAA,IAAW,OAAA,CAAQ,KAAA,CAAM,UAAU,CAAA,EAAG;AACpC,IAAA,OAAO,QAAA;AAAA,EACT,CAAA,MAAA,IAAW,OAAA,CAAQ,KAAA,CAAM,WAAW,CAAA,EAAG;AACrC,IAAA,OAAO,QAAA;AAAA,EACT,CAAA,MAAA,IAAW,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA,EAAG;AAClC,IAAA,OAAO,UAAA;AAAA,EACT,CAAA,MAAA,IAAW,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA,EAAG;AAClC,IAAA,OAAO,cAAA;AAAA,EACT,CAAA,MAAA,IAAW,OAAA,CAAQ,KAAA,CAAM,YAAY,CAAA,EAAG;AACtC,IAAA,OAAO,QAAA;AAAA,EACT,CAAA,MAAA,IAAW,OAAA,CAAQ,KAAA,CAAM,SAAS,CAAA,EAAG;AACnC,IAAA,OAAO,QAAA;AAAA,EACT,CAAA,MAAO;AACL,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,mBAAA,CACG,MAAA,CAAO,OAAO,CAAA,CACd,OAAO,wCAAwC;AAAA,KACpD;AAAA,EACF;AACF;AAQA,SAAS,gBAAgB,KAAA,EAAiC;AACxD,EAAA,MAAM,QAAA,GAAWG,WAAA,CAAO,MAAA,CAAO,KAAK,CAAA;AACpC,EAAA,IAAI,SAAS,MAAA,KAAW,EAAA,EAAI,MAAM,IAAI,MAAM,0BAA0B,CAAA;AACtE,EAAA,MAAM,QAAA,GAAW,QAAA,CAAS,CAAC,CAAA,GAAI,EAAA;AAC/B,EAAA,IAAI,QAAA,GAAW,EAAA,IAAM,QAAA,GAAW,CAAA,EAAG;AACjC,IAAA,MAAM,IAAI,MAAM,6BAA6B,CAAA;AAAA,EAC/C;AACA,EAAA,MAAM,UAAA,GAAa,CAAC,EAAE,QAAA,GAAW,EAAA,CAAA;AACjC,EAAA,MAAM,WAAW,QAAA,GAAW,CAAA;AAC5B,EAAA,MAAM,YAAYC,mBAAA,CAAU,SAAA,CAAU,YAAY,QAAA,CAAS,KAAA,CAAM,CAAC,CAAC,CAAA;AAEnE,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,UAAA,EAAY,EAAE,QAAA,GAAW,CAAA,CAAA,GACrB,SACA,EAAE,QAAA,GAAW,KACb,cAAA,qBACA,QAAA;AAAA,IACJ,SAAA,EAAW,SAAA,CAAU,cAAA,CAAe,QAAQ;AAAA,GAC9C;AACF;AAEA,SAAS,MAAA,CACP,WAAA,EACA,OAAA,EACA,KAAA,EACA,mBACA,WAAA,EACA;AACA,EAAA,MAAM,EAAE,UAAA,EAAY,UAAA,EAAY,SAAA,EAAU,GAAI,gBAAgB,KAAK,CAAA;AACnE,EAAA,IAAI,iBAAA,IAAqB,CAAC,UAAA,EAAY;AACpC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAO,SAAA,CAAU,WAAA,EAAa,WAAA,CAAY,aAAa,CAAA;AAC7D,EAAA,MAAM,SAAA,GAAY,SAAA,CAAU,gBAAA,CAAiB,IAAI,CAAA;AACjD,EAAA,MAAM,cAAA,GAAiB,SAAA,CAAU,UAAA,CAAW,UAAU,CAAA;AACtD,EAAA,MAAM,aAAA,GAAgB,QAAQ,cAAc,CAAA;AAC5C,EAAA,IAAI,MAAA,GAAiB,EAAA;AAGrB,EAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AAG3B,IAAA,MAAA,GAAS,yBAAA;AAAA,MACP,WAAA,CAAY,iBAAA;AAAA,MACZ;AAAA,KACF;AAGA,IAAA,OAAO,MAAA,CAAO,WAAW,GAAG,CAAA;AAAA,EAC9B;AAEA,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,IAAI,eAAe,cAAA,oBAA0B;AAC3C,MAAA,MAAA,GAAS,yBAAA;AAAA,QACP,WAAA,CAAY,iBAAA;AAAA,QACZ;AAAA,OACF;AAAA,IACF,CAAA,MAAO;AAEL,MAAA,IAAI,YAAY,YAAA,EAAc;AAC5B,QAAA,MAAA,GAAS,mBAAA,CAAoB,aAAA,EAAe,WAAA,CAAY,YAAY,CAAA;AAAA,MACtE,CAAA,MAAO;AAEL,QAAA,MAAA,GAAS,yBAAA;AAAA,UACP,WAAA,CAAY,iBAAA;AAAA,UACZ;AAAA,SACF;AAAA,MAEF;AAAA,IACF;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,IAAK,CAAC,UAAA,EAAY;AAE1C,MAAA,MAAM,YAAA,GAAe,IAAI,UAAA,CAAW,EAAE,CAAA;AACtC,MAAA,YAAA,CAAa,CAAC,CAAA,GAAI,CAAA;AAClB,MAAA,YAAA,CAAa,CAAC,CAAA,GAAI,EAAA;AAClB,MAAA,YAAA,CAAa,GAAA,CAAI,eAAe,CAAC,CAAA;AACjC,MAAA,MAAM,gBAAA,GAAmB,QAAQ,YAAY,CAAA;AAC7C,MAAA,MAAM,UAAA,GAAa,yBAAA;AAAA,QACjB,WAAA,CAAY,iBAAA;AAAA,QACZ;AAAA,OACF;AAEA,MAAA,MAAM,UAAA,GAAa,yBAAA;AAAA,QACjB,WAAA,CAAY,iBAAA;AAAA,QACZ;AAAA,OACF;AACA,MAAA,IAAI,OAAA,KAAY,UAAA,IAAc,OAAA,KAAY,UAAA,EAAY;AACpD,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,MAAA,GAAS,UAAA;AAAA,IACX,CAAA,MAAA,IAAW,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,IAAK,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,IAAK,OAAA,CAAQ,UAAA,CAAW,OAAO,CAAA,EAAG;AAGlG,MAAA,IAAI,YAAY,YAAA,EAAc;AAC5B,QAAA,MAAA,GAAS,mBAAA,CAAoB,aAAA,EAAe,WAAA,CAAY,YAAY,CAAA;AAAA,MACtE,CAAA,MAAO;AACL,QAAA,MAAA,GAAS,yBAAA;AAAA,UACP,WAAA,CAAY,iBAAA;AAAA,UACZ;AAAA,SACF;AAAA,MACF;AAAA,IACF,CAAA,MAAA,IAAW,iBAAA,IAAqB,WAAA,CAAY,YAAA,EAAc;AACxD,MAAA,IAAI;AACF,QAAA,MAAA,GAAS,mBAAA,CAAoB,aAAA,EAAe,WAAA,CAAY,YAAY,CAAA;AAAA,MAGtE,SAAS,CAAA,EAAG;AACV,QAAA,MAAA,GAAS,yBAAA;AAAA,UACP,WAAA,CAAY,iBAAA;AAAA,UACZ;AAAA,SACF;AAAA,MAEF;AAAA,IACF,CAAA,MAAO;AACL,MAAA,MAAA,GAAS,yBAAA;AAAA,QACP,WAAA,CAAY,iBAAA;AAAA,QACZ;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA,KAAW,OAAA;AACpB;AAEA,IAAM,WAAA,GAAcC,sBAAA,CAAkBC,OAAA,CAAK,MAAM,CAAA;AAEjD,SAAS,yBAAA,CACP,SACA,aAAA,EACA;AACA,EAAA,MAAM,cACJ,OAAO,OAAA,KAAY,WAAW,UAAA,CAAW,EAAA,CAAG,OAAO,CAAA,GAAI,OAAA;AAEzD,EAAA,MAAM,UAAU,IAAI,UAAA,CAAW,WAAA,CAAY,MAAA,GAAS,cAAc,MAAM,CAAA;AACxE,EAAA,OAAA,CAAQ,IAAI,WAAW,CAAA;AACvB,EAAA,OAAA,CAAQ,GAAA,CAAI,aAAA,EAAe,WAAA,CAAY,MAAM,CAAA;AAC7C,EAAA,OAAO,WAAA,CAAY,OAAO,OAAO,CAAA;AACnC;AAEA,SAAS,SAAA,CAAU,aAAqB,aAAA,EAAuB;AAC7D,EAAA,MAAM,MAAA,GAAS,IAAI,WAAA,EAAY,CAAE,OAAO,aAAa,CAAA;AACrD,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY,CAAE,OAAO,WAAW,CAAA;AACpD,EAAA,MAAM,MAAA,GAASC,qBAAA,CAAa,OAAA,CAAQ,MAAM,CAAA,CAAE,MAAA;AAC5C,EAAA,MAAM,SAAS,IAAI,UAAA;AAAA,IACjB,MAAA,CAAO,MAAA,GAAS,MAAA,CAAO,UAAA,GAAa,OAAA,CAAQ;AAAA,GAC9C;AACA,EAAA,MAAA,CAAO,IAAI,MAAM,CAAA;AACjB,EAAA,MAAA,CAAO,IAAI,IAAI,UAAA,CAAW,MAAM,CAAA,EAAG,OAAO,MAAM,CAAA;AAChD,EAAA,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,MAAA,GAAS,OAAO,UAAU,CAAA;AACrD,EAAA,OAAO,QAAQ,MAAM,CAAA;AACvB;AAEA,SAAS,mBAAA,CACP,aAAA,EACA,MAAA,GAAiB,IAAA,EACT;AACR,EAAA,MAAM,MAAA,GAASC,WAAA,CAAO,OAAA,CAAQ,aAAa,CAAA;AAC3C,EAAA,MAAA,CAAO,QAAQ,CAAC,CAAA;AAChB,EAAA,OAAOA,WAAA,CAAO,MAAA,CAAO,MAAA,EAAQ,MAAM,CAAA;AACrC;AAEA,SAAS,QAAQ,MAAA,EAAgC;AAC/C,EAAA,OAAOF,OAAA,CAAK,MAAA,CAAOA,OAAA,CAAK,MAAA,CAAO,MAAM,CAAC,CAAA;AACxC;AAEA,SAAS,QAAQ,MAAA,EAAgC;AAC/C,EAAA,OAAOA,OAAA,CAAK,SAAA,CAAUA,OAAA,CAAK,MAAA,CAAO,MAAM,CAAC,CAAA;AAC3C","file":"bitcoin-3CW4MNAW.cjs","sourcesContent":["import {\n ProofStatus,\n ProofTypes,\n SignatureProof,\n} from \"@notabene/javascript-sdk\";\n\nimport { encode as encodeLength } from \"varuint-bitcoin\";\nimport { base64, bech32, createBase58check } from \"@scure/base\";\nimport { Hash } from \"ox\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { SignatureType } from \"@noble/curves/abstract/weierstrass\";\nimport { Verifier } from \"bip322-js\";\n\nenum SEGWIT_TYPES {\n P2WPKH = \"p2wpkh\",\n P2SH_P2WPKH = \"p2sh(p2wpkh)\",\n}\n\ninterface ChainConfig {\n messagePrefix: string;\n pubKeyHashVersion: number | Uint8Array;\n scriptHashVersion: number | Uint8Array;\n bech32Prefix?: string;\n isTestnet?: boolean;\n}\n\nconst CHAIN_CONFIGS: Record<string, ChainConfig> = {\n bitcoin: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x00, // 1...\n scriptHashVersion: 0x05, // 3...\n bech32Prefix: \"bc\",\n isTestnet: false,\n },\n bitcoincash: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x00, // 1...\n scriptHashVersion: 0x05, // 3...\n bech32Prefix: \"bc\",\n isTestnet: false,\n },\n litecoin: {\n messagePrefix: \"\\u0019Litecoin Signed Message:\\n\",\n pubKeyHashVersion: 0x30, // L... or M...\n scriptHashVersion: 0x32, // 3... or M...\n bech32Prefix: \"ltc\",\n isTestnet: false,\n },\n dogecoin: {\n messagePrefix: \"\\u0019Dogecoin Signed Message:\\n\",\n pubKeyHashVersion: 0x1e, // D...\n scriptHashVersion: 0x16, // A...\n isTestnet: false,\n },\n dash: {\n messagePrefix: \"\\u0019DarkCoin Signed Message:\\n\",\n pubKeyHashVersion: 0x4c, // X...\n scriptHashVersion: 0x10, // 7...\n isTestnet: false,\n },\n zcash: {\n messagePrefix: \"\\u0018Zcash Signed Message:\\n\",\n pubKeyHashVersion: Uint8Array.from([0x1c, 0xb8]), // <-- FIXED\n scriptHashVersion: Uint8Array.from([0x1c, 0xbd]),\n isTestnet: false,\n },\n\n testnet: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x6f, // m or n\n scriptHashVersion: 0xc4, // 2\n bech32Prefix: \"tb\",\n isTestnet: true,\n },\n};\n\nenum DerivationMode {\n LEGACY = \"Legacy\",\n NATIVE = \"Native SegWit\",\n SEGWIT = \"SegWit\",\n P2SH_SEGWIT = \"p2sh\",\n BCH = \"Bitcoin Cash\",\n ETHEREUM = \"Ethereum\",\n DOGECOIN = \"Dogecoin\",\n UNKNOWN = \"Unknown\",\n}\n\nexport async function verifyBTCSignature(\n proof: SignatureProof\n): Promise<SignatureProof> {\n const [ns, , address] = proof.address.split(/:/);\n if (ns !== \"bip122\") return { ...proof, status: ProofStatus.FAILED };\n\n // Map chainId to our chain configuration\n const chainConfig = getChainConfig(address);\n if (!chainConfig) return { ...proof, status: ProofStatus.FAILED };\n\n const isZcash = address.startsWith(\"t1\") || address.startsWith(\"t3\");\n if (isZcash) {\n return verifyBIP137(address, proof, chainConfig);\n }\n\n // Use BIP322 for testnet addresses\n if (chainConfig.isTestnet) {\n return verifyBIP322(address, proof);\n }\n\n // Check if this is a Taproot address (bc1p or tb1p)\n const isTaproot = address.startsWith(\"bc1p\") || address.startsWith(\"tb1p\");\n\n // For Taproot addresses with BIP-137 proof type, use BIP-322 verification\n // since BIP-137 doesn't officially support Taproot\n if (isTaproot && proof.type === ProofTypes.BIP137) {\n return verifyBIP322(address, proof);\n }\n\n try {\n switch (proof.type) {\n case ProofTypes.BIP137:\n return verifyBIP137(address, proof, chainConfig);\n case ProofTypes.BIP322:\n return verifyBIP322(address, proof);\n default:\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n } catch {\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n}\n\nfunction getChainConfig(address: string): ChainConfig {\n if (\n address.startsWith(\"1\") ||\n address.startsWith(\"3\") ||\n address.startsWith(\"bc1\")\n ) {\n return CHAIN_CONFIGS[\"bitcoin\"];\n }\n if (address.startsWith(\"t1\") || address.startsWith(\"t3\")) {\n return CHAIN_CONFIGS[\"zcash\"];\n }\n if (\n address.startsWith(\"L\") ||\n address.startsWith(\"M\") ||\n address.startsWith(\"ltc1\")\n ) {\n return CHAIN_CONFIGS[\"litecoin\"];\n }\n if (address.startsWith(\"D\") || address.startsWith(\"A\")) {\n return CHAIN_CONFIGS[\"dogecoin\"];\n }\n if (address.startsWith(\"X\") || address.startsWith(\"7\")) {\n return CHAIN_CONFIGS[\"dash\"];\n }\n if (address.startsWith(\"q\")) {\n return CHAIN_CONFIGS[\"bitcoincash\"];\n }\n if (address.startsWith(\"tb1\")) {\n return CHAIN_CONFIGS[\"testnet\"];\n }\n\n return CHAIN_CONFIGS[\"bitcoin\"];\n}\n\nfunction verifyBIP322(address: string, proof: SignatureProof) {\n const { attestation, proof: signatureProof } = proof;\n const verified = Verifier.verifySignature(\n address,\n attestation,\n signatureProof\n );\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n\nfunction verifyBIP137(\n address: string,\n proof: SignatureProof,\n chainConfig: ChainConfig\n) {\n const derivationMode = getDerivationMode(address);\n\n // For legacy addresses (starting with \"1\"), never use SegWit encoding\n // For P2SH addresses (starting with \"3\"), use SegWit encoding if they have bech32 support\n // For native SegWit addresses (bc1, tb1, ltc1), always use SegWit encoding\n const useSegwitEncoding = Boolean(\n chainConfig.bech32Prefix &&\n (derivationMode === DerivationMode.NATIVE ||\n (derivationMode === DerivationMode.SEGWIT && !address.startsWith(\"1\")))\n );\n\n const verified = verify(\n proof.attestation,\n address,\n proof.proof,\n useSegwitEncoding,\n chainConfig\n );\n\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n\nfunction getDerivationMode(address: string) {\n if (address.match(\"^(bc1|tb1|ltc1).*\")) {\n return DerivationMode.NATIVE;\n } else if (address.match(\"^[32M].*\")) {\n return DerivationMode.SEGWIT;\n } else if (address.match(\"^[1nmL].*\")) {\n return DerivationMode.LEGACY;\n } else if (address.match(\"^(D).*\")) {\n return DerivationMode.DOGECOIN;\n } else if (address.match(\"^(q).*\")) {\n return DerivationMode.BCH;\n } else if (address.match(\"^(t1|t3).*\")) {\n return DerivationMode.LEGACY; // Zcash addresses\n } else if (address.match(\"^[X7].*\")) {\n return DerivationMode.LEGACY; // Dash addresses\n } else {\n throw new Error(\n \"INVALID ADDRESS: \"\n .concat(address)\n .concat(\" is not a valid or a supported address\")\n );\n }\n}\n\ntype DecodedSignature = {\n compressed: boolean;\n segwitType?: SEGWIT_TYPES;\n signature: SignatureType;\n};\n\nfunction decodeSignature(proof: string): DecodedSignature {\n const sigbytes = base64.decode(proof);\n if (sigbytes.length !== 65) throw new Error(\"Invalid signature length\");\n const flagByte = sigbytes[0] - 27;\n if (flagByte > 15 || flagByte < 0) {\n throw new Error(\"Invalid signature parameter\");\n }\n const compressed = !!(flagByte & 12); // Are there cases that aren't compressed?\n const recovery = flagByte & 3;\n const signature = secp256k1.Signature.fromCompact(sigbytes.slice(1));\n\n return {\n compressed,\n segwitType: !(flagByte & 8)\n ? undefined\n : !(flagByte & 4)\n ? SEGWIT_TYPES.P2SH_P2WPKH\n : SEGWIT_TYPES.P2WPKH,\n signature: signature.addRecoveryBit(recovery),\n };\n}\n\nfunction verify(\n attestation: string,\n address: string,\n proof: string,\n checkSegwitAlways: boolean,\n chainConfig: ChainConfig\n) {\n const { compressed, segwitType, signature } = decodeSignature(proof);\n if (checkSegwitAlways && !compressed) {\n throw new Error(\n \"checkSegwitAlways can only be used with a compressed pubkey signature flagbyte\"\n );\n }\n const hash = magicHash(attestation, chainConfig.messagePrefix);\n const publicKey = signature.recoverPublicKey(hash);\n const publicKeyBytes = publicKey.toRawBytes(compressed);\n const publicKeyHash = hash160(publicKeyBytes);\n let actual: string = \"\";\n\n // Special handling for Bitcoin Cash addresses\n if (address.startsWith(\"q\")) {\n // For BCH, we'll compare the public key hash directly since we're getting a CashAddr\n // Convert the CashAddr to legacy format for comparison\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n // Legacy P2PKH addresses in BCH start with '1' just like BTC\n // Source: https://reference.cash/protocol/blockchain/encoding/cashaddr#legacy-address-format\n return actual.startsWith(\"1\");\n }\n\n if (segwitType) {\n if (segwitType === SEGWIT_TYPES.P2SH_P2WPKH) {\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n } else {\n // parsed.segwitType === SEGWIT_TYPES.P2WPKH\n if (chainConfig.bech32Prefix) {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n } else {\n // Fallback to legacy if bech32 not supported\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n // base58 can be p2pkh or p2sh-p2wpkh\n }\n }\n } else {\n // For addresses starting with \"3\" (P2SH), try both P2SH-P2WPKH and legacy P2SH encodings if segwitType is undefined\n if (address.startsWith(\"3\") && !segwitType) {\n // P2SH-P2WPKH: script hash of the redeem script (OP_0 <pubkeyhash>)\n const redeemScript = new Uint8Array(22);\n redeemScript[0] = 0x00; // OP_0\n redeemScript[1] = 0x14; // push 20 bytes\n redeemScript.set(publicKeyHash, 2);\n const redeemScriptHash = hash160(redeemScript);\n const p2shP2wpkh = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n redeemScriptHash\n );\n // Legacy P2SH: script hash of the public key\n const legacyP2sh = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n if (address === p2shP2wpkh || address === legacyP2sh) {\n return true;\n }\n actual = legacyP2sh; // fallback for error reporting\n } else if (address.startsWith(\"bc1q\") || address.startsWith(\"tb1q\") || address.startsWith(\"ltc1q\")) {\n // For native SegWit P2WPKH addresses (bc1q/tb1q/ltc1q), always encode as bech32\n // This handles Ledger wallets that sign without segwit flags\n if (chainConfig.bech32Prefix) {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n } else {\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n }\n } else if (checkSegwitAlways && chainConfig.bech32Prefix) {\n try {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n // if address is bech32 it is not p2sh\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (e) {\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n // base58 can be p2pkh or p2sh-p2wpkh\n }\n } else {\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n }\n }\n\n return actual === address;\n}\n\nconst base58check = createBase58check(Hash.sha256);\n\nfunction encodeBase58AddressFormat(\n version: number | Uint8Array,\n publicKeyHash: Uint8Array\n) {\n const prefixBytes =\n typeof version === \"number\" ? Uint8Array.of(version) : version; // Accept raw Uint8Array for Zcash\n\n const payload = new Uint8Array(prefixBytes.length + publicKeyHash.length);\n payload.set(prefixBytes);\n payload.set(publicKeyHash, prefixBytes.length);\n return base58check.encode(payload);\n}\n\nfunction magicHash(attestation: string, messagePrefix: string) {\n const prefix = new TextEncoder().encode(messagePrefix);\n const message = new TextEncoder().encode(attestation);\n const length = encodeLength(message.length).buffer;\n const buffer = new Uint8Array(\n prefix.length + length.byteLength + message.length\n );\n buffer.set(prefix);\n buffer.set(new Uint8Array(length), prefix.length);\n buffer.set(message, prefix.length + length.byteLength);\n return hash256(buffer);\n}\n\nfunction encodeBech32Address(\n publicKeyHash: Uint8Array,\n prefix: string = \"bc\"\n): string {\n const bwords = bech32.toWords(publicKeyHash);\n bwords.unshift(0);\n return bech32.encode(prefix, bwords);\n}\n\nfunction hash256(buffer: Uint8Array): Uint8Array {\n return Hash.sha256(Hash.sha256(buffer));\n}\n\nfunction hash160(buffer: Uint8Array): Uint8Array {\n return Hash.ripemd160(Hash.sha256(buffer));\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bitcoin.ts"],"names":["encodeLength"],"mappings":"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","file":"bitcoin-QK53ILBF.js","sourcesContent":["import {\n ProofStatus,\n ProofTypes,\n SignatureProof,\n} from \"@notabene/javascript-sdk\";\n\nimport { encode as encodeLength } from \"varuint-bitcoin\";\nimport { base64, bech32, createBase58check } from \"@scure/base\";\nimport { Hash } from \"ox\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { SignatureType } from \"@noble/curves/abstract/weierstrass\";\nimport { Verifier } from \"bip322-js\";\n\nenum SEGWIT_TYPES {\n P2WPKH = \"p2wpkh\",\n P2SH_P2WPKH = \"p2sh(p2wpkh)\",\n}\n\ninterface ChainConfig {\n messagePrefix: string;\n pubKeyHashVersion: number | Uint8Array;\n scriptHashVersion: number | Uint8Array;\n bech32Prefix?: string;\n isTestnet?: boolean;\n}\n\nconst CHAIN_CONFIGS: Record<string, ChainConfig> = {\n bitcoin: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x00, // 1...\n scriptHashVersion: 0x05, // 3...\n bech32Prefix: \"bc\",\n isTestnet: false,\n },\n bitcoincash: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x00, // 1...\n scriptHashVersion: 0x05, // 3...\n bech32Prefix: \"bc\",\n isTestnet: false,\n },\n litecoin: {\n messagePrefix: \"\\u0019Litecoin Signed Message:\\n\",\n pubKeyHashVersion: 0x30, // L... or M...\n scriptHashVersion: 0x32, // 3... or M...\n bech32Prefix: \"ltc\",\n isTestnet: false,\n },\n dogecoin: {\n messagePrefix: \"\\u0019Dogecoin Signed Message:\\n\",\n pubKeyHashVersion: 0x1e, // D...\n scriptHashVersion: 0x16, // A...\n isTestnet: false,\n },\n dash: {\n messagePrefix: \"\\u0019DarkCoin Signed Message:\\n\",\n pubKeyHashVersion: 0x4c, // X...\n scriptHashVersion: 0x10, // 7...\n isTestnet: false,\n },\n zcash: {\n messagePrefix: \"\\u0018Zcash Signed Message:\\n\",\n pubKeyHashVersion: Uint8Array.from([0x1c, 0xb8]), // <-- FIXED\n scriptHashVersion: Uint8Array.from([0x1c, 0xbd]),\n isTestnet: false,\n },\n\n testnet: {\n messagePrefix: \"\\u0018Bitcoin Signed Message:\\n\",\n pubKeyHashVersion: 0x6f, // m or n\n scriptHashVersion: 0xc4, // 2\n bech32Prefix: \"tb\",\n isTestnet: true,\n },\n};\n\nenum DerivationMode {\n LEGACY = \"Legacy\",\n NATIVE = \"Native SegWit\",\n SEGWIT = \"SegWit\",\n P2SH_SEGWIT = \"p2sh\",\n BCH = \"Bitcoin Cash\",\n ETHEREUM = \"Ethereum\",\n DOGECOIN = \"Dogecoin\",\n UNKNOWN = \"Unknown\",\n}\n\nexport async function verifyBTCSignature(\n proof: SignatureProof\n): Promise<SignatureProof> {\n const [ns, , address] = proof.address.split(/:/);\n if (ns !== \"bip122\") return { ...proof, status: ProofStatus.FAILED };\n\n // Map chainId to our chain configuration\n const chainConfig = getChainConfig(address);\n if (!chainConfig) return { ...proof, status: ProofStatus.FAILED };\n\n const isZcash = address.startsWith(\"t1\") || address.startsWith(\"t3\");\n if (isZcash) {\n return verifyBIP137(address, proof, chainConfig);\n }\n\n // Use BIP322 for testnet addresses\n if (chainConfig.isTestnet) {\n return verifyBIP322(address, proof);\n }\n\n // Check if this is a Taproot address (bc1p or tb1p)\n const isTaproot = address.startsWith(\"bc1p\") || address.startsWith(\"tb1p\");\n\n // For Taproot addresses with BIP-137 proof type, use BIP-322 verification\n // since BIP-137 doesn't officially support Taproot\n if (isTaproot && proof.type === ProofTypes.BIP137) {\n return verifyBIP322(address, proof);\n }\n\n try {\n switch (proof.type) {\n case ProofTypes.BIP137:\n return verifyBIP137(address, proof, chainConfig);\n case ProofTypes.BIP322:\n return verifyBIP322(address, proof);\n default:\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n } catch {\n return {\n ...proof,\n status: ProofStatus.FAILED,\n };\n }\n}\n\nfunction getChainConfig(address: string): ChainConfig {\n if (\n address.startsWith(\"1\") ||\n address.startsWith(\"3\") ||\n address.startsWith(\"bc1\")\n ) {\n return CHAIN_CONFIGS[\"bitcoin\"];\n }\n if (address.startsWith(\"t1\") || address.startsWith(\"t3\")) {\n return CHAIN_CONFIGS[\"zcash\"];\n }\n if (\n address.startsWith(\"L\") ||\n address.startsWith(\"M\") ||\n address.startsWith(\"ltc1\")\n ) {\n return CHAIN_CONFIGS[\"litecoin\"];\n }\n if (address.startsWith(\"D\") || address.startsWith(\"A\")) {\n return CHAIN_CONFIGS[\"dogecoin\"];\n }\n if (address.startsWith(\"X\") || address.startsWith(\"7\")) {\n return CHAIN_CONFIGS[\"dash\"];\n }\n if (address.startsWith(\"q\")) {\n return CHAIN_CONFIGS[\"bitcoincash\"];\n }\n if (address.startsWith(\"tb1\")) {\n return CHAIN_CONFIGS[\"testnet\"];\n }\n\n return CHAIN_CONFIGS[\"bitcoin\"];\n}\n\nfunction verifyBIP322(address: string, proof: SignatureProof) {\n const { attestation, proof: signatureProof } = proof;\n const verified = Verifier.verifySignature(\n address,\n attestation,\n signatureProof\n );\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n\nfunction verifyBIP137(\n address: string,\n proof: SignatureProof,\n chainConfig: ChainConfig\n) {\n const derivationMode = getDerivationMode(address);\n\n // For legacy addresses (starting with \"1\"), never use SegWit encoding\n // For P2SH addresses (starting with \"3\"), use SegWit encoding if they have bech32 support\n // For native SegWit addresses (bc1, tb1, ltc1), always use SegWit encoding\n const useSegwitEncoding = Boolean(\n chainConfig.bech32Prefix &&\n (derivationMode === DerivationMode.NATIVE ||\n (derivationMode === DerivationMode.SEGWIT && !address.startsWith(\"1\")))\n );\n\n const verified = verify(\n proof.attestation,\n address,\n proof.proof,\n useSegwitEncoding,\n chainConfig\n );\n\n return {\n ...proof,\n status: verified ? ProofStatus.VERIFIED : ProofStatus.FAILED,\n };\n}\n\nfunction getDerivationMode(address: string) {\n if (address.match(\"^(bc1|tb1|ltc1).*\")) {\n return DerivationMode.NATIVE;\n } else if (address.match(\"^[32M].*\")) {\n return DerivationMode.SEGWIT;\n } else if (address.match(\"^[1nmL].*\")) {\n return DerivationMode.LEGACY;\n } else if (address.match(\"^(D).*\")) {\n return DerivationMode.DOGECOIN;\n } else if (address.match(\"^(q).*\")) {\n return DerivationMode.BCH;\n } else if (address.match(\"^(t1|t3).*\")) {\n return DerivationMode.LEGACY; // Zcash addresses\n } else if (address.match(\"^[X7].*\")) {\n return DerivationMode.LEGACY; // Dash addresses\n } else {\n throw new Error(\n \"INVALID ADDRESS: \"\n .concat(address)\n .concat(\" is not a valid or a supported address\")\n );\n }\n}\n\ntype DecodedSignature = {\n compressed: boolean;\n segwitType?: SEGWIT_TYPES;\n signature: SignatureType;\n};\n\nfunction decodeSignature(proof: string): DecodedSignature {\n const sigbytes = base64.decode(proof);\n if (sigbytes.length !== 65) throw new Error(\"Invalid signature length\");\n const flagByte = sigbytes[0] - 27;\n if (flagByte > 15 || flagByte < 0) {\n throw new Error(\"Invalid signature parameter\");\n }\n const compressed = !!(flagByte & 12); // Are there cases that aren't compressed?\n const recovery = flagByte & 3;\n const signature = secp256k1.Signature.fromCompact(sigbytes.slice(1));\n\n return {\n compressed,\n segwitType: !(flagByte & 8)\n ? undefined\n : !(flagByte & 4)\n ? SEGWIT_TYPES.P2SH_P2WPKH\n : SEGWIT_TYPES.P2WPKH,\n signature: signature.addRecoveryBit(recovery),\n };\n}\n\nfunction verify(\n attestation: string,\n address: string,\n proof: string,\n checkSegwitAlways: boolean,\n chainConfig: ChainConfig\n) {\n const { compressed, segwitType, signature } = decodeSignature(proof);\n if (checkSegwitAlways && !compressed) {\n throw new Error(\n \"checkSegwitAlways can only be used with a compressed pubkey signature flagbyte\"\n );\n }\n const hash = magicHash(attestation, chainConfig.messagePrefix);\n const publicKey = signature.recoverPublicKey(hash);\n const publicKeyBytes = publicKey.toRawBytes(compressed);\n const publicKeyHash = hash160(publicKeyBytes);\n let actual: string = \"\";\n\n // Special handling for Bitcoin Cash addresses\n if (address.startsWith(\"q\")) {\n // For BCH, we'll compare the public key hash directly since we're getting a CashAddr\n // Convert the CashAddr to legacy format for comparison\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n // Legacy P2PKH addresses in BCH start with '1' just like BTC\n // Source: https://reference.cash/protocol/blockchain/encoding/cashaddr#legacy-address-format\n return actual.startsWith(\"1\");\n }\n\n if (segwitType) {\n if (segwitType === SEGWIT_TYPES.P2SH_P2WPKH) {\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n } else {\n // parsed.segwitType === SEGWIT_TYPES.P2WPKH\n if (chainConfig.bech32Prefix) {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n } else {\n // Fallback to legacy if bech32 not supported\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n // base58 can be p2pkh or p2sh-p2wpkh\n }\n }\n } else {\n // For addresses starting with \"3\" (P2SH), try both P2SH-P2WPKH and legacy P2SH encodings if segwitType is undefined\n if (address.startsWith(\"3\") && !segwitType) {\n // P2SH-P2WPKH: script hash of the redeem script (OP_0 <pubkeyhash>)\n const redeemScript = new Uint8Array(22);\n redeemScript[0] = 0x00; // OP_0\n redeemScript[1] = 0x14; // push 20 bytes\n redeemScript.set(publicKeyHash, 2);\n const redeemScriptHash = hash160(redeemScript);\n const p2shP2wpkh = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n redeemScriptHash\n );\n // Legacy P2SH: script hash of the public key\n const legacyP2sh = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n if (address === p2shP2wpkh || address === legacyP2sh) {\n return true;\n }\n actual = legacyP2sh; // fallback for error reporting\n } else if (address.startsWith(\"bc1q\") || address.startsWith(\"tb1q\") || address.startsWith(\"ltc1q\")) {\n // For native SegWit P2WPKH addresses (bc1q/tb1q/ltc1q), always encode as bech32\n // This handles Ledger wallets that sign without segwit flags\n if (chainConfig.bech32Prefix) {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n } else {\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n }\n } else if (checkSegwitAlways && chainConfig.bech32Prefix) {\n try {\n actual = encodeBech32Address(publicKeyHash, chainConfig.bech32Prefix);\n // if address is bech32 it is not p2sh\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n } catch (e) {\n actual = encodeBase58AddressFormat(\n chainConfig.scriptHashVersion,\n publicKeyHash\n );\n // base58 can be p2pkh or p2sh-p2wpkh\n }\n } else {\n actual = encodeBase58AddressFormat(\n chainConfig.pubKeyHashVersion,\n publicKeyHash\n );\n }\n }\n\n return actual === address;\n}\n\nconst base58check = createBase58check(Hash.sha256);\n\nfunction encodeBase58AddressFormat(\n version: number | Uint8Array,\n publicKeyHash: Uint8Array\n) {\n const prefixBytes =\n typeof version === \"number\" ? Uint8Array.of(version) : version; // Accept raw Uint8Array for Zcash\n\n const payload = new Uint8Array(prefixBytes.length + publicKeyHash.length);\n payload.set(prefixBytes);\n payload.set(publicKeyHash, prefixBytes.length);\n return base58check.encode(payload);\n}\n\nfunction magicHash(attestation: string, messagePrefix: string) {\n const prefix = new TextEncoder().encode(messagePrefix);\n const message = new TextEncoder().encode(attestation);\n const length = encodeLength(message.length).buffer;\n const buffer = new Uint8Array(\n prefix.length + length.byteLength + message.length\n );\n buffer.set(prefix);\n buffer.set(new Uint8Array(length), prefix.length);\n buffer.set(message, prefix.length + length.byteLength);\n return hash256(buffer);\n}\n\nfunction encodeBech32Address(\n publicKeyHash: Uint8Array,\n prefix: string = \"bc\"\n): string {\n const bwords = bech32.toWords(publicKeyHash);\n bwords.unshift(0);\n return bech32.encode(prefix, bwords);\n}\n\nfunction hash256(buffer: Uint8Array): Uint8Array {\n return Hash.sha256(Hash.sha256(buffer));\n}\n\nfunction hash160(buffer: Uint8Array): Uint8Array {\n return Hash.ripemd160(Hash.sha256(buffer));\n}\n"]}