* Implementation of LnCurrencyUnit Fix unary and unneeded comments. Refactor and change arithmetic to use PicoBitcoins. Add property based testing for LnCurrencyUnits Refactor LnCurrencyUnits after code review Fix case and change LnPolicy to val Remove division and add Unit tests * Add additional unit tests and deserialization * WIP: Implement LnHumanReadablePart (#190) * Initial Implementation of LnHumanReadablePart * Add unit tests and improve deserialization from string * Refactor LnParams and LnHrp. Add requirements for instantiating LnHrp. * Clean up and re-organize things Re-working LnHumanReadablePart.fromString Fix unnecessary pattern match Removing test case * Created eclairRpc project (#193) Added getinfo functionality Added connect functionality Added most of the rpcs Added send and checkpayment functionality Added updaterelayfee functionality Fixed compile errors Ran scalafmt Added DaemonInstance and start/stop methods Added TestUtil Added open test Fixed typo in allUpdates Fixed ChannelResult Add eclair prefix to rpc stuff open channel unit test passing Adding instructions to grab default eclair in build Add zmq config to bitcoin.conf rename test log files, bump timeouts on connections Add eclair-rpc README, rework some RpcUtil/TestUtil stuff for async fixing bug in precious block, addressing code review Address more code review comments * Add NodeId, NodeUri, ChannelId (#196) refactor json serializing methods to SerializerUtil, add more types * Adding LnCurrencyUnit types to rpc api, fixing bug where eclair tests were not binding to a random port for zmq (#198) Remove start stuff * Adding more rpc tests, testing open, payment over channel, and closing of the channel (#199) Add checkpayment tests Address code review, create EclairTestUtil.createNodPair * Two way eclair transactions sanity test (#200) * Added a test for sending payments in both directions * Updated travis bitcoin core version * Initial LnInvoice Implementation (#194) Start typing some ln invoice stuff Add support for Fallback Address encoding Part 1: Breaking out Bech32 specific functions into a util class, don't embed in Bech32Address re-naming fromBase8ToBase5 -> from8BitTo5bit Part 1: Breaking out Bech32 specific functions into a util class, don't embed in Bech32Address rework ln invoices tags fix more method names in Bech32 Rename ScriptPubKeyTag -> NodeIdTag All invoice tags tests passing except weird serialization order one Address code review, add some more comments rename 'LnInvoiceTags' -> 'LnInvoiceTaggedFields' create a UInt5 type to represent all of the bech32 data structures Passing all serialization in the BOLT11 examples First cut at deserialization * Adding bitcoin-s types to the eclair-rpc, fixing bug with decoding numbers, refactoring more things (#204) * Switch bech32 p2wpkh hash from RipdeMd160 -> Sha256Hash160Digest (#206) * Add testkit project / dependency (#209) fix core-gen build.sbt name * add correct dependencies to testkit (#210) * Get dep name right (#211) * Add serialization symmetry property for LnInvoice, fixing various bugs in LnInvoice data structures, adding generators for various LnInvoice data structures (#217) * Reworking AuthCredentials and Instances so that we can read from config files (#218) add core files that were missing * Reworking a lot of testkit data structures to be more helpful for testing (#219) Add missing EclairApi file remove noisy log * Rebase onto master, fix testkit compile issues * Simplify LnCurrencyUnit, add MilliSatoshis, refactor EclairRpc to use… (#226) * Simplify LnCurrencyUnit, add MilliSatoshis, refactor EclairRpc to use MilliSatoshis * Add some helper functions around millisatoshis for comparing them to other things * more tests / helper methods, at generator for millisatoshis * Fix typo * Fix comparison operators for millisatoshis, add Writes for MilliSatos… (#227) * Fix comparison operators for millisatoshis, add Writes for MilliSatoshis in JsonWriters * re-add comparison operators to LnCurrencyUnit for convinience * Add millisatoshi reads (#228) * Updating version of eclair to https://github.com/ACINQ/eclair/releases/download/v0.2-beta8/eclair-node-0.2-beta8-52821b8.jar (#229) * Derive nodeId from ln invoice signature, move nodeid case class into … (#230) * Derive nodeId from ln invoice signature, move nodeid case class into the core project * Add missing assert * Fix null pointer exception that could occurred during requiring the invoice's signature to valid. This could occurr if a user tried to construct an invoice with an invalid signature (#233) * Turn down logging / remove logging (#235) * Cleaned up eclair conf (#237) * Cleaned up eclair conf * Added test for bad auth and Reads for LnInvoice * WIP: rebase onto master with new compiler opts fix more compiler warnings with testkit * fix new compiler warnings for scalac 2.12.x on ln (#253) * fix new compiler warnings for scalac 2.12.x on ln * fix missing p2wpkhoutput in rawoutput testkit/CreditingTxGen.scala * First cut at code review for the ln branch (#258) Fix bug in parsing the LnTagPrefix.CltvExpiry, add properties that check if the NodeIdTag is given explicitly to the invoice remove dumb invariants revert version * 2018 12 4 ln code review rd2 (#259) * Amend EclairRpc test case for confirming that channel is closed * Add final check to test case to make sure the bitcoind wallet received funds when closing channel * Address Torkel's code review * Addresses some review on #256 (#260) * Docstring cleanup, small nits * Refactors some redudant data, nested if => switch * Fixes SO error by reversing remowal of `new` * Fixes a couple of bugs * map.get instead of list.find * StringBuilder in HRP * Rework NetworkParam to LnParam * Cleanup * Renames file to match trait/object name * Docstring cleanup, pure formatting * Simplifies a few expressions, doesn't change semantics * Adds overloaded findRoute method instead of Either[NodeId, LnInvoice] * Eclair cleanup * Address concerns from Chris * Type annotation to match case * Address nadav's code review |
||
---|---|---|
bench | ||
core | ||
core-gen/src/test/scala/org/bitcoins/core/gen/ln | ||
core-test | ||
eclair-rpc | ||
project | ||
rpc/src | ||
secp256k1@2e16ac7d6c | ||
secp256k1jni | ||
testkit/src/main/scala/org/bitcoins | ||
zmq/src | ||
.gitignore | ||
.gitmodules | ||
.jvmopts | ||
build.sbt | ||
inThisBuild.sbt | ||
LICENSE | ||
README.md |
Bitcoin-S-Core
This is the core functionality of bitcoin-s.
This repostitory includes the following functionality:
- Native Scala objects for various protocol types (transactions, inputs, outputs, scripts signatures, scriptpubkeys)
- Serializers and deserializers for bitcoin data structures mentioned above
- An implementation of Bitcoin's Script programming language
- Passes all tests found in Bitcoin Core's regression test suite called script_test.json
- Passes all tests inside of Bitcoin Core's transaction regression test suite tx_valid.json / tx_invalid.json / sighash.json
- Payment channel support
- Integration with bitcoin core's optimized secp256k1 library
- Consensus rule set up to date through segregated witness
- A robust set of generators, 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
- Number types to represent C's unsigned numbers (
UInt8
,UInt32
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
Design Principles
- Immutable data structures everywhere
- Algebraic Data Types to allow the compiler to check for exhaustiveness on match statements
- Using property based testing to test robustness of code
Setting up libsecp256k1
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
$ 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
By default, .jvmopts
adds the freshly compiled secp256k1
to the JVM classpath. It should therefore be sufficient to start sbt
by simply doing
$ sbt
If you run into classpath problems, you can manually specify the JVM classpath like this:
$ sbt -Djava.library.path=/your/class/path
You can also copy libsecp256k1.so
to your system library path.
Property based testing
This library aims to achieve high level of correctness via property based testing. At the simplest level, you can think of property based testing as specifying a invariant that must always hold true. Here is an example of a property in the bitcoin-s-core test suite
property("Serialization symmetry") =
Prop.forAll(TransactionGenerators.transactions) { tx =>
Transaction(tx.hex) == tx
}
What this property says is that for every transaction we can generate with TransactionGenerators.transactions
we must be able to serialize it to hex format, then deserialize it back to a transaction and get the original tx
back.
A more complex example of property based testing is checking that a multi signature transaction was signed correctly. First we generate a supposedly validly signed multisig transaction with TransactionGenerators.signedMultiSigTransaction
. These transactions have varying m of n requirements. An interesting corner case if when you have 0 of n signatures. Which means no signature is required. Property based testing is really good at fleshing out these corner cases. We check to see if this transaction is valid by running it through our ScriptInterpreter
. If we have built our functionality correctly the ScriptInterpreter should always return ScriptOk
indicating the script was valid.
property("generate valid signatures for a multisignature transaction") =
Prop.forAllNoShrink(TransactionGenerators.signedMultiSigTransaction) {
case (txSignatureComponent: TxSigComponent, _) =>
//run it through the interpreter
val program = ScriptProgram(txSignatureComponent)
val result = ScriptInterpreter.run(program)
result == ScriptOk
}
TODO
- Simple
TransactionBuilder
similar nbitcoin - Hardware wallet support (trezor, ledger etc)
- Lightning network scriptpubkey types (some work done here)
- sbt respository
- Java support
- Android support
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.
$ 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
Examples
Every bitcoin protocol data structure (and some other data structures) extends NetworkElement
. NetworkElement provides easier methods to convert the data structure to hex or a byte representation. When paired with our Factory
we can easily serialize and deserialize data structures. Most data structures have companion objects that extends Factory
to be able to easily create protocol data structures. An example of this is the ScriptPubKey
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
$ 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._
scala> val hexTx = "0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f8$9c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1$a02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e$90c865c2f6f7a9710a474154ab1423abb5b9288ac00000000"
hexTx: String = 0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1$52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02$45f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c$65c2f6f7a9710a474154ab1423abb5b9288ac00000000
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))
scala> val hexAgain = tx.hex
hexAgain: String = 0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f88$c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1f$02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e6$0c865c2f6f7a9710a474154ab1423abb5b9288ac00000000
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
. 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
, ScriptPubKey
, ScriptWitness
, and Block
.
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:
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
Running tests
To run the entire test suite all you need to do is run the following command
chris@chris:~/dev/bitcoins-core$ sbt test
[info] Elapsed time: 4 min 36.760 sec
[info] ScalaCheck
[info] Passed: Total 149, Failed 0, Errors 0, Passed 149
[info] ScalaTest
[info] Run completed in 4 minutes, 55 seconds.
[info] Total number of tests run: 744
[info] Suites: completed 97, aborted 0
[info] Tests: succeeded 744, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[info] Passed: Total 909, Failed 0, Errors 0, Passed 909
[success] Total time: 297 s, completed Jul 20, 2017 10:34:16 AM
chris@chris:~/dev/bitcoin-s-core$
To run a specific suite of tests you can specify the suite name in the following way
chris@chris:~/dev/bitcoins-core$ sbt
> test-only *ScriptInterpreterTest*
[info] ScriptInterpreterTest:
[info] ScriptInterpreter
[info] - must evaluate all the scripts from the bitcoin core script_tests.json
[info] Run completed in 8 seconds, 208 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
>
Interacting with bitcoind
Please see our other project bitcoin-s-rpc-client
Stand alone SPV Node
Please see our other project bitcoin-s-spv-node