@vbyte/btc-dev
Version:
Batteries-included toolset for plebian bitcoin development
97 lines (78 loc) • 2.61 kB
text/typescript
import { Buff, Bytes } from '@vbyte/buff'
import { Assert, ECC } from '@vbyte/micro-lib'
import { merkleize } from './tree.js'
import { TAPLEAF_DEFAULT_VERSION } from '@/const.js'
import * as Schema from '@/schema/index.js'
import {
encode_tapbranch,
encode_taptweak
} from './encode.js'
import {
parse_pubkey_parity,
parse_cblock
} from './parse.js'
import {
TaprootConfig,
TaprootContext
} from '@/types/index.js'
const DEFAULT_VERSION = TAPLEAF_DEFAULT_VERSION
export function create_taproot (config : TaprootConfig) : TaprootContext {
Schema.taproot.config.parse(config)
const { pubkey, version = DEFAULT_VERSION } = config
const leaves = config.leaves ?? []
const target = (config.target !== undefined)
? Buff.bytes(config.target).hex
: undefined
let path : string[] = [],
taproot : string | undefined
if (leaves.length > 0) {
// Merkelize the leaves into a root hash (with proof).
const [ root, _, proofs ] = merkleize(leaves, target)
// Get the control path from the merkelized output.
path = proofs
// Get the tapped key from the internal key.
taproot = root
} else {
// Get the tapped key from the single tapleaf.
taproot = target
}
const taptweak = encode_taptweak(pubkey, taproot)
const twk_key = ECC.tweak_pubkey(pubkey, taptweak, 'ecdsa')
const parity = parse_pubkey_parity(twk_key)
const tapkey = ECC.serialize_pubkey(twk_key, 'bip340')
// Get the block version / parity bit.
const cbit = Buff.num(version + parity)
// Stack the initial control block data.
const block : Bytes[] = [ cbit, Buff.bytes(pubkey) ]
// If there is more than one path, add to block.
if (path.length > 0) {
path.forEach(e => block.push(e))
}
// Merge the data together into one array.
const cblock = Buff.join(block)
return {
int_key : Buff.bytes(pubkey).hex,
path,
parity,
taproot : taproot ?? null,
cblock : cblock.hex,
tapkey : tapkey.hex,
taptweak : taptweak.hex
}
}
export function verify_taproot (
tapkey : string,
target : string,
cblock : string
) : boolean {
Assert.size(tapkey, 32)
const { parity, path, int_key } = parse_cblock(cblock)
const ext_key = Buff.join([ parity, tapkey ])
let branch = Buff.bytes(target).hex
for (const leaf of path) {
branch = encode_tapbranch(branch, leaf).hex
}
const tap_tweak = encode_taptweak(int_key, branch)
const tweaked_key = ECC.tweak_pubkey(int_key, tap_tweak, 'ecdsa')
return (ext_key.hex === tweaked_key.hex)
}