bitcoin-s/docs/core/psbts/index.html

231 lines
26 KiB
HTML
Raw Normal View History

<!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"/><meta name="generator" content="Docusaurus"/><meta name="description" content="Creating unsigned or partially signed transactions to be passed "/><meta name="docsearch:version" content="0.3.0"/><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>0.3.0</h3></a><div class="navigationWrapper navigationSlider"><nav class="slidingNav"><ul class="nav-site nav-site-internal"><li class="siteNavGroupActive"><a href="/docs/core/core-intro" target="_self">Docs</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/getting-started">Intro and Getting Started</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Getting Setup</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/getting-setup">Getting Bitcoin-S installed on your machine</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Applications</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/applications/cli">bitcoin-s cli</a></li><li class="navListItem"><a class="navItem" href="/docs/applications/server">Application Server</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Chain</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/chain/chain">Blockchain Verification</a></li><li class="navListItem"><a class="navItem" href="/docs/chain/filter-sync">Syncing Blockfilters</a></li></ul></div><div class="navGroup"><h3 class="navGroupCategoryTitle">Configuration</h3><ul class=""><li class="navListItem"><a class="navItem" href="/docs/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/core/core-intro">Core Module</a></li><li class="navListItem"><a class="navItem" href="/docs/core/addresses">Generating Addresses</a></li><li class="navListItem"><a class="navItem" href="/docs/core/hd-keys">HD Key Generation</a></li><li class="navListItem"><a class="navItem" href="/docs/core/adding-spks">Adding New Script Types</a></li><li class="navListItem"><a class="navItem" href="/docs/core/spending-info">Signing Transactions</a></li><li class="navListItem"><a class="navItem" href="/docs/core/sign">Sign API</a></li><li class="navListItem navListItemActive"><a class="navItem" href="/docs/core/psbts">Partially Signed Bitcoin Transactions</a></li><li class="navListItem"><a class="navItem" href="/docs/core/txbuilder">TxBuilder Example</a><
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">ECPrivateKey</span>.fromWIFToPrivateKey(
<span class="hljs-string">"cP53pDbR5WtAD8dYAW9hhTjuvvTVaEiQBdrz9XPrgLBeRFiyCbQr"</span>)
<span class="hljs-keyword">val</span> privKey1 = <span class="hljs-type">ECPrivateKey</span>.fromWIFToPrivateKey(
<span class="hljs-string">"cR6SXDoyfQrcp4piaiHE97Rsgta9mNhGTen9XeonVgwsh4iSgw6d"</span>)
<span class="hljs-keyword">val</span> psbtFirstSigF =
psbtWithSigHashFlags
.sign(inputIndex = <span class="hljs-number">0</span>, signer = privKey0)
.flatMap(_.sign(inputIndex = <span class="hljs-number">0</span>, signer = privKey1))
<span class="hljs-comment">// Alternatively, you can use produce a signature with a BitcoinUTXOSpendingInfoSingle</span>
<span class="hljs-comment">// using the BitcoinSingleSigner 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">BitcoinUTXOSpendingInfoSingle</span>(
outPoint = outPoint,
output = output,
signer = privKey0,
redeemScriptOpt = <span class="hljs-type">Some</span>(redeemScript0),
scriptWitnessOpt = <span class="hljs-type">None</span>,
hashType = <span class="hljs-type">HashType</span>.sigHashAll,
conditionalPath = <span class="hljs-type">ConditionalPath</span>.<span class="hljs-type">NoConditionsLeft</span>
)
<span class="hljs-comment">// Then we can sign the transaction</span>
<span class="hljs-keyword">val</span> signatureF = <span class="hljs-type">BitcoinSignerSingle</span>.signSingle(
spendingInfo = spendingInfoSingle,
unsignedTx = unsignedTransaction,
isDummySignature = <span class="hljs-literal">false</span>)
<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>
signatureF.map(sig =&gt; psbtWithSigHashFlags.addSignature(sig, 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-keyword">val</span> signedTransactionF = psbtFirstSigF.map { psbtFirstSig =&gt;
<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
}
<span class="hljs-type">Await</span>.result(signedTransactionF, <span class="hljs-number">30.</span>seconds)
</code></pre>
</span></div></article></div><div class="docLastUpdate"><em>Last updated on 2020-6-15 by Nadav Kohen</em></div><div class="docs-prevnext"><a class="docs-prev button" href="/docs/core/sign"><span class="arrow-prev"></span><span>Previous</span></a><a class="docs-next button" href="/docs/core/txbuilder"><span class="function-name-prevnext">TxBuilder Example</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/enQtNDEyMjY3MTg1MTg3LTYyYjkwOGUzMDQ4NDAwZjE1M2I3MmQyNWNlZjNlYjg4OGRjYTRjNWUwNjRjNjg4Y2NjZjAxYjU1N2JjMTU1YWM" 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 © 2020 Suredbits &amp; 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>