UNPKG

@vbyte/btc-dev

Version:

Batteries-included toolset for plebian bitcoin development

66 lines (52 loc) 1.88 kB
import { Buff, Stream } from '@vbyte/buff' import { Assert, ECC } from '@vbyte/micro-lib' import { parse_witness } from '@/lib/witness/parse.js' import { encode_tapbranch, encode_tapscript, encode_taptweak, } from './encode.js' import type { ControlBlock } from '@/types/index.js' export function parse_taproot_witness (witness : string[]) { const { cblock, params, script } = parse_witness(witness) Assert.exists(cblock, 'cblock is null') Assert.exists(script, 'script is null') const cblk = parse_cblock(cblock) const target = encode_tapscript(script, cblk.version) let branch = target.hex for (const leaf of cblk.path) { branch = encode_tapbranch(branch, leaf).hex } const tweak = encode_taptweak(cblk.int_key, branch) const tapkey = ECC.tweak_pubkey(cblk.int_key, tweak, 'bip340') params.map(e => Buff.bytes(e).hex) return { cblock: cblk, params, script, tapkey: tapkey.hex, tweak: tweak.hex } } export function parse_cblock (cblock : string | Uint8Array) : ControlBlock { const buffer = new Stream(cblock) const cbyte = buffer.read(1).num const int_key = buffer.read(32).hex const [ version, parity ] = parse_cblock_parity(cbyte) const path = [] while (buffer.size >= 32) { path.push(buffer.read(32).hex) } if (buffer.size !== 0) { throw new Error('Non-empty buffer on control block: ' + String(buffer)) } return { int_key, path, parity, version } } export function parse_cblock_parity (cbits : number) { return (cbits % 2 === 0) ? [ cbits - 0, 0x02 ] : [ cbits - 1, 0x03 ] } export function parse_pubkey_parity ( pubkey : string | Uint8Array ) : number { Assert.size(pubkey, 33, 'invalid pubkey size') const [ parity ] = Buff.bytes(pubkey) if (parity === 0x02) return 0 if (parity === 0x03) return 1 throw new Error('Invalid parity bit: ' + String(parity)) }