Prepare for 1.9.6 (#4820)

This commit is contained in:
Chris Stewart 2022-10-09 11:12:43 -05:00 committed by GitHub
parent de43dadf52
commit e73b328833
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 1143 additions and 2 deletions

View file

@ -1,5 +1,5 @@
package org.bitcoins.node.constant
case object NodeConstants {
val userAgent = "/bitcoin-s:1.9.5/"
val userAgent = "/bitcoin-s:1.9.6/"
}

View file

@ -18,7 +18,7 @@ import scala.util.Properties
object CommonSettings {
val previousStableVersion: String = "1.9.5"
val previousStableVersion: String = "1.9.6"
private def isCI = {
Properties

View file

@ -0,0 +1,144 @@
# 1.9.6
This release is backwards compatible with the 1.9.x series of bitcoin-s
See the individual module sections for more information on lower level updates to the codebase.
Want to get started quickly? See our `docker-compose.yml` file. [See instructions here](https://github.com/bitcoin-s/bitcoin-s/#docker)
If you are a typescript developer, [you can access the backend via our typescript library](https://github.com/bitcoin-s/bitcoin-s-ts)
# Executive Summary
This release adds network notifications via the websocket when various tor interactions fail when negotiating a DLC.
This release delete all DLCs that are settled using the `Alpha` version of the DLC protocol.
This makes upgrading to the new v0 format of the dlc protocol easier for an implementation point of view.
This will not delete alpha DLCs that are still in progress, rather throw an exception on wallet startup.
This release also fixes a bug in the wallet where utxos would be stuck in an unconfirmed state.
## Running bitcoin-s
If you want to run the standalone server binary, after verifying gpg signatures, you
can `unzip bitcoin-s-server-1.9.6.zip` and then run it with `chmod +x ./bin/bitcoin-s-server && ./bin/bitcoin-s-server` to start the node. You will need to
configure the node properly first, you can find example
configurations [here](https://bitcoin-s.org/docs/config/configuration#example-configuration-file).
You can then unzip the `bitcoin-s-cli-1.9.6.zip` folder and start using the `bitcoin-s-cli` like this:
```bashrc
./bin/bitcoin-s-cli --help
Usage: bitcoin-s-cli [options] [<cmd>]
-n, --network <value> Select the active network.
--debug Print debugging information
--rpcport <value> The port to send our rpc request to on the server
-h, --help Display this help message and exit
```
For more information on what commands `bitcoin-s-cli` supports check the documentation, here is where to
start: https://bitcoin-s.org/docs/next/applications/server#server-endpoints
## Verifying signatures
This release is signed with [Chris's signing key](https://bitcoin-s.org/docs/next/security#disclosure) with
fingerprint `9234F4D6AF47C71B741A390F8976CA0AF71A7A2A`
To do the verification, first hash the executable using `sha256sum`. You should check that the result is listed in
the `SHA256SUMS.asc` file next to its file name. After doing that you can use `gpg --verify` to authenticate the
signature.
Example:
```
$ gpg -d SHA256SUMS.asc > SHA256SUMS.stripped
gpg: Signature made Mon 18 Apr 2022 02:19:54 PM CDT
gpg: using RSA key 9234F4D6AF47C71B741A390F8976CA0AF71A7A2A
gpg: Good signature from "Chris Stewart <stewart.chris1234@gmail.com>" [ultimate]
$ sha256sum -c SHA256SUMS.stripped
bitcoin-s_1.9.3-1_amd64.deb: OK
bitcoin-s-1.9.3.dmg: OK
bitcoin-s-bundle.msi: OK
bitcoin-s-cli-x86_64-apple-darwin: OK
bitcoin-s-cli-x86_64-pc-linux: OK
bitcoin-s-server-1.9.3.zip: OK
```
### Website
https://bitcoin-s.org/
### Releases
https://repo1.maven.org/maven2/org/bitcoin-s/
### Snapshot releases
https://oss.sonatype.org/content/repositories/snapshots/org/bitcoin-s/
# Modules
## app commons
## App server
## bitcoind rpc
## bundle
## Build
## chain
## Core
## Crypto
## db commons
## DLC node
de43dadf52b Network notifications (#4774)
## DLC Oracle
## DLC wallet
62081a43ecd 2022 10 05 Delete legacy `DLCSerializationVersion.Alpha` DLCs for a cleaner upgrade to v0 spec (#4817)
## gui
## fee rate
## keymanager
## Lnd rpc
## Lnurl
## node
718053668d8 2022 10 07 node test fixes (#4819)
## Oracle Explorer Client
## wallet
Fix bug where transactions would be stuck in a unconfirmed state
ddc672cc466 Fix unconfirmed -> confirmed state change (#4816)
## testkit-core
## testkit
## tor
## Website
## Dependencies
c075112db5b Upgrade sbt to 1.7.2 (#4818)

View file

@ -0,0 +1,59 @@
---
id: version-1.9.6-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(031491da7bde590f90fe3cd2e4664888ea5c52cd008f50306bb98161d83b09440a)
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 = tb1qxteuu6th8v8qh5hjv0vss4qgxj2dfzmwu6lsjc
println(segwitAddress.toString)
// tb1qxteuu6th8v8qh5hjv0vss4qgxj2dfzmwu6lsjc
```
## 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 = mkAN9bzB9WQtZraGyy8mkeWKg9HZiyNW9Y
println(legacyAddress.toString)
// mkAN9bzB9WQtZraGyy8mkeWKg9HZiyNW9Y
```

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,145 @@
---
id: version-1.9.6-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, 0x3d2e35e48b9eec87da46d5cc0b2e4cf8289395ebfbef07ef68c1d9b548a18e23)
val mnemonicCode = MnemonicCode.fromEntropy(entropy)
// mnemonicCode: MnemonicCode = Masked(MnemonicCodeImpl)
mnemonicCode.words // the phrase the user should write down
// res0: Vector[String] = Vector(diamond, immune, junior, blame, uphold, dumb, harbor, survey, slot, floor, nasty, utility, matrix, slab, quiz, law, among, kitten, corn, recall, fee, express, decade, giraffe) // 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 = zpub6jftahH18ngZy7kb18qC5tVwvb61zyxF8FYVpvHJ3TEqoPVUWa77RQvq8HNzGLkL7qBxrRddwGdvoDNHp3acjXpWahoQ2QCZpefBoyFNqcj
// 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 = zpub6qoYRU88iCy3grnYpf2WyQFzkih9eNxEsS1zrf8H9SRuFtfKW2ZWd2Q79BCCzxG4JxmKKgCa45JbC2oMBvg9V1ynonts7KNrvpAvcsYmEZD
// 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 = tb1q403gq542v33vywyem8ztfk7l0g63gflzlc5zwk
// tada! We just generated an address you can send money to,
// without having access to the private key!
firstAccountAddress.value
// res2: String = tb1q403gq542v33vywyem8ztfk7l0g63gflzlc5zwk
// 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.

View file

@ -0,0 +1,158 @@
---
id: version-1.9.6-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@2915f741[Running, parallelism = 8, size = 0, active = 0, running = 0, steals = 0, 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(03890e2a8ddb32cd2e0de2a4a5b2c5c35a5e017937696f2765537ed3cff43f8035)
// 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(77ce65d7c78515f00874841cdf5b2f8fc387b5ce)
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(77ce65d7c78515f00874841cdf5b2f8fc387b5ce))
// 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(8a78c70ce814946fb2983a285fc40bb6efe7b6c6)
// 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(8a78c70ce814946fb2983a285fc40bb6efe7b6c6)))
// 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(77ce65d7c78515f00874841cdf5b2f8fc387b5ce))),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(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2:0)
val input = TransactionInput(
outPoint,
EmptyScriptSignature,
sequenceNumber = UInt32.zero)
// input: TransactionInput = TransactionInputImpl(TransactionOutPoint(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2: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(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2:0),EmptyScriptSignature,UInt32Impl(0))),Vector(TransactionOutput(5000 sats,pkh(8a78c70ce814946fb2983a285fc40bb6efe7b6c6))),UInt32Impl(0))
// this contains the information needed to analyze our input during finalization
val inputInfo = P2PKHInputInfo(outPoint, amount, privKey.publicKey)
// inputInfo: P2PKHInputInfo = P2PKHInputInfo(TransactionOutPoint(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2:0),10000 sats,ECPublicKey(03890e2a8ddb32cd2e0de2a4a5b2c5c35a5e017937696f2765537ed3cff43f8035))
// 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(3517619d461a3deb79bd4cbf8adb0b6ffb9fb3c4)
// 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(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2:0),10000 sats,ECPublicKey(03890e2a8ddb32cd2e0de2a4a5b2c5c35a5e017937696f2765537ed3cff43f8035))),1 sats/byte,pkh(3517619d461a3deb79bd4cbf8adb0b6ffb9fb3c4))
// 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(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2:0),EmptyScriptSignature,UInt32Impl(0))),Vector(TransactionOutput(5000 sats,pkh(8a78c70ce814946fb2983a285fc40bb6efe7b6c6)), TransactionOutput(4775 sats,pkh(3517619d461a3deb79bd4cbf8adb0b6ffb9fb3c4))),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(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2:0),10000 sats,ECPublicKey(03890e2a8ddb32cd2e0de2a4a5b2c5c35a5e017937696f2765537ed3cff43f8035)),BaseTransaction(Int32Impl(1),Vector(),Vector(TransactionOutput(10000 sats,pkh(77ce65d7c78515f00874841cdf5b2f8fc387b5ce))),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(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2:0),10000 sats,ECPublicKey(03890e2a8ddb32cd2e0de2a4a5b2c5c35a5e017937696f2765537ed3cff43f8035)),BaseTransaction(Int32Impl(1),Vector(),Vector(TransactionOutput(10000 sats,pkh(77ce65d7c78515f00874841cdf5b2f8fc387b5ce))),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(df675236e164774db8794bf3a61a7dcca521b84244909437f3e33c94c0507cd2:0),P2PKHScriptSignature(ECPublicKeyBytes(ByteVector(33 bytes, 0x03890e2a8ddb32cd2e0de2a4a5b2c5c35a5e017937696f2765537ed3cff43f8035)), ECDigitalSignature(304402203773de96dce15c8e144fee0b579b58c79073230a64f5bac153441afc1aad80d00220482bae1b87351702ebd18689580b06e0ebc9ef19b3050630d9ced09c082470d101)),UInt32Impl(0))),Vector(TransactionOutput(5000 sats,pkh(8a78c70ce814946fb2983a285fc40bb6efe7b6c6)), TransactionOutput(4775 sats,pkh(3517619d461a3deb79bd4cbf8adb0b6ffb9fb3c4))),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 = 0200000001d27c50c0943ce3f33794904442b821a5cc7d1aa6f34b79b84d7764e1365267df000000006a47304402203773de96dce15c8e144fee0b579b58c79073230a64f5bac153441afc1aad80d00220482bae1b87351702ebd18689580b06e0ebc9ef19b3050630d9ced09c082470d1012103890e2a8ddb32cd2e0de2a4a5b2c5c35a5e017937696f2765537ed3cff43f8035000000000288130000000000001976a9148a78c70ce814946fb2983a285fc40bb6efe7b6c688aca7120000000000001976a9143517619d461a3deb79bd4cbf8adb0b6ffb9fb3c488ac00000000
```

View file

@ -0,0 +1,65 @@
---
id: version-1.9.6-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(3045022100c81a3e2a65ada4ea00cde9cf1618ebcda9f8b40ef4bf2a8f8b6b46a70dec008f022048a628634602e51301b2a0fc9ddb5504a642bf464bb806b51bf90de2e3dc665e)
val path = BIP32Path(Vector(BIP32Node(0,false)))
// path: BIP32Path = m/0
extPrivKey.sign(DoubleSha256Digest.empty.bytes,path)
// res1: ECDigitalSignature = ECDigitalSignature(304402205f7826826b5d4eb3373b5a62dc68320bcba5677d38b40005bf64e396778726e302200b949cbceaed378e6c53663f00591046e23489bca2835fa31544262c86bda4d1)
```
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

View file

@ -0,0 +1,127 @@
---
id: version-1.9.6-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.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-core" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-chain" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-dlc-oracle" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-eclair-rpc" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-fee-provider" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-key-manager" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-lnd-rpc" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-node" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-oracle-explorer-client" % "1.9.4"
libraryDependencies += "org.bitcoin-s" % "bitcoin-s-secp256k1jni" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit-core" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-testkit" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-wallet" % "1.9.4"
libraryDependencies += "org.bitcoin-s" %% "bitcoin-s-zmq" % "1.9.4"
```
### 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.3-68-80deeed1-20220926-1805-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)

View file

@ -0,0 +1,124 @@
---
id: version-1.9.6-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, 0xab0f0bec8940364a139d6987d560ad962918338a06121f9b39deebad3d313316)
val mnemonic = MnemonicCode.fromEntropy(entropy)
// mnemonic: MnemonicCode = Masked(MnemonicCodeImpl)
//you can print that mnemonic seed with this
println(mnemonic.words)
// Vector(prison, joke, will, barely, address, celery, example, pudding, march, prison, clock, clump, muscle, border, chimney, seat, buzz, supply, jeans, struggle, stable, obtain, slush, spike)
```
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-example171839988051633638/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-example171839988051633638/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@1cbb44ab)
val rootXPub = km.right.get.getRootXPub
// rootXPub: ExtPublicKey = vpub5SLqN2bLY4WeYbzqd1Co9uY8noP4gBYJrKbsJfqgTNUZKUWxPaZih6ieTgVxFJvvWh2Ac5MP2GnQckBhDdRbi3gCw3uyZELkxrZSvBmenRu
println(rootXPub)
// vpub5SLqN2bLY4WeYbzqd1Co9uY8noP4gBYJrKbsJfqgTNUZKUWxPaZih6ieTgVxFJvvWh2Ac5MP2GnQckBhDdRbi3gCw3uyZELkxrZSvBmenRu
```
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-example171839988051633638/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@3e293f84
val mainnetXpub = mainnetKeyManager.getRootXPub
// mainnetXpub: ExtPublicKey = zpub6jftahH18ngZwnmJxSMHzFv9UfxrSfWJWmgkSFRDyPz5XsmsQDDyBMMCYWLJEwYc9FVPbyjcrvCc9tdx6R5etzQcQQhftsci3kp2UZVBxgn
println(mainnetXpub)
// zpub6jftahH18ngZwnmJxSMHzFv9UfxrSfWJWmgkSFRDyPz5XsmsQDDyBMMCYWLJEwYc9FVPbyjcrvCc9tdx6R5etzQcQQhftsci3kp2UZVBxgn
```
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

View file

@ -0,0 +1,92 @@
---
id: version-1.9.6-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(021df4f25c9c004169d38e789b71e8fd07f5fbd9ccf73ac7d5df7d4ed216fcc392)
val dataToSign = DoubleSha256Digest.empty
// dataToSign: DoubleSha256Digest = DoubleSha256Digest(0000000000000000000000000000000000000000000000000000000000000000)
// calls bouncy castle indirectly via CryptoContext
val signature = privKey.sign(dataToSign.bytes)
// signature: ECDigitalSignature = ECDigitalSignature(30450221008d01381b03e1a64d3d1b4fe58d736c5001800f11671920e4e5954a6cd2b45ae402205a1322a5c30ebfa2de59b76fa4525a036c29df8421b836b36243df2904fdd748)
// 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)

View file

@ -1,4 +1,5 @@
[
"1.9.6",
"1.9.5",
"1.9.4",
"1.9.3",