Add website version for 0.1.0

This commit is contained in:
Torkel Rogstad 2019-06-20 13:47:59 +02:00
parent 0ffee0097e
commit b0bb5adf22
13 changed files with 1113 additions and 0 deletions

View File

@ -0,0 +1,86 @@
---
id: version-0.1.0-contributing-website
title: Contributing to the website
original_id: contributing-website
---
This website is built using [Docusaurus](https://docusaurus.io/).
For simple changes to the documentation, click on the `Edit` button at the top
of each page and submit those changes directly on GitHub.
## Scaladoc
One of the goals of Bitcoin-S is having useful and well-formatted Scaladoc comments on classes,
objects and functions. Here are some useful resources on how to properly format your Scaladoc comments:
- [Scaladoc for library authors](https://docs.scala-lang.org/overviews/scaladoc/for-library-authors.html)
- [Guidelines](https://docs.scala-lang.org/style/scaladoc.html) used by the official Scala language Scaladoc
- [Alvin Alexander guide](https://alvinalexander.com/scala/how-to-generate-scala-documentation-scaladoc-command-examples) on writing Scaladoc
To generate Scaladocs:
```bash
$ sbt
> unidoc
```
This gets placed in `website/static/api`. When viewing the Docusaurus site the generated Scaladocs
appear under the API tab in the header bar,
or in the "API reference" link in the footer.
## Running the site locally
For running the website locally, you'll need:
- `yarn` (https://yarnpkg.com/lang/en/docs/install-ci/)
- `sbt` (https://www.scala-sbt.org/1.0/docs/Setup.html)
> In case you want to contribute substantial structural changes to the website,
> we suggest to read
> [Docusaurus' documentation](https://docusaurus.io/docs/en/installation.html)
> first.
You can now build and launch the website using
these commands:
```sh
cd website
yarn install # only the first time, to install the dependencies
yarn start
```
In a separate shell:
```bash
$ sbt
> doc/mdoc --watch
```
The above commands compiles our Mdoc Markdown files every time you change
them, and puts them in the correct directory for Docusaurus to find them.
Now visit http://localhost:3000/ and you should see a local version of
the website.
## Adding a new page
Whenever you add a new markdown page to the documentation, you'll have to
manually include it in the side menu.
You can do this by editing the `website/sidebars.json` file. The name to use is
the `id` specified in the page metadata (see the existing pages for an example).
## Publishing the site
```bash
$ sbt
> docs/publishWebsite
```
This command first generates Scaladocs, then invokes
`docs/docusaurusPublishGhPages`, which in turn compile our mdoc
files, build the site and push them to GH pages.
Before running those commands, you might have to change a few constants in
`siteConfig.js`. These are specifed in the comments of that file.

View File

@ -0,0 +1,175 @@
---
id: version-0.1.0-contributing
title: Contributing
original_id: contributing
---
Bitcoin-S is an open source project where anyone is welcome to contribute. All contributions are encouraged and appreciated, whether that is code, testing, documentation or something else entirely.
## Communication Channels
It's possible to communicate with other developers through a variety of communication channels:
- [Suredbits Slack](https://join.slack.com/t/suredbits/shared_invite/enQtNDEyMjY3MTg1MTg3LTYyYjkwOGUzMDQ4NDAwZjE1M2I3MmQyNWNlZjNlYjg4OGRjYTRjNWUwNjRjNjg4Y2NjZjAxYjU1N2JjMTU1YWM) - Suredbits is a company monetizing APIs through the Lightning Network. Suredbits doesn't own Bitcoin-S, but the Suredbits CEO Chris Stewart is the maintainer of this library. There's a separate Bitcoin-S channel on their Slack, this is probably the easiest way of getting in touch with someone working on this project.
- [Bitcoin-S Gitter](https://gitter.im/bitcoin-s-core/)
- [#bitcoin-scala](https://webchat.freenode.net/?channels=bitcoin-scala) on IRC Freenode
## Developer productivity
### Bloop
If you're tired of waiting around for sbt all day, there's a new,
cool kid on the block. It is called [Bloop](https://scalacenter.github.io/bloop/),
and it makes compilations in general faster, and in particular
incremental, small compilation units (which greatly help editor
performance). Bloop is a server that runs in the background of
your computer, and keeps several "hot" JVMs running at all
times. These JVMs serve compilation requests. Because the JVMs
are running in the background you avoid the startup lag, and you
also get code that's already [JIT compiled](https://en.wikipedia.org/wiki/Just-in-time_compilation)
for you.
The documentation on Bloops [site](https://scalacenter.github.io/bloop/) is good, but here is the highlights:
1. Install Bloop by doing step 1 & 2 in the [official guide](https://scalacenter.github.io/bloop/setup#universal)
2. Enable the Bloop background daemon
1. macOS:
```bash
$ brew services start bloop
```
2. Ubuntu:
```bash
$ systemctl --user enable $HOME/.bloop/systemd/bloop.service
$ systemctl --user daemon-reload
$ systemctl --user start bloop
```
3. Enable shell completion for the Bloop CLI
1. Bash:
```bash
$ echo '. $HOME/.bloop/bash/bloop' >> $HOME/.bash_profile
```
2. Zsh:
```bash
$ echo 'autoload -U compinit' >> $HOME/.zshrc
$ echo 'fpath=($HOME/.bloop/zsh $fpath)' >> $HOME/.bashrc
$ echo 'compinit' >> $HOME/.bashrc
```
3. Fish:
```bash
$ ln -s $HOME/.bloop/fish/bloop.fish ~/.config/fish/completions/bloop.fish
```
4. Generate configuration files
```bash
$ sbt bloopInstall
```
5. Import Bitcoin-S into IntelliJ again, as a bsp (Build Server Protocol) project (instead of a sbt project). Make sure you're running on the most recent IntelliJ and Scala plugin. See [official docs](https://scalacenter.github.io/bloop/docs/ides/intellij) for details.
6. _(Bonus step):_ Lightning fast recompilations on file save:
```bash
$ bloop compile --project <name of module your're working on> --watch
```
Your editor should now be much faster and require less resources :tada:
## Testing
### Property based testing
This library aims to achieve high level of correctness via property based
testing. At the simplest level, you can think of property based testing as
specifying a invariant that must always hold true.
[Here](https://github.com/bitcoin-s/bitcoin-s-core/blob/89fbf35d78046b7ed21fd93fec05bb57cba023bb/src/test/scala/org/bitcoins/core/protocol/transaction/TransactionSpec.scala#L13-L17)
is an example of a property in the bitcoin-s-core test suite
```scala
property("Serialization symmetry") =
Prop.forAll(TransactionGenerators.transactions) { tx =>
Transaction(tx.hex) == tx
}
```
What this property says is that for every transaction we can generate with
[`TransactionGenerators.transactions`](/api/org/bitcoins/core/gen/TransactionGenerators)
we _must_ be able to serialize it to hex format, then deserialize it back
to a transaction and get the original `tx` back.
A more complex example of property based testing is checking that a
multisignature transaction was signed correctly (see
[`TransactionSignatureCreatorSpec`](core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureCreatorSpec.scala)
line 29-34). First we generate a _supposedly_ validly signed multisig
transaction with [`TransactionGenerators.signedMultiSigTransaction`](/api/org/bitcoins/testkit/core/gen/TransactionGenerators)
(line 102-108). These transactions have varying `m` of `n` requirements.
An interesting corner case if when you have 0 of `n` signatures, which
means no signature is required. Property based testing is really good at
fleshing out these corner cases. We check to see if this transaction is
valid by running it through our [`ScriptInterpreter`](/api/org/bitcoins/core/script/interpreter/ScriptInterpreter).
If we have built our functionality correctly the `ScriptInterpreter` should
always return [`ScriptOk`](/api/org/bitcoins/core/script/result/ScriptResult)
indicating the script was valid.
```scala
property("generate valid signatures for a multisignature transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedMultiSigTransaction) {
case (txSignatureComponent: TxSigComponent, _) =>
//run it through the interpreter
val program = ScriptProgram(txSignatureComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
```
### Running tests
To run the entire test suite all you need to do is run the following command:
> This takes a long time, and runs a lot of tests that require IO. It may hog your computer at times.
```scala
$ sbt test
[info] Elapsed time: 4 min 36.760 sec
[info] ScalaCheck
[info] Passed: Total 149, Failed 0, Errors 0, Passed 149
[info] ScalaTest
[info] Run completed in 4 minutes, 55 seconds.
[info] Total number of tests run: 744
[info] Suites: completed 97, aborted 0
[info] Tests: succeeded 744, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[info] Passed: Total 909, Failed 0, Errors 0, Passed 909
[success] Total time: 297 s, completed Jul 20, 2017 10:34:16 AM
```
To run a specific suite of tests you can specify the suite name in the following way
```scala
$ sbt testOnly *ScriptInterpreterTest*
[info] ScriptInterpreterTest:
[info] ScriptInterpreter
[info] - must evaluate all the scripts from the bitcoin core script_tests.json
[info] Run completed in 8 seconds, 208 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
```
The command `sbt testQuick` can also be handy. It runs tests that either:
1. Failed previously
2. Has not been run previously
3. Either the test or one of its dependencies has been recompiled
For more information on `testQuick`, see the offical
[sbt docs](https://www.scala-sbt.org/1.x/docs/Testing.html#testQuick).

View File

@ -0,0 +1,68 @@
---
id: version-0.1.0-addresses
title: Generating addresses
original_id: addresses
---
Almost all Bitcoin applications need to generate addresses
for their users somehow. There's a lot going on in getting
a correct bitcoin address, but our APIs make it possible to
to get started with all types of addresses in a matter of
minutes.
## Generating SegWit (bech32) addresses
Generating native SegWit addresses in the bech32 format
is something that all Bitcoin applications should enable,
as it makes the transaction fees less expensive, and also
makes the addresses more readable by humans. However, it
has seen slower than necessary adoption. With Bitcoin-S
you can generate bech32 addresses in four(!) lines of code
(not counting comments and imports), so now there's no
reason to keep using legacy transaction formats.
```scala
import org.bitcoins.core.{crypto, protocol, config}
// if you want to get addresses for mainnet, just import
// config.MainNet here instead
import config.TestNet3
import crypto.ECPrivateKey
// this gets all addresses into scope
import protocol._
// this gets all scriptPubKeys into scope
import protocol.script._
// this generates a random private key
val privkey = ECPrivateKey()
// privkey: ECPrivateKey = ECPrivateKey(9a56486eeeef146555ee6dcde9039d205649d896155855b44c8296c0464dfdb0,true)
val pubkey = privkey.publicKey
// pubkey: crypto.ECPublicKey = ECPublicKey(0367b1156e1c11d69de16ff76b2c4b249396f9bb86cf5933837e147638cdae9485)
val segwitAddress = {
// see https://bitcoin.org/en/glossary/pubkey-script
// for reading resources on the details of scriptPubKeys
// pay-to-witness-pubkey-hash scriptPubKey V0
val scriptPubKey = P2WPKHWitnessSPKV0(pubkey)
Bech32Address(scriptPubKey, TestNet3)
}
// segwitAddress: Bech32Address = Bech32Address(tb1qde6457araspjqa4hjkqfsm8z3jax97exte40h9)
```
## Generating legacy (base58) addresses
If you need to generate legacy addresses for backwards
compatability reasons, that's also a walk in the park.
Take a look:
```scala
// pay-to-pubkey-hash address
import org.bitcoins.core.protocol.P2PKHAddress
// we're reusing the same private/public key pair
// from before. don't do this in an actual application!
val legacyAddress = P2PKHAddress(pubkey, TestNet3)
// legacyAddress: P2PKHAddress = mqb1D1swAkQEAsXbznjis6CWcKp7241cM8
```

View File

@ -0,0 +1,192 @@
---
id: version-0.1.0-core-intro
title: Core module
original_id: core-intro
---
The `core` module is the core (duh!) functionality of Bitcoin-S. The goal is to provide basic
data structures that are found in the Bitcoin and Lightning protocols while
minimizing external depedencies for security purposes. We aim to have an extremely
high level of test coverage in this module to flesh out bugs. We use property based
testing heavily in this library to ensure high quality of code.
## The basics
Every bitcoin protocol data structure (and some other data structures) extends [`NetworkElement`](/api/org/bitcoins/core/protocol/NetworkElement). `NetworkElement` provides methods to convert the data structure to hex or byte representation. When paired with [`Factory`](/api/org/bitcoins/core/util/Factory) we can easily serialize and deserialize data structures.
Most data structures have companion objects that extends `Factory` to be able to easily create protocol data structures. An example of this is the [`ScriptPubKey`](/api/org/bitcoins/core/protocol/script/ScriptPubKey) companion object. You can use this companion object to create a `ScriptPubKey` from a hex string or a byte array.
## Main modules in `core`
1. [`protocol`](/api/org/bitcoins/core/protocol) - basic protocol data structures. Useful for serializing/deserializing things
2. [`crypto`](/api/org/bitcoins/core/crypto) - cryptograhic functionality used in Bitcoin and Lightning
3. [`script`](/api/org/bitcoins/core/script) - an implementation of [Script](https://en.bitcoin.it/wiki/Script) - the programming language in Bitcoin
4. [`wallet`](/api/org/bitcoins/core/wallet) - implements signing logic for Bitcoin transactions. This module is not named well as there is **NO** functionality to persist wallet state to disk as it stands. This will most likely be renamed in the future.
5. [`config`](/api/org/bitcoins/core/config) - Contains information about a chain's genesis block and DNS seeds
6. [`number`](/api/org/bitcoins/core/number) - Implements number types that are native in C, i.e. `UInt8`, `UInt32`, `UInt64`, etc.
7. [`currency`](/api/org/bitcoins/core/currency) - Implements currency units in the Bitcoin protocol
8. [`bloom`](/api/org/bitcoins/core/bloom) - Implements [Bloom filters](https://en.wikipedia.org/wiki/Bloom_filter) and [merkle blocks](https://bitcoin.org/en/glossary/merkle-block) needed for [BIP37](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki)
9. [`hd`](/api/org/bitcoins/core/hd) - Contains implementations of hierarchical deterministic (HD) paths, that when combined with `ExtPrivKey` and `ExtPubKey` in `crypto` can implement BIP32, BIP44, BIP49 and BIP84.
## Examples
### Serializing and deserializing a `Transaction`
Here is an example scala console session with bitcoins-core
```scala
import org.bitcoins.core.protocol.transaction._
val hexTx = "0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f8$9c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1$a02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e$90c865c2f6f7a9710a474154ab1423abb5b9288ac00000000"
// hexTx: String = "0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f8$9c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1$a02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e$90c865c2f6f7a9710a474154ab1423abb5b9288ac00000000"
```
This gives us an example of a hex encoded Bitcoin transaction that is deserialized to a native Scala object called a [`Transaction`](/api/org/bitcoins/core/protocol/transaction/Transaction). You could also serialize the transaction to bytes using `tx.bytes` instead of `tx.hex`. These methods are available on every data structure that extends NetworkElement, like [`ECPrivateKey`](/api/org/bitcoins/core/crypto/ECPrivateKey), [`ScriptPubKey`](/api/org/bitcoins/core/protocol/script/ScriptPubKey), [`ScriptWitness`](/api/org/bitcoins/core/protocol/script/ScriptWitness), and [`Block`](/api/org/bitcoins/core/protocol/blockchain/Block).
#### Generating a BIP39 mnemonic phrase and an `xpriv`
BIP39 mnemonic phrases are the most common way of creating backups of wallets.
They are between 12 and 24 words the user writes down, and can later be used to restore
their bitcoins. From the mnemonic phrase we generate a wallet seed, and that seed
can be used to generate what's called an extended private key
([`ExtPrivateKey`](/api/org/bitcoins/core/crypto/ExtPrivateKey) in Bitcoin-S).
Here's an example:
```scala
import scodec.bits._
import org.bitcoins.core.crypto._
import org.bitcoins.core.hd._
// the length of the entropy bit vector determine
// how long our phrase ends up being
// 256 bits of entropy results in 24 words
val entropy: BitVector = MnemonicCode.getEntropy256Bits
// entropy: BitVector = BitVector(256 bits, 0x574d20e6ea2087645573fba9f81e8ce37978145d099093e973e19dc5bbec4a28)
val mnemonicCode = MnemonicCode.fromEntropy(entropy)
// mnemonicCode: MnemonicCode = MnemonicCodeImpl(Vector(firm, harbor, defy, stairs, anchor, rate, fiction, legal, prepare, science, permit, shoulder, nurse, any, injury, craft, negative, entire, wear, describe, forum, wage, chunk, buyer))
mnemonicCode.words // the phrase the user should write down
// res0: Vector[String] = Vector(firm, harbor, defy, stairs, anchor, rate, fiction, legal, prepare, science, permit, shoulder, nurse, any, injury, craft, negative, entire, wear, describe, forum, wage, chunk, buyer) // the phrase the user should write down
// the password argument is an optional, extra security
// measure. all MnemonicCode instances will give you a
// valid BIP39 seed, but different passwords will give
// you different seeds. So you could have as many wallets
// from the same seed as you'd like, by simply giving them
// different passwords.
val bip39Seed = BIP39Seed.fromMnemonic(mnemonicCode,
password = "secret password")
// bip39Seed: BIP39Seed = BIP39SeedImpl(ByteVector(64 bytes, 0x736cda24cdaa9d037276191a07f0bd067c0bf75366241f35830f7d99741428ec2d03fd7ff896e7f30f4776bba8bfa5fb4fc9692443f20d23c840d0a59a774541))
val xpriv = ExtPrivateKey.fromBIP39Seed(ExtKeyVersion.SegWitMainNetPriv,
bip39Seed)
// xpriv: ExtPrivateKey = zprvAWgYBBk7JR8GjK4toR1D3nBk7hzpR4Ub6teUFG29phdrKk9f1ERymniBTYR4586pZiTwC6FyCgrnG9SEeRNRqBehteqSsydiezHJfJTDPZL
val xpub = xpriv.extPublicKey
// xpub: ExtPublicKey = zpub6jftahH18ngZwo9MuSYDQv8UfjqJpXCSU7a53eRmP3AqCYUoYmkEKb2fJpPXe2QLTDunZhBE8j8Bg5twZ3SVvpSb2suuSbmp3zEW5XTV6vM
// you can now use the generated xpriv to derive further
// private or public keys
// this can be done with BIP89 paths (called SegWitHDPath in bitcoin-s)
val segwitPath = SegWitHDPath.fromString("m/84'/0'/0'/0/0")
// segwitPath: SegWitHDPath = m/84'/0'/0'/0/0
// alternatively:
val otherSegwitPath =
SegWitHDPath(HDCoinType.Bitcoin,
accountIndex = 0,
HDChainType.External,
addressIndex = 0)
// otherSegwitPath: SegWitHDPath = m/84'/0'/0'/0/0
segwitPath == otherSegwitPath
// res1: Boolean = true
```
### Building a signed transaction
Bitcoin Core supports building unsigned transactions and then signing them with a set of private keys. The first important thing to look at is [`UTXOSpendingInfo`](/api/org/bitcoins/core/wallet/utxo/UTXOSpendingInfo). This contains all of the information needed to create a validly signed [`ScriptSignature`](/api/org/bitcoins/core/protocol/script/ScriptSignature) that spends this output.
Our [`TxBuilder`](/api/org/bitcoins/core/wallet/builder/TxBuilder) class requires you to provide the following:
1. `destinations` - the places we are sending bitcoin to. These are [TransactionOutputs](/api/org/bitcoins/core/protocol/transaction/TransactionOutput) you are sending coins too
2. `utxos` - these are the [UTXOs](/api/org/bitcoins/core/wallet/utxo/UTXOSpendingInfo) used to fund your transaction. These must exist in your wallet, and you must know how to spend them (i.e. have the private key)
3. `feeRate` - the fee rate you want to pay for this transaction
4. `changeSPK` - where the change (i.e. `creditingAmount - destinationAmount - fee`) from the transaction will be sent
5. `network` - the network(/api/org/bitcoins/core/config/NetworkParameters) we are transacting on
After providing this information, you can generate a validly signed bitcoin transaction by calling the `sign` method.
To see a complete example of this, see [our `TxBuilder` example](txbuilder.md)
### The [`Sign` API](/api/org/bitcoins/core/crypto/Sign)
This is the API we define to sign things with. It takes in an arbitrary byte vector and returns a `Future[ECDigitalSignature]`. The reason we incorporate `Future`s here is for extensibility of this API. We would like to provide implementations of this API for hardware devices, which need to be asynchrnous since they may require user input.
From [`core/src/main/scala/org/bitcoins/core/crypto/Sign.scala`](/api/org/bitcoins/core/crypto/Sign):
```scala
import scala.concurrent._
import scala.concurrent.duration._
trait Sign {
def signFunction: ByteVector => Future[ECDigitalSignature]
def signFuture(bytes: ByteVector): Future[ECDigitalSignature] =
signFunction(bytes)
def sign(bytes: ByteVector): ECDigitalSignature = {
Await.result(signFuture(bytes), 30.seconds)
}
def publicKey: ECPublicKey
}
```
The `ByteVector` that is input to the `signFunction` should be the hash that is output from [`TransactionSignatureSerializer`](/api/org/bitcoins/core/crypto/TransactionSignatureSerializer)'s `hashForSignature` method. Our in-memory [`ECKey`](/api/org/bitcoins/core/crypto/ECKey) types implement the `Sign` API.
If you wanted to implement a new `Sign` api for a hardware wallet, you can easily pass it into the `TxBuilder`/`Signer` classes to allow for you to use those devices to sign with Bitcoin-S.
This API is currently used to sign ordinary transactions with our [`Signer`](/api/org/bitcoins/core/wallet/signer/Signer)s. The `Signer` subtypes (i.e. `P2PKHSigner`) implement the specific functionality needed to produce a valid digital signature for their corresponding script type.
### Verifying a transaction's script is valid (does not check if UTXO is valid)
Transactions are run through the interpreter to check their validity. These are packaged up into an object called `ScriptProgram`, which contains the following:
- The transaction that is being checked
- The specific input index that it is checking
- The `scriptPubKey` for the crediting transaction
- The flags used to verify the script
Here is an example of a transaction spending a `scriptPubKey` which is correctly evaluated with our interpreter implementation:
```scala
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script._
import org.bitcoins.core.script.interpreter._
import org.bitcoins.core.policy._
import org.bitcoins.core.number._
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency._
val spendingTx = Transaction.fromHex("0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c865c2f6f7a9710a474154ab1423abb5b9288ac00000000")
val scriptPubKey = ScriptPubKey.fromAsmHex("76a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac")
val output = TransactionOutput(CurrencyUnits.zero, scriptPubKey)
val inputIndex = UInt32.zero
val btxsc = BaseTxSigComponent(spendingTx,inputIndex,output,Policy.standardScriptVerifyFlags)
val preExecution = PreExecutionScriptProgram(btxsc)
```
```scala
val result = ScriptInterpreter.run(preExecution)
// result: org.bitcoins.core.script.result.ScriptResult = ScriptOk
```

View File

@ -0,0 +1,142 @@
---
id: version-0.1.0-hd-keys
title: HD key generation
original_id: hd-keys
---
In modern Bitcoin wallets, users only need to write down
a sequence of words, and that sequence is a complete backup
of their wallet. This is thanks to what's called Hierarchical
Deterministic key generation. In short, every wallet using HD
key generation has a root seed for each wallet, and this
seed can be used to generate an arbitrary amount of later
private and public keys. This is done in a standardized manner,
so different wallets can operate with the same standard.
> If you want to jump into the details of how this work,
> you should check out
> [BIP 32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki).
Bitcoin-S supports generating keys in this fashion. Here's a
full example of how to obtain a wallet seed, and then
use that to generate further private and public keys:
```scala
import scodec.bits._
import org.bitcoins.core.crypto._
import org.bitcoins.core.hd._
// the length of the entropy bit vector determine
// how long our phrase ends up being
// 256 bits of entropy results in 24 words
val entropy: BitVector = MnemonicCode.getEntropy256Bits
// entropy: BitVector = BitVector(256 bits, 0x36b8317570cf6a0dd2838b07cb02be971c82b996d44b987530c506f2c8fef6fd)
val mnemonicCode = MnemonicCode.fromEntropy(entropy)
// mnemonicCode: MnemonicCode = MnemonicCodeImpl(Vector(cute, screen, front, thunder, wall, alone, energy, imitate, amazing, fix, quick, comfort, sight, fresh, forget, maximum, observe, praise, course, assume, clutch, legal, swim, pink))
mnemonicCode.words // the phrase the user should write down
// res0: Vector[String] = Vector(cute, screen, front, thunder, wall, alone, energy, imitate, amazing, fix, quick, comfort, sight, fresh, forget, maximum, observe, praise, course, assume, clutch, legal, swim, pink) // the phrase the user should write down
// the password argument is an optional, extra security
// measure. all MnemonicCode instances will give you a
// valid BIP39 seed, but different passwords will give
// you different seeds. So you could have as many wallets
// from the same seed as you'd like, by simply giving them
// different passwords.
val bip39Seed = BIP39Seed.fromMnemonic(mnemonicCode,
password = "secret password")
// bip39Seed: BIP39Seed = BIP39SeedImpl(ByteVector(64 bytes, 0xf6bc20ecba293c86f6c5672ebda54b5eed4f718333ea89de7a782385d61b42b39b15751adbe3247f5baebdcaf57e60ca3eb8b1845cd61166d6b37b21cf7ad8cb))
val xpriv = ExtPrivateKey.fromBIP39Seed(ExtKeyVersion.SegWitMainNetPriv,
bip39Seed)
// xpriv: ExtPrivateKey = zprvAWgYBBk7JR8GjdnxjoVfK12jRpQxjXM2hA6wkDwq624VkBcGsgprix1DtgtDMwtmahSBmfypGafJNFxLvjpMkKmH2nVq1cM4ddoFYkuRmhA
val xpub = xpriv.extPublicKey
// xpub: ExtPublicKey = zpub6jftahH18ngZx7sRqq2fg8yTyrFT8z4t4P2YYcMSeMbUcywRRE97GkKhjzA4s36tsp3ijNCSchMfUcrFTGWbCPzteku1CjmHmDqET8FeGSa
// you can now use the generated xpriv to derive further
// private or public keys
// this can be done with BIP89 paths (called SegWitHDPath in bitcoin-s)
val segwitPath = SegWitHDPath.fromString("m/84'/0'/0'/0/0")
// segwitPath: SegWitHDPath = m/84'/0'/0'/0/0
// alternatively:
val otherSegwitPath =
SegWitHDPath(HDCoinType.Bitcoin,
accountIndex = 0,
HDChainType.External,
addressIndex = 0)
// otherSegwitPath: SegWitHDPath = m/84'/0'/0'/0/0
segwitPath == otherSegwitPath
// res1: Boolean = true
```
## Generating new addresses without having access to the private key
One the coolest features of HD wallets is that it's possible
to generate addresses offline, without having access to the
private keys. This feature is commonly called watch-only
wallets, where a wallet can import information about all
your past and future transactions, without being able to
spend or steal any of your money.
Let's see an example of this:
```scala
import scala.util.Success
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.Bech32Address
import org.bitcoins.core.config.TestNet3
// first account -------┐
// bitcoin ----------┐ |
// segwit --------┐ | |
val accountPath = BIP32Path.fromString("m/84'/0'/0'")
// accountPath: BIP32Path = m/84'/0'/0'
val accountXpub = {
// this key is sensitive, keep away from prying eyes!
val accountXpriv = xpriv.deriveChildPrivKey(accountPath)
// this key is not sufficient to spend from, but we
// can generate addresses with it!
accountXpriv.extPublicKey
}
// accountXpub: ExtPublicKey = zpub6rkV5qiypfYUAmMWAvq6vGDnQCDRRnMPyGCG24YJKhAtCW87eicxGEoVu62CC8ZRkEsFYTZr4cSNDnoD7z7nuW2i8yGTTFsZTZB3ePRJHR8
// address no. 0 ---------------┐
// external address ----------┐ |
val firstAddressPath = SegWitHDPath.fromString("m/84'/0'/0'/0/0")
// firstAddressPath: SegWitHDPath = m/84'/0'/0'/0/0
val firstAccountAddress = {
// this is a bit quirky, but we're not interesting in
// deriving the complete path from our account xpub
// instead, we're only interested in the part after
// the account level (3rd level). the .diff() method
// achieves that
val Some(pathDiff) = accountPath.diff(firstAddressPath)
// deriving public keys from hardened extended keys
// is not possible, that's why .deriveChildPubKey()
// returns a Try[ExtPublicKey]. A hardened key is marked
// by a ' after the number in the notation we use above.
val Success(extPubKey) = accountXpub.deriveChildPubKey(pathDiff)
val pubkey = extPubKey.key
val scriptPubKey = P2WPKHWitnessSPKV0(pubkey)
Bech32Address(scriptPubKey, TestNet3)
}
// firstAccountAddress: Bech32Address = Bech32Address(tb1q5stv7ruhmnn58q2kwlrjscdld3pwwtd8d4prw6)
// tada! We just generated an address you can send money to,
// without having access to the private key!
firstAccountAddress.value
// res2: String = tb1q5stv7ruhmnn58q2kwlrjscdld3pwwtd8d4prw6
// you can now continue deriving addresses from the same public
// key, by imitating what we did above. To get the next
// HD path to generate an address at:
val nextAddressPath: SegWitHDPath = firstAddressPath.next
// nextAddressPath: SegWitHDPath = m/84'/0'/0'/0/1
```

View File

@ -0,0 +1,137 @@
---
id: version-0.1.0-txbuilder
title: TxBuilder example
original_id: txbuilder
---
Bitcoin-S features a transaction buidlder that constructs and signs Bitcoin
transactions. Here's an example of how to use it
```scala
import scala.concurrent._
import scala.concurrent.duration._
import org.bitcoins.core._
import number._
import config._
import currency._
import crypto._
import script.crypto._
import protocol.transaction._
import protocol.script._
import wallet.builder._
import wallet.fee._
import wallet.utxo._
implicit val ec: ExecutionContext = ExecutionContext.Implicits.global
// generate a fresh private key that we are going to use in the scriptpubkey
val privKey = ECPrivateKey.freshPrivateKey
val pubKey = privKey.publicKey
// this is the script that the TxBuilder is going to create a
// script signature that validly spends this scriptPubKey
val creditingSpk = P2PKHScriptPubKey(pubKey = privKey.publicKey)
val amount = 10000.satoshis
// this is the UTXO we are going to be spending
val utxo =
TransactionOutput(currencyUnit = amount, scriptPubKey = creditingSpk)
// the private key that locks the funds for the script we are spending too
val destinationPrivKey = ECPrivateKey.freshPrivateKey
// the amount we are sending -- 5000 satoshis -- to the destinationSPK
val destinationAmount = 5000.satoshis
// the script that corresponds to destination private key, this is what is protecting the money
val destinationSPK =
P2PKHScriptPubKey(pubKey = destinationPrivKey.publicKey)
// this is where we are sending money too
// we could add more destinations here if we
// wanted to batch transactions
val destinations = {
val destination1 = TransactionOutput(currencyUnit = destinationAmount,
scriptPubKey = destinationSPK)
List(destination1)
}
// we have to fabricate a transaction that contains the
// UTXO we are trying to spend. If this were a real blockchain
// we would need to reference the UTXO set
val creditingTx = BaseTransaction(version = Int32.one,
inputs = List.empty,
outputs = List(utxo),
lockTime = UInt32.zero)
// this is the information we need from the crediting TX
// to properly "link" it in the transaction we are creating
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
// this contains all the information we need to
// validly sign the UTXO above
val utxoSpendingInfo = BitcoinUTXOSpendingInfo(outPoint = outPoint,
output = utxo,
signers = List(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType =
HashType.sigHashAll)
// all of the UTXO spending information, since we are only
//spending one UTXO, this is just one element
val utxos: List[BitcoinUTXOSpendingInfo] = List(utxoSpendingInfo)
// this is how much we are going to pay as a fee to the network
// for this example, we are going to pay 1 satoshi per byte
val feeRate = SatoshisPerByte(1.satoshi)
val changePrivKey = ECPrivateKey.freshPrivateKey
val changeSPK = P2PKHScriptPubKey(pubKey = changePrivKey.publicKey)
// the network we are on, for this example we are using
// the regression test network. This is a network you control
// on your own machine
val networkParams = RegTest
// Yay! Now we have a TxBuilder object that we can use
// to sign the TX.
val txBuilder: BitcoinTxBuilder = {
val builderF = BitcoinTxBuilder(
destinations = destinations,
utxos = utxos,
feeRate = feeRate,
changeSPK = changeSPK,
network = networkParams)
Await.result(builderF, 30.seconds)
}
// Let's finally produce a validly signed tx!
// The 'sign' method is going produce a validly signed transaction
// This is going to iterate through each of the UTXOs and use
// the corresponding UTXOSpendingInfo to produce a validly
// signed input. This UTXO has:
// 1: one input
// 2: outputs (destination and change outputs)
// 3: a fee rate of 1 satoshi/byte
val signedTx: Transaction = {
val signF = txBuilder.sign
Await.result(signF, 30.seconds)
}
```
```scala
signedTx.inputs.length
// res0: Int = 1
signedTx.outputs.length
// res1: Int = 2
//remember, you can call .hex on any bitcoin-s data structure to get the hex representation!
signedTx.hex
// res2: String = "02000000011246e1e161b239715655cd320f01dbf182614c85a788bb61c8d393b740c355da000000006a47304402202221bf73ec3ee6d0e10b1804eb6926313a1ca927f266bb46122780df519881c502207d121507813ec4e6c5ee9c6d782252829e05e77418a41ae3c7aa74f45e41426401210282d386db7421d36dcd8a130725429789bf7f817a595a99aaad502e9e49c8d281000000000288130000000000001976a9145093de131f5ca169aed2c3f2c85eb77074920bea88aca6120000000000001976a9149e2f2ba93f21679d7676aa643489d7eca2362cb588ac00000000"
```

View File

@ -0,0 +1,70 @@
---
id: version-0.1.0-getting-started
title: Add Bitcoin-S to your project
original_id: getting-started
---
## REPL
You can try out Bitcoin-S in a REPL in a matter of seconds. Run the provided
["try bitcoin-s"](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/try-bitcoin-s.sh)
script, which has no dependencies other than an installed JDK. The script
downloads and installs [Coursier](https://get-coursier.io/) and uses it to
fetch the [Ammonite](https://ammonite.io) REPL and the latest version of
Bitcoin-S. It then drops you into immediately into a REPL session.
```bash
$ curl -s https://raw.githubusercontent.com/bitcoin-s/bitcoin-s/master/try-bitcoin-s.sh | bash
Loading...
Welcome the Bitcoin-S REPL, powered by Ammonite
Check out our documentation and examples at
https://bitcoin-s.org/docs/getting-started
@ val priv = ECPrivateKey()
@ val pub = priv.publicKey
@ val spk = P2WPKHWitnessSPKV0(pub)
@ val address = Bech32Address(spk, MainNet)
@ address.value # Tada! You've just made a Bech32 address
res4: String = "bc1q7ynsz7tamtnvlmts4snrl7e98jc9d8gqwsjsr5"
```
## Build tools
If you want to add Bitcoin-S to your project, follow the
instructions for your build tool
### sbt
Add this to your `build.sbt`:
```scala
libraryDependencies +="org.bitcoin-s" % "bitcoin-s-secp256k1jni" % "0.1.0"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-core" % "0.1.0"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-bitcoind-rpc" % "0.1.0"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-eclair-rpc" % "0.1.0"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit" % "0.1.0"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-zmq" % "0.1.0"
```
### Nightly builds
You can also run on the bleeding edge of Bitcoin-S, by
adding a snapshot build to your `build.sbt`. The most
recent snapshot published is `0.1.0+45-91633375+20190620-1322-SNAPSHOT`.
To fetch snapshots, you will need to add the correct
resolver in your `build.sbt`:
```sbt
resolvers += Resolver.sonatypeRepo("snapshots")
```
### Mill
TODO

View File

@ -0,0 +1,109 @@
---
id: version-0.1.0-rpc-bitcoind
title: bitcoind/Bitcoin Core
original_id: rpc-bitcoind
---
> Note: `bitcoin-s-bitcoind-rpc` requires you to have `bitcoind` (Bitcoin Core daemon) installed. Grab this at [bitcoincore.org](https://bitcoincore.org/en/download/)
The Bitcoin Core RPC client in Bitcoin-S currently supports the Bitcoin Core 0.16 and 0.17
version lines. It can be set up to work with both local and remote Bitcoin Core servers.
## Connecting to a local `bitcoind` instance
### Getting started quickly, with default options:
```scala
import scala.concurrent._
import akka.actor.ActorSystem
import org.bitcoins.{rpc, core}
import core.currency.Bitcoins
import rpc.client.common._
implicit val system = ActorSystem.create()
implicit val ec: ExecutionContext = system.dispatcher
// this reads authentication credentials and
// connection details from the default data
// directory on your platform
val client = BitcoindRpcClient.fromDatadir()
val balance: Future[Bitcoins] = for {
_ <- client.start()
balance <- client.getBalance
} yield balance
```
## Connecting to a remote `bitcoind`
First, we create a secure connection to our `bitcoind` instance by setting
up a SSH tunnel:
```bash
$ ssh -L 8332:localhost:8332 \
my-cool-user@my-cool-website.com
```
> Note: the port number '8332' is the default for mainnet. If you want to
> connect to a testnet `bitcoind`, the default port is '18332'
Now that we have a secure connection between our remote `bitcoind`, we're
ready to create the connection with our RPC client
```scala
import akka.actor.ActorSystem
import java.net.URI
import scala.concurrent._
import org.bitcoins.core.config._
import org.bitcoins.rpc.config._
import org.bitcoins.rpc.client.common._
val username = "FILL_ME_IN" //this username comes from 'rpcuser' in your bitcoin.conf file //this username comes from 'rpcuser' in your bitcoin.conf file
val password = "FILL_ME_IN" //this password comes from your 'rpcpassword' in your bitcoin.conf file //this password comes from your 'rpcpassword' in your bitcoin.conf file
val authCredentials = BitcoindAuthCredentials.PasswordBased(
username = username,
password = password
)
val bitcoindInstance = {
BitcoindInstance (
network = MainNet,
uri = new URI(s"http://localhost:${MainNet.port}"),
rpcUri = new URI(s"http://localhost:${MainNet.rpcPort}"),
authCredentials = authCredentials
)
}
implicit val system: ActorSystem = ActorSystem.create()
implicit val ec: ExecutionContext = system.dispatcher
val rpcCli = new BitcoindRpcClient(bitcoindInstance)
rpcCli.getBalance.onComplete { case balance =>
println(s"Wallet balance=${balance}")
system.terminate()
}
```
## Testing
To test the Bitcoin-S RPC project you need both version 0.16 and 0.17 of Bitcoin Core. A list of current and previous releases can be found [here](https://bitcoincore.org/en/releases/).
You then need to set environment variables to indicate where Bitcoin-S can find the different versions:
```bash
$ export BITCOIND_V16_PATH=/path/to/v16/bitcoind
$ export BITCOIND_V17_PATH=/path/to/v17/bitcoind
```
If you just run tests testing common functionality it's enough to have either version 0.16 or 0.17 on your `PATH`.
To run all RPC related tests:
```bash
$ bash sbt bitcoindRpcTest/test
```

View File

@ -0,0 +1,28 @@
---
id: version-0.1.0-rpc-eclair
title: Eclair
original_id: rpc-eclair
---
This is a RPC client for [Eclair](https://github.com/acinq/eclair). It assumes that a bitcoind instance is running.
Currently this RPC client is written for [v0.2-beta8](https://github.com/ACINQ/eclair/releases/tag/v0.2-beta8) version of Eclair.
## Configuration of Eclair
Please see the configuration secion of the
[Eclair README](https://github.com/acinq/eclair#configuring-eclair).
## Starting the jar
You need to download the jar from the [Eclair GitHub](https://github.com/ACINQ/eclair/releases/tag/v0.2-beta8).
To run Eclair you can use this command:
```bash
$ java -jar eclair-node-0.2-beta8-52821b8.jar &
```
Alternatively you can set the `ECLAIR_PATH` env variable and then you can start Eclair with the `start` method on `EclairRpcClient`.
**YOU NEED TO SET `ECLAIR_PATH` CORRECTLY TO BE ABLE TO RUN THE UNIT TESTS**

View File

@ -0,0 +1,7 @@
---
id: version-0.1.0-rpc-clients-intro
title: Introduction
original_id: rpc-clients-intro
---
Bitcoin-S contains RPC clients for interacting with both Bitcoin Core and Eclair.

View File

@ -0,0 +1,73 @@
---
id: version-0.1.0-security
title: Security
original_id: security
---
The Bitcoin-S developers take security very seriously. This library has
very few dependencies (at least in the `core` module), which is for
security reasons.
## Disclosure
If you have any security disclosures related to Bitcoin-S, please send an
email to either [stewart.chris1234@gmail.com](mailto:stewart.chris1234@gmail.com?subject=Bitcoin-S%20Security%20Disclosure)
or [torkel@rogstad.io](mailto:torkel@rogstad.io?subject=Bitcoin-S%20Security%20Disclosure).
If you want to encrypt said email (which you should), Torkel's key is available on [Keybase](https://keybase.io/torkelrogstad/pgp_keys.asc?fingerprint=c5dc349be6daa834ad9d59d712beee5312accec4), and Chris' is posted here:
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
mQINBFku8rYBEACub6cz6nAy/MHK2TlTztkSJbNpc2B3tZoVCKRz987yLX578JiO
EDYosAsD4b4nIjY1O6kD5FUjoJttQO+7IR8RwkmBFRsPqXaryrGr69FqaPlGSMLi
uNVlawe0qMY79DPNw03Pu2tdgiq94sWplmOnuyQqcg2lkoK1+8/DXX3801s164wq
4ZCCmkAAxJDhoLTyXB6LNI586vJhQ5+SVh8z/kAtp6NVXU5v2LcnkrqlX4oaakbt
nY2FYpAZgEI8T+Fg+btGbt3muajhM9ooq0LBtaIMwgcTC4JPyFg6acWaluMGCOtW
k1j8eu2/J4ly27YGCtWoDwrDen5qzHF6mm1HI4ko9fG2L4SxxXkl7DpGgtR71D1R
mSA9yot8oPwWW1NRncIct188uRuhy4CJIylNU88iqgpUZfYRqwaDl2KCrEbetGMT
yZSBVyilXdNjzFUJFtOPAxKmYPztUcEL1hEyGOu03YFt3FQr7QHhNR8+v688DdH4
QHpwcrXkB0x/wq18tCO6gNTbnt94VR4PJUvz3BjP7XrHGn3ljKt5PPHO7cmPkDH5
/iP928VC1U5FjAJh6dpsGgFb7k51CKx9bUJRDmL3GUMxFvmo+4YZpTKS5G+Ighs3
vLIzO1X0aV5b4mltn4Fz4o/Cp+CSVqK//YnetezkmrvwQ2u/iOGeqRKhXwARAQAB
tCtDaHJpcyBTdGV3YXJ0IDxzdGV3YXJ0LmNocmlzMTIzNEBnbWFpbC5jb20+iQI+
BBMBAgAoAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAUCWxW1AAUJBakpQwAK
CRCYRuzvvweIvijkD/0RQzQAWcJY5XJ503vj0CidzKym4ueykxhgISiZ+hmjr+u5
/Poceo5z3nXj1zI8/yMIb//7WRiX61+JGFKwN/OsM07KHa/J8Rf3NCASO2hYCGjq
ev0UOWUeSqHwN8zr4kAY9hKZJVR7byKmYV/iqmuAFTB4Nrs8567CzqKaPezB1mKv
JMTxx4XiL4M6cFiBSbCBi0qGYS6M0zBETJvFAmSh3H3PtUUdGz0kAyE2Ju11qhPh
yZVCjILnzHbsguqPSPsj3Cba6+WcbGzm7HIBPdZDjIvcnuw3Mq8kjtcpOWTMoGP1
q9xHioP97Gh+E+z7iFo8g2NszZ1wEzL4k3TnO0keknOmfOVn5zSINp6M8CHCTbzr
ls+qqHjcbAfs5BbNRMXxapO78kvSQSDenRjpC7JCl3FIwWqoMMi7T3PIYVFQfMG+
EyC3UMVOGmIDavw6yDCCdILcGeMdafEuzZmmV3Qvtt3LsqQjjm+TyBhWazRR+9sJ
kRgSdvBkF8XgqyOpaLffWPoKxnaQtJwylQnYuUM8kLP52aH2C9ctKxLChu1BDmZm
aWZNpm8h9jySeybQSBlMeDd8TrscuGJ8qBEOfREuv7fqYxsIi+G74+9h5kjiOe+y
hkIkSI440mAHRIoCyAkBx4EFZN5lRdYShRGRQrCWD/RrFssABmXhBik707V0nLkC
DQRZLvK2ARAA1gn4P9fm9KJpYuwYfxwrl0VfDPMNct5dDY6zbdQGV3djBTcuyeVU
EL6w4rRfF1aPFFEXGhT2zn2tPY410fswTNANLKuP/sFEiGEfaMjmYu0DucCMyznK
KMbE5FGD/ue0SHAN2K+A13OkPVv6kabu7XiK2QzYBrw1Lk7kGhSqMb8t3hVs0A9B
iAqWFhSIBfmnd5SKyJKNksv7DuBcgK9Xjszlbr2gV0v1kc0aMHGdZH4omB5qmq8I
aFkgZcY3MbA1zmetd1YauwzhZCFyTFcebm9trTn4D5htHGgXSehP3DMc4y9HVaLL
rANAi0Ykk7Bi+BmYHkjsvt7FZj2/V/KWkwgiPjEBvnKX3iWpbJdqU2os9VZM3ctB
WaE4pwsXt1kpiEgc/qUJ2xQL3po0dTO8q5heE6iZUNlD+xBLwpUxGjS8rN+V0LCt
muqtZ8N8lSZD5U1XVVEpEdfe8lPKQ0wvjZZUTjix1j18mhiwE+cC66JNyM3mlMUx
GKHg6M8nnXxTSbv2ogj7wXmqPF5dq5uGzCeDhdnR5tRmNQCpW/QUq8AJzV7VXRJl
+qZXar7DgbGUeRBY4M3LJptYUYqJ3LdxYPAO0AeKdlO0e843BaeG1ASGlhCZRGOn
Z0xYL7wBiVgwtx57y7D1zpQE1a9Mh7+jnUnH4tplYv70TsQ9wF5yuRsAEQEAAYkC
JQQYAQIADwIbDAUCWxW0LAUJBakobgAKCRCYRuzvvweIvhSvD/0RlNWzls+OlLqc
6NT/ZGGpECntrfTpQqA0MakuuIkf6/RgBU4OfHqqcBrG8onV1vuu0hH0KnYvv658
01PHRthIZN1K/+jcaIdNkOLbl9k9rWaDxKkHzH+IcIF61b6oCW86ffZ1yzZE2nxY
zjO0MVM4IlUxtHbrItVj5Itfj7QxPf1YdmLxO/xH2NRbmVwwTSKe4/xK3v1LSxlH
R5vknUWIT69VJoINB7jye/aSpBYpJgxXXiGbdYyt6fLyLxds7HXLJ6v6SfBKLuvm
qXsDWX5Jyv7EWzzHgl7iCIkCe9tr8ic9IyyWiRn5bjoo5vVFVMYWU/ZtGpYerAh/
sp8M09q7OeJbtbYkh9Nqz78ALt7ldT+PeAmqBVqb188F3tG8yG9lQNy43bk90Q8N
o7ZpjxlpKFYoSq/Ow7TZgMCdMPkGbcCaSkGDv/McGsaOa7IYf+7n/yE0xMSb0x5A
wyaXSLZjScTPiKGAP4fAf6hb0H1Je/69DOJynaODONAYfmlM+9WQjuVlQ3J0rlcK
Cgvon6XyxrZ4q1tKheAxOmyM0OSuPYyeXSqCYu5bt7MzNEDUChydZM3v1QvEB9iG
6eLX7fq2ipGGDoVl8y1yVRe4BWa3cJfCPC7jT+9VtD/qdlpHSwotYTWj5metYgqB
LTIbdd7r9XCGoKIxMJRqNFXc8kylUg==
=J0NH
-----END PGP PUBLIC KEY BLOCK-----
```

View File

@ -0,0 +1,23 @@
{
"version-0.1.0-docs": {
"Getting started": [
"version-0.1.0-getting-started"
],
"Core module": [
"version-0.1.0-core/core-intro",
"version-0.1.0-core/txbuilder"
],
"RPC clients": [
"version-0.1.0-rpc/rpc-clients-intro",
"version-0.1.0-rpc/rpc-eclair",
"version-0.1.0-rpc/rpc-bitcoind"
],
"Contributing": [
"version-0.1.0-contributing",
"version-0.1.0-contributing-website"
],
"Security": [
"version-0.1.0-security"
]
}
}

3
website/versions.json Normal file
View File

@ -0,0 +1,3 @@
[
"0.1.0"
]