UNPKG

@roochnetwork/rooch-sdk

Version:
212 lines (211 loc) 7.11 kB
import { bech32, bech32m } from "@scure/base"; import { bcs } from "../bcs/index.js"; import { EmptyBytes } from "../types/index.js"; import { blake2b, bytes, isHex, validateWitness } from "../utils/index.js"; import { ROOCH_ADDRESS_LENGTH } from "./address.js"; import { RoochAddress } from "./rooch.js"; import { MultiChainID } from "./types.js"; import { ThirdPartyAddress } from "./thirdparty-address.js"; import { Buffer } from "buffer"; import bs58check from "bs58check"; import { schnorr, secp256k1 } from "@noble/curves/secp256k1"; var BitcoinNetowkType = /* @__PURE__ */ ((BitcoinNetowkType2) => { BitcoinNetowkType2[BitcoinNetowkType2["Bitcoin"] = 0] = "Bitcoin"; BitcoinNetowkType2[BitcoinNetowkType2["Testnet"] = 1] = "Testnet"; BitcoinNetowkType2[BitcoinNetowkType2["Signet"] = 2] = "Signet"; BitcoinNetowkType2[BitcoinNetowkType2["Regtest"] = 3] = "Regtest"; return BitcoinNetowkType2; })(BitcoinNetowkType || {}); var BitcoinAddressType = /* @__PURE__ */ ((BitcoinAddressType2) => { BitcoinAddressType2[BitcoinAddressType2["pkh"] = 0] = "pkh"; BitcoinAddressType2[BitcoinAddressType2["sh"] = 1] = "sh"; BitcoinAddressType2[BitcoinAddressType2["witness"] = 2] = "witness"; return BitcoinAddressType2; })(BitcoinAddressType || {}); const PUBKEY_ADDRESS_PREFIX_MAIN = 0; const PUBKEY_ADDRESS_PREFIX_TEST = 111; const SCRIPT_ADDRESS_PREFIX_MAIN = 5; const SCRIPT_ADDRESS_PREFIX_TEST = 196; class BitcoinNetwork { constructor(network) { this.network = network ?? 0 /* Bitcoin */; } static fromBech32Prefix(prefix) { switch (prefix) { case "bc": return new BitcoinNetwork(0 /* Bitcoin */); case "tb": return new BitcoinNetwork(1 /* Testnet */); case "bcrt": return new BitcoinNetwork(3 /* Regtest */); default: return void 0; } } bech32HRP() { switch (this.network) { case 0 /* Bitcoin */: return "bc"; case 1 /* Testnet */: return "tb"; case 2 /* Signet */: return "tb"; case 3 /* Regtest */: return "bcrt"; } } } class BitcoinAddress extends ThirdPartyAddress { constructor(input, network) { super(input); if (isHex(input)) { this.bytes = bytes("hex", input.startsWith("0x") ? input.slice(2) : input); let prefixed; let version = this.bytes[1]; switch (this.bytes[0]) { case 0 /* pkh */: prefixed = new Uint8Array(22); prefixed[0] = version; prefixed[1] = this.getPubkeyAddressPrefix(network); prefixed.set(this.bytes.slice(2)); this.rawAddress = bs58check.encode(prefixed); break; case 1 /* sh */: prefixed = new Uint8Array(22); prefixed[0] = version; prefixed[1] = this.getScriptAddressPrefix(network); prefixed.set(this.bytes.slice(2)); this.rawAddress = bs58check.encode(prefixed); break; case 2 /* witness */: const hrp = new BitcoinNetwork(network).bech32HRP(); const words = bech32.toWords(Buffer.from(this.bytes.slice(2))); words.unshift(version); this.rawAddress = version === 0 ? bech32.encode(hrp, words, false) : bech32m.encode(hrp, words, false); } } else { let info = this.decode(); this.bytes = this.wrapAddress(info.type, info.bytes, info.version); } } static fromPublicKey(publicKey, network = 3 /* Regtest */) { const tapTweak = (a, b) => { const u2 = schnorr.utils; const t2 = u2.taggedHash("TapTweak", a, b); const tn = u2.bytesToNumberBE(t2); if (tn >= secp256k1.CURVE.n) throw new Error("tweak higher than curve order"); return tn; }; const u = schnorr.utils; const t = tapTweak(publicKey, EmptyBytes); const P = u.lift_x(u.bytesToNumberBE(publicKey)); const Q = P.add(secp256k1.ProjectivePoint.fromPrivateKey(t)); const tweakedPubkey = u.pointToBytes(Q); return new BitcoinAddress( bech32m.encode( new BitcoinNetwork(network).bech32HRP(), [1].concat(bech32m.toWords(tweakedPubkey)), false ) ); } getPubkeyAddressPrefix(network = 0 /* Bitcoin */) { return network === 0 /* Bitcoin */ ? PUBKEY_ADDRESS_PREFIX_MAIN : PUBKEY_ADDRESS_PREFIX_TEST; } getScriptAddressPrefix(network = 0 /* Bitcoin */) { return network === 0 /* Bitcoin */ ? SCRIPT_ADDRESS_PREFIX_MAIN : SCRIPT_ADDRESS_PREFIX_TEST; } toBytes() { return bytes("utf8", this.rawAddress); } genMultiChainAddress() { return bcs.MultiChainAddress.serialize({ multiChainId: MultiChainID.Bitcoin, rawAddress: this.bytes }).toBytes(); } genRoochAddress() { if (!this.roochAddress) { this.roochAddress = new RoochAddress(blake2b(this.bytes, { dkLen: ROOCH_ADDRESS_LENGTH })); } return this.roochAddress; } decode() { let input = this.rawAddress; if (input.length < 14 || input.length > 74) throw new Error("Invalid address length"); const bech32_network = (() => { const sep = input.lastIndexOf("1"); const bech32Prefix = sep === -1 ? input : input.substring(0, sep); return BitcoinNetwork.fromBech32Prefix(bech32Prefix); })(); if (bech32_network !== void 0) { let res; try { res = bech32.decode(input); if (res.words[0] !== 0) throw new Error(`bech32: wrong version=${res.words[0]}`); } catch (_) { res = bech32m.decode(input); if (res.words[0] === 0) throw new Error(`bech32m: wrong version=${res.words[0]}`); } const [version, ...program] = res.words; const data2 = bech32.fromWords(program); validateWitness(version, data2); if (version === 0 && data2.length === 32) return { bytes: data2, type: 2 /* witness */, //wsh version }; else if (version === 0 && data2.length === 20) return { bytes: data2, type: 2 /* witness */, //wpkh version }; else if (version === 1 && data2.length === 32) return { bytes: data2, type: 2 /* witness */, //tr version }; else throw new Error("Unknown witness program"); } const data = bs58check.decode(input); if (data.length !== 21) throw new Error("Invalid base58 address"); if (data[0] === 0) { return { bytes: data.slice(1), type: 0 /* pkh */ }; } else if (data[0] === 5) { return { bytes: data.slice(1), type: 1 /* sh */ }; } throw new Error(`Invalid address prefix=${data[0]}`); } wrapAddress(type, bytes2, version) { const addr = new Uint8Array(bytes2.length + 1 + (version !== void 0 ? 1 : 0)); addr.set([type]); if (version !== void 0) { addr.set([version], 1); addr.set(bytes2, 2); } else { addr.set(bytes2, 1); } return addr; } } export { BitcoinAddress, BitcoinNetowkType, BitcoinNetwork }; //# sourceMappingURL=bitcoin.js.map