j-bitcoin
Version:
Comprehensive JavaScript/TypeScript Bitcoin (BTC) wallet library with custodial and non-custodial support, hierarchical deterministic keys, threshold signatures, and advanced cryptographic features
616 lines (538 loc) • 53.2 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>src/transaction/script-builder.js - J-Bitcoin - Bitcoin Wallet Library</title>
<meta name="description" content="JavaScript/TypeScript Bitcoin wallet library with HD wallets, threshold signatures, and BIP compliance" />
<meta name="keywords" content="bitcoin, btc, cryptocurrency, wallet, javascript, typescript, threshold signatures, bip32, bip39" />
<meta name="keyword" content="bitcoin, btc, cryptocurrency, wallet, javascript, typescript, threshold signatures, bip32, bip39" />
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
<script src="scripts/nav.js" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav class="wrap">
<input type="text" id="nav-search" placeholder="Search" />
<h2><a href="index.html">Home</a></h2><h2><a href="https://github.com/yfbsei/J-Bitcoin" target="_blank" class="menu-item home-link" id="home-link" >🏠 Home</a></h2><h2><a href="https://github.com/yfbsei/J-Bitcoin" target="_blank" class="menu-item github-link" id="github-link" >📚 GitHub</a></h2><h2><a href="https://www.npmjs.com/package/j-bitcoin" target="_blank" class="menu-item npm-link" id="npm-link" >📦 NPM</a></h2><h2><a href="https://github.com/yfbsei/J-Bitcoin/tree/main/examples" target="_blank" class="menu-item examples-link" id="examples-link" >🔧 Examples</a></h2><h2><a href="https://github.com/yfbsei/J-Bitcoin/blob/main/CHANGELOG.md" target="_blank" class="menu-item changelog-link" id="changelog-link" >📊 Changelog</a></h2><h3>Classes</h3><ul><li><a href="BIP143.html">BIP143</a><ul class='methods'><li data-type='method' style='display: none;'><a href="BIP143.html#.calculate">calculate</a></li><li data-type='method' style='display: none;'><a href="BIP143.html#.forP2WPKH">forP2WPKH</a></li><li data-type='method' style='display: none;'><a href="BIP143.html#.hashOutputs">hashOutputs</a></li><li data-type='method' style='display: none;'><a href="BIP143.html#.hashPrevouts">hashPrevouts</a></li><li data-type='method' style='display: none;'><a href="BIP143.html#.hashSequence">hashSequence</a></li></ul></li><li><a href="BIP322.html">BIP322</a><ul class='methods'><li data-type='method' style='display: none;'><a href="BIP322.html#.createToSign">createToSign</a></li><li data-type='method' style='display: none;'><a href="BIP322.html#.createToSpend">createToSpend</a></li><li data-type='method' style='display: none;'><a href="BIP322.html#.hashMessage">hashMessage</a></li><li data-type='method' style='display: none;'><a href="BIP322.html#.sign">sign</a></li><li data-type='method' style='display: none;'><a href="BIP322.html#.signLegacy">signLegacy</a></li><li data-type='method' style='display: none;'><a href="BIP322.html#.verify">verify</a></li><li data-type='method' style='display: none;'><a href="BIP322.html#.verifyLegacy">verifyLegacy</a></li></ul></li><li><a href="BIP322Error.html">BIP322Error</a></li><li><a href="BIP341.html">BIP341</a><ul class='methods'><li data-type='method' style='display: none;'><a href="BIP341.html#.calculate">calculate</a></li><li data-type='method' style='display: none;'><a href="BIP341.html#.forScriptPath">forScriptPath</a></li><li data-type='method' style='display: none;'><a href="BIP341.html#.shaAmounts">shaAmounts</a></li><li data-type='method' style='display: none;'><a href="BIP341.html#.shaOutputs">shaOutputs</a></li><li data-type='method' style='display: none;'><a href="BIP341.html#.shaPrevouts">shaPrevouts</a></li><li data-type='method' style='display: none;'><a href="BIP341.html#.shaScriptPubkeys">shaScriptPubkeys</a></li><li data-type='method' style='display: none;'><a href="BIP341.html#.shaSequences">shaSequences</a></li></ul></li><li><a href="BIP49.html">BIP49</a><ul class='methods'><li data-type='method' style='display: none;'><a href="BIP49.html#.createRedeemScript">createRedeemScript</a></li><li data-type='method' style='display: none;'><a href="BIP49.html#.createScriptPubKey">createScriptPubKey</a></li><li data-type='method' style='display: none;'><a href="BIP49.html#.getAccountPath">getAccountPath</a></li><li data-type='method' style='display: none;'><a href="BIP49.html#.getDerivationPath">getDerivationPath</a></li><li data-type='method' style='display: none;'><a href="BIP49.html#.toAddress">toAddress</a></li></ul></li><li><a href="CustodialWallet.html">CustodialWallet</a><ul class='methods'><li data-type='method' style='display: none;'><a href="CustodialWallet.html#canSign">canSign</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#clearCache">clearCache</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#createTransaction">createTransaction</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#deriveAddress">deriveAddress</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#destroy">destroy</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#exportWIF">exportWIF</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#getAddresses">getAddresses</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#getChangeAddress">getChangeAddress</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#getExtendedPrivateKey">getExtendedPrivateKey</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#getExtendedPublicKey">getExtendedPublicKey</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#getMnemonic">getMnemonic</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#getNetwork">getNetwork</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#getReceivingAddress">getReceivingAddress</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#signMessage">signMessage</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#signMessageBIP322">signMessageBIP322</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#signTransaction">signTransaction</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#toJSON">toJSON</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#verifyMessage">verifyMessage</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#.createNew">createNew</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#.fromExtendedKey">fromExtendedKey</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#.fromMnemonic">fromMnemonic</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#.fromSeed">fromSeed</a></li><li data-type='method' style='display: none;'><a href="CustodialWallet.html#.fromWIF">fromWIF</a></li></ul></li><li></li><li><a href="CustodialWalletError.html">CustodialWalletError</a></li><li></li><li><a href="DecodingError.html">DecodingError</a></li><li><a href="ECDSAError.html">ECDSAError</a></li><li></li><li><a href="EncodingError.html">EncodingError</a></li><li><a href="JVRSS.html">JVRSS</a><ul class='methods'><li data-type='method' style='display: none;'><a href="JVRSS.html#broadcastObfuscatedCoefficients">broadcastObfuscatedCoefficients</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#calculateSharedPublicKey">calculateSharedPublicKey</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#calculateShares">calculateShares</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#clear">clear</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#distributePolynomialPoints">distributePolynomialPoints</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#generatePolynomials">generatePolynomials</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#getParticipant">getParticipant</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#getSharesForInterpolation">getSharesForInterpolation</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#reconstructSecret">reconstructSecret</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#runProtocol">runProtocol</a></li><li data-type='method' style='display: none;'><a href="JVRSS.html#verifyAllShares">verifyAllShares</a></li></ul></li><li><a href="JVRSSError.html">JVRSSError</a></li><li><a href="LegacySighash.html">LegacySighash</a><ul class='methods'><li data-type='method' style='display: none;'><a href="LegacySighash.html#.calculate">calculate</a></li></ul></li><li><a href="MPCError.html">MPCError</a></li><li><a href="NonCustodialWallet.html">NonCustodialWallet</a><ul class='methods'><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#_exportShares">_exportShares</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#_reconstructPublicKey">_reconstructPublicKey</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#canSign">canSign</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#canSignHD">canSignHD</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#clear">clear</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#clearAddressCache">clearAddressCache</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#createTransaction">createTransaction</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#deriveAddress">deriveAddress</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#destroy">destroy</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#exportShares">exportShares</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#exportWIF">exportWIF</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#generateEphemeralKeys">generateEphemeralKeys</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getAddress">getAddress</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getAddressCount">getAddressCount</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getAddresses">getAddresses</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getChangeAddress">getChangeAddress</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getExtendedPrivateKey">getExtendedPrivateKey</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getExtendedPublicKey">getExtendedPublicKey</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getMnemonic">getMnemonic</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getNetwork">getNetwork</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getPublicKey">getPublicKey</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getReceivingAddress">getReceivingAddress</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getShare">getShare</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getShares">getShares</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#getThresholdConfig">getThresholdConfig</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#hasHD">hasHD</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#initialize">initialize</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#listAddresses">listAddresses</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#sign">sign</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#signMessage">signMessage</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#signMessageBIP322">signMessageBIP322</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#signMessageHD">signMessageHD</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#signTransaction">signTransaction</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#toJSON">toJSON</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#verify">verify</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#verifyMessageHD">verifyMessageHD</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#.createNew">createNew</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#.createNewHD">createNewHD</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#.fromExtendedKey">fromExtendedKey</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#.fromMnemonic">fromMnemonic</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#.fromSeed">fromSeed</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#.fromShares">fromShares</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#.fromWIF">fromWIF</a></li><li data-type='method' style='display: none;'><a href="NonCustodialWallet.html#.importShares">importShares</a></li></ul></li><li><a href="Participant.html">Participant</a><ul class='methods'><li data-type='method' style='display: none;'><a href="Participant.html#calculateKeyShare">calculateKeyShare</a></li><li data-type='method' style='display: none;'><a href="Participant.html#clear">clear</a></li><li data-type='method' style='display: none;'><a href="Participant.html#evaluateAt">evaluateAt</a></li><li data-type='method' style='display: none;'><a href="Participant.html#generatePolynomial">generatePolynomial</a></li><li data-type='method' style='display: none;'><a href="Participant.html#generateSignatureShare">generateSignatureShare</a></li><li data-type='method' style='display: none;'><a href="Participant.html#getBlindingShare">getBlindingShare</a></li><li data-type='method' style='display: none;'><a href="Participant.html#getKeyShare">getKeyShare</a></li><li data-type='method' style='display: none;'><a href="Participant.html#getKeySharePublicKey">getKeySharePublicKey</a></li><li data-type='method' style='display: none;'><a href="Participant.html#getNextEphemeralKey">getNextEphemeralKey</a></li><li data-type='method' style='display: none;'><a href="Participant.html#getObfuscatedCoefficients">getObfuscatedCoefficients</a></li><li data-type='method' style='display: none;'><a href="Participant.html#getPolynomialPointFor">getPolynomialPointFor</a></li><li data-type='method' style='display: none;'><a href="Participant.html#receivePolynomialPoint">receivePolynomialPoint</a></li><li data-type='method' style='display: none;'><a href="Participant.html#setBlindingShare">setBlindingShare</a></li><li data-type='method' style='display: none;'><a href="Participant.html#storeEphemeralKey">storeEphemeralKey</a></li><li data-type='method' style='display: none;'><a href="Participant.html#verifyPolynomialPoint">verifyPolynomialPoint</a></li></ul></li><li><a href="ParticipantError.html">ParticipantError</a></li><li><a href="ParticipantShare.html">ParticipantShare</a></li><li><a href="Polynomial.html">Polynomial</a><ul class='methods'><li data-type='method' style='display: none;'><a href="Polynomial.html#clear">clear</a></li><li data-type='method' style='display: none;'><a href="Polynomial.html#evaluate">evaluate</a></li><li data-type='method' style='display: none;'><a href="Polynomial.html#generateShares">generateShares</a></li><li data-type='method' style='display: none;'><a href="Polynomial.html#getCoefficients">getCoefficients</a></li><li data-type='method' style='display: none;'><a href="Polynomial.html#getSecret">getSecret</a></li><li data-type='method' style='display: none;'><a href="Polynomial.html#.interpolate">interpolate</a></li><li data-type='method' style='display: none;'><a href="Polynomial.html#.lagrangeCoefficient">lagrangeCoefficient</a></li><li data-type='method' style='display: none;'><a href="Polynomial.html#.reconstructSecret">reconstructSecret</a></li></ul></li><li><a href="PolynomialError.html">PolynomialError</a></li><li><a href="Schnorr.html">Schnorr</a><ul class='methods'><li data-type='method' style='display: none;'><a href="Schnorr.html#sign">sign</a></li></ul></li><li><a href="SchnorrError.html">SchnorrError</a></li><li></li><li><a href="ScriptBuilder.html">ScriptBuilder</a><ul class='methods'><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#addOp">addOp</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#build">build</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#pushData">pushData</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#pushNumber">pushNumber</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#reset">reset</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createCLTV">createCLTV</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createCSV">createCSV</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createMultisig">createMultisig</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createOpReturn">createOpReturn</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2PKH">createP2PKH</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2PKHFromPubkey">createP2PKHFromPubkey</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2PKHScriptSig">createP2PKHScriptSig</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2SH">createP2SH</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2SHScriptSig">createP2SHScriptSig</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2TR">createP2TR</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2WPKH">createP2WPKH</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2WPKHFromPubkey">createP2WPKHFromPubkey</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.createP2WSH">createP2WSH</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.detectType">detectType</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.disassemble">disassemble</a></li><li data-type='method' style='display: none;'><a href="ScriptBuilder.html#.parse">parse</a></li></ul></li><li><a href="ScriptError.html">ScriptError</a></li><li><a href="SighashCalculator.html">SighashCalculator</a><ul class='methods'><li data-type='method' style='display: none;'><a href="SighashCalculator.html#.calculate">calculate</a></li></ul></li><li><a href="SighashError.html">SighashError</a></li><li><a href="TaggedHash.html">TaggedHash</a><ul class='methods'><li data-type='method' style='display: none;'><a href="TaggedHash.html#.create">create</a></li></ul></li><li><a href="ThresholdSignatureError.html">ThresholdSignatureError</a></li><li><a href="ThresholdSignatureScheme.html">ThresholdSignatureScheme</a><ul class='methods'><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#clear">clear</a></li><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#generateEphemeralKeys">generateEphemeralKeys</a></li><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#generateSharedPrivateKey">generateSharedPrivateKey</a></li><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#getConfig">getConfig</a></li><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#getNextEphemeralKey">getNextEphemeralKey</a></li><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#getPublicKey">getPublicKey</a></li><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#sign">sign</a></li><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#signMessage">signMessage</a></li><li data-type='method' style='display: none;'><a href="ThresholdSignatureScheme.html#verify">verify</a></li></ul></li><li><a href="TransactionBuilder.html">TransactionBuilder</a><ul class='methods'><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#addInput">addInput</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#addOpReturn">addOpReturn</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#addOutput">addOutput</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#addWitness">addWitness</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#build">build</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#calculateFee">calculateFee</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#clone">clone</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#enableRBF">enableRBF</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#getTxid">getTxid</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#getVirtualSize">getVirtualSize</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#getWeight">getWeight</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#getWtxid">getWtxid</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#isFullySigned">isFullySigned</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#reset">reset</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#serialize">serialize</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#setInputSequence">setInputSequence</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#setLocktime">setLocktime</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#setVersion">setVersion</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#signAllInputs">signAllInputs</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#signInput">signInput</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#signInputs">signInputs</a></li><li data-type='method' style='display: none;'><a href="TransactionBuilder.html#toHex">toHex</a></li></ul></li><li></li><li><a href="TransactionBuilderError.html">TransactionBuilderError</a></li><li><a href="TransactionParseError.html">TransactionParseError</a></li><li><a href="TransactionParser.html">TransactionParser</a><ul class='methods'><li data-type='method' style='display: none;'><a href="TransactionParser.html#.fromBuffer">fromBuffer</a></li><li data-type='method' style='display: none;'><a href="TransactionParser.html#.fromHex">fromHex</a></li><li data-type='method' style='display: none;'><a href="TransactionParser.html#.getTxid">getTxid</a></li><li data-type='method' style='display: none;'><a href="TransactionParser.html#.serializeWithoutWitness">serializeWithoutWitness</a></li></ul></li><li><a href="ValidationError.html">ValidationError</a></li><li><a href="WitnessBuilder.html">WitnessBuilder</a><ul class='methods'><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.buildControlBlock">buildControlBlock</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.buildP2TRKeyPath">buildP2TRKeyPath</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.buildP2TRScriptPath">buildP2TRScriptPath</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.buildP2WPKH">buildP2WPKH</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.buildP2WSH">buildP2WSH</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.buildP2WSHMultisig">buildP2WSHMultisig</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.calculateTapbranchHash">calculateTapbranchHash</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.calculateTapleafHash">calculateTapleafHash</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.calculateTaptweak">calculateTaptweak</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.parse">parse</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.serialize">serialize</a></li><li data-type='method' style='display: none;'><a href="WitnessBuilder.html#.validate">validate</a></li></ul></li><li><a href="WitnessError.html">WitnessError</a></li></ul><h3>Namespaces</h3><ul><li><a href="BECH32.html">BECH32</a><ul class='methods'><li data-type='method' style='display: none;'><a href="BECH32.html#.decode">decode</a></li><li data-type='method' style='display: none;'><a href="BECH32.html#.getAddressType">getAddressType</a></li><li data-type='method' style='display: none;'><a href="BECH32.html#.getPrefix">getPrefix</a></li><li data-type='method' style='display: none;'><a href="BECH32.html#.to_P2TR">to_P2TR</a></li><li data-type='method' style='display: none;'><a href="BECH32.html#.to_P2WPKH">to_P2WPKH</a></li><li data-type='method' style='display: none;'><a href="BECH32.html#.to_P2WSH">to_P2WSH</a></li><li data-type='method' style='display: none;'><a href="BECH32.html#.validate">validate</a></li></ul></li><li><a href="BIP39.html">BIP39</a><ul class='methods'><li data-type='method' style='display: none;'><a href="BIP39.html#.deriveSeed">deriveSeed</a></li><li data-type='method' style='display: none;'><a href="BIP39.html#.generateMnemonic">generateMnemonic</a></li><li data-type='method' style='display: none;'><a href="BIP39.html#.getWordList">getWordList</a></li><li data-type='method' style='display: none;'><a href="BIP39.html#.mnemonicToEntropy">mnemonicToEntropy</a></li><li data-type='method' style='display: none;'><a href="BIP39.html#.validateChecksum">validateChecksum</a></li><li data-type='method' style='display: none;'><a href="BIP39.html#.validateMnemonic">validateMnemonic</a></li></ul></li><li><a href="ECDSA.html">ECDSA</a><ul class='methods'><li data-type='method' style='display: none;'><a href="ECDSA.html#.getPublicKey">getPublicKey</a></li><li data-type='method' style='display: none;'><a href="ECDSA.html#.recoverPublicKey">recoverPublicKey</a></li><li data-type='method' style='display: none;'><a href="ECDSA.html#.sign">sign</a></li><li data-type='method' style='display: none;'><a href="ECDSA.html#.signMessage">signMessage</a></li><li data-type='method' style='display: none;'><a href="ECDSA.html#.verify">verify</a></li><li data-type='method' style='display: none;'><a href="ECDSA.html#.verifyMessage">verifyMessage</a></li></ul></li></ul><h3>Global</h3><ul><li><a href="global.html#ADDRESS_FORMATS">ADDRESS_FORMATS</a></li><li><a href="global.html#ADDRESS_TYPE_PURPOSE">ADDRESS_TYPE_PURPOSE</a></li><li><a href="global.html#ADDSS">ADDSS</a></li><li><a href="global.html#BECH32_PREFIXES">BECH32_PREFIXES</a></li><li><a href="global.html#BIP322_CONSTANTS">BIP322_CONSTANTS</a></li><li><a href="global.html#BIP32_CONSTANTS">BIP32_CONSTANTS</a></li><li><a href="global.html#BIP340_CONSTANTS">BIP340_CONSTANTS</a></li><li><a href="global.html#BIP39_CONSTANTS">BIP39_CONSTANTS</a></li><li><a href="global.html#BIP44_CONSTANTS">BIP44_CONSTANTS</a></li><li><a href="global.html#BIP49_CONSTANTS">BIP49_CONSTANTS</a></li><li><a href="global.html#BIP_COMPLIANCE">BIP_COMPLIANCE</a></li><li><a href="global.html#BIP_PURPOSES">BIP_PURPOSES</a></li><li><a href="global.html#CRYPTO_CONSTANTS">CRYPTO_CONSTANTS</a></li><li><a href="global.html#CURVE_ORDER">CURVE_ORDER</a></li><li><a href="global.html#ENCODING_CONSTANTS">ENCODING_CONSTANTS</a></li><li><a href="global.html#FEATURES">FEATURES</a></li><li><a href="global.html#G">G</a></li><li><a href="global.html#HARDENED_OFFSET">HARDENED_OFFSET</a></li><li><a href="global.html#INVSS">INVSS</a></li><li><a href="global.html#LIBRARY_INFO">LIBRARY_INFO</a></li><li><a href="global.html#NETWORKS">NETWORKS</a></li><li><a href="global.html#NETWORK_VERSIONS">NETWORK_VERSIONS</a></li><li><a href="global.html#OPCODES">OPCODES</a></li><li><a href="global.html#PROSS">PROSS</a></li><li><a href="global.html#SECURITY_CONSTANTS">SECURITY_CONSTANTS</a></li><li><a href="global.html#SIGHASH">SIGHASH</a></li><li><a href="global.html#TAPROOT_CONSTANTS">TAPROOT_CONSTANTS</a></li><li><a href="global.html#THRESHOLD_CONSTANTS">THRESHOLD_CONSTANTS</a></li><li><a href="global.html#TRANSACTION_CONSTANTS">TRANSACTION_CONSTANTS</a></li><li><a href="global.html#TX_CONSTANTS">TX_CONSTANTS</a></li><li><a href="global.html#b58decode">b58decode</a></li><li><a href="global.html#b58encode">b58encode</a></li><li><a href="global.html#computeAdditiveShares">computeAdditiveShares</a></li><li><a href="global.html#computeMultiplicativeShares">computeMultiplicativeShares</a></li><li><a href="global.html#convertBits">convertBits</a></li><li><a href="global.html#createP2WPKHScriptCode">createP2WPKHScriptCode</a></li><li><a href="global.html#createThresholdScheme">createThresholdScheme</a></li><li><a href="global.html#decode">decode</a></li><li><a href="global.html#decodeAddress">decodeAddress</a></li><li><a href="global.html#decodeExtendedKey">decodeExtendedKey</a></li><li><a href="global.html#decodeLegacyAddress">decodeLegacyAddress</a></li><li><a href="global.html#decodeSegwit">decodeSegwit</a></li><li><a href="global.html#decodeSegwitAddress">decodeSegwitAddress</a></li><li><a href="global.html#decodeWIFPrivateKey">decodeWIFPrivateKey</a></li><li><a href="global.html#derive">derive</a></li><li><a href="global.html#deriveChildKey">deriveChildKey</a></li><li><a href="global.html#doubleHash256">doubleHash256</a></li><li><a href="global.html#encode">encode</a></li><li><a href="global.html#encodeAddress">encodeAddress</a></li><li><a href="global.html#encodeExtendedKey">encodeExtendedKey</a></li><li><a href="global.html#encodeP2PKH">encodeP2PKH</a></li><li><a href="global.html#encodeP2SH">encodeP2SH</a></li><li><a href="global.html#encodePush">encodePush</a></li><li><a href="global.html#encodeSegwit">encodeSegwit</a></li><li><a href="global.html#encodeVarInt">encodeVarInt</a></li><li><a href="global.html#encodeWIF">encodeWIF</a></li><li><a href="global.html#generateDerivationPath">generateDerivationPath</a></li><li><a href="global.html#generateEphemeralKeyShares">generateEphemeralKeyShares</a></li><li><a href="global.html#generateMasterKey">generateMasterKey</a></li><li><a href="global.html#generateSecureRandom">generateSecureRandom</a></li><li><a href="global.html#getFingerprint">getFingerprint</a></li><li><a href="global.html#hash160">hash160</a></li><li><a href="global.html#hash256">hash256</a></li><li><a href="global.html#normalizeUnicode">normalizeUnicode</a></li><li><a href="global.html#polymod">polymod</a></li><li><a href="global.html#rmd160">rmd160</a></li><li><a href="global.html#rotl">rotl</a></li><li><a href="global.html#runJVRSS">runJVRSS</a></li><li><a href="global.html#secureClear">secureClear</a></li><li><a href="global.html#sha256">sha256</a></li><li><a href="global.html#taggedHash">taggedHash</a></li><li><a href="global.html#validateAddress">validateAddress</a></li><li><a href="global.html#validateAndGetNetwork">validateAndGetNetwork</a></li><li><a href="global.html#validateBuffer">validateBuffer</a></li><li><a href="global.html#validateBufferLength">validateBufferLength</a></li><li><a href="global.html#validateChildKey">validateChildKey</a></li><li><a href="global.html#validateDerivationPath">validateDerivationPath</a></li><li><a href="global.html#validateEntropyQuality">validateEntropyQuality</a></li><li><a href="global.html#validateExtendedKey">validateExtendedKey</a></li><li><a href="global.html#validateHexString">validateHexString</a></li><li><a href="global.html#validateNumberRange">validateNumberRange</a></li><li><a href="global.html#validatePrivateKey">validatePrivateKey</a></li><li><a href="global.html#validatePublicKey">validatePublicKey</a></li><li><a href="global.html#validateSeed">validateSeed</a></li></ul>
</nav>
<div id="main">
<h1 class="page-title">src/transaction/script-builder.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>/**
* @fileoverview Bitcoin Script Builder
* @description Build and parse Bitcoin scripts for all address types
* @version 1.0.0
* @author yfbsei
* @license ISC
*/
import { createHash } from 'node:crypto';
import rmd160 from '../core/crypto/hash/ripemd160.js';
/**
* Bitcoin opcodes
* @constant {Object}
*/
const OPCODES = {
// Push operations
OP_0: 0x00,
OP_FALSE: 0x00,
OP_PUSHDATA1: 0x4c,
OP_PUSHDATA2: 0x4d,
OP_PUSHDATA4: 0x4e,
OP_1NEGATE: 0x4f,
OP_RESERVED: 0x50,
OP_1: 0x51,
OP_TRUE: 0x51,
OP_2: 0x52,
OP_3: 0x53,
OP_4: 0x54,
OP_5: 0x55,
OP_6: 0x56,
OP_7: 0x57,
OP_8: 0x58,
OP_9: 0x59,
OP_10: 0x5a,
OP_11: 0x5b,
OP_12: 0x5c,
OP_13: 0x5d,
OP_14: 0x5e,
OP_15: 0x5f,
OP_16: 0x60,
// Flow control
OP_NOP: 0x61,
OP_VER: 0x62,
OP_IF: 0x63,
OP_NOTIF: 0x64,
OP_VERIF: 0x65,
OP_VERNOTIF: 0x66,
OP_ELSE: 0x67,
OP_ENDIF: 0x68,
OP_VERIFY: 0x69,
OP_RETURN: 0x6a,
// Stack operations
OP_TOALTSTACK: 0x6b,
OP_FROMALTSTACK: 0x6c,
OP_2DROP: 0x6d,
OP_2DUP: 0x6e,
OP_3DUP: 0x6f,
OP_2OVER: 0x70,
OP_2ROT: 0x71,
OP_2SWAP: 0x72,
OP_IFDUP: 0x73,
OP_DEPTH: 0x74,
OP_DROP: 0x75,
OP_DUP: 0x76,
OP_NIP: 0x77,
OP_OVER: 0x78,
OP_PICK: 0x79,
OP_ROLL: 0x7a,
OP_ROT: 0x7b,
OP_SWAP: 0x7c,
OP_TUCK: 0x7d,
// Splice operations
OP_CAT: 0x7e,
OP_SUBSTR: 0x7f,
OP_LEFT: 0x80,
OP_RIGHT: 0x81,
OP_SIZE: 0x82,
// Bitwise logic
OP_INVERT: 0x83,
OP_AND: 0x84,
OP_OR: 0x85,
OP_XOR: 0x86,
OP_EQUAL: 0x87,
OP_EQUALVERIFY: 0x88,
// Arithmetic
OP_1ADD: 0x8b,
OP_1SUB: 0x8c,
OP_2MUL: 0x8d,
OP_2DIV: 0x8e,
OP_NEGATE: 0x8f,
OP_ABS: 0x90,
OP_NOT: 0x91,
OP_0NOTEQUAL: 0x92,
OP_ADD: 0x93,
OP_SUB: 0x94,
OP_MUL: 0x95,
OP_DIV: 0x96,
OP_MOD: 0x97,
OP_LSHIFT: 0x98,
OP_RSHIFT: 0x99,
OP_BOOLAND: 0x9a,
OP_BOOLOR: 0x9b,
OP_NUMEQUAL: 0x9c,
OP_NUMEQUALVERIFY: 0x9d,
OP_NUMNOTEQUAL: 0x9e,
OP_LESSTHAN: 0x9f,
OP_GREATERTHAN: 0xa0,
OP_LESSTHANOREQUAL: 0xa1,
OP_GREATERTHANOREQUAL: 0xa2,
OP_MIN: 0xa3,
OP_MAX: 0xa4,
OP_WITHIN: 0xa5,
// Crypto
OP_RIPEMD160: 0xa6,
OP_SHA1: 0xa7,
OP_SHA256: 0xa8,
OP_HASH160: 0xa9,
OP_HASH256: 0xaa,
OP_CODESEPARATOR: 0xab,
OP_CHECKSIG: 0xac,
OP_CHECKSIGVERIFY: 0xad,
OP_CHECKMULTISIG: 0xae,
OP_CHECKMULTISIGVERIFY: 0xaf,
// Expansion
OP_NOP1: 0xb0,
OP_CHECKLOCKTIMEVERIFY: 0xb1,
OP_CLTV: 0xb1,
OP_CHECKSEQUENCEVERIFY: 0xb2,
OP_CSV: 0xb2,
OP_NOP4: 0xb3,
OP_NOP5: 0xb4,
OP_NOP6: 0xb5,
OP_NOP7: 0xb6,
OP_NOP8: 0xb7,
OP_NOP9: 0xb8,
OP_NOP10: 0xb9,
// Taproot
OP_CHECKSIGADD: 0xba
};
// Reverse mapping for disassembly
const OPCODE_NAMES = Object.entries(OPCODES).reduce((acc, [name, code]) => {
if (!acc[code]) acc[code] = name;
return acc;
}, {});
/**
* Custom error class for script operations
* @class ScriptError
* @extends Error
*/
class ScriptError extends Error {
constructor(message, code, details = {}) {
super(message);
this.name = 'ScriptError';
this.code = code;
this.details = details;
}
}
/**
* Hash160 (SHA256 + RIPEMD160)
* @param {Buffer} data - Data to hash
* @returns {Buffer} 20-byte hash
*/
function hash160(data) {
const sha = createHash('sha256').update(data).digest();
return rmd160(sha);
}
/**
* Encode data as a push operation
* @param {Buffer} data - Data to push
* @returns {Buffer} Push operation bytes
*/
function encodePush(data) {
if (!Buffer.isBuffer(data)) {
data = Buffer.from(data, 'hex');
}
const len = data.length;
if (len === 0) {
return Buffer.from([OPCODES.OP_0]);
}
if (len === 1 && data[0] >= 1 && data[0] <= 16) {
return Buffer.from([OPCODES.OP_1 + data[0] - 1]);
}
if (len === 1 && data[0] === 0x81) {
return Buffer.from([OPCODES.OP_1NEGATE]);
}
if (len < 0x4c) {
return Buffer.concat([Buffer.from([len]), data]);
}
if (len <= 0xff) {
return Buffer.concat([Buffer.from([OPCODES.OP_PUSHDATA1, len]), data]);
}
if (len <= 0xffff) {
const lenBuf = Buffer.alloc(2);
lenBuf.writeUInt16LE(len, 0);
return Buffer.concat([Buffer.from([OPCODES.OP_PUSHDATA2]), lenBuf, data]);
}
const lenBuf = Buffer.alloc(4);
lenBuf.writeUInt32LE(len, 0);
return Buffer.concat([Buffer.from([OPCODES.OP_PUSHDATA4]), lenBuf, data]);
}
/**
* Script Builder class
* @class ScriptBuilder
*/
class ScriptBuilder {
constructor() {
this.chunks = [];
}
/**
* Add an opcode
* @param {number} opcode - Opcode to add
* @returns {ScriptBuilder} this
*/
addOp(opcode) {
this.chunks.push(Buffer.from([opcode]));
return this;
}
/**
* Push data onto the stack
* @param {Buffer|string} data - Data to push
* @returns {ScriptBuilder} this
*/
pushData(data) {
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, 'hex');
this.chunks.push(encodePush(buf));
return this;
}
/**
* Push a number onto the stack
* @param {number} num - Number to push
* @returns {ScriptBuilder} this
*/
pushNumber(num) {
if (num === 0) {
return this.addOp(OPCODES.OP_0);
}
if (num === -1) {
return this.addOp(OPCODES.OP_1NEGATE);
}
if (num >= 1 && num <= 16) {
return this.addOp(OPCODES.OP_1 + num - 1);
}
// Encode as minimal push
const negative = num < 0;
let absNum = Math.abs(num);
const bytes = [];
while (absNum > 0) {
bytes.push(absNum & 0xff);
absNum >>= 8;
}
if (bytes[bytes.length - 1] & 0x80) {
bytes.push(negative ? 0x80 : 0x00);
} else if (negative) {
bytes[bytes.length - 1] |= 0x80;
}
return this.pushData(Buffer.from(bytes));
}
/**
* Build the script
* @returns {Buffer} Compiled script
*/
build() {
return Buffer.concat(this.chunks);
}
/**
* Reset the builder
* @returns {ScriptBuilder} this
*/
reset() {
this.chunks = [];
return this;
}
// ===== Static factory methods =====
/**
* Create P2PKH scriptPubKey
* @param {Buffer} pubkeyHash - 20-byte public key hash
* @returns {Buffer} scriptPubKey
*/
static createP2PKH(pubkeyHash) {
if (pubkeyHash.length !== 20) {
throw new ScriptError('P2PKH requires 20-byte hash', 'INVALID_HASH');
}
// OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG
return Buffer.concat([
Buffer.from([OPCODES.OP_DUP, OPCODES.OP_HASH160]),
encodePush(pubkeyHash),
Buffer.from([OPCODES.OP_EQUALVERIFY, OPCODES.OP_CHECKSIG])
]);
}
/**
* Create P2PKH scriptPubKey from public key
* @param {Buffer} publicKey - Compressed or uncompressed public key
* @returns {Buffer} scriptPubKey
*/
static createP2PKHFromPubkey(publicKey) {
return this.createP2PKH(hash160(publicKey));
}
/**
* Create P2SH scriptPubKey
* @param {Buffer} scriptHash - 20-byte script hash
* @returns {Buffer} scriptPubKey
*/
static createP2SH(scriptHash) {
if (scriptHash.length !== 20) {
throw new ScriptError('P2SH requires 20-byte hash', 'INVALID_HASH');
}
// OP_HASH160 <20 bytes> OP_EQUAL
return Buffer.concat([
Buffer.from([OPCODES.OP_HASH160]),
encodePush(scriptHash),
Buffer.from([OPCODES.OP_EQUAL])
]);
}
/**
* Create P2WPKH scriptPubKey (SegWit v0)
* @param {Buffer} pubkeyHash - 20-byte public key hash
* @returns {Buffer} scriptPubKey
*/
static createP2WPKH(pubkeyHash) {
if (pubkeyHash.length !== 20) {
throw new ScriptError('P2WPKH requires 20-byte hash', 'INVALID_HASH');
}
// OP_0 <20 bytes>
return Buffer.concat([
Buffer.from([OPCODES.OP_0, 0x14]),
pubkeyHash
]);
}
/**
* Create P2WPKH scriptPubKey from public key
* @param {Buffer} publicKey - Compressed public key
* @returns {Buffer} scriptPubKey
*/
static createP2WPKHFromPubkey(publicKey) {
return this.createP2WPKH(hash160(publicKey));
}
/**
* Create P2WSH scriptPubKey (SegWit v0)
* @param {Buffer} scriptHash - 32-byte script hash (SHA256)
* @returns {Buffer} scriptPubKey
*/
static createP2WSH(scriptHash) {
if (scriptHash.length !== 32) {
throw new ScriptError('P2WSH requires 32-byte hash', 'INVALID_HASH');
}
// OP_0 <32 bytes>
return Buffer.concat([
Buffer.from([OPCODES.OP_0, 0x20]),
scriptHash
]);
}
/**
* Create P2TR scriptPubKey (Taproot)
* @param {Buffer} xOnlyPubkey - 32-byte x-only public key
* @returns {Buffer} scriptPubKey
*/
static createP2TR(xOnlyPubkey) {
if (xOnlyPubkey.length !== 32) {
throw new ScriptError('P2TR requires 32-byte x-only pubkey', 'INVALID_PUBKEY');
}
// OP_1 <32 bytes>
return Buffer.concat([
Buffer.from([OPCODES.OP_1, 0x20]),
xOnlyPubkey
]);
}
/**
* Create OP_RETURN output script
* @param {Buffer|string} data - Data to embed (max 80 bytes)
* @returns {Buffer} scriptPubKey
*/
static createOpReturn(data) {
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, 'utf8');
if (buf.length > 80) {
throw new ScriptError('OP_RETURN data exceeds 80 bytes', 'DATA_TOO_LONG');
}
return Buffer.concat([
Buffer.from([OPCODES.OP_RETURN]),
encodePush(buf)
]);
}
/**
* Create P2PKH scriptSig (unlocking script)
* @param {Buffer} signature - DER signature with sighash type
* @param {Buffer} publicKey - Public key
* @returns {Buffer} scriptSig
*/
static createP2PKHScriptSig(signature, publicKey) {
return Buffer.concat([
encodePush(signature),
encodePush(publicKey)
]);
}
/**
* Create P2SH scriptSig
* @param {Array<Buffer>} pushData - Array of data to push
* @param {Buffer} redeemScript - The redeem script
* @returns {Buffer} scriptSig
*/
static createP2SHScriptSig(pushData, redeemScript) {
const parts = pushData.map(d => encodePush(d));
parts.push(encodePush(redeemScript));
return Buffer.concat(parts);
}
/**
* Create multisig script
* @param {number} m - Required signatures
* @param {Array<Buffer>} publicKeys - Public keys
* @returns {Buffer} Multisig script
*/
static createMultisig(m, publicKeys) {
if (m < 1 || m > publicKeys.length) {
throw new ScriptError('Invalid M value for multisig', 'INVALID_M');
}
if (publicKeys.length > 16) {
throw new ScriptError('Too many keys for multisig', 'TOO_MANY_KEYS');
}
const builder = new ScriptBuilder();
builder.pushNumber(m);
for (const pk of publicKeys) {
builder.pushData(pk);
}
builder.pushNumber(publicKeys.length);
builder.addOp(OPCODES.OP_CHECKMULTISIG);
return builder.build();
}
/**
* Create CLTV (CheckLockTimeVerify) script
* @param {number} locktime - Locktime value
* @param {Buffer} pubkeyHash - Public key hash
* @returns {Buffer} CLTV script
*/
static createCLTV(locktime, pubkeyHash) {
const builder = new ScriptBuilder();
builder.pushNumber(locktime);
builder.addOp(OPCODES.OP_CHECKLOCKTIMEVERIFY);
builder.addOp(OPCODES.OP_DROP);
builder.addOp(OPCODES.OP_DUP);
builder.addOp(OPCODES.OP_HASH160);
builder.pushData(pubkeyHash);
builder.addOp(OPCODES.OP_EQUALVERIFY);
builder.addOp(OPCODES.OP_CHECKSIG);
return builder.build();
}
/**
* Create CSV (CheckSequenceVerify) script
* @param {number} sequence - Relative locktime
* @param {Buffer} pubkeyHash - Public key hash
* @returns {Buffer} CSV script
*/
static createCSV(sequence, pubkeyHash) {
const builder = new ScriptBuilder();
builder.pushNumber(sequence);
builder.addOp(OPCODES.OP_CHECKSEQUENCEVERIFY);
builder.addOp(OPCODES.OP_DROP);
builder.addOp(OPCODES.OP_DUP);
builder.addOp(OPCODES.OP_HASH160);
builder.pushData(pubkeyHash);
builder.addOp(OPCODES.OP_EQUALVERIFY);
builder.addOp(OPCODES.OP_CHECKSIG);
return builder.build();
}
/**
* Parse a script into human-readable format
* @param {Buffer} script - Script to parse
* @returns {Array} Array of parsed elements
*/
static parse(script) {
const result = [];
let i = 0;
while (i < script.length) {
const opcode = script[i];
// Direct push (1-75 bytes)
if (opcode >= 0x01 && opcode <= 0x4b) {
const len = opcode;
const data = script.slice(i + 1, i + 1 + len);
result.push({ type: 'data', value: data, hex: data.toString('hex') });
i += 1 + len;
continue;
}
// OP_PUSHDATA1
if (opcode === OPCODES.OP_PUSHDATA1) {
const len = script[i + 1];
const data = script.slice(i + 2, i + 2 + len);
result.push({ type: 'data', value: data, hex: data.toString('hex') });
i += 2 + len;
continue;
}
// OP_PUSHDATA2
if (opcode === OPCODES.OP_PUSHDATA2) {
const len = script.readUInt16LE(i + 1);
const data = script.slice(i + 3, i + 3 + len);
result.push({ type: 'data', value: data, hex: data.toString('hex') });
i += 3 + len;
continue;
}
// OP_PUSHDATA4
if (opcode === OPCODES.OP_PUSHDATA4) {
const len = script.readUInt32LE(i + 1);
const data = script.slice(i + 5, i + 5 + len);
result.push({ type: 'data', value: data, hex: data.toString('hex') });
i += 5 + len;
continue;
}
// Regular opcode
const name = OPCODE_NAMES[opcode] || `OP_UNKNOWN_${opcode.toString(16)}`;
result.push({ type: 'opcode', value: opcode, name