mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-15 04:11:26 +01:00
229 lines
No EOL
30 KiB
HTML
229 lines
No EOL
30 KiB
HTML
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><title>Partially Signed Bitcoin Transactions · bitcoin-s</title><meta name="viewport" content="width=device-width, initial-scale=1.0"/><meta name="generator" content="Docusaurus"/><meta name="description" content="Creating unsigned or partially signed transactions to be passed "/><meta name="docsearch:version" content="next"/><meta name="docsearch:language" content="en"/><meta property="og:title" content="Partially Signed Bitcoin Transactions · bitcoin-s"/><meta property="og:type" content="website"/><meta property="og:url" content="https://bitcoin-s.org/"/><meta property="og:description" content="Creating unsigned or partially signed transactions to be passed "/><meta property="og:image" content="https://bitcoin-s.org/img/undraw_online.svg"/><meta name="twitter:card" content="summary"/><meta name="twitter:image" content="https://bitcoin-s.org/img/undraw_tweetstorm.svg"/><link rel="shortcut icon" href="/img/favicon.ico"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css"/><link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css"/><script>
|
||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||
|
||
ga('create', 'UA-61958686-2', 'auto');
|
||
ga('send', 'pageview');
|
||
</script><link rel="stylesheet" href="/css/code-block-buttons.css"/><script type="text/javascript" src="https://buttons.github.io/buttons.js"></script><script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js"></script><script type="text/javascript" src="https://fonts.googleapis.com/css?family=Montserrat:500"></script><script type="text/javascript" src="https://www.googletagmanager.com/gtag/js?id=UA-61958686-2"></script><script type="text/javascript" src="/js/code-block-buttons.js"></script><script src="/js/scrollSpy.js"></script><link rel="stylesheet" href="/css/main.css"/><script src="/js/codetabs.js"></script></head><body class="sideNavVisible separateOnPageNav"><div class="fixedHeaderContainer"><div class="headerWrapper wrapper"><header><a href="/"><img class="logo" src="/img/favicon.ico" alt="bitcoin-s"/><h2 class="headerTitleWithLogo">bitcoin-s</h2></a><a href="/versions"><h3>next</h3></a><div class="navigationWrapper navigationSlider"><nav class="slidingNav"><ul class="nav-site nav-site-internal"><li class="siteNavGroupActive"><a href="/docs/next/core/core-intro" target="_self">Docs</a></li><li class=""><a href="/download" target="_self">Download</a></li><li class=""><a href="/api/org/bitcoins" target="_self">API</a></li><li class=""><a href="/help" target="_self">Help</a></li><li class="navSearchWrapper reactNavSearchWrapper"><input type="text" id="search_input_react" placeholder="Search" title="Search"/></li></ul></nav></div></header></div></div><div class="navPusher"><div class="docMainWrapper wrapper"><div class="docsNavContainer" id="docsNav"><nav class="toc"><div class="toggleNav"><section class="navWrapper wrapper"><div class="navBreadcrumb wrapper"><div class="navToggle" id="navToggler"><div class="hamburger-menu"><div class="line1"></div><div class="line2"></div><div class="line3"></div></div></div><h2><i>›</i><span>Core Module</span></h2><div class="tocToggler" id="tocToggler"><i class="icon-toc"></i></div></div><div class="navGroups"><div class="navGroup"><h3 class="navGroupCategoryTitle">Getting Started</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/getting-started">Intro and Getting Started</a></li><li class="navListItem"><a class="navItem" href="/docs/next/bips">Supported BIPs</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Getting Setup</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/getting-setup">Getting Bitcoin-S installed on your machine</a></li><li class="navListItem"><a class="navItem" href="/docs/next/ui-setup">Installing the DLC Wallet UI</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Applications</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/applications/cli">CLI</a></li><li class="navListItem"><a class="navItem" href="/docs/next/applications/server">Application Server</a></li><li class="navListItem"><a class="navItem" href="/docs/next/applications/gui">GUI</a></li><li class="navListItem"><a class="navItem" href="/docs/next/applications/server-systemd">Systemd installation</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Chain</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/chain/chain">Blockchain Verification</a></li><li class="navListItem"><a class="navItem" href="/docs/next/chain/filter-sync">Syncing Blockfilters</a></li><li class="navListItem"><a class="navItem" href="/docs/next/chain/chain-query-api">Chain Query API</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Configuration</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/config/configuration">Application Configuration</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Core Module</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/core/core-intro">Core Module</a></li><li class="navListItem"><a class="navItem" href="/docs/next/core/addresses">Generating Addresses</a></li><li class="navListItem"><a class="navItem" href="/docs/next/core/hd-keys">HD Key Generation</a></li><li class="navListItem"><a class="navItem" href="/docs/next/core/adding-spks">Adding New Script Types</a></li><li class="navListItem"><a class="navItem" href="/docs/next/core/spending-info">Signing Transactions</a></li><li class="navListItem navListItemActive"><a class="navItem" href="/docs/next/core/psbts">Partially Signed Bitcoin Transactions</a></li><li class="navListItem"><a class="navItem" href="/docs/next/core/dlc">Discreet Log Contract Data Structures</a></li><li class="navListItem"><a class="navItem" href="/docs/next/core/txbuilder">TxBuilder Example</a></li><li class="navListItem"><a class="navItem" href="/docs/next/core/lightning-network">Lightning Network Data Types</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Crypto Module</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/crypto/crypto-intro">Crypto Module</a></li><li class="navListItem"><a class="navItem" href="/docs/next/crypto/sign">Sign API</a></li><li class="navListItem"><a class="navItem" href="/docs/next/crypto/adaptor-signatures">Adaptor Signatures</a></li><li class="navListItem"><a class="navItem" href="/docs/next/crypto/musig">MuSig</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Fee Provider</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/fee-provider/fee-provider">Fee Provider</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Key Manager</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/key-manager/server-key-manager">Server Key Manager</a></li><li class="navListItem"><a class="navItem" href="/docs/next/key-manager/key-manager">Key Manager</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Node</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/node/node">Light Client</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Wallet</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet">Wallet</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet-callbacks">Wallet Callbacks</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet-get-address">Wallet Get Address APIs</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/address-tagging">Address and UTXO tagging</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/dlc">Executing A DLC with Bitcoin-S</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet-rescan">wallet/wallet-rescan</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet-sync">Wallet Sync</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet-rpc">Wallet RPC Examples</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/backups">Wallet Backups</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet-election-example">Wallet Election Example</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet-price-example">Wallet Price Example</a></li><li class="navListItem"><a class="navItem" href="/docs/next/wallet/wallet-sports-betting-example">Wallet Sports Betting Example</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Tor</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/tor/tor">Tor Setup</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">RPC Clients</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/rpc/rpc-clients-intro">Introduction</a></li><li class="navListItem"><a class="navItem" href="/docs/next/rpc/rpc-eclair">Eclair</a></li><li class="navListItem"><a class="navItem" href="/docs/next/rpc/rpc-bitcoind">bitcoind/Bitcoin Core</a></li><li class="navListItem"><a class="navItem" href="/docs/next/rpc/lnd-rpc">LND</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Secp256k1</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/secp256k1/secp256k1">Secp256k1</a></li><li class="navListItem"><a class="navItem" href="/docs/next/secp256k1/jni-modify">Adding to Secp256k1 JNI</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Testkit</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/testkit/testkit">Testkit</a></li><li class="navListItem"><a class="navItem" href="/docs/next/testkit/testkit-core">Testkit Core</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">DLC Oracle</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/oracle/build-oracle-server">Building the Oracle Server</a></li><li class="navListItem"><a class="navItem" href="/docs/next/oracle/oracle-server">Oracle Server</a></li><li class="navListItem"><a class="navItem" href="/docs/next/oracle/oracle-election-example">Election Example</a></li><li class="navListItem"><a class="navItem" href="/docs/next/oracle/oracle-price-example">Price Example</a></li><li class="navListItem"><a class="navItem" href="/docs/next/oracle/oracle-sports-betting-example">Sports Betting Example</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Contributing</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/contributing">Contributing</a></li><li class="navListItem"><a class="navItem" href="/docs/next/contributing-website">Contributing to the website</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Security</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/next/security">Security</a></li></ul></div></div></section></div><script>
|
||
var coll = document.getElementsByClassName('collapsible');
|
||
var checkActiveCategory = true;
|
||
for (var i = 0; i < coll.length; i++) {
|
||
var links = coll[i].nextElementSibling.getElementsByTagName('*');
|
||
if (checkActiveCategory){
|
||
for (var j = 0; j < links.length; j++) {
|
||
if (links[j].classList.contains('navListItemActive')){
|
||
coll[i].nextElementSibling.classList.toggle('hide');
|
||
coll[i].childNodes[1].classList.toggle('rotate');
|
||
checkActiveCategory = false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
coll[i].addEventListener('click', function() {
|
||
var arrow = this.childNodes[1];
|
||
arrow.classList.toggle('rotate');
|
||
var content = this.nextElementSibling;
|
||
content.classList.toggle('hide');
|
||
});
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
createToggler('#navToggler', '#docsNav', 'docsSliderActive');
|
||
createToggler('#tocToggler', 'body', 'tocActive');
|
||
|
||
var headings = document.querySelector('.toc-headings');
|
||
headings && headings.addEventListener('click', function(event) {
|
||
var el = event.target;
|
||
while(el !== headings){
|
||
if (el.tagName === 'A') {
|
||
document.body.classList.remove('tocActive');
|
||
break;
|
||
} else{
|
||
el = el.parentNode;
|
||
}
|
||
}
|
||
}, false);
|
||
|
||
function createToggler(togglerSelector, targetSelector, className) {
|
||
var toggler = document.querySelector(togglerSelector);
|
||
var target = document.querySelector(targetSelector);
|
||
|
||
if (!toggler) {
|
||
return;
|
||
}
|
||
|
||
toggler.onclick = function(event) {
|
||
event.preventDefault();
|
||
|
||
target.classList.toggle(className);
|
||
};
|
||
}
|
||
});
|
||
</script></nav></div><div class="container mainContainer docsContainer"><div class="wrapper"><div class="post"><header class="postHeader"><a class="edit-page-link button" href="https://github.com/bitcoin-s/bitcoin-s/blob/master/docs/core/psbts.md" target="_blank" rel="noreferrer noopener">Edit</a><h1 id="__docusaurus" class="postHeaderTitle">Partially Signed Bitcoin Transactions</h1></header><article><div><span><p>Creating unsigned or partially signed transactions to be passed
|
||
around to other signers can be a useful for many applications.
|
||
PSBTs offer a standardized format to serialize the necessary data
|
||
for signing the transaction, as well as, validating that you in fact
|
||
want to sign this transaction.</p>
|
||
<blockquote>
|
||
<p>If you want to jump into the details of the specification,
|
||
you should checkout
|
||
<a href="https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki">BIP 174</a>.</p>
|
||
</blockquote>
|
||
<p>Bitcoin-S fully supports PSBTs with functionality for
|
||
creation, updating, combining, signing, finalizing,
|
||
and transaction extraction.</p>
|
||
<p>An example on a typical PSBT workflow:</p>
|
||
<pre><code class="hljs css language-scala"><span class="hljs-keyword">implicit</span> <span class="hljs-keyword">val</span> ec: <span class="hljs-type">ExecutionContextExecutor</span> = <span class="hljs-type">ExecutionContext</span>.global
|
||
|
||
<span class="hljs-comment">// First you need an unsigned transaction,</span>
|
||
<span class="hljs-comment">// here we have a standard 2 input, 2 output transaction</span>
|
||
<span class="hljs-comment">// This transaction must be of type BaseTransaction</span>
|
||
<span class="hljs-comment">// and have empty ScriptSignatures for all of it's inputs</span>
|
||
<span class="hljs-keyword">val</span> unsignedTransaction = <span class="hljs-type">BaseTransaction</span>(
|
||
<span class="hljs-string">"020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000"</span>)
|
||
|
||
<span class="hljs-comment">// To create the initial PSBT all we need to do is</span>
|
||
<span class="hljs-keyword">val</span> emptyPSBT = <span class="hljs-type">PSBT</span>.fromUnsignedTx(unsignedTransaction)
|
||
|
||
<span class="hljs-comment">// Now that we have an empty PSBT we can start updating it with data we know</span>
|
||
|
||
<span class="hljs-comment">// First, we want to fill the UTXO fields that we will need for signing and extraction</span>
|
||
<span class="hljs-comment">// The transactions we add are the fully serialized transaction that we are spending from</span>
|
||
<span class="hljs-keyword">val</span> utxo0 = <span class="hljs-type">Transaction</span>(
|
||
<span class="hljs-string">"0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000"</span>)
|
||
|
||
<span class="hljs-keyword">val</span> utxo1 = <span class="hljs-type">Transaction</span>(
|
||
<span class="hljs-string">"0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"</span>)
|
||
|
||
<span class="hljs-keyword">val</span> psbtWithUTXOs = emptyPSBT.addUTXOToInput(utxo0, index = <span class="hljs-number">0</span>).addUTXOToInput(utxo1, index = <span class="hljs-number">1</span>)
|
||
<span class="hljs-comment">// After we have the relevant UTXOs we can add the</span>
|
||
<span class="hljs-comment">// redeem scripts, witness scripts, and BIP 32 derivation paths if needed</span>
|
||
|
||
<span class="hljs-comment">// In this transaction the first input is a P2SH 2-of-2 multisig</span>
|
||
<span class="hljs-comment">// so we need to add its corresponding redeem script.</span>
|
||
<span class="hljs-comment">// Here we are just using a deserialized version of the redeem script but</span>
|
||
<span class="hljs-comment">// you may generate your ScriptPubKey another way in practice</span>
|
||
|
||
<span class="hljs-keyword">val</span> redeemScript0 = <span class="hljs-type">ScriptPubKey</span>.fromAsmBytes(
|
||
<span class="hljs-string">hex"5221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae"</span>)
|
||
|
||
<span class="hljs-keyword">val</span> psbtWithUpdatedFirstInput =
|
||
psbtWithUTXOs.addRedeemOrWitnessScriptToInput(redeemScript0, index = <span class="hljs-number">0</span>)
|
||
|
||
<span class="hljs-comment">// The second input in this transaction is a P2SH(P2WSH) 2-of-2 multisig</span>
|
||
<span class="hljs-comment">// so we need to add its corresponding redeem script and witness script.</span>
|
||
<span class="hljs-comment">// Here we add them both using the same function, the PSBT updater will</span>
|
||
<span class="hljs-comment">// be able to figure out, based on the available data, where to correctly</span>
|
||
|
||
<span class="hljs-keyword">val</span> redeemScript1 = <span class="hljs-type">ScriptPubKey</span>.fromAsmBytes(
|
||
<span class="hljs-string">hex"00208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903"</span>)
|
||
|
||
<span class="hljs-keyword">val</span> witnessScript = <span class="hljs-type">ScriptPubKey</span>.fromAsmBytes(
|
||
<span class="hljs-string">hex"522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae"</span>)
|
||
|
||
<span class="hljs-comment">// put the data in the PSBT</span>
|
||
<span class="hljs-keyword">val</span> psbtWithUpdatedSecondInput = psbtWithUpdatedFirstInput
|
||
.addRedeemOrWitnessScriptToInput(redeemScript1, index = <span class="hljs-number">1</span>)
|
||
.addRedeemOrWitnessScriptToInput(witnessScript, index = <span class="hljs-number">1</span>)
|
||
|
||
<span class="hljs-comment">// Before signing we need to add the needed SigHash flags so we know how to sign the transaction</span>
|
||
<span class="hljs-comment">// If one is not provided it will be assumed to be SigHashAll</span>
|
||
|
||
<span class="hljs-keyword">val</span> psbtWithSigHashFlags = psbtWithUpdatedSecondInput
|
||
.addSigHashTypeToInput(<span class="hljs-type">HashType</span>.sigHashAll, index = <span class="hljs-number">0</span>)
|
||
.addSigHashTypeToInput(<span class="hljs-type">HashType</span>.sigHashAll, index = <span class="hljs-number">1</span>)
|
||
|
||
<span class="hljs-comment">// Next, we can now sign the PSBT</span>
|
||
<span class="hljs-comment">// Signing a PSBT will return a Future[PSBT] so this will need to be handled</span>
|
||
<span class="hljs-comment">// correctly in an application</span>
|
||
<span class="hljs-comment">// Here we use the relevant private keys to sign the first input</span>
|
||
<span class="hljs-keyword">val</span> privKey0 = <span class="hljs-type">ECPrivateKeyUtil</span>.fromWIFToPrivateKey(
|
||
<span class="hljs-string">"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr"</span>).toPrivateKey
|
||
|
||
<span class="hljs-keyword">val</span> privKey1 = <span class="hljs-type">ECPrivateKeyUtil</span>.fromWIFToPrivateKey(
|
||
<span class="hljs-string">"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d"</span>).toPrivateKey
|
||
|
||
<span class="hljs-keyword">val</span> psbtFirstSig =
|
||
psbtWithSigHashFlags
|
||
.sign(inputIndex = <span class="hljs-number">0</span>, signer = privKey0)
|
||
.sign(inputIndex = <span class="hljs-number">0</span>, signer = privKey1)
|
||
|
||
<span class="hljs-comment">// Alternatively, you can use produce a signature with a ECSignatureParams</span>
|
||
<span class="hljs-comment">// using the BitcoinSigner will return a PartialSignature that can be added to a PSBT</span>
|
||
|
||
<span class="hljs-comment">// First we need to declare out spendingInfoSingle</span>
|
||
<span class="hljs-keyword">val</span> outPoint = unsignedTransaction.inputs.head.previousOutput
|
||
<span class="hljs-keyword">val</span> output = utxo0.outputs(outPoint.vout.toInt)
|
||
|
||
<span class="hljs-keyword">val</span> spendingInfoSingle = <span class="hljs-type">ECSignatureParams</span>(
|
||
<span class="hljs-type">InputInfo</span>(outPoint = outPoint,
|
||
output = output,
|
||
redeemScriptOpt = <span class="hljs-type">Some</span>(redeemScript0),
|
||
scriptWitnessOpt = <span class="hljs-type">None</span>,
|
||
conditionalPath = <span class="hljs-type">ConditionalPath</span>.<span class="hljs-type">NoCondition</span>,
|
||
previousOutputMap = <span class="hljs-type">PreviousOutputMap</span>.empty),
|
||
prevTransaction = utxo0,
|
||
signer = privKey0,
|
||
hashType = <span class="hljs-type">HashType</span>.sigHashAll
|
||
)
|
||
|
||
<span class="hljs-comment">// Then we can sign the transaction</span>
|
||
<span class="hljs-keyword">val</span> signature = <span class="hljs-type">BitcoinSigner</span>.signSingle(
|
||
spendingInfo = spendingInfoSingle,
|
||
unsignedTx = unsignedTransaction,
|
||
privKey0.signLowRWithHashType)
|
||
|
||
<span class="hljs-comment">// We can then add the signature to the PSBT</span>
|
||
<span class="hljs-comment">// Note: this signature could be produced by us or another party</span>
|
||
psbtWithSigHashFlags.addSignature(signature, inputIndex = <span class="hljs-number">0</span>)
|
||
|
||
<span class="hljs-comment">// With our first input signed we can now move on to showing how another party could sign our second input</span>
|
||
<span class="hljs-comment">// In this scenario, let's say that the second input does not belong to us and we need</span>
|
||
<span class="hljs-comment">// another party to sign it. In this case we would need to send the PSBT to the other party.</span>
|
||
<span class="hljs-comment">// The two standard formats for this are in byte form or in base64 you can access these easily.</span>
|
||
<span class="hljs-keyword">val</span> bytes = psbtFirstSig.bytes
|
||
<span class="hljs-keyword">val</span> base64 = psbtFirstSig.base64
|
||
|
||
<span class="hljs-comment">// After the other party has signed their input they can send us back the PSBT with the signatures</span>
|
||
<span class="hljs-comment">// To import we can use any of these functions</span>
|
||
<span class="hljs-keyword">val</span> fromBytes = <span class="hljs-type">PSBT</span>.fromBytes(
|
||
<span class="hljs-string">hex"70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8872202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"</span>)
|
||
|
||
<span class="hljs-keyword">val</span> fromBase64 = <span class="hljs-type">PSBT</span>.fromBase64(
|
||
<span class="hljs-string">"cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD"</span>)
|
||
|
||
<span class="hljs-comment">// After we've imported the PSBT we can combine it with our own signed PSBT so we can</span>
|
||
<span class="hljs-comment">// have one PSBT with all of the necessary data</span>
|
||
<span class="hljs-keyword">val</span> combinedPSBT = fromBase64.combinePSBT(psbtFirstSig)
|
||
|
||
<span class="hljs-comment">// Now that the PSBT has all the necessary data, we can finalize it and extract the transaction</span>
|
||
<span class="hljs-comment">// This will return a Try[PSBT] and will fail if you do not have all the required fields filled</span>
|
||
<span class="hljs-keyword">val</span> finalizedPSBT = combinedPSBT.finalizePSBT
|
||
|
||
<span class="hljs-comment">// After it has been finalized we can extract the fully signed transaction that is ready</span>
|
||
<span class="hljs-comment">// to be broadcast to the network.</span>
|
||
<span class="hljs-comment">// You can also use extractTransactionAndValidate that will validate if the transaction is valid</span>
|
||
finalizedPSBT.get.extractTransaction
|
||
</code></pre>
|
||
</span></div></article></div><div class="docs-prevnext"><a class="docs-prev button" href="/docs/next/core/spending-info"><span class="arrow-prev">← </span><span>Signing Transactions</span></a><a class="docs-next button" href="/docs/next/core/dlc"><span>Discreet Log Contract Data Structures</span><span class="arrow-next"> →</span></a></div></div></div><nav class="onPageNav"></nav></div><footer class="nav-footer" id="footer"><section class="sitemap"><a href="/" class="nav-home"><img src="/img/favicon.ico" alt="bitcoin-s" width="66" height="58"/></a><div><h5>Docs</h5><a href="/docs/en/getting-started">Getting Started</a><a href="/docs/en/core/core-intro">Guides</a><a href="/api/org/bitcoins">API Reference</a></div><div><h5>Community</h5><a href="/en/users.html">User Showcase</a><a href="https://join.slack.com/t/suredbits/shared_invite/zt-eavycu0x-WQL7XOakzQo8tAy7jHHZUw" target="_blank" rel="noreferrer noopener">Slack</a><a href="https://gitter.im/bitcoin-s-core/">Gitter chat</a></div><div><h5>More</h5><a href="https://github.com/bitcoin-s/bitcoin-s">GitHub</a><a class="github-button" href="https://github.com/bitcoin-s/bitcoin-s" data-icon="octicon-star" data-count-href="/bitcoin-s/bitcoin-s-core/stargazers" data-show-count="true" data-count-aria-label="# stargazers on GitHub" aria-label="Star this project on GitHub">Star</a></div></section><section class="copyright">Copyright © 2025 Suredbits & the bitcoin-s developers</section></footer></div><script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script><script>
|
||
document.addEventListener('keyup', function(e) {
|
||
if (e.target !== document.body) {
|
||
return;
|
||
}
|
||
// keyCode for '/' (slash)
|
||
if (e.keyCode === 191) {
|
||
const search = document.getElementById('search_input_react');
|
||
search && search.focus();
|
||
}
|
||
});
|
||
</script><script>
|
||
var search = docsearch({
|
||
|
||
apiKey: '0a510688bf8448e19aeb380377d328d3',
|
||
indexName: 'bitcoin-s',
|
||
inputSelector: '#search_input_react'
|
||
});
|
||
</script></body></html> |