Add secp256k1jni readme, start working main project readme, move old readme to core/README.md (#276)

Add documentation for TxBuilder

Try to fix links on core/README.md

add readme badges for bintray

Nits and formatting of README (#23)

ran scalafmt, fixed format in secp readme
This commit is contained in:
Chris Stewart 2019-01-08 09:29:06 -06:00 committed by GitHub
parent 1a1409c832
commit d01e37cfa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 632 additions and 286 deletions

175
README.md
View file

@ -1,176 +1,53 @@
[![Build Status](https://travis-ci.org/bitcoin-s/bitcoin-s-core.svg?branch=master)](https://travis-ci.org/bitcoin-s/bitcoin-s-core) [![Coverage Status](https://coveralls.io/repos/github/bitcoin-s/bitcoin-s-core/badge.svg?branch=master)](https://coveralls.io/github/bitcoin-s/bitcoin-s-core?branch=master) [![IRC Network](https://img.shields.io/badge/irc-%23bitcoin--scala-blue.svg "IRC Freenode")](https://webchat.freenode.net/?channels=bitcoin-scala)[![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/bitcoin-s-core)
# Bitcoin-S-Core
# Bitcoin-S
This is the core functionality of bitcoin-s.
## Design Principles
[Quick Build Guide](BUILD_README.md)
- Immutable data structures everywhere
- [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) to allow the compiler to check for exhaustiveness on match statements
- Using [property based testing](http://www.scalatest.org/user_guide/property_based_testing) to test robustness of code
- Minimize dependencies to reduce attack surface
This repostitory includes the following functionality:
- Native Scala objects for various protocol types ([transactions](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/protocol/transaction/Transaction.scala), [inputs](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/protocol/transaction/TransactionInput.scala), [outputs](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/protocol/transaction/TransactionOutput.scala), [scripts signatures](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala), [scriptpubkeys](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala))
- [Serializers and deserializers for bitcoin data structures mentioned above](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/serializers)
- [An implementation of Bitcoin's Script programming language](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/script)
- Passes all tests found in Bitcoin Core's regression test suite called [script_test.json](https://github.com/bitcoin/bitcoin/blob/master/src/test/data/script_tests.json)
- Passes all tests inside of Bitcoin Core's transaction regression test suite [tx_valid.json](https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json) / [tx_invalid.json](https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_invalid.json) /
[sighash.json](https://github.com/bitcoin/bitcoin/blob/master/src/test/data/sighash.json)
- [Payment channel support](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/channels/Channel.scala)
- Integration with [bitcoin core's optimized secp256k1](https://github.com/bitcoin-core/secp256k1/) library
- Consensus rule set up to date through segregated witness
- A robust set of [generators](https://github.com/bitcoin-s/bitcoin-s-core/tree/master/core-gen/src/test/scala/org/bitcoins/core/gen), which are used in property based testing
- These are extremely useful for testing bitcoin applications
- Here is an example of a specification for our [ECPrivateKey](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core-test/src/test/scala/org/bitcoins/core/crypto/ECPrivateKeySpec.scala)
- [Number types](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/number/NumberType.scala#L16) to represent C's unsigned numbers ([`UInt8`](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/number/NumberType.scala#L95), [`UInt32`](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/number/NumberType.scala#L106) etc) which are used in the bitcoin protocol
- 90% test coverage throughout the codebase to ensure high quality code.
- Functions documented with Scaladocs for user friendliness
## Projects
# Design Principles
- Immutable data structures everywhere
- Algebraic Data Types to allow the compiler to check for exhaustiveness on match statements
- Using [property based testing](http://www.scalatest.org/user_guide/property_based_testing) to test robustness of code
1. `core` - this is where protocol data structures live, like [Transactions](core/src/main/scala/org/bitcoins/core/protocol/transaction/Transaction.scala), [Blocks](core/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala), or [PrivateKeys](core/src/main/scala/org/bitcoins/core/crypto/ECKey.scala). For more info read [`core/README.md`](core/README.md)
# Setting up libsecp256k1
2. `core-test` - this is where all test cases for the `core` project live
libsecp256k1 needs to be built with the java interface enabled. Use the following commands to build secp256k1 with jni enabled. [Here is the official documentation for doing this in secp256k1](https://github.com/bitcoin-core/secp256k1/blob/master/src/java/org/bitcoin/NativeSecp256k1.java#L35)
```
$ cd secp256k1
$ sh autogen.sh && ./configure --enable-jni --enable-experimental --enable-module-ecdh && make
$ sudo make install #optional, this installs the lib on your system
```
3. `rpc` - this is a RPC client implementation for `bitcoind`. For more info read [`rpc/README.md`](rpc/README.md)
By default, `.jvmopts` adds the freshly compiled `secp256k1` to the JVM classpath. It should therefore be sufficient to start `sbt` by simply doing
4. `eclair-rpc` - this is a RPC client implementation for [Eclair](https://en.wikipedia.org/wiki/Algebraic_data_type), which is a Lightning Network implementation. For more information please read [`eclair-rpc/README.md`](eclair-rpc-README.md)
```bash
$ sbt
```
5. `bench` - benchmarks for Bitcoin-S. For more information please read [`bench/README.md`](bench/README.md)
If you run into classpath problems, you can manually specify the JVM classpath like this:
6. `testkit` - This is a useful testkit for testing Bitcoin related applications. You can spin up Bitcoin and Lightning nodes arbitrarily and set them in specific states. For more information please read [`testkit/README.md`](testkit/README.md)
```
$ sbt -Djava.library.path=/your/class/path
```
7. `zmq` - `bitcoind` has a setting that publishes information about the state of the network over ZMQ. This project implements a subscriber that allows you to read and parse that information. For more information see [`zmq/README.md`](zmq/README.md) as well as the official [Bitcoin Core ZMQ documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md)
You can also copy `libsecp256k1.so` to your system library path.
## Artifacts
# TODO
- Simple [`TransactionBuilder`](https://github.com/MetacoSA/NBitcoin/blob/56bfd1cbca535424b160d8a40688a79de4800630/NBitcoin/TransactionBuilder.cs#L172) similar nbitcoin
- Hardware wallet support (trezor, ledger etc)
- Lightning network scriptpubkey types (some work done [here](https://github.com/Christewart/bitcoin-s-core/commits/lightning_contracts))
- sbt respository
- Java support
- Android support
You need to add the Bitcoin-S Bintray to your resolvers to be able to access published artifacts.
# Creating fat jar
Here is how you build a bitcoin-s-core fat jar file. Note this command will run the entire test suite in bitcoin-s-core.
With sbt, this can be done like this:
```scala
$ sbt assembly
[info] ScalaCheck
[info] Passed: Total 149, Failed 0, Errors 0, Passed 149
[info] ScalaTest
[info] Run completed in 5 minutes, 33 seconds.
[info] Total number of tests run: 744
[info] Suites: completed 97, aborted 0
[info] Tests: succeeded 744, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[info] Passed: Total 909, Failed 0, Errors 0, Passed 909
[info] Checking every *.class/*.jar file's SHA-1.
[info] Merging files...
[warn] Merging 'META-INF/MANIFEST.MF' with strategy 'discard'
[warn] Strategy 'discard' was applied to a file
[info] SHA-1: 6ea465dcc996cefb68fc334778cac60d892bd7f0
[info] Packaging /home/chris/dev/bitcoin-s-core/target/scala-2.11/bitcoin-s-core-assembly-0.0.1.jar ...
[info] Done packaging.
[success] Total time: 337 s, completed Jul 20, 2017 1:53:11 PM
resolvers += Resolver.bintrayRepo("bitcoin-s", "bitcoin-s-core"),
```
# Examples
Every bitcoin protocol data structure (and some other data structures) extends [`NetworkElement`](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/protocol/NetworkElement.scala). NetworkElement provides easier methods to convert the data structure to hex or a byte representation. When paired with our [`Factory`](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/core/src/main/scala/org/bitcoins/core/util/Factory.scala) 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`](https://github.com/bitcoin-s/bitcoin-s-core/blob/1c7a7b9f46679a753248d9f55246c272bb3d63b9/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala#L462) companion object. You can use this companion object to create a SPK from hex or a byte array.
Here is an example scala console session with bitcoins-core
Now you should be able to add Bitcoin-S artifacts like this:
```scala
$ sbt console
[info] Loading global plugins from /home/chris/.sbt/0.13/plugins
[info] Loading project definition from /home/chris/dev/bitcoin-s-core/project
[info] Set current project to bitcoin-s-core (in build file:/home/chris/dev/bitcoin-s-core/)
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_151).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.protocol.transaction._
"org.bitcoins" % "bitcoin-s-secp256k1jni" % "0.0.1"
scala> val hexTx = "0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f8$9c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1$a02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e$90c865c2f6f7a9710a474154ab1423abb5b9288ac00000000"
hexTx: String = 0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1$52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02$45f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c$65c2f6f7a9710a474154ab1423abb5b9288ac00000000
"org.bitcoins" %% "bitcoin-s-core" % "0.0.1" withSources() withJavadoc()
scala> val tx = Transaction(hexTx)
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
tx: org.bitcoins.core.protocol.transaction.Transaction = BaseTransactionImpl(UInt32Impl(1),List(TransactionInputImpl(TransactionOutPointImpl$DoubleSha256DigestImpl(ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3),UInt32Impl(0)),P2PKHScriptSignatureImpl(6b483045022$008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf012102$1d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353),UInt32Impl(4294967295))),List(TransactionOutputImpl(SatoshisImpl(Int64Impl($9994000)),P2PKHScriptPubKeyImpl(1976a914b1d7591b69e9def0feb13254bace942923c7922d88ac)), TransactionOutputImpl(SatoshisImpl(Int64Impl(840)),P$PKHScriptPubKeyImpl(1976a9145e690c865c2f6f7a9710a474154ab1423abb5b9288ac))),UInt32Impl(0))
"org.bitcoins" %% "bitcoin-s-bitcoind-rpc" % "0.0.1" withSources() withJavadoc()
scala> val hexAgain = tx.hex
hexAgain: String = 0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f88$c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1f$02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e6$0c865c2f6f7a9710a474154ab1423abb5b9288ac00000000
"org.bitcoins" %% "bitcoin-s-eclair-rpc" % "0.0.1" withSources() withJavadoc()
"org.bitcoins" %% "bitcoin-s-testkit" % "0.0.1" withSources() withJavadoc()
"org.bitcoins" %% "bitcoin-s-zmq" % "0.0.1" withSources() withJavadoc()
```
This gives us an example of a bitcoin transaction that is encoded in hex format that is deserialized to a native Scala object called a [`Transaction`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/protocol/transaction/Transaction.scala#L14-L42). 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`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/crypto/ECKey.scala#L23-L67), [`ScriptPubKey`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala#L23), [`ScriptWitness`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/protocol/script/ScriptWitness.scala#L13), and [`Block`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala#L17).
Transactions are run through the interpreter to check the validity of the Transaction. 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
chris@chris:~/dev/bitcoins-core$ sbt console
[info] Loading project definition from /home/chris/dev/bitcoins-core/project
[info] Set current project to bitcoins (in build file:/home/chris/dev/bitcoins-core/)
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.script._
scala> import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.protocol.transaction._
scala> import org.bitcoins.core.script._
import org.bitcoins.core.script._
scala> import org.bitcoins.core.script.interpreter._
import org.bitcoins.core.script.interpreter._
scala> import org.bitcoins.core.policy._
import org.bitcoins.core.policy._
scala> val spendingTx = Transaction("0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c865c2f6f7a9710a474154ab1423abb5b9288ac00000000")
spendingTx: org.bitcoins.core.protocol.transaction.Transaction = TransactionImpl(1,List(TransactionInputImpl(TransactionOutPointImpl(b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc,0),P2PKHScriptSignatureImpl(4830450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353,List(BytesToPushOntoStackImpl(72), ScriptConstantImpl(30450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01), BytesToPushOntoStackImpl(33), ScriptConstantImpl(0241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353))),4294967295)),List(TransactionOutputImpl(8...
scala> val scriptPubKey = ScriptPubKey("76a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac")
scriptPubKey: org.bitcoins.core.protocol.script.ScriptPubKey = P2PKHScriptPubKeyImpl(76a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac,List(OP_DUP, OP_HASH160, BytesToPushOntoStackImpl(20), ScriptConstantImpl(31a420903c05a0a7de2de40c9f02ebedbacdc172), OP_EQUALVERIFY, OP_CHECKSIG))
scala> val inputIndex = 0
inputIndex: Int = 0
scala> val program = ScriptProgram(spendingTx,scriptPubKey,inputIndex, Policy.standardScriptVerifyFlags)
program: org.bitcoins.core.script.PreExecutionScriptProgram = PreExecutionScriptProgramImpl(TransactionSignatureComponentImpl(TransactionImpl(1,List(TransactionInputImpl(TransactionOutPointImpl(b30d3148927f620f5b1228ba941c211fdabdae75d0ba0b688a58accbf018f3cc,0),P2PKHScriptSignatureImpl(4830450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353,List(BytesToPushOntoStackImpl(72), ScriptConstantImpl(30450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01), BytesToPushOntoStackImpl(33), ScriptConstantImpl(0241d746ca08da0a668735c3e01c1fa02045f2f399c5937079...
scala> ScriptInterpreter.run(program)
res0: org.bitcoins.core.script.result.ScriptResult = ScriptOk
```
# Interacting with bitcoind
Please see our other project [`bitcoin-s-rpc-client`](https://github.com/bitcoin-s/bitcoin-s-rpc-client)
# Stand alone SPV Node
Please see our other project [`bitcoin-s-spv-node`](https://github.com/bitcoin-s/bitcoin-s-spv-node)

View file

@ -1,3 +1,5 @@
[ ![Download](https://api.bintray.com/packages/bitcoin-s/bitcoin-s-core/bitcoin-s-bench/images/download.svg) ](https://bintray.com/bitcoin-s/bitcoin-s-core/bitcoin-s-bench/_latestVersion)
# Benchmark suite
This is a WIP. It currently contains one bench mark for de-serializing large blocks

View file

@ -33,6 +33,7 @@ lazy val testCompilerOpts = commonCompilerOpts
lazy val commonSettings = List(
scalacOptions in Compile := compilerOpts,
scalacOptions in Test := testCompilerOpts,
assemblyOption in assembly := (assemblyOption in assembly).value
.copy(includeScala = false),
@ -89,7 +90,8 @@ lazy val root = project
rpc,
bench,
eclairRpc,
testkit
testkit,
doc
)
.settings(commonSettings: _*)
@ -134,9 +136,6 @@ lazy val rpc = project
.dependsOn(
core
)
.settings(
testOptions in Test += Tests.Argument("-oF")
)
lazy val bench = project
.in(file("bench"))
@ -170,4 +169,15 @@ lazy val testkit = project
)
lazy val doc = project
.in(file("doc"))
.settings(
name := "bitcoin-s-doc",
libraryDependencies ++= Deps.doc
)
.dependsOn(
secp256k1jni,
core
)
publishArtifact in root := false

1
core-test/README.md Normal file
View file

@ -0,0 +1 @@
[ ![Download](https://api.bintray.com/packages/bitcoin-s/bitcoin-s-core/bitcoin-s-core-test/images/download.svg) ](https://bintray.com/bitcoin-s/bitcoin-s-core/bitcoin-s-core-test/_latestVersion)

View file

@ -21,25 +21,25 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
tc.lockTime)
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(outPoint,
creditingOutput,
Seq(privKey),
None,
None,
HashType.sigHashAll)
val utxo = BitcoinUTXOSpendingInfo(outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val txBuilder = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
EmptyScriptPubKey,
TestNet3)
val txBuilder = BitcoinTxBuilder(destinations = destinations,
utxos = utxoMap,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey,
network = TestNet3)
val resultFuture = txBuilder.flatMap(_.sign)
recoverToSucceededIf[IllegalArgumentException] {
resultFuture
@ -49,25 +49,25 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
it must "fail to build a transaction when we pass in a negative fee rate" in {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
tc.lockTime)
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(outPoint,
creditingOutput,
Seq(privKey),
None,
None,
HashType.sigHashAll)
val utxo = BitcoinUTXOSpendingInfo(outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(-1)))
val txBuilder = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
EmptyScriptPubKey,
TestNet3)
val txBuilder = BitcoinTxBuilder(destinations = destinations,
utxos = utxoMap,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey,
network = TestNet3)
recoverToSucceededIf[IllegalArgumentException] {
txBuilder
}
@ -76,7 +76,7 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
it must "fail a transaction when the user invariants fail" in {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
@ -89,12 +89,12 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
None,
HashType.sigHashAll)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(1)))
val txBuilder = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
EmptyScriptPubKey,
TestNet3)
val feeUnit = SatoshisPerVirtualByte(currencyUnit = Satoshis(Int64(1)))
val txBuilder = BitcoinTxBuilder(destinations = destinations,
utxos = utxoMap,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey,
network = TestNet3)
//trivially false
val f = (_: Seq[BitcoinUTXOSpendingInfo], _: Transaction) => false
val resultFuture = txBuilder.flatMap(_.sign(f))
@ -104,39 +104,44 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
}
it must "be able to create a BitcoinTxBuilder from UTXOTuple and UTXOMap" in {
val creditingOutput = TransactionOutput(CurrencyUnits.zero, spk)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
tc.lockTime)
val creditingOutput =
TransactionOutput(currencyUnit = CurrencyUnits.zero, scriptPubKey = spk)
val destinations = {
Seq(
TransactionOutput(currencyUnit = Satoshis.one,
scriptPubKey = EmptyScriptPubKey))
}
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(outPoint,
creditingOutput,
Seq(privKey),
None,
None,
HashType.sigHashAll)
val utxo = BitcoinUTXOSpendingInfo(outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType = HashType.sigHashAll)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val utxoSpendingInfo = BitcoinUTXOSpendingInfo(outPoint,
creditingOutput,
Seq(privKey),
None,
None,
HashType.sigHashAll)
val utxoSpendingInfo = BitcoinUTXOSpendingInfo(outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType =
HashType.sigHashAll)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(1)))
val txBuilderMap = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
EmptyScriptPubKey,
TestNet3)
val txBuilderTuple = BitcoinTxBuilder(destinations,
Seq(utxoSpendingInfo),
feeUnit,
EmptyScriptPubKey,
TestNet3)
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val txBuilderMap = BitcoinTxBuilder(destinations = destinations,
utxos = utxoMap,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey,
network = TestNet3)
val txBuilderTuple = BitcoinTxBuilder(destinations = destinations,
utxos = Seq(utxoSpendingInfo),
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey,
network = TestNet3)
txBuilderTuple.flatMap { tup =>
txBuilderMap.map { map =>
@ -149,25 +154,27 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
val p2sh = P2SHScriptPubKey(spk)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2sh)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
tc.lockTime)
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
lockTime = tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(outPoint,
creditingOutput,
Seq(privKey),
Some(EmptyScriptPubKey),
None,
HashType.sigHashAll)
val utxo = BitcoinUTXOSpendingInfo(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
redeemScriptOpt = Some(EmptyScriptPubKey),
scriptWitnessOpt = None,
hashType = HashType.sigHashAll
)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(1)))
val txBuilderNoRedeem = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
EmptyScriptPubKey,
TestNet3)
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val txBuilderNoRedeem = BitcoinTxBuilder(destinations = destinations,
utxos = utxoMap,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey,
network = TestNet3)
val resultFuture = txBuilderNoRedeem.flatMap(_.sign)
recoverToSucceededIf[IllegalArgumentException] {
resultFuture
@ -178,7 +185,7 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
val p2wsh = P2WSHWitnessSPKV0(spk)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2wsh)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
@ -192,7 +199,7 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
HashType.sigHashAll)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(1)))
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val txBuilderWitness = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
@ -208,7 +215,7 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
val p2pkh = P2PKHScriptPubKey(privKey.publicKey)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2pkh)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
@ -222,7 +229,7 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
HashType.sigHashAll)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(1)))
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val txBuilderWitness = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
@ -239,26 +246,29 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
val pubKey2 = ECPrivateKey().publicKey
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2pkh)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(outPoint,
creditingOutput,
Seq(privKey),
None,
Some(P2WSHWitnessV0(EmptyScriptPubKey)),
HashType.sigHashAll)
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
lockTime = tc.lockTime)
val outPoint =
TransactionOutPoint(txId = creditingTx.txId, index = UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
hashType = HashType.sigHashAll
)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(1)))
val txBuilderWitness = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
EmptyScriptPubKey,
TestNet3)
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val txBuilderWitness = BitcoinTxBuilder(destinations = destinations,
utxos = utxoMap,
feeRate = feeUnit,
changeSPK = EmptyScriptPubKey,
network = TestNet3)
val resultFuture = txBuilderWitness.flatMap(_.sign)
recoverToSucceededIf[IllegalArgumentException] {
resultFuture
@ -266,24 +276,30 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
}
it must "fail to sign a p2wpkh if we don't pass in the public key" in {
val p2wpkh = P2WPKHWitnessSPKV0(privKey.publicKey)
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2wpkh)
val p2wpkh = P2WPKHWitnessSPKV0(pubKey = privKey.publicKey)
val creditingOutput = TransactionOutput(currencyUnit = CurrencyUnits.zero,
scriptPubKey = p2wpkh)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
tc.lockTime)
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(outPoint,
creditingOutput,
Seq(privKey),
None,
Some(P2WSHWitnessV0(EmptyScriptPubKey)),
HashType.sigHashAll)
Seq(
TransactionOutput(currencyUnit = Satoshis.one,
scriptPubKey = EmptyScriptPubKey))
val creditingTx = BaseTransaction(version = tc.validLockVersion,
inputs = Nil,
outputs = Seq(creditingOutput),
lockTime = tc.lockTime)
val outPoint =
TransactionOutPoint(txId = creditingTx.txId, index = UInt32.zero)
val utxo = BitcoinUTXOSpendingInfo(
outPoint = outPoint,
output = creditingOutput,
signers = Seq(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = Some(P2WSHWitnessV0(EmptyScriptPubKey)),
hashType = HashType.sigHashAll
)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(1)))
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val txBuilderWitness = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,
@ -300,7 +316,7 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
val pubKey2 = ECPrivateKey().publicKey
val creditingOutput = TransactionOutput(CurrencyUnits.zero, p2wpkh)
val destinations =
Seq(TransactionOutput(Satoshis(Int64(1)), EmptyScriptPubKey))
Seq(TransactionOutput(Satoshis.one, EmptyScriptPubKey))
val creditingTx = BaseTransaction(tc.validLockVersion,
Nil,
Seq(creditingOutput),
@ -314,7 +330,7 @@ class BitcoinTxBuilderTest extends AsyncFlatSpec with MustMatchers {
HashType.sigHashAll)
val utxoMap: BitcoinTxBuilder.UTXOMap = Map(outPoint -> utxo)
val feeUnit = SatoshisPerVirtualByte(Satoshis(Int64(1)))
val feeUnit = SatoshisPerVirtualByte(Satoshis.one)
val txBuilderWitness = BitcoinTxBuilder(destinations,
utxoMap,
feeUnit,

157
core/README.md Normal file
View file

@ -0,0 +1,157 @@
[ ![Download](https://api.bintray.com/packages/bitcoin-s/bitcoin-s-core/bitcoin-s-core/images/download.svg) ](https://bintray.com/bitcoin-s/bitcoin-s-core/bitcoin-s-core/_latestVersion)
# `core` module
This is the core functionality of Bitcoin-S. The goal is to provide basic data structures that are found in the Bitcoin and Lightning protocols while minimizing external depedencies for security purposes. We aim to have an extremely high level of test coverage in this module to flesh out bugs. We use [property based testing](http://www.scalatest.org/user_guide/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`](src/main/scala/org/bitcoins/core/protocol/NetworkElement.scala). `NetworkElement` provides methods to convert the data structure to hex or byte representation. When paired with [`Factory`](src/main/scala/org/bitcoins/core/util/Factory.scala) 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`](https://github.com/bitcoin-s/bitcoin-s-core/blob/1c7a7b9f46679a753248d9f55246c272bb3d63b9/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala#L462) 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`](src/main/scala/org/bitcoins/core/protocol) - basic protocol data structures. Useful for serializing/deserializing things
2. [`script`](src/main/scala/org/bitcoins/core/script) - an implementation of [Script](https://en.bitcoin.it/wiki/Script) - the programming language in Bitcoin
3. [`wallet`](src/main/scala/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.
4. [`config`](src/main/scala/org/bitcoins/core/config) - Contains information about a chain's genesis block and DNS seeds
5. [`number`](src/main/scala/org/bitcoins/core/number) - Implements number types that are native in C, i.e. `UInt8`, `UInt32`, `UInt64`, etc.
6. [`currency`](src/main/scala/org/bitcoins/core/currency) - Implements currency units in the Bitcoin protocol
7. [`bloom`](src/main/scala/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)
## Examples
### Serializing and deserializing a `Transaction`
Here is an example scala console session with bitcoins-core
```scala
$ sbt core/console
[info] Loading global plugins from /home/chris/.sbt/0.13/plugins
[info] Loading project definition from /home/chris/dev/bitcoin-s-core/project
[info] Set current project to bitcoin-s-core (in build file:/home/chris/dev/bitcoin-s-core/)
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_151).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.protocol.transaction._
scala> val hexTx = "0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f8$9c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1$a02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e$90c865c2f6f7a9710a474154ab1423abb5b9288ac00000000"
hexTx: String = 0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1$52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02$45f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c$65c2f6f7a9710a474154ab1423abb5b9288ac00000000
scala> val tx = Transaction.fromHex(hexTx)
tx: org.bitcoins.core.protocol.transaction.Transaction = BaseTransactionImpl(UInt32Impl(1),List(TransactionInputImpl(TransactionOutPointImpl$DoubleSha256DigestImpl(ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3),UInt32Impl(0)),P2PKHScriptSignatureImpl(6b483045022$008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf012102$1d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353),UInt32Impl(4294967295))),List(TransactionOutputImpl(SatoshisImpl(Int64Impl($9994000)),P2PKHScriptPubKeyImpl(1976a914b1d7591b69e9def0feb13254bace942923c7922d88ac)), TransactionOutputImpl(SatoshisImpl(Int64Impl(840)),P$PKHScriptPubKeyImpl(1976a9145e690c865c2f6f7a9710a474154ab1423abb5b9288ac))),UInt32Impl(0))
scala> val hexAgain = tx.hex
hexAgain: String = 0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f88$c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1f$02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e6$0c865c2f6f7a9710a474154ab1423abb5b9288ac00000000
```
This gives us an example of a hex encoded Bitcoin transaction that is deserialized to a native Scala object called a [`Transaction`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/protocol/transaction/Transaction.scala#L14-L42). 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`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/crypto/ECKey.scala#L23-L67), [`ScriptPubKey`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/protocol/script/ScriptPubKey.scala#L23), [`ScriptWitness`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/protocol/script/ScriptWitness.scala#L13), and [`Block`](https://github.com/bitcoin-s/bitcoin-s-core/blob/6358eb83067909771f989d615b422759222d060a/src/main/scala/org/bitcoins/core/protocol/blockchain/Block.scala#L17).
### 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`](src/main/scala/org/bitcoins/core/wallet/utxo/UTXOSpendingInfo.scala). This contains all of the information needed to create a validly signed [`ScriptSignature`](src/main/scala/org/bitcoins/core/protocol/script/ScriptSignature.scala) that spends this output.
Our [`TxBuilder`](src/main/scala/org/bitcoins/core/wallet/builder/TxBuilder.scala) class requires you to provide the following:
1. `destinations` - the places we are sending bitcoin to. These are [TransactionOutputs](src/main/scala/org/bitcoins/core/protocol/transaction/TransactionOutput.scala) you are sending coins too
2. `utxos` - these are the [UTXOs](src/main/scala/org/bitcoins/core/wallet/utxo/UTXOSpendingInfo.scala) 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`](src/main/scala/org/bitcoins/core/config/NetworkParameters.scala) we are transacting on
After providing this information, you can generate a validly signed bitcoin transaction by calling the `sign` method.
### The [`Sign` API](src/main/scala/org/bitcoins/core/crypto/Sign.scala)
This is the API we define to sign things with. It takes in an arbitrary byte vector and returns a `Future[ECDigitalSignature]`. The reason we incorporate `Future`s here is for extensibility of this API. We would like to provide implementations of this API for hardware devices, which need to be asynchrnous since they may require user input.
From [`core/src/main/scala/org/bitcoins/core/crypto/Sign.scala`](src/main/scala/org/bitcoins/core/crypto/Sign.scala):
```scala
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`](src/main/scala/org/bitcoins/core/crypto/TransactionSignatureSerializer.scala)'s `hashForSignature` method. Our in-memory [`ECKey`](src/main/scala/org/bitcoins/core/crypto/ECKey.scala) 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`](src/main/scala/org/bitcoins/core/wallet/signer/Signer.scala)s. The `Signer` subtypes (i.e. `P2PKHSigner`) implement the specific functionality needed to produce a valid digital signature for their corresponding script type.
#### Complete `TxBuilder` example
For an example of how to use the `TxBuilder` please see [`TxBuilderExample.scala`](../doc/src/test/scala/TxBuilderExample.scala).
### 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
chris@chris:~/dev/bitcoins-core$ sbt core/console
scala> import org.bitcoins.core.protocol.script._
scala> import org.bitcoins.core.protocol.transaction._
scala> import org.bitcoins.core.script._
scala> import org.bitcoins.core.script.interpreter._
scala> import org.bitcoins.core.policy._
scala> import org.bitcoins.core.number._
scala> import org.bitcoins.core.crypto._
scala> import org.bitcoins.core.currency._
scala> val spendingTx = Transaction.fromHex("0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c865c2f6f7a9710a474154ab1423abb5b9288ac00000000")
spendingTx: org.bitcoins.core.protocol.transaction.Transaction = ... // omitted for brevity
scala> val scriptPubKey = ScriptPubKey.fromAsmHex("76a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac")
scriptPubKey: org.bitcoins.core.protocol.script.ScriptPubKey = P2PKHScriptPubKeyImpl(1976a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac)
scala> val output = TransactionOutput(CurrencyUnits.zero, scriptPubKey)
output: org.bitcoins.core.protocol.transaction.TransactionOutput = ... // omitted for brevity
scala> val inputIndex = UInt32.zero
inputIndex: org.bitcoins.core.number.UInt32 = UInt32Impl(0)
scala> val btxsc = BaseTxSigComponent(spendingTx,inputIndex,output,Policy.standardScriptVerifyFlags)
btxsc: org.bitcoins.core.crypto.BaseTxSigComponent = ... // omitted for brevity
scala> val preExecution = PreExecutionScriptProgram(btxsc)
preExecution: org.bitcoins.core.script.PreExecutionScriptProgram = ... // omitted for brevity
scala> val result = ScriptInterpreter.run(preExecution)
result: org.bitcoins.core.script.result.ScriptResult = ScriptOk
```
## Running `core` tests
To run the entire `core` test suite:
```bash
chris@chris:~/dev/bitcoins-core$ sbt coreTest/test
# lots of output, should end up with something like this
[info] All tests passed.
[info] Passed: Total 1008, Failed 0, Errors 0, Passed 1008
[success] Total time: 38 s, completed Dec 23, 2018 4:39:58 PM
```

View file

@ -139,8 +139,7 @@ object ECPrivateKey extends Factory[ECPrivateKey] {
extends ECPrivateKey {
require(
NativeSecp256k1.secKeyVerify(bytes.toArray),
"Invalid key according to secp256k1, hex: " + BitcoinSUtil.encodeHex(
bytes))
s"Invalid key according to secp256k1, hex: ${bytes.toHex}")
}
def apply(bytes: ByteVector, isCompressed: Boolean)(

View file

@ -932,12 +932,12 @@ object BitcoinTxBuilder {
utxos match {
case Nil => accum
case h :: t =>
val u = BitcoinUTXOSpendingInfo(h.outPoint,
h.output,
h.signers,
h.redeemScriptOpt,
h.scriptWitnessOpt,
h.hashType)
val u = BitcoinUTXOSpendingInfo(outPoint = h.outPoint,
output = h.output,
signers = h.signers,
redeemScriptOpt = h.redeemScriptOpt,
scriptWitnessOpt = h.scriptWitnessOpt,
hashType = h.hashType)
val result: BitcoinTxBuilder.UTXOMap = accum.updated(h.outPoint, u)
loop(t, result)
}

58
doc/SECURITY.md Normal file
View file

@ -0,0 +1,58 @@
## Disclosure
Please send an email to stewart.chris1234@gmail.com encrypted with this gpg key
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
mQINBFku8rYBEACub6cz6nAy/MHK2TlTztkSJbNpc2B3tZoVCKRz987yLX578JiO
EDYosAsD4b4nIjY1O6kD5FUjoJttQO+7IR8RwkmBFRsPqXaryrGr69FqaPlGSMLi
uNVlawe0qMY79DPNw03Pu2tdgiq94sWplmOnuyQqcg2lkoK1+8/DXX3801s164wq
4ZCCmkAAxJDhoLTyXB6LNI586vJhQ5+SVh8z/kAtp6NVXU5v2LcnkrqlX4oaakbt
nY2FYpAZgEI8T+Fg+btGbt3muajhM9ooq0LBtaIMwgcTC4JPyFg6acWaluMGCOtW
k1j8eu2/J4ly27YGCtWoDwrDen5qzHF6mm1HI4ko9fG2L4SxxXkl7DpGgtR71D1R
mSA9yot8oPwWW1NRncIct188uRuhy4CJIylNU88iqgpUZfYRqwaDl2KCrEbetGMT
yZSBVyilXdNjzFUJFtOPAxKmYPztUcEL1hEyGOu03YFt3FQr7QHhNR8+v688DdH4
QHpwcrXkB0x/wq18tCO6gNTbnt94VR4PJUvz3BjP7XrHGn3ljKt5PPHO7cmPkDH5
/iP928VC1U5FjAJh6dpsGgFb7k51CKx9bUJRDmL3GUMxFvmo+4YZpTKS5G+Ighs3
vLIzO1X0aV5b4mltn4Fz4o/Cp+CSVqK//YnetezkmrvwQ2u/iOGeqRKhXwARAQAB
tCtDaHJpcyBTdGV3YXJ0IDxzdGV3YXJ0LmNocmlzMTIzNEBnbWFpbC5jb20+iQI+
BBMBAgAoAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAUCWxW1AAUJBakpQwAK
CRCYRuzvvweIvijkD/0RQzQAWcJY5XJ503vj0CidzKym4ueykxhgISiZ+hmjr+u5
/Poceo5z3nXj1zI8/yMIb//7WRiX61+JGFKwN/OsM07KHa/J8Rf3NCASO2hYCGjq
ev0UOWUeSqHwN8zr4kAY9hKZJVR7byKmYV/iqmuAFTB4Nrs8567CzqKaPezB1mKv
JMTxx4XiL4M6cFiBSbCBi0qGYS6M0zBETJvFAmSh3H3PtUUdGz0kAyE2Ju11qhPh
yZVCjILnzHbsguqPSPsj3Cba6+WcbGzm7HIBPdZDjIvcnuw3Mq8kjtcpOWTMoGP1
q9xHioP97Gh+E+z7iFo8g2NszZ1wEzL4k3TnO0keknOmfOVn5zSINp6M8CHCTbzr
ls+qqHjcbAfs5BbNRMXxapO78kvSQSDenRjpC7JCl3FIwWqoMMi7T3PIYVFQfMG+
EyC3UMVOGmIDavw6yDCCdILcGeMdafEuzZmmV3Qvtt3LsqQjjm+TyBhWazRR+9sJ
kRgSdvBkF8XgqyOpaLffWPoKxnaQtJwylQnYuUM8kLP52aH2C9ctKxLChu1BDmZm
aWZNpm8h9jySeybQSBlMeDd8TrscuGJ8qBEOfREuv7fqYxsIi+G74+9h5kjiOe+y
hkIkSI440mAHRIoCyAkBx4EFZN5lRdYShRGRQrCWD/RrFssABmXhBik707V0nLkC
DQRZLvK2ARAA1gn4P9fm9KJpYuwYfxwrl0VfDPMNct5dDY6zbdQGV3djBTcuyeVU
EL6w4rRfF1aPFFEXGhT2zn2tPY410fswTNANLKuP/sFEiGEfaMjmYu0DucCMyznK
KMbE5FGD/ue0SHAN2K+A13OkPVv6kabu7XiK2QzYBrw1Lk7kGhSqMb8t3hVs0A9B
iAqWFhSIBfmnd5SKyJKNksv7DuBcgK9Xjszlbr2gV0v1kc0aMHGdZH4omB5qmq8I
aFkgZcY3MbA1zmetd1YauwzhZCFyTFcebm9trTn4D5htHGgXSehP3DMc4y9HVaLL
rANAi0Ykk7Bi+BmYHkjsvt7FZj2/V/KWkwgiPjEBvnKX3iWpbJdqU2os9VZM3ctB
WaE4pwsXt1kpiEgc/qUJ2xQL3po0dTO8q5heE6iZUNlD+xBLwpUxGjS8rN+V0LCt
muqtZ8N8lSZD5U1XVVEpEdfe8lPKQ0wvjZZUTjix1j18mhiwE+cC66JNyM3mlMUx
GKHg6M8nnXxTSbv2ogj7wXmqPF5dq5uGzCeDhdnR5tRmNQCpW/QUq8AJzV7VXRJl
+qZXar7DgbGUeRBY4M3LJptYUYqJ3LdxYPAO0AeKdlO0e843BaeG1ASGlhCZRGOn
Z0xYL7wBiVgwtx57y7D1zpQE1a9Mh7+jnUnH4tplYv70TsQ9wF5yuRsAEQEAAYkC
JQQYAQIADwIbDAUCWxW0LAUJBakobgAKCRCYRuzvvweIvhSvD/0RlNWzls+OlLqc
6NT/ZGGpECntrfTpQqA0MakuuIkf6/RgBU4OfHqqcBrG8onV1vuu0hH0KnYvv658
01PHRthIZN1K/+jcaIdNkOLbl9k9rWaDxKkHzH+IcIF61b6oCW86ffZ1yzZE2nxY
zjO0MVM4IlUxtHbrItVj5Itfj7QxPf1YdmLxO/xH2NRbmVwwTSKe4/xK3v1LSxlH
R5vknUWIT69VJoINB7jye/aSpBYpJgxXXiGbdYyt6fLyLxds7HXLJ6v6SfBKLuvm
qXsDWX5Jyv7EWzzHgl7iCIkCe9tr8ic9IyyWiRn5bjoo5vVFVMYWU/ZtGpYerAh/
sp8M09q7OeJbtbYkh9Nqz78ALt7ldT+PeAmqBVqb188F3tG8yG9lQNy43bk90Q8N
o7ZpjxlpKFYoSq/Ow7TZgMCdMPkGbcCaSkGDv/McGsaOa7IYf+7n/yE0xMSb0x5A
wyaXSLZjScTPiKGAP4fAf6hb0H1Je/69DOJynaODONAYfmlM+9WQjuVlQ3J0rlcK
Cgvon6XyxrZ4q1tKheAxOmyM0OSuPYyeXSqCYu5bt7MzNEDUChydZM3v1QvEB9iG
6eLX7fq2ipGGDoVl8y1yVRe4BWa3cJfCPC7jT+9VtD/qdlpHSwotYTWj5metYgqB
LTIbdd7r9XCGoKIxMJRqNFXc8kylUg==
=J0NH
-----END PGP PUBLIC KEY BLOCK-----
```

View file

@ -0,0 +1,157 @@
import org.bitcoins.core.config.RegTest
import org.bitcoins.core.crypto.ECPrivateKey
import org.bitcoins.core.currency.Satoshis
import org.bitcoins.core.number.{Int32, Int64, UInt32}
import org.bitcoins.core.protocol.script.P2PKHScriptPubKey
import org.bitcoins.core.protocol.transaction.{
BaseTransaction,
Transaction,
TransactionOutPoint,
TransactionOutput
}
import org.bitcoins.core.script.crypto.HashType
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder
import org.bitcoins.core.wallet.fee.SatoshisPerByte
import org.bitcoins.core.wallet.utxo.BitcoinUTXOSpendingInfo
import org.scalatest.{FlatSpec, MustMatchers}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
class TxBuilderExample extends FlatSpec with MustMatchers {
behavior of "TxBuilderExample"
it must "build a signed tx" in {
//This is a documented example of how to create a signed bitcoin transaction
//with bitcoin-s. You can run this test case with the following sbt command
//$ sbt "doc/testOnly *TxBuilderExample -- -z signed"
//generate a fresh private key that we are going to use in the scriptpubkey
val privKey = ECPrivateKey.freshPrivateKey
//this is the script that the TxBuilder is going to create a
//script signature that validly spends this scriptPubKey
val creditingSpk = P2PKHScriptPubKey(pubKey = privKey.publicKey)
val amount = Satoshis(Int64(10000))
//this is the utxo we are going to be spending
val utxo =
TransactionOutput(currencyUnit = amount, scriptPubKey = creditingSpk)
//the private key that locks the funds for the script we are spending too
val destinationPrivKey = ECPrivateKey.freshPrivateKey
//the amount we are sending -- 5000 satoshis -- to the destinationSPK
val destinationAmount = Satoshis(Int64(5000))
//the script that corresponds to destination private key, this is what is protecting the money
val destinationSPK =
P2PKHScriptPubKey(pubKey = destinationPrivKey.publicKey)
//this is where we are sending money too
//we could add more destinations here if we
//wanted to batch transactions
val destinations = {
val destination1 = TransactionOutput(currencyUnit = destinationAmount,
scriptPubKey = destinationSPK)
List(destination1)
}
//we have to fabricate a transaction that contains the
//utxo we are trying to spend. If this were a real blockchain
//we would need to reference the utxo set
val creditingTx = BaseTransaction(version = Int32.one,
inputs = List.empty,
outputs = List(utxo),
lockTime = UInt32.zero)
//this is the information we need from the crediting tx
//to properly "link" it in the transaction we are creating
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
// this contains all the information we need to
// validly sign the utxo above
val utxoSpendingInfo = BitcoinUTXOSpendingInfo(outPoint = outPoint,
output = utxo,
signers = List(privKey),
redeemScriptOpt = None,
scriptWitnessOpt = None,
hashType =
HashType.sigHashAll)
//all of the utxo spending information, since we are only
//spending one utxo, this is just one element
val utxos: List[BitcoinUTXOSpendingInfo] = List(utxoSpendingInfo)
//this is how much we are going to pay as a fee to the network
//for this example, we are going to pay 1 satoshi per byte
val feeRate = SatoshisPerByte(Satoshis.one)
val changePrivKey = ECPrivateKey.freshPrivateKey
val changeSPK = P2PKHScriptPubKey(pubKey = changePrivKey.publicKey)
// the network we are on, for this example we are using
// the regression test network. This is a network you control
// on your own machine
val networkParams = RegTest
//yay! Now we have a TxBuilder object that we can use
//to sign the tx.
val txBuilder: Future[BitcoinTxBuilder] = {
BitcoinTxBuilder(
destinations = destinations,
utxos = utxos,
feeRate = feeRate,
changeSPK = changeSPK,
network = networkParams
)
}
txBuilder.failed.foreach { case err => println(err.getMessage) }
//let's finally produce a validly signed tx
//The 'sign' method is going produce a validly signed transaction
//This is going to iterate through each of the 'utxos' and use
//the corresponding 'UTXOSpendingInfo' to produce a validly
//signed input. This tx has a
//
//1 input
//2 outputs (destination and change outputs)
//3 a fee rate of 1 satoshi/byte
val signedTxF: Future[Transaction] = txBuilder.flatMap(_.sign)
//let's print these things out so you can example them
signedTxF.map { tx =>
println("\nInputs:")
tx.inputs.foreach(println)
println("\nOutputs:")
tx.outputs.foreach(println)
//here is the fully signed serialized tx that
//you COULD broadcast to a cryptocurrency p2p network
println(s"\nFully signed tx in hex:")
println(s"${tx.hex}")
}
//The output from the print statements should read something like this
//Inputs:
//TransactionInputImpl(TransactionOutPointImpl(DoubleSha256DigestImpl(43c75d1d59e6f13f2ad3baf6e124685ba0919bccdbdf89c362fe2f30fee4bdfc),UInt32Impl(0)),P2PKHScriptSignature(6a4730440220573a7bbbd59192c4bf01b8f1dcafe981d11ab8528fead9d66d702c1b72e5dc76022007946a423073c949e85a4ca3901ab10a2d6b72873a347d2a55ef873016adae8601210356d581971934349333066ed933cdea45ae9c72829ce34d8dd6a758d56967e4cb),UInt32Impl(0))
//
//Outputs:
//TransactionOutputImpl(SatoshisImpl(Int64Impl(5000)),P2PKHScriptPubKeyImpl(1976a914dbdadae42124c46a00d81181e5d9ab28fbf546ed88ac))
//TransactionOutputImpl(SatoshisImpl(Int64Impl(4774)),P2PKHScriptPubKeyImpl(1976a914a95eb0d284593f0c8f818f64a55fa6e3852012a688ac))
//
//Fully signed tx in hex:
//020000000143c75d1d59e6f13f2ad3baf6e124685ba0919bccdbdf89c362fe2f30fee4bdfc000000006a4730440220573a7bbbd59192c4bf01b8f1dcafe981d11ab8528fead9d66d702c1b72e5dc76022007946a423073c949e85a4ca3901ab10a2d6b72873a347d2a55ef873016adae8601210356d581971934349333066ed933cdea45ae9c72829ce34d8dd6a758d56967e4cb000000000288130000000000001976a914dbdadae42124c46a00d81181e5d9ab28fbf546ed88aca6120000000000001976a914a95eb0d284593f0c8f818f64a55fa6e3852012a688ac00000000
//remember, you can call .hex on any bitcoin-s data structure to get the hex representation!
}
}

View file

@ -1,3 +1,5 @@
[ ![Download](https://api.bintray.com/packages/bitcoin-s/bitcoin-s-core/bitcoin-s-eclair-rpc/images/download.svg) ](https://bintray.com/bitcoin-s/bitcoin-s-core/bitcoin-s-eclair-rpc/_latestVersion)
# Bitcoin-s Eclair RPC client
This is a RPC client for [Eclair](https://github.com/acinq/eclair). It assumes that a bitcoind instance is running.

View file

@ -117,4 +117,9 @@ object Deps {
Compile.slf4j,
"org.scalacheck" %% "scalacheck" % V.scalacheck withSources() withJavadoc()
)
val doc = List(
Test.scalaTest,
Test.logback
)
}

1
rpc/README.md Normal file
View file

@ -0,0 +1 @@
[ ![Download](https://api.bintray.com/packages/bitcoin-s/bitcoin-s-core/bitcoin-s-bitcoind-rpc/images/download.svg) ](https://bintray.com/bitcoin-s/bitcoin-s-core/bitcoin-s-bitcoind-rpc/_latestVersion)

60
secp256k1jni/README.md Normal file
View file

@ -0,0 +1,60 @@
[ ![Download](https://api.bintray.com/packages/bitcoin-s/bitcoin-s-core/bitcoin-s-secp256k1jni/images/download.svg) ](https://bintray.com/bitcoin-s/bitcoin-s-core/bitcoin-s-secp256k1jni/_latestVersion)
## Secp256k1jni
This project gives people a conviinent way to use [libsecp256k1](https://github.com/bitcoin-core/secp256k1) on the JVM without compiling natives themselves.
Currently we have support for natives on
1. [linux 32 bit](natives/linux_32)
2. [linux 64 bit](natives/linux_64)
3. [mac osx 64 bit](natives/osx_64)
This uses a zero depdency library called `native-lib-loader`. The does the appropriate loading of the library onto your classpath to be accessed. To tell if you have access to libsecp256k1 you can do the following
```scala
sbt:root> project secp256k1jni
[info] Set current project to bitcoin-s-secp256k1jni (in build file:/home/chris/dev/bitcoin-s-core/)
sbt:bitcoin-s-secp256k1jni> console
[info] Starting scala interpreter...
Welcome to Scala 2.12.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_191).
Type in expressions for evaluation. Or try :help.
scala> import org.bitcoin.Secp256k1Context;
scala> Secp256k1Context.isEnabled()
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
res0: Boolean = true
```
## Adding secp256k1jni to your project
To add `bitcoin-s-secp256k1jni` to your project you add it like this
```
"org.bitcoins" % "bitcoin-s-secp256k1jni" % "0.0.1"
```
or with maven
```
<dependency>
<groupId>org.bitcoins</groupId>
<artifactId>bitcoin-s-secp256k1jni</artifactId>
<version>0.0.1</version>
</dependency>
```
## Using secp256k1
The file [NativeSecp256k1.java](src/main/java/org/bitcoin/NativeSecp256k1.java) contains basic functionality for
1. Verifying digital signatures
2. Producing digital signatures
3. Computing a public key from a private key
4. tweaking keys
5. Checking public key validity

1
testkit/README.md Normal file
View file

@ -0,0 +1 @@
[ ![Download](https://api.bintray.com/packages/bitcoin-s/bitcoin-s-core/bitcoin-s-testkit/images/download.svg) ](https://bintray.com/bitcoin-s/bitcoin-s-core/bitcoin-s-testkit/_latestVersion)