UNPKG

@okxweb3/coin-bitcoin

Version:

@okxweb3/coin-bitcoin is a Bitcoin SDK for building Web3 wallets and applications. It supports BTC, BSV, DOGE, LTC, and TBTC, enabling private key management, transaction signing, address generation, and inscriptions like BRC-20, Runes, CAT, and Atomicals

300 lines 12.6 kB
"use strict"; 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.p2tr = void 0; const buffer_1 = require("buffer"); const networks_1 = require("../networks"); const bscript = __importStar(require("../script")); const types_1 = require("../types"); const bip341_1 = require("./bip341"); const lazy = __importStar(require("./lazy")); const crypto_lib_1 = require("@okxweb3/crypto-lib"); const OPS = bscript.OPS; const TAPROOT_WITNESS_VERSION = 0x01; const ANNEX_PREFIX = 0x50; function p2tr(a, opts) { if (!a.address && !a.output && !a.pubkey && !a.internalPubkey && !(a.witness && a.witness.length > 1)) throw new TypeError('Not enough data'); opts = Object.assign({ validate: true }, opts || {}); (0, types_1.typeforce)({ address: types_1.typeforce.maybe(types_1.typeforce.String), input: types_1.typeforce.maybe(types_1.typeforce.BufferN(0)), network: types_1.typeforce.maybe(types_1.typeforce.Object), output: types_1.typeforce.maybe(types_1.typeforce.BufferN(34)), internalPubkey: types_1.typeforce.maybe(types_1.typeforce.BufferN(32)), hash: types_1.typeforce.maybe(types_1.typeforce.BufferN(32)), pubkey: types_1.typeforce.maybe(types_1.typeforce.BufferN(32)), witness: types_1.typeforce.maybe(types_1.typeforce.arrayOf(types_1.typeforce.Buffer)), scriptTree: types_1.typeforce.maybe(types_1.isTaptree), redeem: types_1.typeforce.maybe({ output: types_1.typeforce.maybe(types_1.typeforce.Buffer), redeemVersion: types_1.typeforce.maybe(types_1.typeforce.Number), witness: types_1.typeforce.maybe(types_1.typeforce.arrayOf(types_1.typeforce.Buffer)), }), redeemVersion: types_1.typeforce.maybe(types_1.typeforce.Number), }, a); const _address = lazy.value(() => { const result = crypto_lib_1.base.bech32m.decode(a.address); const version = result.words.shift(); const data = crypto_lib_1.base.bech32m.fromWords(result.words); return { version, prefix: result.prefix, data: buffer_1.Buffer.from(data), }; }); const _witness = lazy.value(() => { if (!a.witness || !a.witness.length) return; if (a.witness.length >= 2 && a.witness[a.witness.length - 1][0] === ANNEX_PREFIX) { return a.witness.slice(0, -1); } return a.witness.slice(); }); const _hashTree = lazy.value(() => { if (a.scriptTree) return (0, bip341_1.toHashTree)(a.scriptTree); if (a.hash) return { hash: a.hash }; return; }); const network = a.network || networks_1.bitcoin; const o = { name: 'p2tr', network }; lazy.prop(o, 'address', () => { if (!o.pubkey) return; const words = crypto_lib_1.base.bech32m.toWords(o.pubkey); words.unshift(TAPROOT_WITNESS_VERSION); return crypto_lib_1.base.bech32m.encode(network.bech32, words); }); lazy.prop(o, 'hash', () => { const hashTree = _hashTree(); if (hashTree) return hashTree.hash; const w = _witness(); if (w && w.length > 1) { const controlBlock = w[w.length - 1]; const leafVersion = controlBlock[0] & types_1.TAPLEAF_VERSION_MASK; const script = w[w.length - 2]; const leafHash = (0, bip341_1.tapleafHash)({ output: script, version: leafVersion }); return (0, bip341_1.rootHashFromPath)(controlBlock, leafHash); } return null; }); lazy.prop(o, 'output', () => { if (!o.pubkey) return; return bscript.compile([OPS.OP_1, o.pubkey]); }); lazy.prop(o, 'redeemVersion', () => { if (a.redeemVersion) return a.redeemVersion; if (a.redeem && a.redeem.redeemVersion !== undefined && a.redeem.redeemVersion !== null) { return a.redeem.redeemVersion; } return bip341_1.LEAF_VERSION_TAPSCRIPT; }); lazy.prop(o, 'redeem', () => { const witness = _witness(); if (!witness || witness.length < 2) return; return { output: witness[witness.length - 2], witness: witness.slice(0, -2), redeemVersion: witness[witness.length - 1][0] & types_1.TAPLEAF_VERSION_MASK, }; }); lazy.prop(o, 'pubkey', () => { if (a.pubkey) return a.pubkey; if (a.output) return a.output.slice(2); if (a.address) return _address().data; if (o.internalPubkey) { const tweakedKey = (0, bip341_1.tweakKey)(o.internalPubkey, o.hash); if (tweakedKey) return tweakedKey.x; } }); lazy.prop(o, 'internalPubkey', () => { if (a.internalPubkey) return a.internalPubkey; const witness = _witness(); if (witness && witness.length > 1) return witness[witness.length - 1].slice(1, 33); }); lazy.prop(o, 'signature', () => { if (a.signature) return a.signature; const witness = _witness(); if (!witness || witness.length !== 1) return; return witness[0]; }); lazy.prop(o, 'witness', () => { if (a.witness) return a.witness; const hashTree = _hashTree(); if (hashTree && a.redeem && a.redeem.output && a.internalPubkey) { const leafHash = (0, bip341_1.tapleafHash)({ output: a.redeem.output, version: o.redeemVersion, }); const path = (0, bip341_1.findScriptPath)(hashTree, leafHash); if (!path) return; const outputKey = (0, bip341_1.tweakKey)(a.internalPubkey, hashTree.hash); if (!outputKey) return; const controlBock = buffer_1.Buffer.concat([ buffer_1.Buffer.from([o.redeemVersion | outputKey.parity]), a.internalPubkey, ].concat(path)); return [a.redeem.output, controlBock]; } if (a.signature) return [a.signature]; }); if (opts.validate) { let pubkey = buffer_1.Buffer.from([]); if (a.address) { if (network && network.bech32 !== _address().prefix) throw new TypeError('Invalid prefix or Network mismatch'); if (_address().version !== TAPROOT_WITNESS_VERSION) throw new TypeError('Invalid address version'); if (_address().data.length !== 32) throw new TypeError('Invalid address data'); pubkey = _address().data; } if (a.pubkey) { if (pubkey.length > 0 && !pubkey.equals(a.pubkey)) throw new TypeError('Pubkey mismatch'); else pubkey = a.pubkey; } if (a.output) { if (a.output.length !== 34 || a.output[0] !== OPS.OP_1 || a.output[1] !== 0x20) throw new TypeError('Output is invalid'); if (pubkey.length > 0 && !pubkey.equals(a.output.slice(2))) throw new TypeError('Pubkey mismatch'); else pubkey = a.output.slice(2); } if (a.internalPubkey) { const tweakedKey = (0, bip341_1.tweakKey)(a.internalPubkey, o.hash); if (pubkey.length > 0 && !pubkey.equals(tweakedKey.x)) throw new TypeError('Pubkey mismatch'); else pubkey = tweakedKey.x; } if (pubkey && pubkey.length) { if (pubkey.length !== 32) throw new TypeError('Invalid pubkey for p2tr'); } const hashTree = _hashTree(); if (a.hash && hashTree) { if (!a.hash.equals(hashTree.hash)) throw new TypeError('Hash mismatch'); } if (a.redeem && a.redeem.output && hashTree) { const leafHash = (0, bip341_1.tapleafHash)({ output: a.redeem.output, version: o.redeemVersion, }); if (!(0, bip341_1.findScriptPath)(hashTree, leafHash)) throw new TypeError('Redeem script not in tree'); } const witness = _witness(); if (a.redeem && o.redeem) { if (a.redeem.redeemVersion) { if (a.redeem.redeemVersion !== o.redeem.redeemVersion) throw new TypeError('Redeem.redeemVersion and witness mismatch'); } if (a.redeem.output) { if (bscript.decompile(a.redeem.output).length === 0) throw new TypeError('Redeem.output is invalid'); if (o.redeem.output && !a.redeem.output.equals(o.redeem.output)) throw new TypeError('Redeem.output and witness mismatch'); } if (a.redeem.witness) { if (o.redeem.witness && !stacksEqual(a.redeem.witness, o.redeem.witness)) throw new TypeError('Redeem.witness and witness mismatch'); } } if (witness && witness.length) { if (witness.length === 1) { if (a.signature && !a.signature.equals(witness[0])) throw new TypeError('Signature mismatch'); } else { const controlBlock = witness[witness.length - 1]; if (controlBlock.length < 33) throw new TypeError(`The control-block length is too small. Got ${controlBlock.length}, expected min 33.`); if ((controlBlock.length - 33) % 32 !== 0) throw new TypeError(`The control-block length of ${controlBlock.length} is incorrect!`); const m = (controlBlock.length - 33) / 32; if (m > 128) throw new TypeError(`The script path is too long. Got ${m}, expected max 128.`); const internalPubkey = controlBlock.slice(1, 33); if (a.internalPubkey && !a.internalPubkey.equals(internalPubkey)) throw new TypeError('Internal pubkey mismatch'); if (internalPubkey.length !== 32) throw new TypeError('Invalid internalPubkey for p2tr witness'); const leafVersion = controlBlock[0] & types_1.TAPLEAF_VERSION_MASK; const script = witness[witness.length - 2]; const leafHash = (0, bip341_1.tapleafHash)({ output: script, version: leafVersion }); const hash = (0, bip341_1.rootHashFromPath)(controlBlock, leafHash); const outputKey = (0, bip341_1.tweakKey)(internalPubkey, hash); if (!outputKey) throw new TypeError('Invalid outputKey for p2tr witness'); if (pubkey.length && !pubkey.equals(outputKey.x)) throw new TypeError('Pubkey mismatch for p2tr witness'); if (outputKey.parity !== (controlBlock[0] & 1)) throw new Error('Incorrect parity'); } } } return Object.assign(o, a); } exports.p2tr = p2tr; function stacksEqual(a, b) { if (a.length !== b.length) return false; return a.every((x, i) => { return x.equals(b[i]); }); } //# sourceMappingURL=p2tr.js.map