UNPKG

chaingate

Version:

Multi-chain cryptocurrency SDK for TypeScript — unified API for Bitcoin, Ethereum, Litecoin, Dogecoin, Bitcoin Cash, Polygon, Arbitrum, and any EVM-compatible chain. Create wallets, query balances, send transactions, and manage tokens and NFTs across UTXO

195 lines (194 loc) 7.93 kB
"use strict"; /** * Bitcoin Cash transaction signing and address derivation. * @internal */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.publicKeyToHash160 = publicKeyToHash160; exports.signBchTransaction = signBchTransaction; const secp256k1_js_1 = require("@noble/curves/secp256k1.js"); const hmac_js_1 = require("@noble/hashes/hmac.js"); const legacy_js_1 = require("@noble/hashes/legacy.js"); const sha2_js_1 = require("@noble/hashes/sha2.js"); const btc = __importStar(require("@scure/btc-signer")); const btc_signer_1 = require("@scure/btc-signer"); const encoding_1 = require("../../../utils/encoding"); const SIGHASH_BCH = 0x41; const CURVE_ORDER = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n; const HALF_ORDER = CURVE_ORDER / 2n; const G = secp256k1_js_1.secp256k1.Point.BASE; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- function concat(...arrays) { const len = arrays.reduce((a, b) => a + b.length, 0); const result = new Uint8Array(len); let offset = 0; for (const arr of arrays) { result.set(arr, offset); offset += arr.length; } return result; } function hmacSha256(data, key) { return new Uint8Array((0, hmac_js_1.hmac)(sha2_js_1.sha256, key, data)); } function bytesToBigInt(bytes) { return BigInt('0x' + (0, encoding_1.bytesToHex)(bytes)); } function modInverse(a, m) { let [old_r, r] = [((a % m) + m) % m, m]; let [old_s, s] = [1n, 0n]; while (r !== 0n) { const q = old_r / r; [old_r, r] = [r, old_r - q * r]; [old_s, s] = [s, old_s - q * s]; } return ((old_s % m) + m) % m; } // --------------------------------------------------------------------------- // RFC 6979 deterministic k (bitcore-lib-cash compatible) // --------------------------------------------------------------------------- // BCH-compatible deterministic k generation (double-v-hash variant). function getDeterministicK(hash, privkey) { let v = new Uint8Array(32).fill(0x01); let k = new Uint8Array(32).fill(0x00); // Steps a-d of RFC 6979 §3.2 k = hmacSha256(concat(v, new Uint8Array([0x00]), privkey, hash), k); v = hmacSha256(v, k); k = hmacSha256(concat(v, new Uint8Array([0x01]), privkey, hash), k); // Step h: generate — with double-v-hash quirk v = hmacSha256(v, k); v = hmacSha256(v, k); // second hash (bitcore quirk) let T = bytesToBigInt(v); // Retry until T is in the valid range (0, N) while (T <= 0n || T >= CURVE_ORDER) { k = hmacSha256(concat(v, new Uint8Array([0x00])), k); v = hmacSha256(v, k); v = hmacSha256(v, k); // second hash (bitcore quirk) T = bytesToBigInt(v); } return T; } // --------------------------------------------------------------------------- // ECDSA signing (bitcore-lib-cash compatible) // --------------------------------------------------------------------------- // BCH-compatible ECDSA signing. function signEcdsaBch(hash, privateKey) { const e = bytesToBigInt(hash); const d = bytesToBigInt(privateKey); const kVal = getDeterministicK(hash, privateKey); const Q = G.multiply(kVal); const r = Q.x % CURVE_ORDER; const kInv = modInverse(kVal, CURVE_ORDER); // s = k^-1 * (e + d*r) mod N let s = (kInv * ((((e + ((d * r) % CURVE_ORDER)) % CURVE_ORDER) + CURVE_ORDER) % CURVE_ORDER)) % CURVE_ORDER; // BIP-62 low-S normalization if (s > HALF_ORDER) s = CURVE_ORDER - s; const sig = new secp256k1_js_1.secp256k1.Signature(r, s); return sig.toBytes('der'); } // --------------------------------------------------------------------------- // Address derivation // --------------------------------------------------------------------------- // Computes HASH160 of the input. function hash160(data) { return (0, legacy_js_1.ripemd160)((0, sha2_js_1.sha256)(data)); } /** * Derives the 20-byte public key hash from a compressed public key. * * @param publicKey - Compressed (33-byte) public key. */ function publicKeyToHash160(publicKey) { return hash160(publicKey); } /** * Signs a Bitcoin Cash transaction. * * @param inputs - UTXOs to spend. * @param outputs - Destinations and amounts. * @param privateKey - 32-byte private key. * @param networkParams - Network parameters for address decoding. * @returns Serialized raw transaction bytes. */ function signBchTransaction(inputs, outputs, privateKey, networkParams) { const publicKey = secp256k1_js_1.secp256k1.getPublicKey(privateKey, true); const pubKeyHash = hash160(publicKey); // Build the transaction using @scure/btc-signer for structure. const tx = new btc.Transaction({ allowLegacyWitnessUtxo: true, }); // Add inputs. for (const input of inputs) { tx.addInput({ txid: (0, encoding_1.hexToBytes)(input.txid), index: input.n, witnessUtxo: { script: input.script, amount: input.amount, }, sighashType: SIGHASH_BCH, }); } // Add outputs. for (const output of outputs) { tx.addOutput({ script: btc_signer_1.OutScript.encode(btc.Address(networkParams).decode(output.address)), amount: output.amount, }); } // Sign each input using BIP-143 preimage with SIGHASH_FORKID. const scriptCode = btc_signer_1.OutScript.encode({ type: 'pkh', hash: pubKeyHash }); for (let i = 0; i < inputs.length; i++) { // preimageWitnessV0 computes the double-SHA256 digest of the BIP-143 // preimage. With hashType = 0x41, the FORKID flag is embedded in the // 4-byte LE hashType field, which is exactly what BCH requires. const digest = tx.preimageWitnessV0(i, scriptCode, SIGHASH_BCH, inputs[i].amount); // ECDSA sign using bitcore-lib-cash compatible deterministic k. const derSig = signEcdsaBch(digest, privateKey); // scriptSig = <DER_sig || hashType_byte> <compressed_pubkey> const sigWithHashType = new Uint8Array(derSig.length + 1); sigWithHashType.set(derSig); sigWithHashType[derSig.length] = SIGHASH_BCH; const scriptSig = btc_signer_1.Script.encode([sigWithHashType, publicKey]); tx.updateInput(i, { finalScriptSig: scriptSig }, true); } // Serialize the transaction (legacy format, no SegWit). return (0, encoding_1.hexToBytes)(tx.hex); }