@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
111 lines (95 loc) • 3.36 kB
text/typescript
import bitcoin, {
fromHex,
type Network,
networks,
opcodes,
type PublicKey,
type Script,
script,
type XOnlyPublicKey,
payments,
} from '@btc-vision/bitcoin';
import type { IP2WSHAddress } from './IP2WSHAddress.js';
export class TimeLockGenerator {
private static readonly UNSPENDABLE_INTERNAL_KEY = fromHex(
'50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
);
private static readonly CSV_BLOCKS = 75;
/**
* Generate a P2WSH address with CSV timelock
* Note: This uses ECDSA, not Schnorr (Schnorr only available in Taproot)
*/
public static generateTimeLockAddress(
publicKey: PublicKey,
network: Network = networks.bitcoin,
csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
): IP2WSHAddress {
const witnessScript: Script = this.generateTimeLockScript(publicKey, csvBlocks);
const p2wsh = bitcoin.payments.p2wsh({
redeem: { output: witnessScript },
network,
});
if (!p2wsh.address) {
throw new Error('Failed to generate P2WSH address');
}
return {
address: p2wsh.address,
witnessScript: witnessScript,
};
}
/**
* Generate a P2TR address with CSV time lock
* Note: This uses Schnorr signatures
*/
public static generateTimeLockAddressP2TR(
publicKey: XOnlyPublicKey,
network: Network = networks.bitcoin,
csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
): string {
if (publicKey.length !== 32) {
throw new Error('Public key must be 32 bytes for Taproot');
}
const witnessScript: Script = this.generateTimeLockScript(publicKey, csvBlocks);
const taproot = bitcoin.payments.p2tr({
redeem: { output: witnessScript },
network,
internalPubkey: TimeLockGenerator.UNSPENDABLE_INTERNAL_KEY as XOnlyPublicKey,
});
if (!taproot.address) {
throw new Error('Failed to generate P2TR address');
}
return taproot.address;
}
/**
* Generate a P2MR address with CSV time lock
* Note: This uses Schnorr signatures within a P2MR (BIP 360) script tree
*/
public static generateTimeLockAddressP2MR(
publicKey: XOnlyPublicKey,
network: Network = networks.bitcoin,
csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
): string {
if (publicKey.length !== 32) {
throw new Error('Public key must be 32 bytes for P2MR');
}
const witnessScript: Script = this.generateTimeLockScript(publicKey, csvBlocks);
const scriptTree = { output: witnessScript, version: 192 };
const p2mr = payments.p2mr({ scriptTree, network });
if (!p2mr.address) {
throw new Error('Failed to generate P2MR address');
}
return p2mr.address;
}
private static generateTimeLockScript(
publicKey: PublicKey | XOnlyPublicKey,
csvBlocks: number = TimeLockGenerator.CSV_BLOCKS,
): Script {
return script.compile([
script.number.encode(csvBlocks),
opcodes.OP_CHECKSEQUENCEVERIFY,
opcodes.OP_DROP,
publicKey,
opcodes.OP_CHECKSIG,
]);
}
}