mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
Add version 0.2.0 of the website (#948)
This commit is contained in:
parent
ead511a1ec
commit
d9dcacf2da
@ -1,5 +1,5 @@
|
||||
![Bitcoin-S logo](website/static/img/bitcoin-s-dark-logo.png)
|
||||
[![Build Status](https://travis-ci.org/bitcoin-s/bitcoin-s.svg?branch=master)](https://travis-ci.org/bitcoin-s/bitcoin-s) [![Coverage Status](https://coveralls.io/repos/github/bitcoin-s/bitcoin-s/badge.svg?branch=master)](https://coveralls.io/github/bitcoin-s/bitcoin-s?branch=master) [![Maven Central](https://img.shields.io/badge/Maven%20Central-0.1.0-brightgreen.svg)](https://mvnrepository.com/artifact/org.bitcoin-s) [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/bitcoin-s-core)
|
||||
[![Build Status](https://travis-ci.org/bitcoin-s/bitcoin-s.svg?branch=master)](https://travis-ci.org/bitcoin-s/bitcoin-s) [![Coverage Status](https://coveralls.io/repos/github/bitcoin-s/bitcoin-s/badge.svg?branch=master)](https://coveralls.io/github/bitcoin-s/bitcoin-s?branch=master) [![Maven Central](https://img.shields.io/badge/Maven%20Central-0.2.0-brightgreen.svg)](https://mvnrepository.com/artifact/org.bitcoin-s) [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/bitcoin-s-core)
|
||||
|
||||
Feature rich toolkit for making Bitcoin and Lightning applications
|
||||
on the JVM.
|
||||
|
@ -365,10 +365,14 @@ lazy val docs = project
|
||||
.settings(CommonSettings.testSettings: _*)
|
||||
.dependsOn(
|
||||
bitcoindRpc,
|
||||
chain,
|
||||
cli,
|
||||
core,
|
||||
eclairRpc,
|
||||
keyManager,
|
||||
secp256k1jni,
|
||||
testkit,
|
||||
wallet,
|
||||
zmq
|
||||
)
|
||||
|
||||
|
@ -94,6 +94,57 @@
|
||||
},
|
||||
"version-0.1.0/version-0.1.0-security": {
|
||||
"title": "Security"
|
||||
},
|
||||
"version-0.2.0/applications/version-0.2.0-chain": {
|
||||
"title": "Blockchain Verification"
|
||||
},
|
||||
"version-0.2.0/applications/version-0.2.0-cli": {
|
||||
"title": "bitcoin-s cli"
|
||||
},
|
||||
"version-0.2.0/applications/version-0.2.0-configuration": {
|
||||
"title": "Application configuration"
|
||||
},
|
||||
"version-0.2.0/applications/version-0.2.0-key-manager": {
|
||||
"title": "Key Manager"
|
||||
},
|
||||
"version-0.2.0/applications/version-0.2.0-node": {
|
||||
"title": "Light client"
|
||||
},
|
||||
"version-0.2.0/applications/version-0.2.0-wallet": {
|
||||
"title": "Wallet"
|
||||
},
|
||||
"version-0.2.0/version-0.2.0-contributing-website": {
|
||||
"title": "Contributing to the website"
|
||||
},
|
||||
"version-0.2.0/version-0.2.0-contributing": {
|
||||
"title": "Contributing"
|
||||
},
|
||||
"version-0.2.0/core/version-0.2.0-addresses": {
|
||||
"title": "Generating addresses"
|
||||
},
|
||||
"version-0.2.0/core/version-0.2.0-core-intro": {
|
||||
"title": "Core module"
|
||||
},
|
||||
"version-0.2.0/core/version-0.2.0-hd-keys": {
|
||||
"title": "HD key generation"
|
||||
},
|
||||
"version-0.2.0/core/version-0.2.0-txbuilder": {
|
||||
"title": "TxBuilder example"
|
||||
},
|
||||
"version-0.2.0/version-0.2.0-getting-started": {
|
||||
"title": "Add Bitcoin-S to your project"
|
||||
},
|
||||
"version-0.2.0/rpc/version-0.2.0-rpc-bitcoind": {
|
||||
"title": "bitcoind/Bitcoin Core"
|
||||
},
|
||||
"version-0.2.0/rpc/version-0.2.0-rpc-eclair": {
|
||||
"title": "Eclair"
|
||||
},
|
||||
"version-0.2.0/rpc/version-0.2.0-rpc-clients-intro": {
|
||||
"title": "Introduction"
|
||||
},
|
||||
"version-0.2.0/version-0.2.0-windows-users": {
|
||||
"title": "windows-users"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
|
@ -13,10 +13,13 @@
|
||||
"rpc/rpc-bitcoind"
|
||||
],
|
||||
"Applications": [
|
||||
"applications/node",
|
||||
"applications/wallet",
|
||||
"applications/chain",
|
||||
"applications/configuration"
|
||||
"applications/cli",
|
||||
"applications/configuration",
|
||||
"applications/key-manager",
|
||||
"applications/node",
|
||||
"applications/server",
|
||||
"applications/wallet"
|
||||
],
|
||||
"Contributing": ["contributing", "contributing-website"],
|
||||
"Security": ["security"]
|
||||
|
91
website/versioned_docs/version-0.2.0/applications/chain.md
Normal file
91
website/versioned_docs/version-0.2.0/applications/chain.md
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
title: Blockchain Verification
|
||||
id: version-0.2.0-chain
|
||||
original_id: chain
|
||||
---
|
||||
|
||||
Bitcoin-S comes bundled with a rudimentary blockchain verification
|
||||
module. This module is currently only released as a library, and not as a binary.
|
||||
This is because it (nor the documentation) is not deemed production
|
||||
ready. Use at your own risk, and without too much money depending on it.
|
||||
|
||||
## Syncing and verifying block headers
|
||||
|
||||
Using the `chain` module of Bitcoin-S it's possible to
|
||||
sync and verify block headers from the Bitcoin blockchain. In this document
|
||||
we demonstrate how to do this, while persisting it to disk. We should be
|
||||
able to read this chain on subsequent runs, assuming we are connected
|
||||
to the same `bitcoind` instance.
|
||||
|
||||
```scala
|
||||
import org.bitcoins.chain.blockchain._
|
||||
import org.bitcoins.chain.blockchain.sync._
|
||||
import org.bitcoins.chain.models._
|
||||
|
||||
import org.bitcoins.rpc.client.common._
|
||||
import org.bitcoins.testkit.chain._
|
||||
|
||||
import scala.concurrent._
|
||||
|
||||
implicit val ec = ExecutionContext.global
|
||||
|
||||
// We are assuming that a `bitcoind` regtest node is running the background.
|
||||
// You can see our `bitcoind` guides to see how to connect
|
||||
// to a local or remote `bitcoind` node.
|
||||
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
|
||||
val bitcoindInstance = BitcoindInstance.fromDatadir()
|
||||
val rpcCli = BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
// Next, we need to create a way to monitor the chain:
|
||||
|
||||
val getBestBlockHash = ChainTestUtil.bestBlockHashFnRpc(Future.successful(rpcCli))
|
||||
|
||||
val getBlockHeader = ChainTestUtil.getBlockHeaderFnRpc(Future.successful(rpcCli))
|
||||
|
||||
|
||||
// set a data directory
|
||||
import java.nio.file.Files
|
||||
val datadir = Files.createTempDirectory("bitcoin-s-test")
|
||||
|
||||
// set the currenet network to regtest
|
||||
import com.typesafe.config.ConfigFactory
|
||||
val config = ConfigFactory.parseString {
|
||||
"""
|
||||
| bitcoin-s {
|
||||
| network = regtest
|
||||
| }
|
||||
|""".stripMargin
|
||||
}
|
||||
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
implicit val chainConfig = ChainAppConfig(datadir, config)
|
||||
|
||||
// Initialize the needed database tables if they don't exist:
|
||||
val chainProjectInitF = chainConfig.initialize()
|
||||
val blockHeaderDAO = BlockHeaderDAO()
|
||||
val compactFilterHeaderDAO = CompactFilterHeaderDAO()
|
||||
val compactFilterDAO = CompactFilterDAO()
|
||||
|
||||
// Now, do the actual syncing:
|
||||
val chainHandlerF = ChainHandler.fromDatabase(blockHeaderDAO, compactFilterHeaderDAO, compactFilterDAO)
|
||||
|
||||
val syncedChainApiF = for {
|
||||
_ <- chainProjectInitF
|
||||
handler <- chainHandlerF
|
||||
synced <- ChainSync.sync(handler, getBlockHeader, getBestBlockHash)
|
||||
} yield synced
|
||||
|
||||
|
||||
val syncResultF = syncedChainApiF.flatMap { chainApi =>
|
||||
chainApi.getBlockCount.map(count => println(s"chain api blockcount=${count}"))
|
||||
|
||||
rpcCli.getBlockCount.map(count => println(s"bitcoind blockcount=${count}"))
|
||||
}
|
||||
|
||||
syncResultF.onComplete { case result =>
|
||||
println(s"Sync result=${result}")
|
||||
}
|
||||
```
|
64
website/versioned_docs/version-0.2.0/applications/cli.md
Normal file
64
website/versioned_docs/version-0.2.0/applications/cli.md
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
id: version-0.2.0-cli
|
||||
title: bitcoin-s cli
|
||||
original_id: cli
|
||||
---
|
||||
|
||||
|
||||
## Bitcoin-s command line interface
|
||||
|
||||
The [cli](../../app/cli/) project is meant to be a bitcoin-s command line interface (cli). It uses [graalvm native image](https://www.graalvm.org/docs/reference-manual/native-image/) to
|
||||
create a native executable that circumvents the jvm for fast start up time.
|
||||
|
||||
### Building the command line interface
|
||||
|
||||
#### Installing graalvm native image
|
||||
First to build the command line interface you need to have the graal jdk installed. This can be installed on [graalvm's github](https://github.com/graalvm/graalvm-ce-builds/releases/).
|
||||
|
||||
Make sure you install the [prerequisites](https://www.graalvm.org/docs/reference-manual/native-image/#prerequisites) for installing the `native-image` executable.
|
||||
|
||||
|
||||
If you do not have the `native-image` executable installed, you need to install it with
|
||||
|
||||
```bashrc
|
||||
./graalvm-ce-java8-19.3.0/bin/gu install native-image
|
||||
```
|
||||
|
||||
Once you have the graalvm installed you should be able to verify you have the `native-image` exeuctable installed by running
|
||||
|
||||
```bashrc
|
||||
$ native-image --help
|
||||
```
|
||||
|
||||
If your command did not work, make sure you can find the `native-image` executable on your `PATH`.
|
||||
|
||||
#### Building the native image
|
||||
|
||||
Now that you have graalvm native image installed, you should be able to build the cli with. It is important to note that
|
||||
this only works with Scala `2.12.x`. I'm unsure of why the native image build fails with Scala `2.13.x`
|
||||
|
||||
```bashrc
|
||||
$ sbt ++2.12.10 cli/graalvm-native-image:packageBin
|
||||
```
|
||||
|
||||
After running that command you should find the executable here:
|
||||
|
||||
```bash
|
||||
bitcoin-s/app/cli/target/graalvm-native-image
|
||||
```
|
||||
|
||||
#### Executing commands
|
||||
You can ask the client for help with
|
||||
|
||||
```bash
|
||||
./app/cli/target/graalvm-native-image/bitcoin-s-cli --help
|
||||
Usage: bitcoin-s-cli [options] [<cmd>]
|
||||
|
||||
-n, --network <value> Select the active network.
|
||||
--debug Print debugging information
|
||||
-h, --help Display this help message and exit
|
||||
<cmd> The command and arguments to be executed. Try bitcoin-s-cli help for a list of all commands
|
||||
```
|
||||
|
||||
|
||||
Now you are are ready to start the server that the cli sends commands to. Take a look at our [server](server.md) documentation on how to build and start the server.
|
@ -0,0 +1,51 @@
|
||||
---
|
||||
id: version-0.2.0-configuration
|
||||
title: Application configuration
|
||||
original_id: configuration
|
||||
---
|
||||
|
||||
Bitcoin-S uses [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md)
|
||||
to configure various parts of the application the library offers. HOCON is a
|
||||
superset of JSON, that is, all valid JSON is valid HOCON.
|
||||
|
||||
All configuration for Bitcoin-S is under the `bitcoin-s` key.
|
||||
|
||||
If you have a file `application.conf` anywhere on your classpath when using
|
||||
bitcoin-s, the values there take precedence over the ones found in our
|
||||
`reference.conf`. We also look for the file `bitcoin-s.conf` in the current
|
||||
Bitcoin-S data directory.
|
||||
|
||||
The resolved configuration gets parsed by
|
||||
[`AppConfig`](../../db-commons/src/main/scala/org/bitcoins/db/AppConfig.scala).
|
||||
`AppConfig` is an abstract class that's implemented by corresponding case
|
||||
classes in the `wallet`, `chain` and `node` projects. Here's some examples of how to
|
||||
construct a wallet configuration:
|
||||
|
||||
```scala
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import java.nio.file.Paths
|
||||
import scala.util.Properties
|
||||
|
||||
// reads $HOME/.bitcoin-s/
|
||||
val defaultConfig = WalletAppConfig.fromDefaultDatadir()
|
||||
|
||||
|
||||
// reads a custom data directory
|
||||
val customDirectory = Paths.get(Properties.userHome, "custom-bitcoin-s-directory")
|
||||
val configFromCustomDatadir = WalletAppConfig(customDirectory)
|
||||
|
||||
// reads a custom data directory and overrides the network to be testnet3
|
||||
val customOverride = ConfigFactory.parseString("bitcoin-s.network = testnet3")
|
||||
val configFromCustomDirAndOverride = WalletAppConfig(customDirectory, customOverride)
|
||||
```
|
||||
|
||||
You can pass as many `com.typesafe.config.Config`s as you'd like. If any
|
||||
keys appear multiple times the last one encountered takes precedence.
|
||||
|
||||
## Internal configuration
|
||||
|
||||
Database connections are also configured by using HOCON. This is done in
|
||||
[`db.conf`](../../db-commons/src/main/resources/db.conf). The options
|
||||
exposed here are **not** intended to
|
||||
be used by users of Bitcoin-S, and are internal only.
|
@ -0,0 +1,16 @@
|
||||
---
|
||||
id: version-0.2.0-key-manager
|
||||
title: Key Manager
|
||||
original_id: key-manager
|
||||
---
|
||||
|
||||
|
||||
#### Key Manager
|
||||
|
||||
The key manager module's goal is to encapusulate all private key interactions with the [wallet](wallet.md) project.
|
||||
|
||||
As of this writing, the wallet just delegates storage of the encrypted mnemonic seed to the key manager project. Over the log run, we want to make it so that the wallet project needs to communicate with the key-manager to access private keys.
|
||||
|
||||
This means that ALL SIGNING should be done inside of the key-manager, and private keys should not leave the key manager.
|
||||
|
||||
This makes it easier to reason about the security characteristics of our private keys, and a way to provide a uniform interface for alternative key storage systems (hsm, cloud based key storage, etc) to be plugged into the bitcoin-s library.
|
14
website/versioned_docs/version-0.2.0/applications/node.md
Normal file
14
website/versioned_docs/version-0.2.0/applications/node.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Light client
|
||||
id: version-0.2.0-node
|
||||
original_id: node
|
||||
---
|
||||
|
||||
Bitcoin-S comes bundled with a light client Bitcoin node. this client
|
||||
is capable of doing verification of some parts of the blockchain,
|
||||
and can act as a starting point for building Bitcoin applications
|
||||
that need to connect to the P2P network.
|
||||
|
||||
This node is currently only released as a library, and not as a binary.
|
||||
This is because it (nor the documentation) is not deemed production
|
||||
ready. Use at your own risk, and without too much money depending on it.
|
136
website/versioned_docs/version-0.2.0/applications/wallet.md
Normal file
136
website/versioned_docs/version-0.2.0/applications/wallet.md
Normal file
@ -0,0 +1,136 @@
|
||||
---
|
||||
title: Wallet
|
||||
id: version-0.2.0-wallet
|
||||
original_id: wallet
|
||||
---
|
||||
|
||||
Bitcoin-S comes bundled with a rudimentary Bitcoin wallet. This wallet
|
||||
is capable of managing private keys, generating addresses, constructing
|
||||
and signing transactions, among other things. It is BIP32/BIP44/BIP49/BIP84
|
||||
compatible.
|
||||
|
||||
This wallet is currently only released as a library, and not as a binary.
|
||||
This is because it (nor the documentation) is not deemed production
|
||||
ready. Use at your own risk, and without too much money depending on it.
|
||||
|
||||
## Creating a wallet
|
||||
|
||||
This guide shows how to create a Bitcoin-S wallet and then
|
||||
peer it with a `bitcoind` instance that relays
|
||||
information about what is happening on the blockchain
|
||||
through the P2P network.
|
||||
|
||||
This is useful if you want more flexible signing procedures in
|
||||
the JVM ecosystem and more granular control over your
|
||||
UTXOs with popular database like Postgres, SQLite, etc.
|
||||
|
||||
This code snippet you have a running `bitcoind` instance, locally
|
||||
on regtest.
|
||||
|
||||
```scala
|
||||
implicit val ec = scala.concurrent.ExecutionContext.global
|
||||
|
||||
import com.typesafe.config.ConfigFactory
|
||||
val config = ConfigFactory.parseString {
|
||||
"""
|
||||
| bitcoin-s {
|
||||
| network = regtest
|
||||
| }
|
||||
""".stripMargin
|
||||
}
|
||||
|
||||
import java.nio.file.Files
|
||||
val datadir = Files.createTempDirectory("bitcoin-s-wallet")
|
||||
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
implicit val walletConfig = WalletAppConfig(datadir, config)
|
||||
|
||||
// we also need to store chain state for syncing purposes
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
implicit val chainConfig = ChainAppConfig(datadir, config)
|
||||
|
||||
// when this future completes, we have
|
||||
// created the necessary directories and
|
||||
// databases for managing both chain state
|
||||
// and wallet state
|
||||
import scala.concurrent._
|
||||
val configF: Future[Unit] = for {
|
||||
_ <- walletConfig.initialize()
|
||||
_ <- chainConfig.initialize()
|
||||
} yield ()
|
||||
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
val bitcoindInstance = BitcoindInstance.fromDatadir()
|
||||
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
val bitcoind = BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
// when this future completes, we have
|
||||
// synced our chain handler to our bitcoind
|
||||
// peer
|
||||
import org.bitcoins.chain.api.ChainApi
|
||||
val syncF: Future[ChainApi] = configF.flatMap { _ =>
|
||||
val getBestBlockHashFunc = { () =>
|
||||
bitcoind.getBestBlockHash
|
||||
}
|
||||
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
val getBlockHeaderFunc = { hash: DoubleSha256DigestBE =>
|
||||
bitcoind.getBlockHeader(hash).map(_.blockHeader)
|
||||
}
|
||||
|
||||
|
||||
import org.bitcoins.chain.models._
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
val blockHeaderDAO = BlockHeaderDAO()
|
||||
val compactFilterHeaderDAO = CompactFilterHeaderDAO()
|
||||
val compactFilterDAO = CompactFilterDAO()
|
||||
val chainHandler = ChainHandler(
|
||||
blockHeaderDAO,
|
||||
compactFilterHeaderDAO,
|
||||
compactFilterDAO,
|
||||
blockchains = Vector.empty,
|
||||
blockFilterCheckpoints = Map.empty)
|
||||
|
||||
import org.bitcoins.chain.blockchain.sync.ChainSync
|
||||
ChainSync.sync(chainHandler, getBlockHeaderFunc, getBestBlockHashFunc)
|
||||
}
|
||||
|
||||
// once this future completes, we have a initialized
|
||||
// wallet
|
||||
import org.bitcoins.wallet.api.LockedWalletApi
|
||||
import org.bitcoins.wallet.api.InitializeWalletSuccess
|
||||
import org.bitcoins.wallet.Wallet
|
||||
import org.bitcoins.core.api._
|
||||
val walletF: Future[LockedWalletApi] = configF.flatMap { _ =>
|
||||
Wallet.initialize(NodeApi.NoOp, ChainQueryApi.NoOp).collect {
|
||||
case InitializeWalletSuccess(wallet) => wallet
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// when this future completes, ww have sent a transaction
|
||||
// from bitcoind to the Bitcoin-S wallet
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.currency._
|
||||
val transactionF: Future[Transaction] = for {
|
||||
wallet <- walletF
|
||||
address <- wallet.getNewAddress()
|
||||
txid <- bitcoind.sendToAddress(address, 3.bitcoin)
|
||||
transaction <- bitcoind.getRawTransaction(txid)
|
||||
} yield transaction.hex
|
||||
|
||||
// when this future completes, we have processed
|
||||
// the transaction from bitcoind, and we have
|
||||
// queried our balance for the current balance
|
||||
val balanceF: Future[CurrencyUnit] = for {
|
||||
wallet <- walletF
|
||||
tx <- transactionF
|
||||
_ <- wallet.processTransaction(tx, confirmations = 0)
|
||||
balance <- wallet.getBalance
|
||||
} yield balance
|
||||
|
||||
balanceF.foreach { balance =>
|
||||
println(s"Bitcoin-S wallet balance: $balance")
|
||||
}
|
||||
```
|
91
website/versioned_docs/version-0.2.0/contributing-website.md
Normal file
91
website/versioned_docs/version-0.2.0/contributing-website.md
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
id: version-0.2.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
|
||||
$ bloop run docs -- --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.
|
||||
|
||||
### CI
|
||||
|
||||
Bitcoin-S uses Travis to run tests and deploy library and website builds. Generally
|
||||
speaking CI has to pass for a PR to get merged. If you make documentation/website only
|
||||
changes, you can start your PR title with `Docs:`. This skips running tests on CI.
|
255
website/versioned_docs/version-0.2.0/contributing.md
Normal file
255
website/versioned_docs/version-0.2.0/contributing.md
Normal file
@ -0,0 +1,255 @@
|
||||
---
|
||||
id: version-0.2.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
|
||||
|
||||
## Working on Bitcoin-S applications
|
||||
|
||||
Bitcoin-S includes a couple of applications that can be run as standalone executables.
|
||||
This includes the node, wallet and (partial) blockchain verification modules, as well
|
||||
as the server that bundles these three together and the CLI used to communicate with
|
||||
the server. These applications are configured with HOCON files. The file
|
||||
[`reference.conf`](../testkit/src/main/resources/reference.conf)
|
||||
is the basis configuration file, and every option read by Bitcoin-S should be present in
|
||||
this file. This means that you can copy sections from this file and edit them, to tune
|
||||
how the application runs on your machine.
|
||||
|
||||
One example of things you can tune is logging levels. Lets say you wanted general logging
|
||||
to happen at the `WARN` level, but the P2P message handling to be logged at `DEBUG`. Your
|
||||
configuration file would then look like:
|
||||
|
||||
```conf
|
||||
bitcoins-s {
|
||||
logging {
|
||||
level = warn
|
||||
|
||||
p2p = debug
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Running the applications
|
||||
|
||||
When running the applications configuration placed in `bitcoin-s.conf` in the current
|
||||
data directory gets picked up. For linux this is by default `$HOME/.bitcoin-s/`, so the
|
||||
file you should edit would be `$HOME/.bitcoin-s/bitcoin-s.conf`.
|
||||
|
||||
### Running tests for the applications
|
||||
|
||||
You can place a `logback-test.xml` file in the `src/test/resources/` directory in the same project that tests are being run in.
|
||||
|
||||
If the test suite depends on `testkit`, you can modify [`reference.conf`](../testkit/src/main/resources/reference.conf)
|
||||
that is built into the testkit to control logging.
|
||||
|
||||
## Logging when working on Bitcoin-S tests
|
||||
|
||||
When working on various parts of Bitcoin-S the need to log what's going on arises
|
||||
pretty quickly. There's two way of doing this:
|
||||
|
||||
1. Using the way described in the section above, "Working on Bitcoin-S applications".
|
||||
You could either use traits (like `HTTPLogger` or `P2PLogger`) that exposes a
|
||||
field `logger`, or acquire the logger directly through the traits companion
|
||||
object.
|
||||
2. Use the standard `BitcoinSLogger`, which is also available as both a trait and
|
||||
a companion object with a field you can access (`BitcoinSLogger.logger`). Note
|
||||
that by default all logging from this logger is turned off in tests, to make
|
||||
output less noisy. You can tune this by changing the level found in
|
||||
`core-test/src/test/resources/logback-test.xml`.
|
||||
|
||||
### Akka logging
|
||||
|
||||
The test logging for akka is controlled by the [`reference.conf`](../testkit/src/main/resources/reference.conf) file inside of testkit.
|
||||
|
||||
This allows you to debug what is happening in our actors inside of bitcoin-s easier. For examples of what you can enable for akka to log, please look at their [logging documentation](https://doc.akka.io/docs/akka/current/logging.html#auxiliary-logging-options)
|
||||
|
||||
The easiest thing to do to enable akka logging is to adjust the `loglevel` and `stdout-loglevel` from `OFF` to `DEBUG`.
|
||||
|
||||
If you want to enable this when you are running a bitcoin-s application, you will need to modify the [`reference.conf`](../app/server/src/main/resources/reference.conf) file
|
||||
|
||||
## Developer productivity
|
||||
|
||||
### sbt
|
||||
|
||||
The default scala build tool is [sbt](https://www.scala-sbt.org/).
|
||||
|
||||
For the basics of how sbt works see the [sbt guide](https://www.scala-sbt.org/1.x/docs/Getting-Started.html)
|
||||
|
||||
One helpful configuration is the env variable `SBT_OPTS` which allows you to pass jvm arguments for sbt.
|
||||
|
||||
### 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).
|
||||
|
||||
### Coverage
|
||||
|
||||
To produce a report that quantifies how much of our code is covered by tests:
|
||||
|
||||
```bash
|
||||
sbt
|
||||
> coverage
|
||||
> coreTest/test
|
||||
> core/coverageReport
|
||||
```
|
||||
|
||||
This generates three different reports: Cobertura, XML and HTML formats.
|
||||
See the output of your sbt shell to find the location of them.
|
||||
Open up the HTML file in your browser. You'll now see code coverage
|
||||
of all files in `core` project.
|
||||
|
||||
### CI
|
||||
|
||||
Bitcoin-S uses Travis to run tests and deploy library and website builds. Generally
|
||||
speaking CI has to pass for a PR to get merged. If you make documentation/website only
|
||||
changes, you can start your PR title with `Docs:`. This skips running tests on CI.
|
67
website/versioned_docs/version-0.2.0/core/addresses.md
Normal file
67
website/versioned_docs/version-0.2.0/core/addresses.md
Normal file
@ -0,0 +1,67 @@
|
||||
---
|
||||
id: version-0.2.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(7f0fc482719d180ec8281100f977d6ad50ed1971b71d31026d513fb51bb95206,true)
|
||||
val pubkey = privkey.publicKey
|
||||
// pubkey: crypto.ECPublicKey = ECPublicKey(03c585c7c1cd4e670683ec5f3a043a800f7f84ddf39d04a9fe4364802d85f1c756)
|
||||
|
||||
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(tb1q9vyukq9pt9kw0r70e5zpd06mkyk2m6qukd7uc9)
|
||||
```
|
||||
|
||||
## 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 = mjSX5jLeUuAsmxLqzFEUMyjCKAKNo5WHJw
|
||||
```
|
141
website/versioned_docs/version-0.2.0/core/core-intro.md
Normal file
141
website/versioned_docs/version-0.2.0/core/core-intro.md
Normal file
@ -0,0 +1,141 @@
|
||||
---
|
||||
id: version-0.2.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
|
||||
1. [`crypto`](/api/org/bitcoins/core/crypto) - cryptograhic functionality used in Bitcoin and Lightning
|
||||
1. [`script`](/api/org/bitcoins/core/script) - an implementation of [Script](https://en.bitcoin.it/wiki/Script) - the programming language in Bitcoin
|
||||
1. [`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.
|
||||
1. [`config`](/api/org/bitcoins/core/config) - Contains information about a chain's genesis block and DNS seeds
|
||||
1. [`number`](/api/org/bitcoins/core/number) - Implements number types that are native in C, i.e. `UInt8`, `UInt32`, `UInt64`, etc.
|
||||
1. [`currency`](/api/org/bitcoins/core/currency) - Implements currency units in the Bitcoin protocol
|
||||
1. [`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)
|
||||
1. [`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 = "0100000002d8c8df6a6fdd2addaf589a83d860f18b44872d13ee6ec3526b2b470d42a96d4d000000008b483045022100b31557e47191936cb14e013fb421b1860b5e4fd5d2bc5ec1938f4ffb1651dc8902202661c2920771fd29dd91cd4100cefb971269836da4914d970d333861819265ba014104c54f8ea9507f31a05ae325616e3024bd9878cb0a5dff780444002d731577be4e2e69c663ff2da922902a4454841aa1754c1b6292ad7d317150308d8cce0ad7abffffffff2ab3fa4f68a512266134085d3260b94d3b6cfd351450cff021c045a69ba120b2000000008b4830450220230110bc99ef311f1f8bda9d0d968bfe5dfa4af171adbef9ef71678d658823bf022100f956d4fcfa0995a578d84e7e913f9bb1cf5b5be1440bcede07bce9cd5b38115d014104c6ec27cffce0823c3fecb162dbd576c88dd7cda0b7b32b0961188a392b488c94ca174d833ee6a9b71c0996620ae71e799fc7c77901db147fa7d97732e49c8226ffffffff02c0175302000000001976a914a3d89c53bb956f08917b44d113c6b2bcbe0c29b788acc01c3d09000000001976a91408338e1d5e26db3fce21b011795b1c3c8a5a5d0788ac00000000"
|
||||
// hexTx: String = 0100000002d8c8df6a6fdd2addaf589a83d860f18b44872d13ee6ec3526b2b470d42a96d4d000000008b483045022100b31557e47191936cb14e013fb421b1860b5e4fd5d2bc5ec1938f4ffb1651dc8902202661c2920771fd29dd91cd4100cefb971269836da4914d970d333861819265ba014104c54f8ea9507f31a05ae325616e3024bd9878cb0a5dff780444002d731577be4e2e69c663ff2da922902a4454841aa1754c1b6292ad7d317150308d8cce0ad7abffffffff2ab3fa4f68a512266134085d3260b94d3b6cfd351450cff021c045a69ba120b2000000008b4830450220230110bc99ef311f1f8bda9d0d968bfe5dfa4af171adbef9ef71678d658823bf022100f956d4fcfa0995a578d84e7e913f9bb1cf5b5be1440bcede07bce9cd5b38115d014104c6ec27cffce0823c3fecb162dbd576c88dd7cda0b7b32b0961188a392b488c94ca174d833ee6a9b71c0996620ae71e799fc7c77901db147fa7d97732e49c8226ffffffff02c0175302000000001976a914a3d89c53bb956f08917b44d113c6b2bcbe0c29b788acc01c3d09000000001976a91408338e1d5e26db3fce21b011795b1c3c8a5a5d0788ac00000000
|
||||
|
||||
val tx = Transaction.fromHex(hexTx)
|
||||
// tx: Transaction = BaseTransactionImpl(Int32Impl(1),Vector(TransactionInputImpl(TransactionOutPoint(4d6da9420d472b6b52c36eee132d87448bf160d8839a58afdd2add6f6adfc8d8:0),P2PKHScriptSignature(ECPublicKey(04c54f8ea9507f31a05ae325616e3024bd9878cb0a5dff780444002d731577be4e2e69c663ff2da922902a4454841aa1754c1b6292ad7d317150308d8cce0ad7ab), ECDigitalSignature(3045022100b31557e47191936cb14e013fb421b1860b5e4fd5d2bc5ec1938f4ffb1651dc8902202661c2920771fd29dd91cd4100cefb971269836da4914d970d333861819265ba01)),UInt32Impl(4294967295)), TransactionInputImpl(TransactionOutPoint(b220a19ba645c021f0cf501435fd6c3b4db960325d0834612612a5684ffab32a:0),P2PKHScriptSignature(ECPublicKey(04c6ec27cffce0823c3fecb162dbd576c88dd7cda0b7b32b0961188a392b488c94ca174d833ee6a9b71c0996620ae71e799fc7c77901db147fa7d97732e49c8226), ECDigitalSignature(30450220230110bc99ef311f1f8bda9d0d968bfe5dfa4af171adbef9ef71678d658823bf022100f956d4fcfa0995a578d84e7e913f9bb1cf5b5be1440bcede07bce9cd5b38115d01)),UInt32Impl(4294967295))),Vector(TransactionOutput(39000000 sats,P2PKHScriptPubKeyImpl(Sha256Hash160DigestImpl(a3d89c53bb956f08917b44d113c6b2bcbe0c29b7))), TransactionOutput(155000000 sats,P2PKHScriptPubKeyImpl(Sha256Hash160DigestImpl(08338e1d5e26db3fce21b011795b1c3c8a5a5d07)))),UInt32Impl(0))
|
||||
|
||||
tx.hex == hexTx
|
||||
// res0: Boolean = true
|
||||
```
|
||||
|
||||
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`
|
||||
|
||||
See our [HD example](hd-keys)
|
||||
|
||||
### 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 scodec.bits._
|
||||
import org.bitcoins.core.crypto._
|
||||
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
|
||||
```
|
141
website/versioned_docs/version-0.2.0/core/hd-keys.md
Normal file
141
website/versioned_docs/version-0.2.0/core/hd-keys.md
Normal file
@ -0,0 +1,141 @@
|
||||
---
|
||||
id: version-0.2.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, 0x39f30e63d03882325006e97c5a15e6d471a4c7d35112569d2352b052a091e3cf)
|
||||
|
||||
val mnemonicCode = MnemonicCode.fromEntropy(entropy)
|
||||
// mnemonicCode: MnemonicCode = MnemonicCodeImpl(Vector(delay, observe, ocean, parrot, market, bomb, divorce, tag, labor, spawn, keen, pottery, bottom, glue, essence, car, final, trouble, start, gaze, claw, employ, monkey, type))
|
||||
|
||||
mnemonicCode.words // the phrase the user should write down
|
||||
// res0: Vector[String] = Vector(delay, observe, ocean, parrot, market, bomb, divorce, tag, labor, spawn, keen, pottery, bottom, glue, essence, car, final, trouble, start, gaze, claw, employ, monkey, type) // 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, 0xd45cc7ef7aa2aae1a8cd90c91a4f79b866dd00a10cf8cdaae157a822167a4cc740b7364eb43f1474d8c86473db4faf845e6a9e3fd06ee39eeb493c751a0cdfee))
|
||||
|
||||
val xpriv = ExtPrivateKey.fromBIP39Seed(ExtKeyVersion.SegWitMainNetPriv,
|
||||
bip39Seed)
|
||||
// xpriv: ExtPrivateKey = zprvAWgYBBk7JR8GjukHugLbrUkjRE3ycvmhRCT4cE2T2qU53j18nUjL4ocaDsXwBWYzU5u88ZFxQ42YWMLAT1S5V2wwcxgogsMrDQtNeLBwr3D
|
||||
val xpub = xpriv.extPublicKey
|
||||
// xpub: ExtPublicKey = zpub6jftahH18ngZxPpm1hscDchTyFtU2PVYnRNfQcS4bB13vXLHL23acbw459g4XxYYHfo44rTmYSXzBQzzBEVqT3PfPcqL69fpdrgkxMHuQCy
|
||||
|
||||
// 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 = zpub6qUM2b9Nq9v51X1sPsXpXWznJnBhvhHUfhrXvMFntXQT8ZkK2tizi29cMm2HH3vb1Zz8CHb3933J9tvaH97GS4s6XUZQsrTYf7cWGYtmYLA
|
||||
|
||||
// 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(tb1quqvdsqdzt2nlszkfqsxndu5pg6ahzyjk2e5qmz)
|
||||
|
||||
// tada! We just generated an address you can send money to,
|
||||
// without having access to the private key!
|
||||
firstAccountAddress.value
|
||||
// res2: String = tb1quqvdsqdzt2nlszkfqsxndu5pg6ahzyjk2e5qmz
|
||||
|
||||
// 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
|
||||
```
|
138
website/versioned_docs/version-0.2.0/core/txbuilder.md
Normal file
138
website/versioned_docs/version-0.2.0/core/txbuilder.md
Normal file
@ -0,0 +1,138 @@
|
||||
---
|
||||
id: version-0.2.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(value = 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(value = 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,
|
||||
conditionalPath =
|
||||
ConditionalPath.NoConditionsLeft)
|
||||
|
||||
// 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 = "0200000001aaa194af011ead436c86eddd507b9a02fc7b15b190d6975fa68757f5cf6ef640000000006a473044022070862947a8ee417f0873e9edb9484684c3671b109e1f69403135220bf4c9687c02207d2395edb2a88522d42fb3e34c6e86f8b81b333b18759dfb6a78ed978d30a07c01210342b79cc77b59e28a5f569348e9efd5fa9a37982f18c1d29b0818bc8890a1c60b000000000288130000000000001976a9146c9e69cc34268e73bbf02c0819d92db359385cd188aca6120000000000001976a914e5050227c7823cb7c5da0f8d879b1562b4ca581c88ac00000000"
|
||||
```
|
79
website/versioned_docs/version-0.2.0/getting-started.md
Normal file
79
website/versioned_docs/version-0.2.0/getting-started.md
Normal file
@ -0,0 +1,79 @@
|
||||
---
|
||||
id: version-0.2.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"
|
||||
```
|
||||
|
||||
## Getting prebuilt JARs
|
||||
|
||||
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.2.0"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-core" % "0.2.0"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-bitcoind-rpc" % "0.2.0"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-eclair-rpc" % "0.2.0"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit" % "0.2.0"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-zmq" % "0.2.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.2.0+113-52351447+20191215-2018-SNAPSHOT`.
|
||||
|
||||
To fetch snapshots, you will need to add the correct
|
||||
resolver in your `build.sbt`:
|
||||
|
||||
```sbt
|
||||
resolvers += Resolver.sonatypeRepo("snapshots")
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Mill
|
||||
|
||||
TODO
|
||||
|
||||
## Building JARs yourself
|
||||
|
||||
If you want to build Bitcoin-S JARs yourself, you need to use the
|
||||
[sbt](https://www.scala-sbt.org/) build tool. Once you have sbt
|
||||
installed, run `sbt publishLocal`. This places the required JAR
|
||||
files in your `.ivy2/local` folder. On Linux, this is located at
|
||||
`$HOME/.ivy2/local/` by default.
|
123
website/versioned_docs/version-0.2.0/rpc/bitcoind.md
Normal file
123
website/versioned_docs/version-0.2.0/rpc/bitcoind.md
Normal file
@ -0,0 +1,123 @@
|
||||
---
|
||||
id: version-0.2.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 org.bitcoins.{rpc, core}
|
||||
import core.currency.Bitcoins
|
||||
import rpc.client.common._
|
||||
import java.io._
|
||||
|
||||
implicit val ec: ExecutionContext = ExecutionContext.global
|
||||
|
||||
// this reads authentication credentials and
|
||||
// connection details from the default data
|
||||
// directory on your platform
|
||||
val client = BitcoindRpcClient.fromDatadir(binary=new File("/path/to/bitcoind"), datadir=new File("/path/to/bitcoind-datadir"))
|
||||
|
||||
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 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 ec: ExecutionContext = ExecutionContext.global
|
||||
|
||||
val rpcCli = BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
rpcCli.getBalance.onComplete { case balance =>
|
||||
println(s"Wallet balance=${balance}")
|
||||
}
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
All errors returned by Bitcoin Core are mapped to a corresponding
|
||||
[`BitcoindException`](https://github.com/bitcoin-s/bitcoin-s/blob/master/bitcoind-rpc/src/main/scala/org/bitcoins/rpc/BitcoindException.scala).
|
||||
These exceptions contain an error code and a message. `BitcoindException` is a sealed
|
||||
trait, which means you can easily pattern match exhaustively. Of course, other errors
|
||||
could also happen: network errors, stack overflows or out-of-memory errors. The provided
|
||||
class is only intended to cover errors returned by Bitcoin Core. An example of how error
|
||||
handling could look:
|
||||
|
||||
```scala
|
||||
import org.bitcoins.rpc.client.common._
|
||||
import org.bitcoins.rpc.BitcoindWalletException
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.protocol._
|
||||
import org.bitcoins.core.currency._
|
||||
import java.io._
|
||||
|
||||
import scala.concurrent._
|
||||
|
||||
implicit val ec = ExecutionContext.global
|
||||
|
||||
// let's assume you have an already running client,
|
||||
// so there's no need to start this one
|
||||
val cli = BitcoindRpcClient.fromDatadir(binary=new File("/path/to/bitcoind"), datadir=new File("/path/to/bitcoind-datadir"))
|
||||
|
||||
// let's also assume you have a bitcoin address
|
||||
val address: BitcoinAddress = ???
|
||||
|
||||
val txid: Future[DoubleSha256DigestBE] =
|
||||
cli.sendToAddress(address, 3.bitcoins).recoverWith {
|
||||
case BitcoindWalletException.UnlockNeeded(_) =>
|
||||
cli.walletPassphrase("my_passphrase", 60).flatMap { _ =>
|
||||
cli.sendToAddress(address, 3.bitcoins)
|
||||
}
|
||||
}
|
||||
```
|
29
website/versioned_docs/version-0.2.0/rpc/eclair.md
Normal file
29
website/versioned_docs/version-0.2.0/rpc/eclair.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
id: version-0.2.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 &
|
||||
```
|
||||
|
||||
If you wish to start Eclair from the RPC client, you can do one of the following:
|
||||
|
||||
1. Construct a `EclairRpcClient` with the `binary` field set
|
||||
2. Set the `ECLAIR_PATH` environment variable to the directory where the Eclair Jar is located.
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
id: version-0.2.0-rpc-clients-intro
|
||||
title: Introduction
|
||||
original_id: rpc-clients-intro
|
||||
---
|
||||
|
||||
When working with Bitcoin applications, a common task to
|
||||
accomplish is connecting to a service like Bitcoin Core,
|
||||
and use that for tasks like generating addresses,
|
||||
verifying payments and
|
||||
monitoring the blockchain. This typically happens through
|
||||
tools like `bitcoin-cli`, or the Bitcoin Core HTTP RPC
|
||||
server interface. One big drawback to this, is that you
|
||||
lose all type-safety in your application. Even if you
|
||||
have a custom type that represents a Bitcoin transaction,
|
||||
how do you get that to play nicely with the result that
|
||||
Bitcoin Core gives you after signing a transaction? A
|
||||
random hexadecimal string in a HTTP response could be
|
||||
anything from a public key, a transaction or a block
|
||||
header.
|
||||
|
||||
We've done all the mundane work of wiring requests and
|
||||
responses from Bitcoin Core to the powerful and safe types
|
||||
found in Bitcoin-S. We've also written a bunch of tests,
|
||||
that verify that all of this actually work.
|
||||
You'll know for sure that you're sending
|
||||
a valid public key to `importmulti`, and you when doing
|
||||
RPC calls like `getblockheader` we'll even parse the
|
||||
hexadecimal string into a complete header that you can
|
||||
interact with without goofing around with bits and bytes.
|
||||
|
||||
We currently have RPC clients for Bitcoin Core and Eclair.
|
29
website/versioned_docs/version-0.2.0/windows-users.md
Normal file
29
website/versioned_docs/version-0.2.0/windows-users.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
id: version-0.2.0-windows-users
|
||||
title: windows-users
|
||||
original_id: windows-users
|
||||
---
|
||||
This will be a guide directed to Windows Users hoping to contribute to Bitcoin-s and its development.
|
||||
## Bloop
|
||||
Reference the `contributing.md` document for a more descriptive guide on what bloop is and how to use it.
|
||||
When following the installation guide to bloop on their website you will use `scoop` to install `bloop`. After installation
|
||||
there will more than likely be issues with `bloop` looking for `.jar` files in a folder like `C:\root\.ivy2\`. The true installation
|
||||
location of the `.ivy2` folder is likely in `C:\users\{your_username}\.ivy2\`. Once we have located our `.ivy2` folder
|
||||
we will want to direct `bloop` on how to find the files in the new location. I found it was quite simple to use Windows
|
||||
symbolic link (Note: if you are a running a Linux Subsystem this will also influence where your Linux bloop looks for this directory and thus will break as subsystems will have to use the `mnt` folder to access `C` drive).
|
||||
To create the symbolic link we run
|
||||
```mklink /D C:\root\.ivy2\ C:\users\{your_username}\.ivy2\```
|
||||
the `/D` option specifies that it is a directory. You will need to run this command in `cmd.exe` instead of `Windows Powershell` as it is not a standalone executable.
|
||||
|
||||
## Running a Bitcoind node
|
||||
Currently there are written changes in code to make it so you are run a node on Windows. Path specs are based on out of box installation. In the case you receive an error like `Could not locate bitcoind on user PATH`
|
||||
then you will need to do some tweaking of either the code or moving your folder into the correct location. Currently this is specified by the `DEFAULT_DATADIR` in `BitcoindConfig` within `Bitcoind-rpc`. When you install bitcoin out of box there are 2 folders
|
||||
created, 1 which is contained in the location on the wiki and that contains data generated after syncing. The current directory `DEFAULT_DATADIR` is pointing at which contains the `bitcoind.exe` file.
|
||||
That file is what is necessary to start up a bitcoind node.
|
||||
|
||||
## Running Linux on a Windows Machine
|
||||
If you are looking to develop in a Linux environment on a Windows Machine I have had success with [Windows Subsystem for Linux (WSL)](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
|
||||
If you are interested in working on both Linux and Windows for development reasons I would also recommend [Windows Terminal](https://github.com/microsoft/terminal) as a way to hold many different terminals all in one window including your
|
||||
|
||||
Linux distro if you set up a WSL. With that being said the most important thing to consider when running Linux on a Windows Machine while also developing on Windows, there will be differences in setting up directories and pathing especially for `bloop`. I will update if I find a functional workaround.
|
||||
Currently trying to get bloop working on a WSL that already has linked folders to have bloop work on Windows. Linux cannot recognize the file paths for directories as they are in Windows format, trying to find a workaround.
|
34
website/versioned_sidebars/version-0.2.0-sidebars.json
Normal file
34
website/versioned_sidebars/version-0.2.0-sidebars.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"version-0.2.0-docs": {
|
||||
"Getting started": [
|
||||
"version-0.2.0-getting-started"
|
||||
],
|
||||
"Core module": [
|
||||
"version-0.2.0-core/core-intro",
|
||||
"version-0.2.0-core/addresses",
|
||||
"version-0.2.0-core/hd-keys",
|
||||
"version-0.2.0-core/txbuilder"
|
||||
],
|
||||
"RPC clients": [
|
||||
"version-0.2.0-rpc/rpc-clients-intro",
|
||||
"version-0.2.0-rpc/rpc-eclair",
|
||||
"version-0.2.0-rpc/rpc-bitcoind"
|
||||
],
|
||||
"Applications": [
|
||||
"version-0.2.0-applications/chain",
|
||||
"version-0.2.0-applications/cli",
|
||||
"version-0.2.0-applications/configuration",
|
||||
"version-0.2.0-applications/key-manager",
|
||||
"version-0.2.0-applications/node",
|
||||
"version-0.2.0-applications/server",
|
||||
"version-0.2.0-applications/wallet"
|
||||
],
|
||||
"Contributing": [
|
||||
"version-0.2.0-contributing",
|
||||
"version-0.2.0-contributing-website"
|
||||
],
|
||||
"Security": [
|
||||
"version-0.2.0-security"
|
||||
]
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
[
|
||||
"0.2.0",
|
||||
"0.1.0"
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user