@scrow/tapscript
Version:
A development build of tapscript. Built for escrow.
139 lines (116 loc) • 3.47 kB
text/typescript
import { Buff, Bytes } from '@cmdcode/buff'
import { convert_32b } from '@cmdcode/crypto-tools/keys'
import { merkleize } from './tree.js'
import { encode_tapbranch, encode_tapscript } from './encode.js'
import { DEFAULT_VERSION } from './const.js'
import { config_tapleaf } from './util.js'
import {
get_taptweak,
tweak_pubkey
} from './tweak.js'
import {
parse_cblock,
parse_parity
} from './parse.js'
import {
ScriptWord,
TapConfig,
TapContext
} from '../../types/index.js'
import * as assert from '../../assert.js'
export function get_taproot_key (
internal_key : string,
scripts : Array<ScriptWord[]>,
version ?: number
) {
const taptree = scripts.map(e => encode_tapscript(e, version))
const tapdata = tap_pubkey(internal_key, { taptree })
return tapdata.tapkey
}
export function get_ctrl_block (
internal_key : string,
scripts : Array<ScriptWord[]> = [],
target : ScriptWord[] = [],
version ?: number
) {
const tapleaf = encode_tapscript(target, version)
const taptree = scripts.map(e => encode_tapscript(e, version))
const tapdata = tap_pubkey(internal_key, { tapleaf, taptree })
return tapdata.cblock
}
export function tap_pubkey (
pubkey : Bytes,
config : TapConfig = {}
) : TapContext {
const {
taptree = [],
version = DEFAULT_VERSION
} = config
let path : string[] = [],
taproot : string | undefined
const int_pub = Buff.bytes(pubkey)
const { data, extension, script } = config_tapleaf(config)
assert.size(int_pub, 32)
if (extension !== null) {
if (taptree.length > 0) {
// Merkelize the leaves into a root hash (with proof).
const [ root, _, proofs ] = merkleize(taptree, extension)
// Get the control path from the merkelized output.
path = proofs
// Get the tapped key from the internal key.
taproot = root
// Get the tapped key from the single tapleaf.
} else {
taproot = extension
}
}
const taptweak = get_taptweak(int_pub, taproot)
const twk_key = tweak_pubkey(int_pub, taptweak)
const parity = parse_parity(twk_key)
const tapkey = convert_32b(twk_key)
// Get the block version / parity bit.
const cbit = Buff.num(version + parity)
// Create the control block, starting with
// the control bit and the (x-only) pubkey.
const block = [ cbit, int_pub.hex ]
// If there is more than one path, add to block.
if (path.length > 0) {
path.forEach(e => block.push(Buff.hex(e)))
}
// Merge the data together into one array.
const cblock = Buff.join(block)
return {
data,
extension,
parity,
path,
script,
taproot,
taptree,
version,
cblock : cblock.hex,
int_pub : int_pub.hex,
tapkey : tapkey.hex,
taptweak : taptweak.hex
}
}
export function verify_cblock (
tapkey : Bytes,
target : Bytes,
cblock : Bytes
) : boolean {
assert.size(tapkey, 32)
const { parity, path, int_pub } = parse_cblock(cblock)
const extkey = Buff.join([ parity, tapkey ])
let branch = Buff.bytes(target).hex
for (const leaf of path) {
branch = encode_tapbranch(branch, leaf)
}
const twk = get_taptweak(int_pub, branch)
const key = tweak_pubkey(int_pub, twk)
// console.log('branch:', branch)
// console.log('intkey:', int_pub)
// console.log('extkey:', extkey.hex)
// console.log('tapkey:', key.hex)
return (Buff.raw(key).hex === Buff.raw(extkey).hex)
}