mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
Version 1.9.3 of everything (#4643)
This commit is contained in:
parent
2c2e03b279
commit
e65346f558
71
README.md
71
README.md
@ -1,5 +1,5 @@
|
||||
![Bitcoin-S logo](website/static/img/bitcoin-s-dark-logo.png)
|
||||
[![Build Status](https://github.com/bitcoin-s/bitcoin-s/workflows/Release/badge.svg)](https://github.com/bitcoin-s/bitcoin-s/actions) [![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-1.9.2
|
||||
[![Build Status](https://github.com/bitcoin-s/bitcoin-s/workflows/Release/badge.svg)](https://github.com/bitcoin-s/bitcoin-s/actions) [![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-1.9.3
|
||||
-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.
|
||||
@ -41,75 +41,52 @@ This link is intended for setting up development of bitcoin-s. If you want to ju
|
||||
|
||||
### Adding bitcoin-s to your library
|
||||
|
||||
The latest release of bitcoin-s is `1.9.2
|
||||
`, here is how you can use the dependencies in your projects:
|
||||
The latest release of bitcoin-s is `1.9.3`, here is how you can use the dependencies in your projects:
|
||||
|
||||
```
|
||||
libraryDependencies += "org.bitcoin-s" % "bitcoin-s-secp256k1jni" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" % "bitcoin-s-secp256k1jni" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-core" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-core" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-crypto" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-crypto" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-chain" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-chain" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-dlc-oracle" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-dlc-oracle" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-oracle-explorer-client" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-oracle-explorer-client" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-app-commons" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-app-commons" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-db-commons" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-db-commons" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-fee-provider" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-fee-provider" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-bitcoind-rpc" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-bitcoind-rpc" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-eclair-rpc" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-eclair-rpc" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-lnd-rpc" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-lnd-rpc" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-key-manager" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-key-manager" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-node" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-node" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-dlc-node" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-dlc-node" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-wallet" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-wallet" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-dlc-wallet" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-dlc-wallet" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit-core" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit-core" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-zmq" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-zmq" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-tor" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-tor" % "1.9.3"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-cli" % "1.9.2
|
||||
"
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-cli" % "1.9.3"
|
||||
|
||||
```
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
package org.bitcoins.node.constant
|
||||
|
||||
case object NodeConstants {
|
||||
val userAgent = "/bitcoin-s:1.9.2/"
|
||||
val userAgent = "/bitcoin-s:1.9.3/"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import scala.util.Properties
|
||||
|
||||
object CommonSettings {
|
||||
|
||||
val previousStableVersion: String = "1.9.2"
|
||||
val previousStableVersion: String = "1.9.3"
|
||||
|
||||
private def isCI = {
|
||||
Properties
|
||||
|
408
website/versioned_docs/version-1.9.3/applications/server.md
Normal file
408
website/versioned_docs/version-1.9.3/applications/server.md
Normal file
@ -0,0 +1,408 @@
|
||||
---
|
||||
id: version-1.9.3-server
|
||||
title: Application Server
|
||||
original_id: server
|
||||
---
|
||||
|
||||
|
||||
## App server
|
||||
|
||||
The server project is the aggregation of these three sub projects
|
||||
|
||||
1. [Wallet](../wallet/wallet.md)
|
||||
2. [Chain](../chain/chain.md)
|
||||
3. [Node](../node/node.md)
|
||||
|
||||
The server project provides a away to access information from these three projects via a JSON RPC.
|
||||
|
||||
## Building the server
|
||||
|
||||
### Java binary
|
||||
You can build the server with the [sbt native packager](https://github.com/sbt/sbt-native-packager).
|
||||
The native packager offers [numerous ways to package the project](https://github.com/sbt/sbt-native-packager#examples).
|
||||
|
||||
In this example we are going to use `stage` which will produce bash scripts we can easily execute. You can stage the server with the following command.
|
||||
|
||||
```bash
|
||||
sbt appServer/universal:stage
|
||||
```
|
||||
|
||||
This will produce a script to execute bitcoin-s which you can start with
|
||||
|
||||
```bash
|
||||
./app/server/target/universal/stage/bin/bitcoin-s-server
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
The oracle server also has docker support. You can build a docker image with the following commands
|
||||
|
||||
#### Using an existing docker image
|
||||
|
||||
We publish docker images on every PR that is merged to bitcoin-s.
|
||||
|
||||
You can find the docker repo for the app server [here](https://hub.docker.com/r/bitcoinscala/bitcoin-s-server/tags?page=1&ordering=last_updated)
|
||||
|
||||
#### Building a docker image
|
||||
```
|
||||
sbt "appServer/docker:stage"
|
||||
```
|
||||
|
||||
This will build a `Dockerfile` that is located in `app/server/target/docker/stage`
|
||||
|
||||
You can now build the docker image with
|
||||
|
||||
```
|
||||
docker build app/server/target/docker/stage/ -t bitcoinscala/bitcoin-s-server:latest
|
||||
```
|
||||
|
||||
Finally, let's run the image! It's important that you correctly configure port forwarding with the docker container so
|
||||
you can interact with the running container with `bitcoin-s-cli` or `curl`. By default, our oracle
|
||||
server listens for requests on port `9999`. By default, the server listens for websocket connections on port `19999` at `/events`.
|
||||
|
||||
This means we need to forward requests on the host machine to the docker container correctly.
|
||||
|
||||
This can be done with the following command
|
||||
```
|
||||
docker run -d -p 9999:9999 -p 19999:19999 -e BITCOIN_S_SERVER_RPC_PASSWORD='topsecret' bitcoinscala/bitcoin-s-server:latest
|
||||
```
|
||||
|
||||
Now you can send requests with `bitcoin-s-cli` or `curl`.
|
||||
Here is an example with `bitcoin-s-cli`
|
||||
```
|
||||
./bitcoin-s-cli getblockcount --password topsecret
|
||||
10000
|
||||
```
|
||||
|
||||
For more information on build configuration options with `sbt` please see the [sbt native packager docs](https://sbt-native-packager.readthedocs.io/en/latest/formats/docker.html#tasks)
|
||||
|
||||
## Configuration
|
||||
|
||||
### Java binary configuration
|
||||
If you would like to pass in a custom datadir for your server, you can do
|
||||
|
||||
```bash
|
||||
./app/server/target/universal/stage/bin/bitcoin-s-server --datadir /path/to/datadir/
|
||||
```
|
||||
|
||||
To use a config file that is not the `bitcoin-s.conf` file in your datadir, you can do
|
||||
|
||||
```bash
|
||||
./app/server/target/universal/stage/bin/bitcoin-s-server --conf /path/to/file.conf
|
||||
```
|
||||
|
||||
You can also pass in a custom `rpcport` to bind to
|
||||
|
||||
```bash
|
||||
./app/server/target/universal/stage/bin/bitcoin-s-server --rpcport 12345
|
||||
```
|
||||
|
||||
Or set a custom `wsport` to bind to
|
||||
|
||||
```bash
|
||||
./app/server/target/universal/stage/bin/bitcoin-s-server --wsport 54321
|
||||
```
|
||||
|
||||
For more information on configuring the server please see our [configuration](../config/configuration.md) document
|
||||
|
||||
For more information on how to use our built in `cli` to interact with the server please see [cli.md](cli.md)
|
||||
|
||||
### Docker configuration
|
||||
|
||||
In this example, we are using the latest docker image published to our [docker hub](https://hub.docker.com/repository/docker/bitcoinscala/bitcoin-s-oracle-server/tags?page=1&ordering=last_updated)
|
||||
which is referenced by `bitcoinscala/bitcoin-s-server:latest`
|
||||
|
||||
You can use bitcoin-s with docker volumes. You can also pass in a custom configuration at container runtime.
|
||||
|
||||
#### Using a docker volume
|
||||
|
||||
```basrc
|
||||
docker volume create bitcoin-s
|
||||
docker run -p 9999:9999 -p 19999:19999 \
|
||||
--mount source=bitcoin-s,target=/home/bitcoin-s/ bitcoinscala/bitcoin-s-server:latest
|
||||
```
|
||||
|
||||
Now you can re-use this volume across container runs. It will keep the same oracle database
|
||||
and seeds directory located at `/home/bitcoin-s/.bitcoin-s/seeds` in the volume.
|
||||
|
||||
#### Using a custom bitcoin-s configuration with docker
|
||||
|
||||
You can also specify a custom bitcoin-s configuration at container runtime.
|
||||
You can mount the configuration file on the docker container and that
|
||||
configuration will be used in the docker container runtime rather than
|
||||
the default one we provide [here](https://github.com/bitcoin-s/bitcoin-s/blob/master/app/oracle-server/src/universal/docker-application.conf)
|
||||
|
||||
You can do this with the following command
|
||||
|
||||
```bashrc
|
||||
docker run -p 9999:9999 -p 19999:19999 \
|
||||
--mount type=bind,source=/my/new/config/,target=/home/bitcoin-s/.bitcoin-s/ \
|
||||
bitcoinscala/bitcoin-s-server:latest --conf /home/bitcoin-s/.bitcoin-s/bitcoin-s.conf
|
||||
```
|
||||
|
||||
Note: If you adjust the `bitcoin-s.server.rpcport` setting you will need to adjust
|
||||
the `-p 9999:9999` port mapping on the docker container to adjust for this.
|
||||
|
||||
## Server Endpoints
|
||||
|
||||
### Common
|
||||
- `getversion` - The version of our application you are using
|
||||
- `zipdatadir` `location` - Backs up the datadir in a safe and consistent manner.
|
||||
- `location` - The locations of the backup zip
|
||||
|
||||
### Blockchain
|
||||
- `getblockcount` - Get the current block height
|
||||
- `getfiltercount` - Get the number of filters
|
||||
- `getfilterheadercount` - Get the number of filter headers
|
||||
- `getbestblockhash` - Get the best block hash
|
||||
- `getblockheader` - Returns information about block header <hash>
|
||||
- `hash` - The block hash
|
||||
- `decoderawtransaction` `tx` - `Decode the given raw hex transaction`
|
||||
- `tx` - Transaction encoded in hex to decode
|
||||
- `getmediantimepast` - Returns the median time past
|
||||
|
||||
### Wallet
|
||||
- `rescan` `[options]` - Rescan for wallet UTXOs
|
||||
- `--force` - Clears existing wallet records. Warning! Use with caution!
|
||||
- `--batch-size <value>` - Number of filters that can be matched in one batch
|
||||
- `--start <value>` - Start height
|
||||
- `--end <value>` - End height
|
||||
- `--ignorecreationtime` - Ignores the wallet creation date and will instead do a full rescan
|
||||
- `isempty` - Checks if the wallet contains any data
|
||||
- `walletinfo` - Returns data about the current wallet being used
|
||||
- `getbalance` `[options]` - Get the wallet balance
|
||||
- `--sats ` - Display balance in satoshis
|
||||
- `getconfirmedbalance` `[options]` - Get the wallet balance of confirmed utxos
|
||||
- `--sats ` - Display balance in satoshis
|
||||
- `getunconfirmedbalance` `[options]` - Get the wallet balance of unconfirmed utxos
|
||||
- `--sats ` - Display balance in satoshis
|
||||
- `getbalances` `[options]` - Get the wallet balance by utxo state
|
||||
- `--sats ` - Display balance in satoshis
|
||||
- `getutxos` - Returns list of all wallet utxos
|
||||
- `getaddresses` - Returns list of all wallet addresses currently being watched
|
||||
- `getspentaddresses` - Returns list of all wallet addresses that have received funds and been spent
|
||||
- `getfundedaddresses` - Returns list of all wallet addresses that are holding funds
|
||||
- `getunusedaddresses` - Returns list of all wallet addresses that have not been used
|
||||
- `getaccounts` - Returns list of all wallet accounts
|
||||
- `walletinfo` - Returns meta information about the wallet
|
||||
- `createnewaccount` - Creates a new wallet account
|
||||
- `getaddressinfo` `address` - Returns list of all wallet accounts
|
||||
- `address` - Address to get information about
|
||||
- `getnewaddress` - Get a new address
|
||||
- `listreservedutxos` - lists all utxos that are reserved in the wallet
|
||||
- `sendtoaddress` `address` `amount` `[options]` - Send money to the given address
|
||||
- `address` - Address to send to
|
||||
- `amount` - Amount to send in BTC
|
||||
- `--feerate <value>` - Fee rate in sats per virtual byte
|
||||
- `sendfromoutpoints` `outpoints` `address` `amount` `[options]` - Send money to the given address
|
||||
- `outpoints` - Out Points to send from
|
||||
- `address` - Address to send to
|
||||
- `amount` - Amount to send in BTC
|
||||
- `--feerate <value>` - Fee rate in sats per virtual byte
|
||||
- `sweepwallet` `address` `[options]` - Sends the entire wallet balance to the given address
|
||||
- `address` - Address to send to
|
||||
- `--feerate <value>` - Fee rate in sats per virtual byte
|
||||
- `sendwithalgo` `address` `amount` `algo` `[options]` - Send money to the given address using a specific coin selection algo
|
||||
- `address` - Address to send to
|
||||
- `amount` - Amount to send in BTC
|
||||
- `algo` - Coin selection algo
|
||||
- `--feerate <value>` - Fee rate in sats per virtual byte
|
||||
- `signpsbt` `psbt` - Signs the PSBT's inputs with keys that are associated with the wallet
|
||||
- `psbt` - PSBT to sign
|
||||
- `opreturncommit` `message` `[options]` - Creates OP_RETURN commitment transaction
|
||||
- `message` - message to put into OP_RETURN commitment
|
||||
- `--hashMessage` - should the message be hashed before commitment
|
||||
- `--feerate <value>` - Fee rate in sats per virtual byte
|
||||
- `bumpfeecpfp` `txid` `feerate` - Bump the fee of the given transaction id with a child tx using the given fee rate
|
||||
- `txid` - Id of transaction to bump fee
|
||||
- `feerate` - Fee rate in sats per virtual byte of the child transaction
|
||||
- `bumpfeerbf` `txid` `feerate` - Replace given transaction with one with the new fee rate
|
||||
- `txid` - Id of transaction to bump fee
|
||||
- `feerate` - New fee rate in sats per virtual byte
|
||||
- `gettransaction` `txid` - Get detailed information about in-wallet transaction <txid>
|
||||
- `txid` - The transaction id
|
||||
- `lockunspent` `unlock` `transactions` - Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.
|
||||
- `unlock` - Whether to unlock (true) or lock (false) the specified transactions
|
||||
- `transactions` - The transaction outpoints to unlock/lock, empty to apply to all utxos
|
||||
- `importseed` `walletname` `words` `passphrase` - Imports a mnemonic seed as a new seed file
|
||||
- `walletname` - Name to associate with this seed
|
||||
- `words` - Mnemonic seed words, space separated
|
||||
- `passphrase` - Passphrase to encrypt this seed with
|
||||
- `importxprv` `walletname` `xprv` `passphrase` - Imports a mnemonic seed as a new seed file
|
||||
- `walletname` - Name to associate with this seed
|
||||
- `xprv` - base58 encoded extended private key
|
||||
- `passphrase` - Passphrase to encrypt this seed with
|
||||
- `exportseed` `walletname` `passphrase` - Exports the mnemonic seed phrase
|
||||
- `walletname` - Name to associate with this seed
|
||||
- `passphrase` - Passphrase to decrypt this seed with
|
||||
- `markseedasbackedup` `walletname` `passphrase` - Marks the seed as backed up. It prevents `exportseed` from returning the mnemonic phrase
|
||||
- `walletname` - Name to associate with this seed
|
||||
- `passphrase` - Passphrase to decrypt this seed with
|
||||
- `getseedbackuptime` `walletname` `passphrase` - Returns time when the seed was backed up
|
||||
- `walletname` - Name to associate with this seed
|
||||
- `passphrase` - Passphrase to decrypt this seed with
|
||||
- `keymanagerpassphrasechange` `oldpassphrase` `newpassphrase` - Changes the wallet passphrase
|
||||
- `oldpassphrase` - The current passphrase
|
||||
- `newpassphrase` - The new passphrase
|
||||
- `keymanagerpassphraseset` `passphrase` - Encrypts the wallet with the given passphrase
|
||||
- `passphrase` - The passphrase to encrypt the wallet with
|
||||
|
||||
### DLC
|
||||
- `createcontractinfo` `announcement` `totalCollateral` `payouts`
|
||||
- the announcement to build the contract info for
|
||||
- the total amount of collateral in the DLC
|
||||
- The payouts can be in two formats
|
||||
1. `{ "outcomes" : { "outcome0" : 0, "outcome1": 1, ... }}`. The number is the amount of sats paid to YOU for that outcome.
|
||||
2. `[{"outcome":0,"payout":0,"extraPrecision":0,"isEndpoint":true}, {"outcome":1,"payout":1,"extraPrecision":0,"isEndpoint":true}, ...]`
|
||||
- `decodecontractinfo` `contractinfo` - Decodes a contract info into json
|
||||
- `contractinfo` - Hex encoded contract info
|
||||
- `decodeoffer` `offer` - Decodes an offer message into json
|
||||
- `offer` - Hex encoded dlc offer message
|
||||
- `decodeaccept` `accept` - Decodes an accept message into json
|
||||
- `accept` - Hex encoded dlc accept message
|
||||
- `decodesign` `sign` - Decodes a sign message into json
|
||||
- `sign` - Hex encoded dlc sign message
|
||||
- `decodeannouncement` `announcement` - Decodes an oracle announcement message into json
|
||||
- `announcement` - Hex encoded oracle announcement message
|
||||
- `decodeattestments` `attestments` - Decodes an oracle attestments message into json
|
||||
- `attestments` - Hex encoded oracle attestments message
|
||||
- `getdlchostaddress` - Returns the public listening address of the DLC Node
|
||||
- `createdlcoffer` `contractInfo` `collateral` `[feerate]` `refundlocktime` `[options]` - Creates a DLC offer that another party can accept
|
||||
- `contractInfo` - Hex encoded contractInfo message
|
||||
- `collateral` - Satoshis to fund your side of the DLC
|
||||
- `feerate` - Fee rate for both funding and closing transactions, in sats/vbytes
|
||||
- `refundlocktime` - Locktime of the refund transaction
|
||||
- `cetlocktime <value>` - Should not be set unless you know what you are doing. Locktime of the contract execution transactions (defaults to current height)
|
||||
- `peer` - Peer's network address
|
||||
- `acceptdlc` `offer` `peer` - Accepts a DLC offer given from another party
|
||||
- `offer` - Hex encoded dlc offer message
|
||||
- `peer` - Peer's network address
|
||||
- `acceptdlcoffer` `offer` - Accepts a DLC offer given from another party
|
||||
- `offer` - Hex encoded offer message
|
||||
- `peer` - Peer's network address
|
||||
- `acceptdlcofferfromfile` `path` `[destination]` - Accepts a DLC offer given from another party
|
||||
- `path` - Path to dlc offer file
|
||||
- `destination` - Path to write dlc accept message
|
||||
- `contact-add` `alias` `address` `memo`
|
||||
- `alias` - alias for the address like a name
|
||||
- `address` - the tor address for the peer
|
||||
- `memo` - a memo for this contact
|
||||
- `contacts-list` - lists all contacts in the wallet
|
||||
- `contact-remove` `address`
|
||||
- `address` - the tor address for the peer to remove
|
||||
- `dlc-contact-add` `dlcid` `address` - Associated a DLC with a peer
|
||||
- `dlc-contact-remove` `dlcid` - Removes a DLC-peer association
|
||||
- `address` - the tor address for the peer to remove
|
||||
- `signdlc` `accept` - Signs a DLC
|
||||
- `accept` - Hex encoded dlc accept message
|
||||
- `signdlcfromfile` `path` `[destination]` - Signs a DLC
|
||||
- `path` - Path to dlc accept file
|
||||
- `destination` - Path to write dlc sign message
|
||||
- `adddlcsigs` `sigs` - Adds DLC Signatures into the database
|
||||
- `sigs` - Hex encoded dlc sign message
|
||||
- `adddlcsigsfromfile` `path` - Adds DLC Signatures into the database
|
||||
- `path` - Path to dlc sign file
|
||||
- `adddlcsigsandbroadcast` `sigs` - Adds DLC Signatures into the database and broadcasts the funding transaction
|
||||
- `sigs` - Hex encoded dlc sign message
|
||||
- `adddlcsigsandbroadcastfromfile` `path` - Adds DLC Signatures into the database and broadcasts the funding transaction
|
||||
- `path` - Path to dlc sign file
|
||||
- `getdlcfundingtx` `contractId` - Returns the Funding Tx corresponding to the DLC with the given contractId
|
||||
- `contractId` - ContractId of the DLC
|
||||
- `broadcastdlcfundingtx` `contractId` - Broadcasts the funding Tx corresponding to the DLC with the given contractId
|
||||
- `contractId` - ContractId of the DLC
|
||||
- `executedlc` `contractId` `oraclesigs` `[options]` - Executes the DLC with the given contractId
|
||||
- `contractId` - ContractId of the DLC
|
||||
- `oraclesigs` - Array of oracle attestations
|
||||
- `--noBroadcast` - Gives full serialized transaction instead of broadcasting
|
||||
- `executedlcrefund` `contractId` `[options]` - Executes the Refund transaction for the given DLC
|
||||
- `contractId` - ContractId of the DLC
|
||||
- `--noBroadcast` - Gives full serialized transaction instead of broadcasting
|
||||
- `canceldlc` `dlcId` - Cancels a DLC and unreserves used utxos
|
||||
- `dlcId` - Internal id of the DLC
|
||||
- `getdlcs` - Returns all dlcs in the wallet
|
||||
- `address` - optional contact address, if specified the RPC returns only DLCs associated with the given address
|
||||
- `getdlc` `dlcId` - Gets a specific dlc in the wallet
|
||||
- `dlcId` - Internal id of the DLC
|
||||
- `offer-add` `offerTLV` `peerAddress` `message` - Puts an incoming offer into the inbox
|
||||
- `offerTLV` - Offer TLV
|
||||
- `peer` - Peer URI (optional)
|
||||
- `message` - Peer's message or note (optional)
|
||||
- `"offer-remove` `hash` - Remove an incoming offer from inbox
|
||||
- `hash` - Hash of the offer TLV
|
||||
- `offer-send` `offerOrTempContractId` `peerAddress` `message` - Sends an offer to a peer. `offerOrTempContractId` is either an offer TLV or a temporary contract ID.
|
||||
- `offers-list` - List all incoming offers from the inbox
|
||||
- `getdlcoffer` `tempContractId` - Gets a DLC offer by temporary contract ID.
|
||||
- `getaddresslabel` `address` - gets all labels for an address
|
||||
- `getaddresslabels` - returns all addresses with labels in the wallet
|
||||
- `dropaddresslabel` `address` `label` - drops the label for a given address
|
||||
- `dropaddresslabels` `address` - drops all labels for the given address
|
||||
|
||||
### Network
|
||||
- `getpeers` - List the connected peers
|
||||
- `stop` - Request a graceful shutdown of Bitcoin-S
|
||||
- `sendrawtransaction` `tx` `Broadcasts the raw transaction`
|
||||
- `tx` - Transaction serialized in hex
|
||||
|
||||
### PSBT
|
||||
- `decodepsbt` `psbt` - Return a JSON object representing the serialized, base64-encoded partially signed Bitcoin transaction.
|
||||
- `psbt` - PSBT serialized in hex or base64 format
|
||||
- `combinepsbts` `psbts` - Combines all the given PSBTs
|
||||
- `psbts` - PSBTs serialized in hex or base64 format
|
||||
- `joinpsbts` `psbts` - Combines all the given PSBTs
|
||||
- `psbts` - PSBTs serialized in hex or base64 format
|
||||
- `finalizepsbt` `psbt` - Finalizes the given PSBT if it can
|
||||
- `psbt` - PSBT serialized in hex or base64 format
|
||||
- `extractfrompsbt` `psbt` - Extracts a transaction from the given PSBT if it can
|
||||
- `psbt` - PSBT serialized in hex or base64 format
|
||||
- `converttopsbt` `unsignedTx` - Creates an empty psbt from the given transaction
|
||||
- `unsignedTx` - serialized unsigned transaction in hex
|
||||
|
||||
### Util
|
||||
- `createmultisig` `nrequired` `keys` `[address_type]` - Creates a multi-signature address with n signature of m keys required.
|
||||
- `nrequired` - The number of required signatures out of the n keys.
|
||||
- `keys` - The hex-encoded public keys.
|
||||
- `address_type` -The address type to use. Options are "legacy", "p2sh-segwit", and "bech32"
|
||||
- `estimatefee` - Returns the recommended fee rate using the fee provider
|
||||
|
||||
## Sign PSBT with Wallet Example
|
||||
|
||||
Bitcoin-S CLI:
|
||||
|
||||
```bash
|
||||
bitcoin-s-cli signpsbt cHNidP8BAP0FAQIAAAABWUWxYiPKgdGfXcIxJ6MRDxEpUecw59Gk4NpROI5oukoBAAAAAAAAAAAEPttkvdwAAAAXqRSOVAp6Qe/u2hq74e/ThB8foBKn7IfZYMgGCAAAAADbmaQ2nwAAAEdRIQLpfVqyaL9Jb/IkveatNyVeONE8Q/6TzXAWosxLo9e21SECc5G3XiK7xKLlkBG7prMx7p0fMeQwMH5e9H10mBon39JSrtgtgjjLAQAAUGMhAn2YaZnv25I6d6vbb1kw6Xp5IToDrEzl/0VBIW21gHrTZwXg5jGdALJ1IQKyNpDNiOiN6lWpYethib04+XC9bpFXrdpec+xO3U5IM2is9ckf5AABAD0CAAAAAALuiOL0rRcAABYAFPnpLByQq1Gg3vwiP6qR8FmOOjwxvVllM08DAAALBfXJH+QAsXUAAK4AAAAAAQcBAAAAAAAA
|
||||
cHNidP8BAP0FAQIAAAABWUWxYiPKgdGfXcIxJ6MRDxEpUecw59Gk4NpROI5oukoBAAAAAAAAAAAEPttkvdwAAAAXqRSOVAp6Qe/u2hq74e/ThB8foBKn7IfZYMgGCAAAAADbmaQ2nwAAAEdRIQLpfVqyaL9Jb/IkveatNyVeONE8Q/6TzXAWosxLo9e21SECc5G3XiK7xKLlkBG7prMx7p0fMeQwMH5e9H10mBon39JSrtgtgjjLAQAAUGMhAn2YaZnv25I6d6vbb1kw6Xp5IToDrEzl/0VBIW21gHrTZwXg5jGdALJ1IQKyNpDNiOiN6lWpYethib04+XC9bpFXrdpec+xO3U5IM2is9ckf5AABAD0CAAAAAALuiOL0rRcAABYAFPnpLByQq1Gg3vwiP6qR8FmOOjwxvVllM08DAAALBfXJH+QAsXUAAK4AAAAAAQcBAAAAAAAA
|
||||
```
|
||||
|
||||
CURL:
|
||||
```bash
|
||||
curl --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "signpsbt", "params": ["cHNidP8BAP0FAQIAAAABWUWxYiPKgdGfXcIxJ6MRDxEpUecw59Gk4NpROI5oukoBAAAAAAAAAAAEPttkvdwAAAAXqRSOVAp6Qe/u2hq74e/ThB8foBKn7IfZYMgGCAAAAADbmaQ2nwAAAEdRIQLpfVqyaL9Jb/IkveatNyVeONE8Q/6TzXAWosxLo9e21SECc5G3XiK7xKLlkBG7prMx7p0fMeQwMH5e9H10mBon39JSrtgtgjjLAQAAUGMhAn2YaZnv25I6d6vbb1kw6Xp5IToDrEzl/0VBIW21gHrTZwXg5jGdALJ1IQKyNpDNiOiN6lWpYethib04+XC9bpFXrdpec+xO3U5IM2is9ckf5AABAD0CAAAAAALuiOL0rRcAABYAFPnpLByQq1Gg3vwiP6qR8FmOOjwxvVllM08DAAALBfXJH+QAsXUAAK4AAAAAAQcBAAAAAAAA"]}' -H "Content-Type: application/json" http://127.0.0.1:9999/
|
||||
{"result":"cHNidP8BAP0FAQIAAAABWUWxYiPKgdGfXcIxJ6MRDxEpUecw59Gk4NpROI5oukoBAAAAAAAAAAAEPttkvdwAAAAXqRSOVAp6Qe/u2hq74e/ThB8foBKn7IfZYMgGCAAAAADbmaQ2nwAAAEdRIQLpfVqyaL9Jb/IkveatNyVeONE8Q/6TzXAWosxLo9e21SECc5G3XiK7xKLlkBG7prMx7p0fMeQwMH5e9H10mBon39JSrtgtgjjLAQAAUGMhAn2YaZnv25I6d6vbb1kw6Xp5IToDrEzl/0VBIW21gHrTZwXg5jGdALJ1IQKyNpDNiOiN6lWpYethib04+XC9bpFXrdpec+xO3U5IM2is9ckf5AABAD0CAAAAAALuiOL0rRcAABYAFPnpLByQq1Gg3vwiP6qR8FmOOjwxvVllM08DAAALBfXJH+QAsXUAAK4AAAAAAQcBAAAAAAAA","error":null}
|
||||
```
|
||||
|
||||
## Websocket endpoints
|
||||
|
||||
Bitcoin-s offers websocket endpoints. By default, the endpoint is `ws://localhost:1999/events`
|
||||
|
||||
You can configure where how the endpoints are configured inside your `bitcoin-s.conf`
|
||||
|
||||
```
|
||||
bitcoin-s.server.wsbind=localhost
|
||||
bitcoin-s.server.wsport=19999
|
||||
```
|
||||
|
||||
These events are implemented using our [internal callback mechanism](../wallet/wallet-callbacks.md#wallet-callbacks).
|
||||
|
||||
An example event that is defined is our `blockprocess` event.
|
||||
Everytime our wallet processes a block, our wallet will notify you via websockets.
|
||||
Here is an example payload
|
||||
|
||||
The current types of events defined are
|
||||
|
||||
1. `txprocessed` - when the wallet processes a transaction. Every transaction in a block will get relayed currently
|
||||
2. `reservedutxos` - when the wallet reserves OR unreserves utxos
|
||||
3. `newaddress` - when the wallet generates a new address
|
||||
4. `txbroadcast` - when the wallet broadcasts a tx
|
||||
5. `blockprocessed` - when the wallet processes a block
|
||||
```
|
||||
{"type":"blockprocessed","payload":{"raw":"04e0ff2ffce8c3e866e367d305886a3f9d353e557524f61f9cf0c26c46000000000000001206d2e396387bff1c13cbe572d4646abae1ae405f4066ab5e6f5edd6d8f028008a8bb61ffff001af23dd47e","hash":"00000000000000de21f23f6945f028d5ecb47863428f6e9e035ab2fb7a3ef356","confirmations":1,"height":2131641,"version":805298180,"versionHex":"2fffe004","merkleroot":"80028f6ddd5e6f5eab66405f40aee1ba6a64d472e5cb131cff7b3896e3d20612","time":1639688200,"mediantime":1639688200,"nonce":2127838706,"bits":"1a00ffff","difficulty":1.6069135243303364E60,"chainwork":"00000000000000000000000000000000000000000000062438437ddd009e698b","previousblockhash":"00000000000000466cc2f09c1ff62475553e359d3f6a8805d367e366e8c3e8fc","nextblockhash":null}}
|
||||
```
|
175
website/versioned_docs/version-1.9.3/chain/chain-query-api.md
Normal file
175
website/versioned_docs/version-1.9.3/chain/chain-query-api.md
Normal file
@ -0,0 +1,175 @@
|
||||
---
|
||||
id: version-1.9.3-chain-query-api
|
||||
title: Chain Query API
|
||||
original_id: chain-query-api
|
||||
---
|
||||
|
||||
|
||||
### ChainQueryAPI
|
||||
|
||||
The ChainQueryApi is how the wallet project stays aware of the current best chain.
|
||||
This allows the wallet for example to calculate the number of confirmations for a transaction,
|
||||
get the current chain tip, or even retrieve block filters for a given set of blocks.
|
||||
|
||||
Since this is an API it can be hooked up to the `chain` module of bitcoin-s but it can also be linked to
|
||||
any other implementation of your choosing. This allows you to use the bitcoin-s wallet in any schema that you
|
||||
want.
|
||||
|
||||
The functions that the ChainQueryApi supports are:
|
||||
|
||||
```scala
|
||||
trait ChainQueryApi {
|
||||
|
||||
/** Gets the height of the given block */
|
||||
def getBlockHeight(blockHash: DoubleSha256DigestBE): Future[Option[Int]]
|
||||
|
||||
/** Gets the hash of the block that is what we consider "best" */
|
||||
def getBestBlockHash(): Future[DoubleSha256DigestBE]
|
||||
|
||||
/** Gets number of confirmations for the given block hash*/
|
||||
def getNumberOfConfirmations(
|
||||
blockHashOpt: DoubleSha256DigestBE): Future[Option[Int]]
|
||||
|
||||
/** Gets the number of compact filters in the database */
|
||||
def getFilterCount: Future[Int]
|
||||
|
||||
/** Returns the block height of the given block stamp */
|
||||
def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int]
|
||||
|
||||
def getFiltersBetweenHeights(
|
||||
startHeight: Int,
|
||||
endHeight: Int): Future[Vector[FilterResponse]]
|
||||
|
||||
def getMedianTimePast(): Future[Long]
|
||||
}
|
||||
```
|
||||
|
||||
## Chain query with bitcoind
|
||||
|
||||
As an example, we will show you how to use the `ChainQueryApi` and bitcoind to query chain data.
|
||||
|
||||
```scala
|
||||
implicit val system: ActorSystem = ActorSystem(s"node-api-example")
|
||||
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
||||
implicit val walletConf: WalletAppConfig =
|
||||
BitcoinSTestAppConfig.getNeutrinoTestConfig().walletConf
|
||||
|
||||
// let's use a helper method to get a v19 bitcoind
|
||||
// and a ChainApi
|
||||
val instance = BitcoindInstanceLocal.fromConfigFile(BitcoindConfig.DEFAULT_CONF_FILE)
|
||||
val bitcoind = BitcoindV19RpcClient(instance)
|
||||
val nodeApi = MockNodeApi.mock
|
||||
|
||||
// This function can be used to create a callback for when our chain api receives a transaction, block, or
|
||||
// a block filter, the returned NodeCallbacks will contain the necessary items to initialize the callbacks
|
||||
def createCallbacks(
|
||||
processTransaction: Transaction => Future[Unit],
|
||||
processCompactFilters: (Vector[(DoubleSha256Digest, GolombFilter)]) => Future[Unit],
|
||||
processBlock: Block => Future[Unit]): NodeCallbacks = {
|
||||
lazy val onTx: OnTxReceived = { tx =>
|
||||
processTransaction(tx)
|
||||
}
|
||||
lazy val onCompactFilters: OnCompactFiltersReceived = {
|
||||
blockFilters =>
|
||||
processCompactFilters(blockFilters)
|
||||
}
|
||||
lazy val onBlock: OnBlockReceived = { block =>
|
||||
processBlock(block)
|
||||
}
|
||||
NodeCallbacks(onTxReceived = Vector(onTx),
|
||||
onBlockReceived = Vector(onBlock),
|
||||
onCompactFiltersReceived = Vector(onCompactFilters))
|
||||
}
|
||||
|
||||
// Here is a super simple example of a callback, this could be replaced with anything, from
|
||||
// relaying the block on the network, finding relevant wallet transactions, verifying the block,
|
||||
// or writing it to disk
|
||||
val exampleProcessTx = (tx: Transaction) =>
|
||||
Future.successful(println(s"Received tx: ${tx.txIdBE}"))
|
||||
|
||||
val exampleProcessBlock = (block: Block) =>
|
||||
Future.successful(println(s"Received block: ${block.blockHeader.hashBE}"))
|
||||
|
||||
val exampleProcessFilters =
|
||||
(filters: Vector[(DoubleSha256Digest, GolombFilter)]) =>
|
||||
Future.successful(println(s"Received filter: ${filters.head._1.flip.hex} ${filters.head._2.hash.flip.hex}"))
|
||||
|
||||
val exampleCallbacks =
|
||||
createCallbacks(exampleProcessTx, exampleProcessFilters, exampleProcessBlock)
|
||||
|
||||
// Here is where we are defining our actual chain api, Ideally this could be it's own class
|
||||
// but for the examples sake we will keep it small.
|
||||
val chainApi = new ChainQueryApi {
|
||||
|
||||
override def epochSecondToBlockHeight(time: Long): Future[Int] =
|
||||
Future.successful(0)
|
||||
|
||||
/** Gets the height of the given block */
|
||||
override def getBlockHeight(
|
||||
blockHash: DoubleSha256DigestBE): Future[Option[Int]] = {
|
||||
bitcoind.getBlock(blockHash).map(block => Some(block.height))
|
||||
}
|
||||
|
||||
/** Gets the hash of the block that is what we consider "best" */
|
||||
override def getBestBlockHash(): Future[DoubleSha256DigestBE] = {
|
||||
bitcoind.getBestBlockHash
|
||||
}
|
||||
|
||||
/** Gets number of confirmations for the given block hash */
|
||||
override def getNumberOfConfirmations(
|
||||
blockHash: DoubleSha256DigestBE): Future[Option[Int]] = {
|
||||
for {
|
||||
tip <- bitcoind.getBlockCount
|
||||
block <- bitcoind.getBlock(blockHash)
|
||||
} yield {
|
||||
Some(tip - block.height + 1)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the number of compact filters in the database */
|
||||
override def getFilterCount(): Future[Int] = {
|
||||
// since bitcoind should have the filter for
|
||||
// every block we can just return the block height
|
||||
bitcoind.getBlockCount
|
||||
}
|
||||
|
||||
/** Returns the block height of the given block stamp */
|
||||
override def getHeightByBlockStamp(blockStamp: BlockStamp): Future[Int] = {
|
||||
blockStamp match {
|
||||
case blockHeight: BlockStamp.BlockHeight =>
|
||||
Future.successful(blockHeight.height)
|
||||
case blockHash: BlockStamp.BlockHash =>
|
||||
getBlockHeight(blockHash.hash).map(_.get)
|
||||
case blockTime: BlockStamp.BlockTime =>
|
||||
Future.failed(new RuntimeException(s"Not implemented: $blockTime"))
|
||||
}
|
||||
}
|
||||
|
||||
override def getFiltersBetweenHeights(
|
||||
startHeight: Int,
|
||||
endHeight: Int): Future[Vector[FilterResponse]] = {
|
||||
val filterFs = startHeight
|
||||
.until(endHeight)
|
||||
.map { height =>
|
||||
for {
|
||||
hash <- bitcoind.getBlockHash(height)
|
||||
filter <- bitcoind.getBlockFilter(hash, FilterType.Basic)
|
||||
} yield {
|
||||
FilterResponse(filter.filter, hash, height)
|
||||
}
|
||||
}
|
||||
.toVector
|
||||
|
||||
Future.sequence(filterFs)
|
||||
}
|
||||
|
||||
override def getMedianTimePast(): Future[Long] = bitcoind.getMedianTimePast()
|
||||
}
|
||||
|
||||
// Finally, we can initialize our wallet with our own node api
|
||||
val wallet =
|
||||
Wallet(nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
|
||||
// Then to trigger one of the events we can run
|
||||
wallet.chainQueryApi.getFiltersBetweenHeights(100, 150)
|
||||
```
|
78
website/versioned_docs/version-1.9.3/chain/chain.md
Normal file
78
website/versioned_docs/version-1.9.3/chain/chain.md
Normal file
@ -0,0 +1,78 @@
|
||||
---
|
||||
title: Blockchain Verification
|
||||
id: version-1.9.3-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
|
||||
implicit val ec = ExecutionContext.global
|
||||
implicit val system = ActorSystem("System")
|
||||
// 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.
|
||||
|
||||
val bitcoindInstance = BitcoindInstanceLocal.fromDatadir()
|
||||
val rpcCli = BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
// Next, we need to create a way to monitor the chain:
|
||||
|
||||
val getBestBlockHash = SyncUtil.getBestBlockHashFunc(rpcCli)
|
||||
|
||||
val getBlockHeader = SyncUtil.getBlockHeaderFunc(rpcCli)
|
||||
|
||||
// set a data directory
|
||||
val datadir = Files.createTempDirectory("bitcoin-s-test")
|
||||
|
||||
// set the current network to regtest
|
||||
import com.typesafe.config.ConfigFactory
|
||||
val config = ConfigFactory.parseString {
|
||||
"""
|
||||
| bitcoin-s {
|
||||
| network = regtest
|
||||
| }
|
||||
|""".stripMargin
|
||||
}
|
||||
|
||||
implicit val chainConfig = ChainAppConfig(datadir, Vector(config))
|
||||
|
||||
// Initialize the needed database tables if they don't exist:
|
||||
val chainProjectInitF = chainConfig.start()
|
||||
val blockHeaderDAO = BlockHeaderDAO()
|
||||
val compactFilterHeaderDAO = CompactFilterHeaderDAO()
|
||||
val compactFilterDAO = CompactFilterDAO()
|
||||
val stateDAO = ChainStateDescriptorDAO()
|
||||
|
||||
|
||||
//initialize the chain handler from the database
|
||||
val chainHandler = ChainHandler.fromDatabase(blockHeaderDAO, compactFilterHeaderDAO, compactFilterDAO, stateDAO)
|
||||
|
||||
// Now, do the actual syncing:
|
||||
val syncedChainApiF = for {
|
||||
_ <- chainProjectInitF
|
||||
synced <- ChainSync.sync(chainHandler, 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}")
|
||||
}
|
||||
```
|
525
website/versioned_docs/version-1.9.3/config/configuration.md
Normal file
525
website/versioned_docs/version-1.9.3/config/configuration.md
Normal file
@ -0,0 +1,525 @@
|
||||
---
|
||||
title: Application Configuration
|
||||
id: version-1.9.3-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`](/api/org/bitcoins/db/AppConfig).
|
||||
`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 akka.actor.ActorSystem
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import java.nio.file.Paths
|
||||
import scala.util.Properties
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
implicit val system: ActorSystem = ActorSystem("configuration-example")
|
||||
// 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, Vector.empty)
|
||||
|
||||
// reads a custom data directory and overrides the network to be testnet3
|
||||
val customOverride = ConfigFactory.parseString("bitcoin-s.network = testnet3")
|
||||
val configFromCustomDirAndOverride = WalletAppConfig(customDirectory, Vector(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.
|
||||
|
||||
## Command Line Options
|
||||
|
||||
There are a few command line options available that take precedence over configuration file.
|
||||
|
||||
- `--datadir <directory>`
|
||||
|
||||
`datadir` sets the data directory instead of using the default `$HOME/.bitcoin-s`
|
||||
|
||||
- `--rpcbind <ip>`
|
||||
|
||||
`rpcbind` sets the interface the rpc server binds to instead of using the default `127.0.0.1`
|
||||
|
||||
- `--rpcport <port>`
|
||||
|
||||
`rpcport` sets the port the rpc server binds to instead of using the default `9999`
|
||||
|
||||
- `--force-recalc-chainwork`
|
||||
|
||||
`force-recalc-chainwork` will force a recalculation of the entire chain's chain work, this can be useful if there is
|
||||
an incompatible migration or if it got out of sync.
|
||||
|
||||
- `-Dlogback.configurationFile=/path/to/config.xml`
|
||||
|
||||
You can set a custom logback configuration. If you need help creating a custom logback file you can
|
||||
read [the logback configuration documentation](http://logback.qos.ch/manual/configuration.html).
|
||||
|
||||
## Internal configuration
|
||||
|
||||
Database connections are also configured by using HOCON. This is done
|
||||
in [`reference.conf`](https://github.com/bitcoin-s/bitcoin-s/blob/master/db-commons/src/main/resources/reference.conf)
|
||||
inside the `db-commons` module. The options exposed here are **not** intended to be used by users of Bitcoin-S, and are
|
||||
internal only.
|
||||
|
||||
## Database Migrations
|
||||
|
||||
All of our modules that require databases now have database migrations. The tool we use for these migrations is
|
||||
called [flyway](https://flywaydb.org/). To find your projects migraitons, you need to look inside of the
|
||||
`[project-name]/src/main/resources/[database-name]/migration/`. For example, the chain projects migrations live under
|
||||
the path `chain/src/main/resources/chaindb/migration/V1__chain_db_baseline.sql`.
|
||||
|
||||
Migrations can be executed by calling
|
||||
the [`DbManagement.migrate()`](https://github.com/bitcoin-s/bitcoin-s/blob/e387d075b0ff2e0a0fec15788fcb48e4ddc4d9d5/db-commons/src/main/scala/org/bitcoins/db/DbManagement.scala#L92)
|
||||
method. Migrations are applied by default on server startup, via
|
||||
the [`AppConfig.start()`](https://github.com/bitcoin-s/bitcoin-s/blob/master/db-commons/src/main/scala/org/bitcoins/db/AppConfig.scala#L49)
|
||||
method.
|
||||
|
||||
These migrations are setup so that project's databases and migrations are independent of each other. Therefore if you
|
||||
want to use the `bitcoin-s-chain` project, but not the `bitcoin-s-wallet` project, wallet migrations are not applied. It
|
||||
should be noted if you are using a module as a library, you are responsible for configuring the database via
|
||||
[slick's configuration](https://scala-slick.org/doc/3.3.1/database.html#using-typesafe-config) and calling
|
||||
[`AppConfig.start()`](https://github.com/bitcoin-s/bitcoin-s/blob/master/db-commons/src/main/scala/org/bitcoins/db/AppConfig.scala#L49)
|
||||
to ensure the entire module is initialized correctly.
|
||||
|
||||
## Example Configuration File
|
||||
|
||||
```$xslt
|
||||
bitcoin-s {
|
||||
# the network your bitcoin-s node is running on
|
||||
network = "testnet3" # regtest, testnet3, mainnet, signet
|
||||
|
||||
# specify what backend you are using with bitcoin-s
|
||||
# by default we do neutrino, but you can also connect
|
||||
# bitcoind with the configuration settings in bitcoin-s.bitcoind-rpc
|
||||
node.mode = neutrino # neutrino, bitcoind
|
||||
|
||||
# configurations for connecting to bitcoind
|
||||
bitcoind-rpc {
|
||||
# bitcoind rpc username
|
||||
rpcuser = user
|
||||
# bitcoind rpc password
|
||||
# If your password contains the characters '$','{', '}', '[', ']', ':', '=', ',', '+', '#', '`', '^', '?', '!', '@', '*', '&', whitespace
|
||||
# or the string "//", enclose it in double quotes
|
||||
# rpcpassword = "password=" if the original password is password=, rpcpassword = "passwo//rd" if the original password is passwo//rd etc.
|
||||
# If it contains '\' or '"', escape it with '\'
|
||||
# rpcpassword = "pass\\word" if the original password is pass\word, rpcpassword = "pass\"word" if the original password is pass"word
|
||||
rpcpassword = password
|
||||
|
||||
# Binary location of bitcoind
|
||||
binary = ${HOME}/.bitcoin-s/binaries/bitcoind/bitcoin-0.20.1/bin/bitcoind
|
||||
# bitcoind datadir
|
||||
datadir = ${HOME}/.bitcoin
|
||||
# bitcoind network host
|
||||
connect = localhost
|
||||
# bitcoind p2p port
|
||||
port = 8333
|
||||
# bitcoind rpc host
|
||||
rpcconnect = localhost
|
||||
# bitcoind rpc port
|
||||
rpcport = 8332
|
||||
# bitcoind zmq raw tx
|
||||
zmqpubrawtx = "tcp://127.0.0.1:28332"
|
||||
# bitcoind zmq raw block
|
||||
zmqpubrawblock = "tcp://127.0.0.1:28333"
|
||||
# bitcoind zmq hash tx
|
||||
zmqpubhashtx = "tcp://127.0.0.1:28330"
|
||||
# bitcoind zmq raw block
|
||||
zmqpubhashblock = "tcp://127.0.0.1:28331"
|
||||
|
||||
#If you have a bitcoind instance that is running remotely on another machine, you should set it to true
|
||||
isRemote = false
|
||||
}
|
||||
|
||||
# settings if you are using a neutrino node in bitcoin-s
|
||||
node {
|
||||
|
||||
# a list of peer addresses in form "hostname:portnumber"
|
||||
# Port number is optional, the default value is 8333 for mainnet,
|
||||
# 18333 for testnet and 18444 for regtest.
|
||||
# by default we provide a testnet peer to connect to
|
||||
peers = ["neutrino.testnet3.suredbits.com:18333"]
|
||||
|
||||
# use the defauls suredbits neutrino node as a peer
|
||||
use-default-peers = true
|
||||
|
||||
# try to connect to peers from dns seeds, database, addr messages etc
|
||||
enable-peer-discovery = false
|
||||
|
||||
# number of persistent peer connections to maintain for node use
|
||||
maxConnectedPeers = 1
|
||||
|
||||
# peers discovery configs, ideally you would not want to change this
|
||||
# timeout for tcp connection
|
||||
connection-timeout = 5s
|
||||
# initialization timeout once connected, reconnections resets this
|
||||
initialization-timeout = 10s
|
||||
# time interval for trying next set of peers in peer discovery
|
||||
try-peers-interval = 12s
|
||||
|
||||
# wait time for queries like getheaders etc before switching to another
|
||||
query-wait-time = 15s
|
||||
|
||||
hikari-logging = true
|
||||
hikari-logging-interval = 10 minute
|
||||
|
||||
# whether to have p2p peers relay us unconfirmed txs
|
||||
relay = false
|
||||
}
|
||||
|
||||
proxy {
|
||||
# You can configure SOCKS5 proxy to use Tor for outgoing connections
|
||||
enabled = false
|
||||
socks5 = "127.0.0.1:9050"
|
||||
}
|
||||
|
||||
# tor settings
|
||||
tor {
|
||||
# You can enable Tor for incoming connections
|
||||
enabled = false
|
||||
control = "127.0.0.1:9051"
|
||||
|
||||
# Tor daemon can be provided by the node operator.
|
||||
# If this parameter set to true, bitcoin-s will connect the provided Tor daemon.
|
||||
# Otherwise bitcoin-s will start its own pre-packaged daemon.
|
||||
provided = false
|
||||
|
||||
# This parameter allows to use random port numbers for pre-packaged Tor daemon,
|
||||
# which is useful if another Tor daemon instance already bound SOCKS5 and control ports.
|
||||
# In this case bitcoin-s.tor.control and bitcoin-s.proxy.socks5
|
||||
# addresses will be automatically changed to "localhost:<random port>"
|
||||
use-random-ports = true
|
||||
|
||||
# The password used to arrive at the HashedControlPassword for the control port.
|
||||
# If provided, the HASHEDPASSWORD authentication method will be used instead of
|
||||
# the SAFECOOKIE one.
|
||||
# password = securePassword
|
||||
|
||||
# The path to the private key of the onion service being created
|
||||
# privateKeyPath = /path/to/priv/key
|
||||
}
|
||||
|
||||
# settings for the chain module
|
||||
chain {
|
||||
neutrino {
|
||||
filter-header-batch-size.default = 2000
|
||||
filter-header-batch-size.regtest = 10
|
||||
# You can set a network specific filter-header-batch-size
|
||||
# by adding a trailing `.networkId` (main, test, regtest)
|
||||
# It is recommended to keep the main and test batch size high
|
||||
# to keep the sync time fast, however, for regtest it should be small
|
||||
# so it does not exceed the chain size.
|
||||
|
||||
filter-batch-size = 1000
|
||||
}
|
||||
|
||||
hikari-logging = true
|
||||
hikari-logging-interval = 10 minute
|
||||
}
|
||||
|
||||
# settings for wallet module
|
||||
wallet {
|
||||
# You can have multiple wallets by setting a different
|
||||
# wallet name for each of them. They will each have
|
||||
# their own unique seed and database or schema,
|
||||
# depending on the database driver.
|
||||
# The wallet name can contain letters, numbers, and underscores '_'.
|
||||
# walletName = MyWallet0
|
||||
|
||||
defaultAccountType = segwit # legacy, segwit, nested-segwit
|
||||
|
||||
bloomFalsePositiveRate = 0.0001 # percentage
|
||||
|
||||
# the number of consecutive addresses that we do not
|
||||
# discover funds in before we mark as rescan as exhausted
|
||||
# this is needed because we can never truely tell how many addresses
|
||||
# the wallet has used when executing a rescan from a seed
|
||||
addressGapLimit = 100
|
||||
|
||||
# the number of addresses that get generated everytime
|
||||
# we need to rescan. If a match occurs within the addressGapLimit
|
||||
# we generate another discoveryBatchSize addresses and then rescan again
|
||||
discoveryBatchSize = 100
|
||||
|
||||
requiredConfirmations = 6
|
||||
|
||||
# Expected average fee rate over the long term
|
||||
# in satoshis per virtual byte
|
||||
longTermFeeRate = 10
|
||||
|
||||
# How big the address queue size is before we throw an exception
|
||||
# because of an overflow
|
||||
addressQueueSize = 10
|
||||
|
||||
# How long we attempt to generate an address for
|
||||
# before we timeout
|
||||
addressQueueTimeout = 5 seconds
|
||||
|
||||
# Allow external payout and change addresses in DLCs
|
||||
# By default all DLC addresses are generated by the wallet itself
|
||||
allowExternalDLCAddresses = false
|
||||
|
||||
# How often the wallet will rebroadcast unconfirmed transactions
|
||||
rebroadcastFrequency = 4 hours
|
||||
|
||||
hikari-logging = true
|
||||
hikari-logging-interval = 10 minute
|
||||
}
|
||||
|
||||
keymanager {
|
||||
# You can optionally set a BIP 39 password
|
||||
# bip39password = "changeMe"
|
||||
|
||||
# Password that your seed is encrypted with
|
||||
# aesPassword = changeMe
|
||||
|
||||
# At least 16 bytes of entropy encoded in hex
|
||||
# This will be used as the seed for any
|
||||
# project that is dependent on the keymanager
|
||||
# entropy = ""
|
||||
}
|
||||
|
||||
# Bitcoin-S provides manny different fee providers
|
||||
# You can configure your server to use any of them
|
||||
# Below is some examples of different options
|
||||
fee-provider {
|
||||
# name = mempoolspace # Uses mempool.space's api
|
||||
# The target is optional for mempool.space
|
||||
# It refers to the expected number of blocks until confirmation
|
||||
# target = 6
|
||||
|
||||
# name = bitcoinerlive # Uses bitcoiner.live's api
|
||||
# The target is optional for Bitcoiner Live
|
||||
# It refers to the expected number of blocks until confirmation
|
||||
# target = 6
|
||||
|
||||
# name = bitgo # Uses BitGo's api
|
||||
# The target is optional for BitGo
|
||||
# It refers to the expected number of blocks until confirmation
|
||||
# target = 6
|
||||
|
||||
# name = constant # A constant fee rate in sats/vbyte
|
||||
# target = 1 # Will always use 1 sat/vbyte
|
||||
}
|
||||
|
||||
dlcnode {
|
||||
# The address we are listening on for incoming connections for DLCs
|
||||
# Binding to 0.0.0.0 makes us listen to all incoming connections
|
||||
# Consider using 127.0.0.1 listen address if Tor is enabled.
|
||||
# listen = "0.0.0.0:2862"
|
||||
|
||||
# The address our peers use to connect to our node.
|
||||
# By default it's the same as the listen address,
|
||||
# or if Tor is enabled, the hidden service's onion address.
|
||||
# You can specify a port number like this "192.168.0.1:12345",
|
||||
# The default port number is the same as in the listen adrress
|
||||
# external-ip = "192.168.0.1"
|
||||
}
|
||||
|
||||
server {
|
||||
# The port we bind our rpc server on
|
||||
rpcport = 9999
|
||||
|
||||
# The ip address we bind our server too
|
||||
rpcbind = "127.0.0.1"
|
||||
|
||||
# The port we bind our websocket server on
|
||||
wsport = 19999
|
||||
|
||||
# The ip address we bind the websocket server too
|
||||
wsbind = "127.0.0.1"
|
||||
|
||||
# The basic auth password. It must me must be non empty.
|
||||
password = topsecret
|
||||
}
|
||||
|
||||
oracle {
|
||||
# The port we bind our rpc server on
|
||||
rpcport = 9998
|
||||
|
||||
# The ip address we bind our server too
|
||||
rpcbind = "127.0.0.1"
|
||||
|
||||
# The basic auth password. It must me must be non empty.
|
||||
password = supersecret
|
||||
|
||||
hikari-logging = true
|
||||
hikari-logging-interval = 10 minute
|
||||
|
||||
db {
|
||||
path = ${bitcoin-s.datadir}/oracle/
|
||||
}
|
||||
}
|
||||
|
||||
dbDefault = {
|
||||
dataSourceClass = slick.jdbc.DatabaseUrlDataSource
|
||||
profile = "slick.jdbc.SQLiteProfile$"
|
||||
|
||||
db {
|
||||
# for information on parameters available here see
|
||||
# https://scala-slick.org/doc/3.3.1/api/index.html#slick.jdbc.JdbcBackend$DatabaseFactoryDef@forConfig(String,Config,Driver,ClassLoader):Database
|
||||
path = ${bitcoin-s.datadir}/${bitcoin-s.network}/
|
||||
driver = org.sqlite.JDBC
|
||||
user = ""
|
||||
password = ""
|
||||
host = localhost
|
||||
port = 5432
|
||||
|
||||
# this needs to be set to 1 for SQLITE as it does not support concurrent database operations
|
||||
# see: https://github.com/bitcoin-s/bitcoin-s/pull/1840
|
||||
numThreads = 1
|
||||
queueSize=5000
|
||||
connectionPool = "HikariCP"
|
||||
registerMbeans = true
|
||||
}
|
||||
hikari-logging = false
|
||||
hikari-logging-interval = 10 minute
|
||||
}
|
||||
|
||||
testkit {
|
||||
pg {
|
||||
#enabled postgres backend database for all test cases
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
akka {
|
||||
loglevel = "OFF"
|
||||
stdout-loglevel = "OFF"
|
||||
http {
|
||||
client {
|
||||
# The time after which an idle connection will be automatically closed.
|
||||
# Set to `infinite` to completely disable idle connection timeouts.
|
||||
|
||||
# some requests potentially take a long time, like generate and prune
|
||||
idle-timeout = 5 minutes
|
||||
}
|
||||
|
||||
server {
|
||||
# The amount of time until a request times out on the server
|
||||
# If you have a large payload this may need to be bumped
|
||||
# https://doc.akka.io/docs/akka-http/current/common/timeouts.html#request-timeout
|
||||
request-timeout = 10s
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
actor {
|
||||
debug {
|
||||
# enable DEBUG logging of all AutoReceiveMessages (Kill, PoisonPill etc.)
|
||||
autoreceive= off
|
||||
# enable function of LoggingReceive, which is to log any received message at
|
||||
# DEBUG level
|
||||
receive = on
|
||||
# enable DEBUG logging of unhandled messages
|
||||
unhandled = off
|
||||
|
||||
# enable DEBUG logging of actor lifecycle changes
|
||||
lifecycle = off
|
||||
|
||||
event-stream=off
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Database configuration
|
||||
|
||||
By default, bitcoin-s uses Sqlite to store its data. It creates three Sqlite databases
|
||||
in `~/.bitcoin-s/${network}`: `chain.sqlite` for `chain` project,
|
||||
`node.sqlite` for `node` project and `wallet.sqlite` the wallet. This is the default configuration, it doesn't require
|
||||
additional changes in the config file.
|
||||
|
||||
`bitcoin-s` also supports PostgreSQL as a database backend. In order to use a PostgreSQL database for all project you
|
||||
need to add following into your config file:
|
||||
|
||||
```$xslt
|
||||
bitcoin-s {
|
||||
common {
|
||||
profile = "slick.jdbc.PostgresProfile$"
|
||||
db {
|
||||
driver = org.postgresql.Driver
|
||||
|
||||
# these 3 options will result into a jdbc url of
|
||||
# "jdbc:postgresql://localhost:5432/database"
|
||||
name = database
|
||||
host = localhost
|
||||
port = 5432
|
||||
|
||||
user = "user"
|
||||
password = "topsecret"
|
||||
numThreads = 5
|
||||
|
||||
# http://scala-slick.org/doc/3.3.3/database.html
|
||||
connectionPool = "HikariCP"
|
||||
registerMbeans = true
|
||||
}
|
||||
}
|
||||
|
||||
chain.profile = ${bitcoin-s.common.profile}
|
||||
chain.db = ${bitcoin-s.common.db}
|
||||
chain.db.poolName = "chain-connection-pool"
|
||||
|
||||
node.profile = ${bitcoin-s.common.profile}
|
||||
node.db = ${bitcoin-s.common.db}
|
||||
node.db.poolName = "node-connection-pool"
|
||||
|
||||
wallet.profile = ${bitcoin-s.common.profile}
|
||||
wallet.db = ${bitcoin-s.common.db}
|
||||
wallet.db.poolName = "wallet-connection-pool"
|
||||
|
||||
oracle.profile = ${bitcoin-s.common.profile}
|
||||
oracle.db = ${bitcoin-s.common.db}
|
||||
oracle.db.poolName = "oracle-connection-pool"
|
||||
}
|
||||
```
|
||||
|
||||
The database driver will create a separate SQL namespace for each sub-project: `chain`, `node` and `wallet`.
|
||||
|
||||
Also you can use mix databases and drivers in one configuration. For example, This configuration file enables Sqlite
|
||||
for `node` project (it's default, so its configuration is omitted), and `walletdb` and `chaindb` PostgreSQL databases
|
||||
for `wallet` and `chain` projects:
|
||||
|
||||
```$xslt
|
||||
bitcoin-s {
|
||||
chain {
|
||||
profile = "slick.jdbc.PostgresProfile$"
|
||||
db {
|
||||
driver = org.postgresql.Driver
|
||||
name = chaindb
|
||||
host = localhost
|
||||
port = 5432
|
||||
user = "user"
|
||||
password = "topsecret"
|
||||
}
|
||||
}
|
||||
wallet {
|
||||
profile = "slick.jdbc.PostgresProfile$"
|
||||
db {
|
||||
driver = org.postgresql.Driver
|
||||
name = walletdb
|
||||
host = localhost
|
||||
port = 5432
|
||||
user = "user"
|
||||
password = "topsecret"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
59
website/versioned_docs/version-1.9.3/core/addresses.md
Normal file
59
website/versioned_docs/version-1.9.3/core/addresses.md
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
id: version-1.9.3-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
|
||||
// this generates a random private key
|
||||
val privkey = ECPrivateKey()
|
||||
// privkey: ECPrivateKey = Masked(ECPrivateKey)
|
||||
val pubkey = privkey.publicKey
|
||||
// pubkey: org.bitcoins.crypto.ECPublicKey = ECPublicKey(03c6c96d5c121a533d2618ec9eb6256dbbb85ce5c6fe55bf35d922f254ae596a7b)
|
||||
|
||||
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 = tb1q467txqlep3enhexee9cl0r62q7qstk8grtdl2w
|
||||
|
||||
println(segwitAddress.toString)
|
||||
// tb1q467txqlep3enhexee9cl0r62q7qstk8grtdl2w
|
||||
```
|
||||
|
||||
## 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
|
||||
// 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 = mwSsvVGtG4wKDSYKs2ZDFgMz6c3W9fGPtF
|
||||
|
||||
println(legacyAddress.toString)
|
||||
// mwSsvVGtG4wKDSYKs2ZDFgMz6c3W9fGPtF
|
||||
```
|
108
website/versioned_docs/version-1.9.3/core/core-intro.md
Normal file
108
website/versioned_docs/version-1.9.3/core/core-intro.md
Normal file
@ -0,0 +1,108 @@
|
||||
---
|
||||
id: version-1.9.3-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 dependencies 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/crypto/NetworkElement). `NetworkElement` provides methods to convert the data structure to hex or byte representation. When paired with [`Factory`](/api/org/bitcoins/crypto/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 = BaseTransaction(Int32Impl(1),Vector(TransactionInputImpl(TransactionOutPoint(4d6da9420d472b6b52c36eee132d87448bf160d8839a58afdd2add6f6adfc8d8:0),P2PKHScriptSignature(ECPublicKeyBytes(ByteVector(65 bytes, 0x04c54f8ea9507f31a05ae325616e3024bd9878cb0a5dff780444002d731577be4e2e69c663ff2da922902a4454841aa1754c1b6292ad7d317150308d8cce0ad7ab)), ECDigitalSignature(3045022100b31557e47191936cb14e013fb421b1860b5e4fd5d2bc5ec1938f4ffb1651dc8902202661c2920771fd29dd91cd4100cefb971269836da4914d970d333861819265ba01)),UInt32Impl(4294967295)), TransactionInputImpl(TransactionOutPoint(b220a19ba645c021f0cf501435fd6c3b4db960325d0834612612a5684ffab32a:0),P2PKHScriptSignature(ECPublicKeyBytes(ByteVector(65 bytes, 0x04c6ec27cffce0823c3fecb162dbd576c88dd7cda0b7b32b0961188a392b488c94ca174d833ee6a9b71c0996620ae71e799fc7c77901db147fa7d97732e49c8226)), ECDigitalSignature(30450220230110bc99ef311f1f8bda9d0d968bfe5dfa4af171adbef9ef71678d658823bf022100f956d4fcfa0995a578d84e7e913f9bb1cf5b5be1440bcede07bce9cd5b38115d01)),UInt32Impl(4294967295))),Vector(TransactionOutput(39000000 sats,pkh(a3d89c53bb956f08917b44d113c6b2bcbe0c29b7)), TransactionOutput(155000000 sats,pkh(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/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 [`RawTxBuilder`](/api/org/bitcoins/core/wallet/builder/RawTxBuilder) 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 [InputSigningInfo](/api/org/bitcoins/core/wallet/utxo/InputSigningInfo) 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)
|
||||
|
||||
### 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
|
||||
val spendingTx = Transaction.fromHex("0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c865c2f6f7a9710a474154ab1423abb5b9288ac00000000")
|
||||
// spendingTx: Transaction = BaseTransaction(Int32Impl(1),Vector(TransactionInputImpl(TransactionOutPoint(b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc:0),P2PKHScriptSignature(ECPublicKeyBytes(ByteVector(33 bytes, 0x0241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353)), ECDigitalSignature(30450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01)),UInt32Impl(4294967295))),Vector(TransactionOutput(89994000 sats,pkh(b1d7591b69e9def0feb13254bace942923c7922d)), TransactionOutput(840 sats,pkh(5e690c865c2f6f7a9710a474154ab1423abb5b92))),UInt32Impl(0))
|
||||
|
||||
val scriptPubKey = ScriptPubKey.fromAsmHex("76a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac")
|
||||
// scriptPubKey: ScriptPubKey = pkh(31a420903c05a0a7de2de40c9f02ebedbacdc172)
|
||||
|
||||
val output = TransactionOutput(CurrencyUnits.zero, scriptPubKey)
|
||||
// output: TransactionOutput = TransactionOutput(0 sats,pkh(31a420903c05a0a7de2de40c9f02ebedbacdc172))
|
||||
|
||||
val inputIndex = UInt32.zero
|
||||
// inputIndex: UInt32 = UInt32Impl(0)
|
||||
|
||||
val btxsc = BaseTxSigComponent(spendingTx,inputIndex,output,Policy.standardScriptVerifyFlags)
|
||||
// btxsc: BaseTxSigComponent = BaseTxSigComponentImpl(BaseTransaction(Int32Impl(1),Vector(TransactionInputImpl(TransactionOutPoint(b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc:0),P2PKHScriptSignature(ECPublicKeyBytes(ByteVector(33 bytes, 0x0241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353)), ECDigitalSignature(30450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01)),UInt32Impl(4294967295))),Vector(TransactionOutput(89994000 sats,pkh(b1d7591b69e9def0feb13254bace942923c7922d)), TransactionOutput(840 sats,pkh(5e690c865c2f6f7a9710a474154ab1423abb5b92))),UInt32Impl(0)),UInt32Impl(0),TransactionOutput(0 sats,pkh(31a420903c05a0a7de2de40c9f02ebedbacdc172)),List(ScriptVerifyP2SH, ScriptVerifyDerSig, ScriptVerifyStrictEnc, ScriptVerifyMinimalData, ScriptVerifyDiscourageUpgradableNOPs, ScriptVerifyCleanStack, ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify, ScriptVerifyLowS, ScriptVerifyWitness, ScriptVerifyMinimalIf, ScriptVerifyNullFail, ScriptVerifyNullDummy, ScriptVerifyWitnessPubKeyType, ScriptVerifyDiscourageUpgradableWitnessProgram, ScriptVerifyTaproot, ScriptVerifyDiscourageUpgradableTaprootVersion, ScriptVerifyDiscourageOpSuccess, ScriptVerifyDiscourageUpgradablePubKeyType))
|
||||
|
||||
val preExecution = PreExecutionScriptProgram(btxsc)
|
||||
// preExecution: PreExecutionScriptProgram = PreExecutionScriptProgram(BaseTxSigComponentImpl(BaseTransaction(Int32Impl(1),Vector(TransactionInputImpl(TransactionOutPoint(b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc:0),P2PKHScriptSignature(ECPublicKeyBytes(ByteVector(33 bytes, 0x0241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353)), ECDigitalSignature(30450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01)),UInt32Impl(4294967295))),Vector(TransactionOutput(89994000 sats,pkh(b1d7591b69e9def0feb13254bace942923c7922d)), TransactionOutput(840 sats,pkh(5e690c865c2f6f7a9710a474154ab1423abb5b92))),UInt32Impl(0)),UInt32Impl(0),TransactionOutput(0 sats,pkh(31a420903c05a0a7de2de40c9f02ebedbacdc172)),List(ScriptVerifyP2SH, ScriptVerifyDerSig, ScriptVerifyStrictEnc, ScriptVerifyMinimalData, ScriptVerifyDiscourageUpgradableNOPs, ScriptVerifyCleanStack, ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify, ScriptVerifyLowS, ScriptVerifyWitness, ScriptVerifyMinimalIf, ScriptVerifyNullFail, ScriptVerifyNullDummy, ScriptVerifyWitnessPubKeyType, ScriptVerifyDiscourageUpgradableWitnessProgram, ScriptVerifyTaproot, ScriptVerifyDiscourageUpgradableTaprootVersion, ScriptVerifyDiscourageOpSuccess, ScriptVerifyDiscourageUpgradablePubKeyType)),List(),List(BytesToPushOntoStackImpl(72), ScriptConstantImpl(ByteVector(72 bytes, 0x30450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01)), BytesToPushOntoStackImpl(33), ScriptConstantImpl(ByteVector(33 bytes, 0x0241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353))),List(BytesToPushOntoStackImpl(72), ScriptConstantImpl(ByteVector(72 bytes, 0x30450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01)), BytesToPushOntoStackImpl(33), ScriptConstantImpl(ByteVector(33 bytes, 0x0241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353))),List(),List(ScriptVerifyP2SH, ScriptVerifyDerSig, ScriptVerifyStrictEnc, ScriptVerifyMinimalData, ScriptVerifyDiscourageUpgradableNOPs, ScriptVerifyCleanStack, ScriptVerifyCheckLocktimeVerify, ScriptVerifyCheckSequenceVerify, ScriptVerifyLowS, ScriptVerifyWitness, ScriptVerifyMinimalIf, ScriptVerifyNullFail, ScriptVerifyNullDummy, ScriptVerifyWitnessPubKeyType, ScriptVerifyDiscourageUpgradableWitnessProgram, ScriptVerifyTaproot, ScriptVerifyDiscourageUpgradableTaprootVersion, ScriptVerifyDiscourageOpSuccess, ScriptVerifyDiscourageUpgradablePubKeyType))
|
||||
|
||||
val result = ScriptInterpreter.run(preExecution)
|
||||
// result: org.bitcoins.core.script.result.ScriptResult = ScriptOk
|
||||
|
||||
println(s"Script execution result=${result}")
|
||||
// Script execution result=ScriptOk
|
||||
```
|
225
website/versioned_docs/version-1.9.3/core/dlc.md
Normal file
225
website/versioned_docs/version-1.9.3/core/dlc.md
Normal file
File diff suppressed because one or more lines are too long
145
website/versioned_docs/version-1.9.3/core/hd-keys.md
Normal file
145
website/versioned_docs/version-1.9.3/core/hd-keys.md
Normal file
@ -0,0 +1,145 @@
|
||||
---
|
||||
id: version-1.9.3-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, 0xa898bbab0cf3f229f8268d6e0cb2a77b7105745270e12f1fae5465f7683c29df)
|
||||
|
||||
val mnemonicCode = MnemonicCode.fromEntropy(entropy)
|
||||
// mnemonicCode: MnemonicCode = Masked(MnemonicCodeImpl)
|
||||
|
||||
mnemonicCode.words // the phrase the user should write down
|
||||
// res0: Vector[String] = Vector(possible, shift, turn, boring, dish, belt, they, spider, host, grain, prefer, want, aware, riot, endorse, bring, congress, word, never, slogan, isolate, bulk, polar, water) // 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 = Masked(BIP39SeedImpl)
|
||||
|
||||
val xpriv = ExtPrivateKey.fromBIP39Seed(ExtKeyVersion.SegWitMainNetPriv,
|
||||
bip39Seed)
|
||||
// xpriv: ExtPrivateKey = Masked(ExtPrivateKeyImpl)
|
||||
val xpub = xpriv.extPublicKey
|
||||
// xpub: ExtPublicKey = zpub6jftahH18ngZwh9HrL86foqfZ2P2so3REoXaQHXakrqK5Nf6784DBJwxeKieMwcXTzLpwiUz4TURNeuwy7Ws7MGzP7S8bs2MDnHwF9v31Eo
|
||||
|
||||
// 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 = zpub6sAjCKEoxzq91VrFc4mYjh1c1gYMPXVZdvoNJkVnZMTmcp5N2KZGeYeEvsXsMxngqLLvVdRmwT44ARHooT63PwbPCmQjgzZvAwefmjyhfKJ
|
||||
|
||||
// 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 = tb1qed0he2zcqu5x8e6p78wjmkduljysz5ats2k0uu
|
||||
|
||||
// tada! We just generated an address you can send money to,
|
||||
// without having access to the private key!
|
||||
firstAccountAddress.value
|
||||
// res2: String = tb1qed0he2zcqu5x8e6p78wjmkduljysz5ats2k0uu
|
||||
|
||||
// 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
|
||||
```
|
||||
|
||||
### Signing things with HD keys
|
||||
|
||||
Please see [sign.md](../crypto/sign.md) for information on how to sign things with HD keys.
|
158
website/versioned_docs/version-1.9.3/core/txbuilder.md
Normal file
158
website/versioned_docs/version-1.9.3/core/txbuilder.md
Normal file
@ -0,0 +1,158 @@
|
||||
---
|
||||
id: version-1.9.3-txbuilder
|
||||
title: TxBuilder Example
|
||||
original_id: txbuilder
|
||||
---
|
||||
|
||||
Bitcoin-S features a transaction building API that allows you to construct and sign Bitcoin transactions. Here's an example of how to use it
|
||||
|
||||
|
||||
```scala
|
||||
implicit val ec: ExecutionContext = ExecutionContext.Implicits.global
|
||||
// ec: ExecutionContext = scala.concurrent.impl.ExecutionContextImpl$$anon$3@52f2f4d0[Running, parallelism = 8, size = 2, active = 0, running = 0, steals = 2364, tasks = 0, submissions = 0]
|
||||
|
||||
// Initialize a transaction builder
|
||||
val builder = RawTxBuilder()
|
||||
// builder: RawTxBuilder = RawTxBuilder()
|
||||
|
||||
// generate a fresh private key that we are going to use in the scriptpubkey
|
||||
val privKey = ECPrivateKey.freshPrivateKey
|
||||
// privKey: ECPrivateKey = Masked(ECPrivateKey)
|
||||
val pubKey = privKey.publicKey
|
||||
// pubKey: ECPublicKey = ECPublicKey(02c3055bf0b9f15961bf8e71f0ce1aa91a3f4696f064fd3c73880ef6e4654c1989)
|
||||
|
||||
// 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)
|
||||
// creditingSpk: P2PKHScriptPubKey = pkh(7cc70ef16daacd4fc87b9c9ff78adc0acf517041)
|
||||
val amount = 10000.satoshis
|
||||
// amount: Satoshis = 10000 sats
|
||||
|
||||
// this is the UTXO we are going to be spending
|
||||
val utxo =
|
||||
TransactionOutput(value = amount, scriptPubKey = creditingSpk)
|
||||
// utxo: TransactionOutput = TransactionOutput(10000 sats,pkh(7cc70ef16daacd4fc87b9c9ff78adc0acf517041))
|
||||
|
||||
// the private key that locks the funds for the script we are spending too
|
||||
val destinationPrivKey = ECPrivateKey.freshPrivateKey
|
||||
// destinationPrivKey: ECPrivateKey = Masked(ECPrivateKey)
|
||||
|
||||
// the amount we are sending -- 5000 satoshis -- to the destinationSPK
|
||||
val destinationAmount = 5000.satoshis
|
||||
// destinationAmount: Satoshis = 5000 sats
|
||||
|
||||
// the script that corresponds to destination private key, this is what is receiving the money
|
||||
val destinationSPK =
|
||||
P2PKHScriptPubKey(pubKey = destinationPrivKey.publicKey)
|
||||
// destinationSPK: P2PKHScriptPubKey = pkh(b8a468d1a90743c3ab87511f71ee53c7e2826af3)
|
||||
|
||||
// this is where we are sending money too
|
||||
// we could add more destinations here if we
|
||||
// wanted to batch transactions
|
||||
val destinations = {
|
||||
val destination0 = TransactionOutput(value = destinationAmount,
|
||||
scriptPubKey = destinationSPK)
|
||||
|
||||
Vector(destination0)
|
||||
}
|
||||
// destinations: Vector[TransactionOutput] = Vector(TransactionOutput(5000 sats,pkh(b8a468d1a90743c3ab87511f71ee53c7e2826af3)))
|
||||
|
||||
// Add the destinations to the tx builder
|
||||
builder ++= destinations
|
||||
// res0: RawTxBuilder = RawTxBuilder()
|
||||
|
||||
// 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 = Vector.empty,
|
||||
outputs = Vector(utxo),
|
||||
lockTime = UInt32.zero)
|
||||
// creditingTx: BaseTransaction = BaseTransaction(Int32Impl(1),Vector(),Vector(TransactionOutput(10000 sats,pkh(7cc70ef16daacd4fc87b9c9ff78adc0acf517041))),UInt32Impl(0))
|
||||
|
||||
// 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)
|
||||
// outPoint: TransactionOutPoint = TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0)
|
||||
val input = TransactionInput(
|
||||
outPoint,
|
||||
EmptyScriptSignature,
|
||||
sequenceNumber = UInt32.zero)
|
||||
// input: TransactionInput = TransactionInputImpl(TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0),EmptyScriptSignature,UInt32Impl(0))
|
||||
|
||||
// Add a new input to our builder
|
||||
builder += input
|
||||
// res1: RawTxBuilder = RawTxBuilder()
|
||||
|
||||
// We can now generate a RawTxBuilderResult ready to be finalized
|
||||
val builderResult = builder.result()
|
||||
// builderResult: RawTxBuilderResult = RawTxBuilderResult(Int32Impl(2),Vector(TransactionInputImpl(TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0),EmptyScriptSignature,UInt32Impl(0))),Vector(TransactionOutput(5000 sats,pkh(b8a468d1a90743c3ab87511f71ee53c7e2826af3))),UInt32Impl(0))
|
||||
|
||||
// this contains the information needed to analyze our input during finalization
|
||||
val inputInfo = P2PKHInputInfo(outPoint, amount, privKey.publicKey)
|
||||
// inputInfo: P2PKHInputInfo = P2PKHInputInfo(TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0),10000 sats,ECPublicKey(02c3055bf0b9f15961bf8e71f0ce1aa91a3f4696f064fd3c73880ef6e4654c1989))
|
||||
|
||||
// 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)
|
||||
// feeRate: SatoshisPerByte = 1 sats/byte
|
||||
|
||||
val changePrivKey = ECPrivateKey.freshPrivateKey
|
||||
// changePrivKey: ECPrivateKey = Masked(ECPrivateKey)
|
||||
val changeSPK = P2PKHScriptPubKey(pubKey = changePrivKey.publicKey)
|
||||
// changeSPK: P2PKHScriptPubKey = pkh(d0544762d046ebd4b70aadf583a0599c92842d50)
|
||||
|
||||
// We chose a finalizer that adds a change output to our tx based on a fee rate
|
||||
val finalizer = StandardNonInteractiveFinalizer(
|
||||
Vector(inputInfo),
|
||||
feeRate,
|
||||
changeSPK)
|
||||
// finalizer: StandardNonInteractiveFinalizer = StandardNonInteractiveFinalizer(Vector(P2PKHInputInfo(TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0),10000 sats,ECPublicKey(02c3055bf0b9f15961bf8e71f0ce1aa91a3f4696f064fd3c73880ef6e4654c1989))),1 sats/byte,pkh(d0544762d046ebd4b70aadf583a0599c92842d50))
|
||||
|
||||
// We can now finalize the tx builder result from earlier with this finalizer
|
||||
val unsignedTx: Transaction = finalizer.buildTx(builderResult)
|
||||
// unsignedTx: Transaction = BaseTransaction(Int32Impl(2),Vector(TransactionInputImpl(TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0),EmptyScriptSignature,UInt32Impl(0))),Vector(TransactionOutput(5000 sats,pkh(b8a468d1a90743c3ab87511f71ee53c7e2826af3)), TransactionOutput(4775 sats,pkh(d0544762d046ebd4b70aadf583a0599c92842d50))),UInt32Impl(0))
|
||||
|
||||
// We now turn to signing the unsigned transaction
|
||||
// this contains all the information we need to
|
||||
// validly sign the UTXO above
|
||||
val utxoInfo = ScriptSignatureParams(inputInfo = inputInfo,
|
||||
prevTransaction = creditingTx,
|
||||
signers = Vector(privKey),
|
||||
hashType =
|
||||
HashType.sigHashAll)
|
||||
// utxoInfo: ScriptSignatureParams[P2PKHInputInfo] = ScriptSignatureParams(P2PKHInputInfo(TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0),10000 sats,ECPublicKey(02c3055bf0b9f15961bf8e71f0ce1aa91a3f4696f064fd3c73880ef6e4654c1989)),BaseTransaction(Int32Impl(1),Vector(),Vector(TransactionOutput(10000 sats,pkh(7cc70ef16daacd4fc87b9c9ff78adc0acf517041))),UInt32Impl(0)),Vector(Masked(ECPrivateKey)),SIGHASH_ALL(1))
|
||||
|
||||
// all of the UTXO spending information, since we only have
|
||||
// one input, this is just one element
|
||||
val utxoInfos: Vector[ScriptSignatureParams[InputInfo]] = Vector(utxoInfo)
|
||||
// utxoInfos: Vector[ScriptSignatureParams[InputInfo]] = Vector(ScriptSignatureParams(P2PKHInputInfo(TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0),10000 sats,ECPublicKey(02c3055bf0b9f15961bf8e71f0ce1aa91a3f4696f064fd3c73880ef6e4654c1989)),BaseTransaction(Int32Impl(1),Vector(),Vector(TransactionOutput(10000 sats,pkh(7cc70ef16daacd4fc87b9c9ff78adc0acf517041))),UInt32Impl(0)),Vector(Masked(ECPrivateKey)),SIGHASH_ALL(1)))
|
||||
|
||||
// Yay! Now we use the RawTxSigner object to sign the 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 ScriptSignatureParams 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 =
|
||||
RawTxSigner.sign(
|
||||
utx = unsignedTx,
|
||||
utxoInfos = utxoInfos,
|
||||
expectedFeeRate = feeRate
|
||||
)
|
||||
// signedTx: Transaction = BaseTransaction(Int32Impl(2),Vector(TransactionInputImpl(TransactionOutPoint(a3ceb9d2375cbe24fb973fc2dec976938a29f12aded9dd216ff56197f93e7eb0:0),P2PKHScriptSignature(ECPublicKeyBytes(ByteVector(33 bytes, 0x02c3055bf0b9f15961bf8e71f0ce1aa91a3f4696f064fd3c73880ef6e4654c1989)), ECDigitalSignature(304402206f5050a5648a4738c8c4b3c7bb15131f241cf00690f23bef1b1358db4894130f02205e12386a020fa1b3fcc21abd98ccd187196811ccd344df7feaf1641d4c58ae1001)),UInt32Impl(0))),Vector(TransactionOutput(5000 sats,pkh(b8a468d1a90743c3ab87511f71ee53c7e2826af3)), TransactionOutput(4775 sats,pkh(d0544762d046ebd4b70aadf583a0599c92842d50))),UInt32Impl(0))
|
||||
```
|
||||
|
||||
```scala
|
||||
signedTx.inputs.length
|
||||
// res2: Int = 1
|
||||
|
||||
signedTx.outputs.length
|
||||
// res3: Int = 2
|
||||
|
||||
//remember, you can call .hex on any bitcoin-s data structure to get the hex representation!
|
||||
signedTx.hex
|
||||
// res4: String = 0200000001b07e3ef99761f56f21ddd9de2af1298a9376c9dec23f97fb24be5c37d2b9cea3000000006a47304402206f5050a5648a4738c8c4b3c7bb15131f241cf00690f23bef1b1358db4894130f02205e12386a020fa1b3fcc21abd98ccd187196811ccd344df7feaf1641d4c58ae10012102c3055bf0b9f15961bf8e71f0ce1aa91a3f4696f064fd3c73880ef6e4654c1989000000000288130000000000001976a914b8a468d1a90743c3ab87511f71ee53c7e2826af388aca7120000000000001976a914d0544762d046ebd4b70aadf583a0599c92842d5088ac00000000
|
||||
```
|
84
website/versioned_docs/version-1.9.3/crypto/musig.md
Normal file
84
website/versioned_docs/version-1.9.3/crypto/musig.md
Normal file
@ -0,0 +1,84 @@
|
||||
---
|
||||
id: version-1.9.3-musig
|
||||
title: MuSig
|
||||
original_id: musig
|
||||
---
|
||||
|
||||
Bitcoin-S now has support for [MuSig](https://github.com/jonasnick/bips/blob/musig2/bip-musig2.mediawiki).
|
||||
|
||||
This module contains classes representing public `KeySet`s, MuSig nonces, and MuSig aggregate key tweaks, as well as utility functions for all MuSig computations.
|
||||
|
||||
The functions for aggregating key data are:
|
||||
|
||||
* `aggPubKey`
|
||||
* This is a member of `KeySet` and returns the aggregate public key for this set of signers, including the tweaks provided. In most uses, a subsequent call to `schnorrPublicKey` is required for Bitcoin applications.
|
||||
* `MuSigNoncePub.aggregate`
|
||||
* Given a `Vector[MuSigNoncePub]` of the signer's nonces, returns the aggregate `MuSigNoncePub`. This aggregation can be done before the message, or even the `KeySet` is known.
|
||||
|
||||
The functions for signing and verification are:
|
||||
|
||||
* `MuSigUtil.sign`
|
||||
* This function generates a MuSig partial signature using a private key and `MuSigNoncePriv`. This consists of a pair `(R, s)` where `R` is the aggregate nonce key (same for all signers) and `s` is the actual partial signature that needs to be shared.
|
||||
* `MuSigUtil.partialSigVerify`
|
||||
* This function validates a single partial signature against that signer's public key and `MuSigNoncePub`.
|
||||
* `MuSigUtil.signAgg`
|
||||
* This function aggregates all of the `s` values into a single valid `SchnorrDigitalSignature` (using the aggregate nonce key `R`).
|
||||
|
||||
Note that no new function is required for aggregate verification as `SchnorrPublicKey`'s `verify` function is to be used.
|
||||
|
||||
Lastly, it should be mentioned that `MuSigNoncePriv`s must be constructed using either `MuSigNoncePriv.gen` or `MuSigNoncePriv.genInternal` (the latter should only be used with 32 bytes of secure random entropy). These generation functions take as input any context information that is available at nonce generation time, namely your signing key, aggregate public key, message, and any extra bytes you may have available. Including these optional inputs improves the security of nonce generation (which must be absolutely secure).
|
||||
|
||||
The following code shows a two-party MuSig execution:
|
||||
|
||||
|
||||
```scala
|
||||
// Alice and Bob generate and exchange nonce data (new nonces for every sig)
|
||||
val aliceNoncePriv = MuSigNoncePriv.gen()
|
||||
val aliceNonce = aliceNoncePriv.toPublicNonces // Alice sends this to Bob // Alice sends this to Bob
|
||||
val bobNoncePriv = MuSigNoncePriv.gen()
|
||||
val bobNonce = bobNoncePriv.toPublicNonces // Bob sends this to Alice // Bob sends this to Alice
|
||||
|
||||
// The aggregate musig nonce can be computed from Alice's and Bob's
|
||||
val aggMuSigNonce = MuSigNoncePub.aggregate(Vector(aliceNonce, bobNonce))
|
||||
|
||||
// Any party can (non-interactively) compute the aggregate public key
|
||||
val pubKeys = Vector(alicePubKey, bobPubKey)
|
||||
val keySet = KeySet(pubKeys, tweaks) // This is where you put MuSigTweaks // This is where you put MuSigTweaks
|
||||
val aggPubKey = keySet.aggPubKey.schnorrPublicKey
|
||||
|
||||
// Alice generates a partial signature for the message
|
||||
val (aliceR, aliceSig) =
|
||||
MuSigUtil.sign(aliceNoncePriv, aggMuSigNonce, alicePrivKey, msg, keySet)
|
||||
// Bob generates a partial signature for the message
|
||||
val (bobR, bobSig) =
|
||||
MuSigUtil.sign(bobNoncePriv, aggMuSigNonce, bobPrivKey, msg, keySet)
|
||||
require(aliceR == bobR)
|
||||
val R = aliceR
|
||||
|
||||
// Alice and Bob exchange and verify each other's sigs (s values)
|
||||
require(
|
||||
MuSigUtil.partialSigVerify(aliceSig,
|
||||
aliceNonce,
|
||||
aggMuSigNonce,
|
||||
alicePubKey,
|
||||
keySet,
|
||||
msg))
|
||||
require(
|
||||
MuSigUtil.partialSigVerify(bobSig,
|
||||
bobNonce,
|
||||
aggMuSigNonce,
|
||||
bobPubKey,
|
||||
keySet,
|
||||
msg))
|
||||
|
||||
// In the case that the aggregator is not Alice or Bob, R can be computed as follows
|
||||
val R2 = SigningSession(aggMuSigNonce, keySet, msg).aggNonce
|
||||
require(R2 == R)
|
||||
|
||||
// Finally, any party can aggregate the partial signatures
|
||||
val sig = MuSigUtil.signAgg(Vector(aliceSig, bobSig), R)
|
||||
|
||||
// This signature can now be validated as a normal BIP340 Schnorr signature
|
||||
require(aggPubKey.verify(msg, sig))
|
||||
```
|
||||
|
65
website/versioned_docs/version-1.9.3/crypto/sign.md
Normal file
65
website/versioned_docs/version-1.9.3/crypto/sign.md
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
id: version-1.9.3-sign
|
||||
title: Sign API
|
||||
original_id: sign
|
||||
---
|
||||
|
||||
### The [`Sign` API](/api/org/bitcoins/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 [Sign.scala](/api/org/bitcoins/crypto/Sign):
|
||||
|
||||
```scala
|
||||
import scodec.bits._
|
||||
import org.bitcoins.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 [`BaseECKey`](/api/org/bitcoins/crypto/BaseECKey) 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.
|
||||
|
||||
|
||||
### The [`ExtSign`](/api/org/bitcoins/crypto/Sign) API.
|
||||
|
||||
An [ExtKey](/api/org/bitcoins/core/crypto/ExtKey) is a data structure that can be used to generate more keys from a parent key. For more information look at [hd-keys.md](../core/hd-keys.md)
|
||||
|
||||
You can sign with `ExtPrivateKey` the same way you could with a normal `ECPrivateKey`.
|
||||
|
||||
```scala
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.crypto._
|
||||
|
||||
val extPrivKey = ExtPrivateKey(ExtKeyVersion.SegWitMainNetPriv)
|
||||
// extPrivKey: ExtPrivateKey = Masked(ExtPrivateKeyImpl)
|
||||
|
||||
extPrivKey.sign(DoubleSha256Digest.empty.bytes)
|
||||
// res0: ECDigitalSignature = ECDigitalSignature(30450221009c37f32f57b56933b4b9479dd1595f3c6c154c7a1ca8012e2d3985bbc621fc2f02207d14616eaa6d577bffdb10e388f0402fa333c1fa34c5c9164350045e8d117e3e)
|
||||
|
||||
val path = BIP32Path(Vector(BIP32Node(0,false)))
|
||||
// path: BIP32Path = m/0
|
||||
|
||||
extPrivKey.sign(DoubleSha256Digest.empty.bytes,path)
|
||||
// res1: ECDigitalSignature = ECDigitalSignature(3045022100e93b6e2dd9cedad7b5b9c346d316fade82cf9154e72ea3edbaa74ae0011e888102207cd75663d0dba36a4af736fc4552458c4810f66af77dc0680caaddd77c13953f)
|
||||
```
|
||||
|
||||
With `ExtSign`, you can use `ExtPrivateKey` to sign transactions inside of `TxBuilder` since `UTXOSpendingInfo` takes in `Sign` as a parameter.
|
||||
|
||||
You can also provide a `path` to use to derive a child `ExtPrivateKey`, and then sign with that child private key
|
127
website/versioned_docs/version-1.9.3/getting-started.md
Normal file
127
website/versioned_docs/version-1.9.3/getting-started.md
Normal file
@ -0,0 +1,127 @@
|
||||
---
|
||||
id: version-1.9.3-getting-started
|
||||
title: Intro and Getting Started
|
||||
original_id: getting-started
|
||||
---
|
||||
|
||||
## Philosophy
|
||||
|
||||
Bitcoin-S is a loosely coupled set of cryptocurrency libraries for the JVM. They work well together, but also can be used
|
||||
independently. This project's goal is NOT to be a full node implementation, rather a set of scalable cryptocurrency libraries
|
||||
that use industry standard tools (rather than esoteric tech often found in cryptocurrency) where possible to make the lives of professional
|
||||
software engineers, security engineers, devops engineers and accountants easier.
|
||||
We are rapidly iterating on development with the goal of getting to a set of stable APIs that only change when the underlying bitcoin protocol changes.
|
||||
|
||||
If you are a professional working a cryptocurrency business and
|
||||
have feedback on how to make your lives easier, please reach out on [slack](https://join.slack.com/t/suredbits/shared_invite/zt-eavycu0x-WQL7XOakzQo8tAy7jHHZUw),
|
||||
[gitter](https://gitter.im/bitcoin-s-core/) or [twitter](https://twitter.com/Chris_Stewart_5/)!
|
||||
|
||||
## Getting prebuilt artifacts
|
||||
|
||||
### Java binaries
|
||||
|
||||
<details>
|
||||
|
||||
#### Latest release
|
||||
|
||||
Please see the release page on github, you can find it [here](https://github.com/bitcoin-s/bitcoin-s/releases)
|
||||
|
||||
#### Master builds
|
||||
|
||||
We build installers for mac, linux and windows everytime a PR is merged to master.
|
||||
|
||||
You can find the latest builds at this link:
|
||||
|
||||
https://github.com/bitcoin-s/bitcoin-s/actions/workflows/release.yml
|
||||
|
||||
Here is what the installers look like
|
||||
|
||||
![installers](/img/doc-imgs/github-artifacts.png)
|
||||
|
||||
</details>
|
||||
|
||||
### Docker
|
||||
|
||||
<details>
|
||||
We publish docker images to docker hub on every PR merge and tag on github.
|
||||
You can obtain the images for both the app server and oracle server on these
|
||||
docker hub repos
|
||||
|
||||
[bitcoin-s-server docker hub repo](https://hub.docker.com/r/bitcoinscala/bitcoin-s-server/tags?page=1&ordering=last_updated)
|
||||
|
||||
[bitcoin-s-oracle-server docker hub repo](https://hub.docker.com/r/bitcoinscala/bitcoin-s-oracle-server/tags?page=1&ordering=last_updated)
|
||||
</details>
|
||||
|
||||
### Library jars
|
||||
|
||||
<details>
|
||||
Add this to your `build.sbt`:
|
||||
|
||||
```scala
|
||||
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-bitcoind-rpc" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-core" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-chain" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-dlc-oracle" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-eclair-rpc" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-fee-provider" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-key-manager" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-lnd-rpc" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-node" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-oracle-explorer-client" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" % "bitcoin-s-secp256k1jni" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit-core" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-wallet" % "1.9.2"
|
||||
|
||||
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-zmq" % "1.9.2"
|
||||
|
||||
```
|
||||
|
||||
|
||||
### 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 `1.9.2-118-dbed81c6-SNAPSHOT`.
|
||||
|
||||
|
||||
|
||||
To fetch snapshots, you will need to add the correct
|
||||
resolver in your `build.sbt`:
|
||||
|
||||
```sbt
|
||||
resolvers += Resolver.sonatypeRepo("snapshots")
|
||||
```
|
||||
|
||||
The official maven repo for releases is
|
||||
|
||||
https://repo1.maven.org/maven2/org/bitcoin-s/
|
||||
|
||||
The repo for snapshots, which are published after everytime something is merged to master:
|
||||
|
||||
https://oss.sonatype.org/content/repositories/snapshots/org/bitcoin-s/
|
||||
|
||||
</details>
|
||||
|
||||
## Building JARs yourself
|
||||
|
||||
Please see [our setup docs](getting-setup.md)
|
||||
|
||||
## If you want to setup Bitcoin-S locally for development
|
||||
|
||||
Please see [our setup docs](getting-setup.md)
|
124
website/versioned_docs/version-1.9.3/key-manager/key-manager.md
Normal file
124
website/versioned_docs/version-1.9.3/key-manager/key-manager.md
Normal file
@ -0,0 +1,124 @@
|
||||
---
|
||||
id: version-1.9.3-key-manager
|
||||
title: Key Manager
|
||||
original_id: key-manager
|
||||
---
|
||||
|
||||
|
||||
### Key Manager
|
||||
|
||||
The key manager module's goal is to encapsulate all private key interactions with the [wallet](../wallet/wallet.md) project.
|
||||
|
||||
As of this writing, there is only one type of `KeyManager` - [`BIP39KeyManager`](/api/org/bitcoins/keymanager/bip39/BIP39KeyManager).
|
||||
|
||||
The [`BIP39KeyManager`](/api/org/bitcoins/keymanager/bip39/BIP39KeyManager) stores a [`MnemonicCode`](/api/org/bitcoins/core/crypto/MnemonicCode) on disk which can be decrypted and used as a hot wallet.
|
||||
|
||||
Over the long 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.
|
||||
|
||||
#### Creating a key manager
|
||||
|
||||
The first thing you need create a key manager is some entropy.
|
||||
|
||||
A popular way for bitcoin wallet's to represent entropy is [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) which you [can use in bitcoin-s](/api/org/bitcoins/core/crypto/BIP39Seed)
|
||||
|
||||
You can generate a `MnemonicCode` in bitcoin-s with the following code
|
||||
|
||||
```scala
|
||||
import org.bitcoins.core.crypto._
|
||||
|
||||
//get 256 bits of random entropy
|
||||
val entropy = MnemonicCode.getEntropy256Bits
|
||||
// entropy: scodec.bits.BitVector = BitVector(256 bits, 0xb11819782ee597849d1967cd32f36ef80bc0b950938b1613fdf2d0c7bf76b693)
|
||||
|
||||
val mnemonic = MnemonicCode.fromEntropy(entropy)
|
||||
// mnemonic: MnemonicCode = Masked(MnemonicCodeImpl)
|
||||
|
||||
//you can print that mnemonic seed with this
|
||||
println(mnemonic.words)
|
||||
// Vector(rain, school, fun, fruit, floor, seat, inner, coconut, snake, nurse, sweet, useless, rose, frequent, drastic, imitate, bid, paper, lake, pave, know, robust, surge, giraffe)
|
||||
```
|
||||
|
||||
Now that we have a `MnemonicCode` that was securely generated, we need to now create `KeyManagerParams` which tells us how to generate
|
||||
generate specific kinds of addresses for wallets.
|
||||
|
||||
`KeyManagerParams` takes 3 parameters:
|
||||
|
||||
1. `seedPath` there is where we store the `MnemonicCode` on your file system
|
||||
2. [`purpose`](/api/org/bitcoins/core/hd/HDPurpose) which represents what type of utxo this `KeyManager` is associated with. The specification for this is in [BIP43](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki)
|
||||
3. [`network`](/api/org/bitcoins/core/config/NetworkParameters) what cryptocurrency network this key manager is associated with
|
||||
|
||||
|
||||
This controls how the root key is defined. The combination of `purpose` and `network` determine how the root `ExtKey` is serialized. For more information on how this works please see [hd-keys](../core/hd-keys.md)
|
||||
|
||||
Now we can construct a native segwit key manager for the regtest network!
|
||||
|
||||
```scala
|
||||
//this will create a temp directory with the prefix 'key-manager-example` that will
|
||||
//have a file in it called "encrypted-bitcoin-s-seed.json"
|
||||
val seedPath = Files.createTempDirectory("key-manager-example").resolve(WalletStorage.ENCRYPTED_SEED_FILE_NAME)
|
||||
// seedPath: Path = /var/folders/fg/scntn26d4h55x96zc456l0r40000gn/T/key-manager-example11086394032840961872/encrypted-bitcoin-s-seed.json
|
||||
|
||||
//let's create a native segwit key manager
|
||||
val purpose = HDPurposes.SegWit
|
||||
// purpose: HDPurpose = m/84'
|
||||
|
||||
//let's choose regtest as our network
|
||||
val network = RegTest
|
||||
// network: RegTest.type = RegTest
|
||||
|
||||
val kmParams = KeyManagerParams(seedPath, purpose, network)
|
||||
// kmParams: KeyManagerParams = KeyManagerParams(/var/folders/fg/scntn26d4h55x96zc456l0r40000gn/T/key-manager-example11086394032840961872/encrypted-bitcoin-s-seed.json,m/84',RegTest)
|
||||
|
||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
||||
// aesPasswordOpt: Some[AesPassword] = Some(Masked(AesPassword))
|
||||
|
||||
val km = BIP39KeyManager.initializeWithMnemonic(aesPasswordOpt, mnemonic, None, kmParams)
|
||||
// km: Either[KeyManagerInitializeError, BIP39KeyManager] = Right(org.bitcoins.keymanager.bip39.BIP39KeyManager@77b17792)
|
||||
|
||||
val rootXPub = km.right.get.getRootXPub
|
||||
// rootXPub: ExtPublicKey = vpub5SLqN2bLY4WeYKi7BRZKAS7K9aK3EJcdBqhr3KqkpUqcooZgudT2s782KE5MeGDm9pxAi3oN9ThSCn46Qa2TbNUazA7zCFUJnmN9sogdEcT
|
||||
|
||||
println(rootXPub)
|
||||
// vpub5SLqN2bLY4WeYKi7BRZKAS7K9aK3EJcdBqhr3KqkpUqcooZgudT2s782KE5MeGDm9pxAi3oN9ThSCn46Qa2TbNUazA7zCFUJnmN9sogdEcT
|
||||
```
|
||||
|
||||
Which should print something that looks like this
|
||||
|
||||
`vpub5SLqN2bLY4WeXxMqwJHJFBEwxSscGB2uDUnsTS3edVjZEwTrQDFDNqoR2xLqARQPabGaXsHSTenTRcqm2EnB9MpuC4vSk3LqSgNmGGZtuq7`
|
||||
|
||||
which is a native segwit `ExtPubKey` for the regtest network!
|
||||
|
||||
You can always change the `network` or `purpose` to support different things. You do _not_ need to initialize the key manager
|
||||
again after initializing it once. You can use the same `mnemonic` for different networks, which you control `KeyManagerParams`.
|
||||
|
||||
```scala
|
||||
//let's create a nested segwit key manager for mainnet
|
||||
val mainnetKmParams = KeyManagerParams(seedPath, HDPurposes.SegWit, MainNet)
|
||||
// mainnetKmParams: KeyManagerParams = KeyManagerParams(/var/folders/fg/scntn26d4h55x96zc456l0r40000gn/T/key-manager-example11086394032840961872/encrypted-bitcoin-s-seed.json,m/84',MainNet)
|
||||
|
||||
//we do not need to all `initializeWithMnemonic()` again as we have saved the seed to dis
|
||||
val mainnetKeyManager = BIP39KeyManager.fromMnemonic(mnemonic, mainnetKmParams, None, Instant.now, false)
|
||||
// mainnetKeyManager: BIP39KeyManager = org.bitcoins.keymanager.bip39.BIP39KeyManager@6cd1f7fd
|
||||
|
||||
val mainnetXpub = mainnetKeyManager.getRootXPub
|
||||
// mainnetXpub: ExtPublicKey = zpub6jftahH18ngZwWUaWrhoznVKqStpznacrHnjAuRJLWM92CpbvG7HMMkaQ3uhdtqSnPRPhxBbz77djvWMHMgWnKCzTWugXtkFsfcjS2hPqeX
|
||||
|
||||
println(mainnetXpub)
|
||||
// zpub6jftahH18ngZwWUaWrhoznVKqStpznacrHnjAuRJLWM92CpbvG7HMMkaQ3uhdtqSnPRPhxBbz77djvWMHMgWnKCzTWugXtkFsfcjS2hPqeX
|
||||
```
|
||||
|
||||
Which gives us something that looks like this
|
||||
|
||||
`zpub6jftahH18ngZw98KGjRo5XcxeKTQ2eztsvskb1dC9XF5TLimQquTs6Ry7nBBA425D9joXmfgJJCexmJ1u2SELJZJfRi95gcnXadLpZzYb5c`
|
||||
|
||||
which is a p2sh wrapped segwit `ExtPubKey` for the bitcoin main network!
|
||||
|
||||
#### Creating a key manager from existing mnemonic
|
||||
|
||||
To create a `KeyManager` from existing mnemonic you need to specify the `seedPath` and then construct the `KeyManagerParams` that you would like.
|
||||
|
||||
Finally you call `KeyManager.fromParams()` that reads the mnemonic from disk and create's the key manager
|
83
website/versioned_docs/version-1.9.3/node/node-api.md
Normal file
83
website/versioned_docs/version-1.9.3/node/node-api.md
Normal file
@ -0,0 +1,83 @@
|
||||
---
|
||||
id: version-1.9.3-node-api title: Node API
|
||||
title: node-api title: Node API
|
||||
original_id: node-api title: Node API
|
||||
---
|
||||
|
||||
|
||||
### NodeAPI
|
||||
|
||||
The NodeApi is how the wallet project retrieves relevant node data like blocks. This allows the wallet for example to
|
||||
retrieve blocks for finding its relevant transactions.
|
||||
|
||||
Since this is an API it can be hooked up to the `node` module of bitcoin-s but it can also be linked to any other
|
||||
implementation of your choosing. This allows you to use the bitcoin-s wallet in any schema that you want.
|
||||
|
||||
The functions that the NodeApi supports are:
|
||||
|
||||
```scala
|
||||
trait NodeApi {
|
||||
|
||||
/** Request the underlying node to download the given blocks from its peers and feed the blocks to [[org.bitcoins.node.NodeCallbacks]] */
|
||||
def downloadBlocks(blockHashes: Vector[DoubleSha256Digest]): Future[Unit]
|
||||
}
|
||||
```
|
||||
|
||||
## Downloading blocks with bitcoind
|
||||
|
||||
As an example, we will show you how to use the `NodeApi` and bitcoind to download blocks for a wallet.
|
||||
|
||||
```scala
|
||||
implicit val system: ActorSystem = ActorSystem(s"node-api-example")
|
||||
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
||||
implicit val walletConf: WalletAppConfig =
|
||||
BitcoinSTestAppConfig.getNeutrinoTestConfig().walletConf
|
||||
|
||||
// let's use a helper method to get a v19 bitcoind
|
||||
// and a ChainApi
|
||||
val instance = BitcoindInstanceLocal.fromConfigFile(BitcoindConfig.DEFAULT_CONF_FILE)
|
||||
val bitcoind = BitcoindV19RpcClient(instance)
|
||||
val chainApi = MockChainQueryApi.mock
|
||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
||||
|
||||
// This function can be used to create a callback for when our node api calls downloadBlocks,
|
||||
// more specifically it will call the function every time we receive a block, the returned
|
||||
// NodeCallbacks will contain the necessary items to initialize the callbacks
|
||||
def createCallback(processBlock: Block => Future[Unit]): NodeCallbacks = {
|
||||
lazy val onBlock: OnBlockReceived = { block =>
|
||||
processBlock(block)
|
||||
}
|
||||
NodeCallbacks(onBlockReceived = Vector(onBlock))
|
||||
}
|
||||
|
||||
// Here is a super simple example of a callback, this could be replaced with anything, from
|
||||
// relaying the block on the network, finding relevant wallet transactions, verifying the block,
|
||||
// or writing it to disk
|
||||
val exampleProcessBlock = (block: Block) =>
|
||||
Future.successful(println(s"Received block: ${block.blockHeader.hashBE}"))
|
||||
val exampleCallback = createCallback(exampleProcessBlock)
|
||||
|
||||
// Here is where we are defining our actual node api, Ideally this could be it's own class
|
||||
// but for the examples sake we will keep it small.
|
||||
val nodeApi = new NodeApi {
|
||||
|
||||
override def broadcastTransactions(transactions: Vector[Transaction]): Future[Unit] = {
|
||||
FutureUtil.sequentially(transactions)(bitcoind.sendRawTransaction(_)).map(_ => ())
|
||||
}
|
||||
|
||||
override def downloadBlocks(
|
||||
blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = {
|
||||
val blockFs = blockHashes.map(hash => bitcoind.getBlockRaw(hash))
|
||||
Future.sequence(blockFs).map(_ => ())
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we can initialize our wallet with our own node api
|
||||
val wallet =
|
||||
Wallet(nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
|
||||
// Then to trigger the event we can run
|
||||
val exampleBlock = DoubleSha256Digest(
|
||||
"000000000010dc23dc0d5acad64667a7a2b3010b6e02da4868bf392c90b6431d")
|
||||
wallet.nodeApi.downloadBlocks(Vector(exampleBlock))
|
||||
```
|
145
website/versioned_docs/version-1.9.3/node/node.md
Normal file
145
website/versioned_docs/version-1.9.3/node/node.md
Normal file
@ -0,0 +1,145 @@
|
||||
---
|
||||
id: version-1.9.3-node
|
||||
title: Light Client
|
||||
original_id: node
|
||||
---
|
||||
|
||||
Bitcoin-s has node module that allows you to connect to the p2p network.
|
||||
|
||||
### Neutrino Node
|
||||
|
||||
Bitcoin-s has experimental support for neutrino which is a new lite client proposal on the bitcoin p2p network. You can
|
||||
read more about how neutrino works [here](https://suredbits.com/neutrino-what-is-it-and-why-we-need-it/). At this time,
|
||||
bitcoin-s only supports connecting to one trusted peer.
|
||||
|
||||
#### Limitations
|
||||
|
||||
Currently, the node does not have an active mempool.
|
||||
It is only aware of transactions it broadcasts and ones confirmed in blocks.
|
||||
|
||||
#### Callbacks
|
||||
|
||||
Bitcoin-S support call backs for the following events that happen on the bitcoin p2p network:
|
||||
|
||||
1. onTxReceived
|
||||
2. onBlockReceived
|
||||
3. onMerkleBlockReceived
|
||||
4. onCompactFilterReceived
|
||||
|
||||
That means every time one of these events happens on the p2p network, we will call your callback
|
||||
so that you can be notified of the event. These callbacks will be run after the message has been
|
||||
recieved and will execute sequentially. If any of them fail an error log will be output and the remainder of the callbacks will continue.
|
||||
Let's make an easy one
|
||||
|
||||
#### Example
|
||||
|
||||
Here is an example of constructing a neutrino node and registering a callback so you can be notified of an event.
|
||||
|
||||
To run the example, we need a bitcoind binary that has neutrino support.
|
||||
Bitcoin Core only has p2p neutrino support as of version 0.21.0.
|
||||
You will need to use a version of Bitcoin Core at least as old as 0.21.0.
|
||||
For your node to be able to service these filters you will need set
|
||||
`blockfilterindex=1` and `peerblockfilters=1` in your `bitcoin.conf` file.
|
||||
|
||||
|
||||
```scala
|
||||
implicit val system = ActorSystem(s"node-example")
|
||||
implicit val ec = system.dispatcher
|
||||
|
||||
//we also require a bitcoind instance to connect to
|
||||
//so let's start one (make sure you ran 'sbt downloadBitcoind')
|
||||
val instance = BitcoindRpcTestUtil.instance(versionOpt = Some(BitcoindVersion.Experimental))
|
||||
val p2pPort = instance.p2pPort
|
||||
val bitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient(Some(instance), Vector.newBuilder)
|
||||
|
||||
//contains information on how to connect to bitcoin's p2p info
|
||||
val peerF = bitcoindF.flatMap(b => NodeUnitTest.createPeer(b))
|
||||
|
||||
// set a data directory
|
||||
val prefix = s"node-example-${System.currentTimeMillis()}"
|
||||
val datadir = Files.createTempDirectory(prefix)
|
||||
|
||||
val tmpDir = BitcoinSTestAppConfig.tmpDir()
|
||||
// set the current network to regtest
|
||||
val config = ConfigFactory.parseString {
|
||||
s"""
|
||||
| bitcoin-s {
|
||||
| network = regtest
|
||||
| node {
|
||||
| mode = neutrino # neutrino, spv
|
||||
|
|
||||
| peers = ["127.0.0.1:$p2pPort"] # a list of peer addresses in form "hostname:portnumber"
|
||||
| # (e.g. "neutrino.testnet3.suredbits.com:18333")
|
||||
| # Port number is optional, the default value is 8333 for mainnet,
|
||||
| # 18333 for testnet and 18444 for regtest.
|
||||
| }
|
||||
| }
|
||||
|""".stripMargin
|
||||
}
|
||||
|
||||
implicit val appConfig = BitcoinSAppConfig(datadir, Vector(config))
|
||||
implicit val chainConfig = appConfig.chainConf
|
||||
implicit val nodeConfig = appConfig.nodeConf
|
||||
|
||||
val initNodeF = nodeConfig.start()
|
||||
|
||||
//the node requires a chainHandler to store block information
|
||||
//use a helper method in our testkit to create the chain project
|
||||
val chainApiF = for {
|
||||
chainHandler <- ChainUnitTest.createChainHandler()
|
||||
} yield chainHandler
|
||||
|
||||
|
||||
//yay! All setup done, let's create a node and then start it!
|
||||
val nodeF = for {
|
||||
chainApi <- chainApiF
|
||||
peer <- peerF
|
||||
} yield {
|
||||
//you can set this to only sync compact filters after the timestamp
|
||||
val walletCreationTimeOpt = None
|
||||
val dataMessageHandler = DataMessageHandler(chainApi, walletCreationTimeOpt)
|
||||
NeutrinoNode(paramPeers = Vector(peer),
|
||||
dataMessageHandler = dataMessageHandler,
|
||||
nodeConfig = nodeConfig,
|
||||
chainConfig = chainConfig,
|
||||
actorSystem = system)
|
||||
}
|
||||
|
||||
//let's start it
|
||||
val startedNodeF = nodeF.flatMap(_.start())
|
||||
|
||||
//let's make a simple callback that print's the
|
||||
//blockhash everytime we receive a block on the network
|
||||
val blockReceivedFunc: OnBlockReceived = { block: Block =>
|
||||
Future.successful(
|
||||
println(s"Received blockhash=${block.blockHeader.hashBE}"))
|
||||
}
|
||||
|
||||
// Create callback
|
||||
val nodeCallbacks = NodeCallbacks.onBlockReceived(blockReceivedFunc)
|
||||
|
||||
// Add call to our node's config
|
||||
nodeConfig.addCallbacks(nodeCallbacks)
|
||||
|
||||
//let's test it out by generating a block with bitcoind!
|
||||
|
||||
val genBlockF = for {
|
||||
bitcoind <- bitcoindF
|
||||
addr <- bitcoind.getNewAddress
|
||||
hashes <- bitcoind.generateToAddress(1,addr)
|
||||
} yield hashes
|
||||
|
||||
//you should see our callback print a block hash
|
||||
//when running this code
|
||||
|
||||
//cleanup
|
||||
val cleanupF = for {
|
||||
_ <- genBlockF
|
||||
bitcoind <- bitcoindF
|
||||
node <- startedNodeF
|
||||
x = NeutrinoNodeConnectedWithBitcoind(node.asInstanceOf[NeutrinoNode],bitcoind)
|
||||
_ <- NodeUnitTest.destroyNodeConnectedWithBitcoind(x)
|
||||
} yield ()
|
||||
|
||||
Await.result(cleanupF, 60.seconds)
|
||||
```
|
135
website/versioned_docs/version-1.9.3/rpc/bitcoind.md
Normal file
135
website/versioned_docs/version-1.9.3/rpc/bitcoind.md
Normal file
@ -0,0 +1,135 @@
|
||||
---
|
||||
id: version-1.9.3-rpc-bitcoind
|
||||
title: bitcoind/Bitcoin Core
|
||||
original_id: rpc-bitcoind
|
||||
---
|
||||
|
||||
## Downloading bitcoind
|
||||
|
||||
The Bitcoin Core RPC client in Bitcoin-S currently supports the Bitcoin Core
|
||||
- 0.16
|
||||
- 0.17
|
||||
- 0.18
|
||||
- 0.19
|
||||
- 0.20
|
||||
- 0.21
|
||||
- 22
|
||||
- 23
|
||||
|
||||
version lines. It can be set up to work with both local and remote Bitcoin Core servers.
|
||||
|
||||
You can fetch them using bitcoin-s by running the following sbt command. If you already have bitcoind installed on your machine, you can skip this step.
|
||||
|
||||
|
||||
```bash
|
||||
sbt downloadBitcoind
|
||||
```
|
||||
|
||||
The binaries will be stored in `~/.bitcoin-s/binaries/bitcoind/`
|
||||
|
||||
|
||||
## Connecting to a local `bitcoind` instance
|
||||
|
||||
### Getting started quickly, with default options:
|
||||
|
||||
```scala
|
||||
implicit val ec: ExecutionContext = ExecutionContext.global
|
||||
implicit val system = ActorSystem("System")
|
||||
// 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
|
||||
```
|
||||
|
||||
## Multi-wallet `bitcoind` instances
|
||||
|
||||
When using the `bitcoind` with multiple wallets you will need to specify the wallet's name.
|
||||
To do so the wallet rpc functions have an optional `walletName` parameter.
|
||||
|
||||
```scala
|
||||
implicit val ec: ExecutionContext = ExecutionContext.global
|
||||
implicit val system = ActorSystem("System")
|
||||
val client = BitcoindRpcClient.fromDatadir(binary=new File("/path/to/bitcoind"), datadir=new File("/path/to/bitcoind-datadir"))
|
||||
|
||||
for {
|
||||
_ <- client.start()
|
||||
_ <- client.walletPassphrase("mypassword", 10000, Some("walletName"))
|
||||
balance <- client.getBalance("walletName")
|
||||
} 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
|
||||
implicit val ec: ExecutionContext = ExecutionContext.global
|
||||
implicit val system = ActorSystem("System")
|
||||
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 = {
|
||||
BitcoindInstanceLocal(
|
||||
network = MainNet,
|
||||
uri = new URI(s"http://localhost:${MainNet.port}"),
|
||||
rpcUri = new URI(s"http://localhost:${MainNet.rpcPort}"),
|
||||
authCredentials = authCredentials
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
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 = BitcoinAddress("bc1qm8kec4xvucdgtzppzvvr2n6wp4m4w0k8akhf98")
|
||||
|
||||
val txid: Future[DoubleSha256DigestBE] =
|
||||
cli.sendToAddress(address, 3.bitcoins).recoverWith {
|
||||
case BitcoindWalletException.UnlockNeeded(_) =>
|
||||
cli.walletPassphrase("my_passphrase", 60).flatMap { _ =>
|
||||
cli.sendToAddress(address, 3.bitcoins)
|
||||
}
|
||||
}
|
||||
```
|
92
website/versioned_docs/version-1.9.3/secp256k1/secp256k1.md
Normal file
92
website/versioned_docs/version-1.9.3/secp256k1/secp256k1.md
Normal file
@ -0,0 +1,92 @@
|
||||
---
|
||||
id: version-1.9.3-secp256k1
|
||||
title: Secp256k1
|
||||
original_id: secp256k1
|
||||
---
|
||||
|
||||
[Libsecp256k1](https://github.com/bitcoin-core/secp256k1) is used to preform cryptographic operations on the secp256k1 curve.
|
||||
This is the curve that bitcoin uses. There is a _signficant_ speedup when using this library compared to java crypto libraries
|
||||
like bouncy castle.
|
||||
|
||||
In bitcoin-s, we support native binaries for libsecp256k1
|
||||
|
||||
1. [linux 32 bit](../../secp256k1jni/natives/linux_32)
|
||||
2. [linux 64 bit](../../secp256k1jni/natives/linux_64)
|
||||
3. [mac osx 64 bit](../../secp256k1jni/natives/osx_64)
|
||||
4. [windows 64 bit](../../secp256k1jni/natives/windows_64)
|
||||
|
||||
Bitcoin-s uses a zero dependency library called [`native-lib-loader`](https://github.com/scijava/native-lib-loader).
|
||||
That does the appropriate loading of the library onto your classpath to be accessed.
|
||||
|
||||
### Using libsecp256k1
|
||||
|
||||
To tell if you have access to libsecp256k1 you can do the following
|
||||
|
||||
|
||||
```scala
|
||||
val isEnabled = org.bitcoin.Secp256k1Context.isEnabled()
|
||||
|
||||
println(s"Secp256k1Context.isEnabled=${isEnabled}")
|
||||
```
|
||||
|
||||
If libsecp256k1 is enabled, you can use [NativeSecp256k1](/api/org/bitcoin/NativeSecp256k1)
|
||||
with static method defined in the class.
|
||||
|
||||
|
||||
```scala
|
||||
val privKey = ECPrivateKey.freshPrivateKey
|
||||
val pubKey = privKey.publicKey
|
||||
val dataToSign = DoubleSha256Digest.empty
|
||||
|
||||
val signature = NativeSecp256k1.sign(dataToSign.bytes.toArray, privKey.bytes.toArray)
|
||||
|
||||
val verify = NativeSecp256k1.verify(dataToSign.bytes.toArray, signature, pubKey.bytes.toArray)
|
||||
|
||||
println(s"Verified with NativeSecp256k1 signature=${verify}")
|
||||
|
||||
//you can also just directly sign with the ECKey interface:
|
||||
val signature2 = privKey.sign(dataToSign)
|
||||
|
||||
val verified2 = pubKey.verify(dataToSign, signature2)
|
||||
|
||||
println(s"Verified with NativeSecp256k1 again=${verified2}")
|
||||
```
|
||||
|
||||
### When libsecp256k1 isn't available, or you want to turn it off
|
||||
|
||||
There are two reasons you wouldn't want to use libsecp256k1
|
||||
|
||||
1. You don't trust the pre-compiled binaries we are using
|
||||
2. Your OS/arch is not supported
|
||||
|
||||
There are two ways you can circumvent libsecp256k1
|
||||
|
||||
1. Set `DISABLE_SECP256K1=true` in your environment variables. This will force `CryptoContext.default` to return false which will make Bitcoin-S act like `Secp256k1Context.isEnabled()` has returned false.
|
||||
2. Call Bouncy castle methods in `ECKey`.
|
||||
|
||||
Here is an example of calling bouncy castle methods in `ECKey`
|
||||
|
||||
```scala
|
||||
val privKey = ECPrivateKey.freshPrivateKey
|
||||
// privKey: ECPrivateKey = Masked(ECPrivateKey)
|
||||
// calls bouncy castle indirectly via CryptoContext
|
||||
val publicKey = privKey.publicKey
|
||||
// publicKey: ECPublicKey = ECPublicKey(038fd1c32eb4a76fa994bc1cbb3d0e343cbc041d3b9013f30fff7ed727cebbb8e7)
|
||||
val dataToSign = DoubleSha256Digest.empty
|
||||
// dataToSign: DoubleSha256Digest = DoubleSha256Digest(0000000000000000000000000000000000000000000000000000000000000000)
|
||||
|
||||
// calls bouncy castle indirectly via CryptoContext
|
||||
val signature = privKey.sign(dataToSign.bytes)
|
||||
// signature: ECDigitalSignature = ECDigitalSignature(3044022011fae228ce8f0b4c512127fac0a2fdd7c01469ecb3129dff403edb1c0e62c772022079a341d7d9019aa3677510f30412fb209fca9128ae8845d48f38ffb5ffd3371d)
|
||||
|
||||
// calls bouncy castle indirectly via CryptoContext
|
||||
val verified = publicKey.verify(dataToSign.bytes, signature)
|
||||
// verified: Boolean = true
|
||||
|
||||
println(s"Verified with bouncy castle=${verified}")
|
||||
// Verified with bouncy castle=true
|
||||
```
|
||||
|
||||
### Building libsecp256k1
|
||||
|
||||
[See instructions here](add-to-jni.md#adding-to-bitcoin-s)
|
100
website/versioned_docs/version-1.9.3/ui-setup.md
Normal file
100
website/versioned_docs/version-1.9.3/ui-setup.md
Normal file
@ -0,0 +1,100 @@
|
||||
---
|
||||
id: version-1.9.3-ui-setup
|
||||
title: Installing the DLC Wallet UI
|
||||
original_id: ui-setup
|
||||
---
|
||||
|
||||
# Easy setup
|
||||
|
||||
We have desktop installers for mac, windows and linux. This requires you to be signed into a github account to download.
|
||||
|
||||
The link to the installers is [suredbits.com/bitcoin-s](https://suredbits.com/bitcoin-s)
|
||||
|
||||
You can download the appropriate installer for your machine
|
||||
|
||||
![Example banner](/img/installers.png)
|
||||
|
||||
The downside of this setup is it uses an old UI that we are working to get rid of.
|
||||
This UI is missing new features that drastically improve UX.
|
||||
|
||||
Once you download and install the wallet, deposit 100,000 sats and [find an event you want to bet on](https://oracle.suredbits.com/)!
|
||||
|
||||
The wallet will take roughly 20-30 minutes to synchronize with the bitcoin network. If you deposit funds before
|
||||
the synchronization finishes, the funds may not show up right away. This is expected. They will show up when the sync is done.
|
||||
|
||||
For more information about building a DLC with this UI, checkout this youtube tutorial I recorded:
|
||||
|
||||
https://youtu.be/oR0I0aHxNMM?t=219
|
||||
|
||||
# Advanced setup
|
||||
|
||||
This requires command line skills.
|
||||
|
||||
|
||||
First, clone the bitcoin-s-ts repository by running
|
||||
|
||||
```
|
||||
git clone https://github.com/bitcoin-s/bitcoin-s-ts.git
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
(If on a M1 Mac, go to the Generate M1 Mac server section below before continuing)
|
||||
|
||||
You must have npm, if you don't then run `brew install node`
|
||||
Next, navigate to the `bitcoin-s-ts` directory and in it run
|
||||
|
||||
```
|
||||
./build-wallet-electron.sh
|
||||
```
|
||||
This will download the `bitcoin-s-server.zip` file and set up Suredbits Wallets dependencies.
|
||||
|
||||
Now, if you wish to run the Suredbits Wallet application in dev mode then run
|
||||
|
||||
```
|
||||
cd wallet-electron-ts && npm run start"
|
||||
|
||||
```
|
||||
###Instill desktop application (Optional)
|
||||
|
||||
If you would like to use the desktop application then, search for the Suredbits Wallet application in finder, windows explorer, ect ... double click on the icon and complete the installation
|
||||
|
||||
Once you open the Suredbits Wallet application you should see the following screen
|
||||
|
||||
![Alt text](/img/Screenshot%20from%202022-03-11%2011-20-17.png)
|
||||
|
||||
The password is `none`, enter that and you should see the wallet!
|
||||
|
||||
![Alt text](/img/Screenshot%20from%202022-03-11%2011-21-47.png)
|
||||
|
||||
Deposit 100,000 sats and [find an event you want to bet on](https://oracle.suredbits.com/)!
|
||||
|
||||
The wallet will take roughly 20-30 minutes to synchronize with the bitcoin network. If you deposit funds before
|
||||
the synchronization finishes, the funds may not show up right away. This is expected. They will show up when the sync is done.
|
||||
|
||||
After the synchronization is done, you should be good to do a DLC! :tada:
|
||||
|
||||
### Generate M1 Mac server
|
||||
|
||||
If you are on an M1 Mac, we currently do not offer a `bitcoin-s-sever.zip` file on the website, so the setup requires a few more steps
|
||||
|
||||
First, you will need a bitcoin-s node on your machine. If you don't already have one, follow the instructions here https://bitcoin-s.org/docs/getting-setup
|
||||
|
||||
Next, to generate your the `bitcoin-s-server.zip` file, go into your bitcoin-s directory and run
|
||||
|
||||
```
|
||||
sbt appServer/universal:packageBin
|
||||
```
|
||||
Then navigate to the bitcoin-s sub-directory containing the .zip file by running
|
||||
|
||||
```
|
||||
cd app/server/target/universal
|
||||
```
|
||||
|
||||
Now we will copy this file over to `bitcoin-s-ts/wallet-electron-ts` by running
|
||||
|
||||
```
|
||||
cp <the zip file> ~/bitcoin-s-ts/walllet-electron-ts
|
||||
```
|
||||
You now can return to the Setup section
|
||||
|
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: Wallet Callbacks
|
||||
id: version-1.9.3-wallet-callbacks
|
||||
original_id: wallet-callbacks
|
||||
---
|
||||
|
||||
#### Wallet Callbacks
|
||||
|
||||
Bitcoin-S support call backs for the following events that happen in the wallet:
|
||||
|
||||
1. onTransactionProcessed
|
||||
2. onTransactionBroadcast
|
||||
3. onReservedUtxos
|
||||
4. onNewAddressGenerated
|
||||
5. onBlockProcessed
|
||||
6. onRescanComplete
|
||||
|
||||
That means every time one of these events happens, we will call your callback
|
||||
so that you can be notified of the event. These callbacks will be run after the message has been
|
||||
recieved and will execute synchronously. If any of them fail an error log will be output, and the remainder of the callbacks will continue.
|
||||
Let's make an easy one:
|
||||
|
||||
#### DLC Wallet callbacks
|
||||
Bitcoin-s support callbacks for the following events in the DLC wallet:
|
||||
1. onStateChange
|
||||
|
||||
That means everytime a DLC's state changes, your callback will be executed so
|
||||
that you are notified of the event. For instance, if your DLC transitions from
|
||||
`Offered` -> `Accepted`, your callback will be executed.
|
||||
|
||||
#### Example
|
||||
|
||||
Here is an example of constructing a wallet and registering a callback, so you can be notified of an event.
|
||||
|
||||
|
||||
```scala
|
||||
implicit val system: ActorSystem = ActorSystem("example")
|
||||
implicit val ec: ExecutionContextExecutor = system.dispatcher
|
||||
implicit val walletConf: WalletAppConfig =
|
||||
BitcoinSTestAppConfig.getNeutrinoTestConfig().walletConf
|
||||
|
||||
// let's use a helper method to get a v19 bitcoind
|
||||
// and a ChainApi
|
||||
|
||||
val bitcoind = BitcoindV19RpcClient(BitcoindInstanceLocal.fromConfFile())
|
||||
val aesPasswordOpt = Some(AesPassword.fromString("password"))
|
||||
|
||||
// Here is a super simple example of a callback, this could be replaced with anything, from
|
||||
// relaying the transaction on the network, finding relevant wallet outputs, verifying the transaction,
|
||||
// or writing it to disk
|
||||
val exampleProcessTx: OnTransactionProcessed = (tx: Transaction) =>
|
||||
Future.successful(println(s"Processed Tx: ${tx.txIdBE}"))
|
||||
|
||||
// Create our WalletCallbacks that
|
||||
val exampleCallbacks = WalletCallbacks(
|
||||
onTransactionProcessed = Vector(exampleProcessTx))
|
||||
|
||||
// Now we can create a wallet
|
||||
val wallet =
|
||||
Wallet(
|
||||
nodeApi = bitcoind,
|
||||
chainQueryApi = bitcoind,
|
||||
feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
|
||||
// Finally, we can add the callbacks to our wallet config
|
||||
walletConf.addCallbacks(exampleCallbacks)
|
||||
|
||||
// Then to trigger the event we can run
|
||||
val exampleTx = Transaction(
|
||||
"0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000")
|
||||
wallet.processTransaction(exampleTx, None)
|
||||
```
|
108
website/versioned_docs/version-1.9.3/wallet/wallet-sync.md
Normal file
108
website/versioned_docs/version-1.9.3/wallet/wallet-sync.md
Normal file
@ -0,0 +1,108 @@
|
||||
---
|
||||
title: Wallet Sync
|
||||
id: version-1.9.3-wallet-sync
|
||||
original_id: wallet-sync
|
||||
---
|
||||
|
||||
## High level wallet state
|
||||
|
||||
Our wallet infrastructure has a specific table called `state_descriptors`.
|
||||
This tracks chain state for our wallet.
|
||||
Here is an example of the contents of this table
|
||||
|
||||
>sqlite> select * from state_descriptors;
|
||||
SyncHeight|0000000000000000000134aa9e949ea1d053042b8dfa59bdc73b0322a88f009e 665741
|
||||
|
||||
If you look carefully in the second column, you will see a string encoding indicating
|
||||
what the wallet state is. In this case, the last block hash seen by the wallet is
|
||||
|
||||
>0000000000000000000134aa9e949ea1d053042b8dfa59bdc73b0322a88f009e
|
||||
|
||||
and height
|
||||
|
||||
>665741
|
||||
|
||||
If you have access to a wallet, you can call
|
||||
|
||||
[`wallet.getSyncDescriptorOpt`](https://github.com/bitcoin-s/bitcoin-s/blob/36b5fc142715f8ab3ad053465d53dc29ab319790/wallet/src/main/scala/org/bitcoins/wallet/Wallet.scala#L160) to get access to this information
|
||||
|
||||
#### Wallet state from the cli
|
||||
|
||||
Alternatively, you can retrieve this information with `bitcoin-s-cli`
|
||||
|
||||
```
|
||||
./bitcoin-s-cli walletinfo
|
||||
{
|
||||
"wallet": {
|
||||
"keymanager": {
|
||||
"rootXpub": "..."
|
||||
},
|
||||
"xpub": "...",
|
||||
"hdPath": "...",
|
||||
"height": 1906239,
|
||||
"blockHash": "00000000dcf1066b8cd764a6104a9b5e95a55cd31adf9107974b2581ac90fdb9"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Syncing a wallet
|
||||
|
||||
Bitcoin-s provides a utility object called [`WalletSync`](https://github.com/bitcoin-s/bitcoin-s/blob/f3e81d027dfdda79e26642d5c29d381874ee72da/wallet/src/main/scala/org/bitcoins/wallet/sync/WalletSync.scala#L10)
|
||||
that provides useful utilities for syncing a bitcoin-s wallet.
|
||||
|
||||
### Syncing wallet for with access to full blocks
|
||||
|
||||
Inside of `WalletSync` we have a method called [`WalletSync.syncFullBlocks`](https://github.com/bitcoin-s/bitcoin-s/blob/f3e81d027dfdda79e26642d5c29d381874ee72da/wallet/src/main/scala/org/bitcoins/wallet/sync/WalletSync.scala#L18)
|
||||
This method takes 4 parameters
|
||||
|
||||
- a [Wallet](https://github.com/bitcoin-s/bitcoin-s/blob/36b5fc142715f8ab3ad053465d53dc29ab319790/wallet/src/main/scala/org/bitcoins/wallet/Wallet.scala#L46) to sync
|
||||
- `getBlockHeaderFunc` is a function to retrieve a block header based on a blockHash
|
||||
- `getBestBlockHashFunc` is a function to retrieve the best block hash for our blockchain
|
||||
- `getBlockFunc` is a function to retrieve a full [`Block`](https://github.com/bitcoin-s/bitcoin-s/blob/8a148357d560a40bf21e7c0e3f4074cd276534fe/core/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala#L18) that corresponds to a block hash
|
||||
|
||||
Given these for things, we can use [`WalletSync.syncFullBlocks`](https://github.com/bitcoin-s/bitcoin-s/blob/f3e81d027dfdda79e26642d5c29d381874ee72da/wallet/src/main/scala/org/bitcoins/wallet/sync/WalletSync.scala#L18) to sync our entire wallet.
|
||||
|
||||
Here is a code example
|
||||
|
||||
|
||||
```scala
|
||||
implicit val system: ActorSystem = ActorSystem(s"wallet-sync-example")
|
||||
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(binary=new File("/path/to/bitcoind"), datadir=new File("/path/to/bitcoind-datadir"))
|
||||
|
||||
//yay! Now we have a started bitcoind.
|
||||
//We will use this as our datasource for syncing our wallet
|
||||
val bitcoindRpcClientF: Future[BitcoindRpcClient] = client.start()
|
||||
|
||||
|
||||
//wait for bitcoind to get started
|
||||
val bitcoind = Await.result(bitcoindRpcClientF, 10.seconds)
|
||||
|
||||
val getBestBlockHashFunc = () => bitcoind.getBestBlockHash
|
||||
|
||||
val getBlockHeaderFunc = { hash: DoubleSha256DigestBE => bitcoind.getBlockHeaderRaw(hash) }
|
||||
|
||||
val getBlockFunc = {hash: DoubleSha256DigestBE => bitcoind.getBlockRaw(hash) }
|
||||
|
||||
val genesisHashBEF = bitcoind.getBlockHash(0)
|
||||
|
||||
//yay! We are now all setup. Using our 3 functions above and a wallet, we can now sync
|
||||
//a fresh wallet
|
||||
implicit val walletAppConfig = WalletAppConfig.fromDefaultDatadir()
|
||||
|
||||
val feeRateProvider: FeeRateApi = MempoolSpaceProvider.fromBlockTarget(6, proxyParams = None)
|
||||
val wallet = Wallet(bitcoind, bitcoind, feeRateProvider)
|
||||
|
||||
//yay! we have a synced wallet
|
||||
val syncedWalletF = genesisHashBEF.flatMap { genesisHash =>
|
||||
WalletSync.syncFullBlocks(wallet,
|
||||
getBlockHeaderFunc,
|
||||
getBestBlockHashFunc,
|
||||
getBlockFunc,
|
||||
genesisHash)
|
||||
}
|
||||
```
|
140
website/versioned_docs/version-1.9.3/wallet/wallet.md
Normal file
140
website/versioned_docs/version-1.9.3/wallet/wallet.md
Normal file
@ -0,0 +1,140 @@
|
||||
---
|
||||
title: Wallet
|
||||
id: version-1.9.3-wallet
|
||||
original_id: wallet
|
||||
---
|
||||
|
||||
## Bitcoin-s 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.
|
||||
|
||||
### How is the bitcoin-s wallet implemented
|
||||
|
||||
The bitcoin-s wallet is a scalable way for individuals up to large bitcoin exchanges to safely and securely store their bitcoin in a scalable way.
|
||||
|
||||
All key interactions are delegated to the [key-manager](../key-manager/key-manager.md) which is a minimal dependency library to store and use key material.
|
||||
|
||||
By default, we store the encrypted root key in `$HOME/.bitcoin-s/seeds/encrypted-bitcoin-s-seed.json`. This is the seed that is used for each of the wallets on each bitcoin network.
|
||||
Multiple wallet seeds can be saved using the `bitcoin-s.wallet.walletName` config option.
|
||||
You can read more in the [key manager docs](../key-manager/server-key-manager.md).
|
||||
|
||||
The wallet itself is used to manage the utxo life cycle, create transactions, and update wallet balances to show how much money you have the on a bitcoin network.
|
||||
|
||||
We use [slick](https://scala-slick.org/doc/3.3.1/) as middleware to support different database types. Depending on your use case, you can use something as simple as sqlite, or something much more scalable like postgres.
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
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
|
||||
implicit val system = ActorSystem("System")
|
||||
|
||||
val config = ConfigFactory.parseString {
|
||||
"""
|
||||
| bitcoin-s {
|
||||
| network = regtest
|
||||
| }
|
||||
""".stripMargin
|
||||
}
|
||||
|
||||
|
||||
val datadir = Files.createTempDirectory("bitcoin-s-wallet")
|
||||
|
||||
|
||||
implicit val walletConfig = WalletAppConfig(datadir, Vector(config))
|
||||
|
||||
// we also need to store chain state for syncing purposes
|
||||
implicit val chainConfig = ChainAppConfig(datadir, Vector(config))
|
||||
|
||||
// when this future completes, we have
|
||||
// created the necessary directories and
|
||||
// databases for managing both chain state
|
||||
// and wallet state
|
||||
val configF: Future[Unit] = for {
|
||||
_ <- walletConfig.start()
|
||||
_ <- chainConfig.start()
|
||||
} yield ()
|
||||
|
||||
val bitcoindInstance = BitcoindInstanceLocal.fromDatadir()
|
||||
|
||||
val bitcoind = BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
// when this future completes, we have
|
||||
// synced our chain handler to our bitcoind
|
||||
// peer
|
||||
val syncF: Future[ChainApi] = configF.flatMap { _ =>
|
||||
val getBestBlockHashFunc = { () =>
|
||||
bitcoind.getBestBlockHash
|
||||
}
|
||||
|
||||
|
||||
val getBlockHeaderFunc = { hash: DoubleSha256DigestBE =>
|
||||
bitcoind.getBlockHeader(hash).map(_.blockHeader)
|
||||
}
|
||||
|
||||
val blockHeaderDAO = BlockHeaderDAO()
|
||||
val compactFilterHeaderDAO = CompactFilterHeaderDAO()
|
||||
val compactFilterDAO = CompactFilterDAO()
|
||||
val stateDAO = ChainStateDescriptorDAO()
|
||||
val chainHandler = ChainHandler(
|
||||
blockHeaderDAO,
|
||||
compactFilterHeaderDAO,
|
||||
compactFilterDAO,
|
||||
stateDAO,
|
||||
blockFilterCheckpoints = Map.empty)
|
||||
|
||||
ChainSync.sync(chainHandler, getBlockHeaderFunc, getBestBlockHashFunc)
|
||||
}
|
||||
|
||||
// once this future completes, we have a initialized
|
||||
// wallet
|
||||
val wallet = Wallet(new NodeApi {
|
||||
override def broadcastTransactions(txs: Vector[Transaction]): Future[Unit] = Future.successful(())
|
||||
override def downloadBlocks(blockHashes: Vector[DoubleSha256Digest]): Future[Unit] = Future.successful(())
|
||||
}, chainApi, ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
val walletF: Future[WalletApi] = configF.flatMap { _ =>
|
||||
Wallet.initialize(wallet, None)
|
||||
}
|
||||
|
||||
// when this future completes, ww have sent a transaction
|
||||
// from bitcoind to the Bitcoin-S wallet
|
||||
val transactionF: Future[(Transaction, Option[DoubleSha256DigestBE])] = for {
|
||||
wallet <- walletF
|
||||
address <- wallet.getNewAddress()
|
||||
txid <- bitcoind.sendToAddress(address, 3.bitcoin)
|
||||
transaction <- bitcoind.getRawTransaction(txid)
|
||||
} yield (transaction.hex, transaction.blockhash)
|
||||
|
||||
// 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, blockhash) <- transactionF
|
||||
_ <- wallet.processTransaction(tx, blockhash)
|
||||
balance <- wallet.getBalance()
|
||||
} yield balance
|
||||
|
||||
balanceF.foreach { balance =>
|
||||
println(s"Bitcoin-S wallet balance: $balance")
|
||||
}
|
||||
```
|
@ -1,4 +1,5 @@
|
||||
[
|
||||
"1.9.3",
|
||||
"1.9.2",
|
||||
"1.9.1",
|
||||
"1.9.0",
|
||||
|
Loading…
Reference in New Issue
Block a user