8.0 KiB
Contributing to Bitcoin-S
Bitcoin-S is an open source project where anyone is welcome to contribute. All contributions are encouraged and appreciated, whether that is code, testing, documentation or something else entirely.
Communication Channels
It's possible to communicate with other developers through a variety of communication channels:
- Suredbits Slack - Suredbits is a company monetizing APIs through the Lightning Network. Suredbits doesn't own Bitcoin-S, but the Suredbits CEO Chris Stewart is the maintainer of this library. There's a separate Bitcoin-S channel on their Slack, this is probably the easiest way of getting in touch with someone working on this project.
- Bitcoin-S Gitter
- #bitcoin-scala on IRC Freenode
Documentation
One of the goals of Bitcoin-S is having useful and well-formatted Scaladoc comments on classes, objects and functions. Here are some useful resources on how to properly format your Scaladoc comments:
- Scaladoc for library authors
- Guidelines used by the official Scala language Scaladoc
Bitcoin-S static site
Bitcoin-S comes bundles with it's own web site with documentation about the library. It consists if the generated Scaladoc of the project, as well as the content of src/site
.
Working with documentation locally
View generated site:
$ sbt previewSite
Publishing Bitcoin-S site
$ sbt ghpagesPushSite
Read more on the sbt-ghpages
sbt plugin.
Note: some setup is required before doing this the first time
From thesbt-ghpages
documentation:
Before using sbt-ghpages, you must create the gh-pages branch in your repository and push the branch to GitHub. The quick steps are:
# Using a fresh, temporary clone is safest for this procedure
$ pushd /tmp
$ git clone git@github.com:youruser/yourproject.git
$ cd yourproject
# Create branch with no history or content
$ git checkout --orphan gh-pages
$ git rm -rf .
# Establish the branch existence
$ git commit --allow-empty -m "Initialize gh-pages branch"
$ git push origin gh-pages
# Return to original working copy clone, we're finished with the /tmp one
$ popd
$ rm -rf /tmp/yourproject
Development
testkit
and circular dependencies
One issue you might run into when making local changes and compiling/testing is a circular depedency conundrum. Bitcoin-S has multiple submodules, such as rpc
(interfacing with a Bitcoin Core RPC server), eclair-rpc
(interfacing with an Eclair RPC server) and testkit
(utilities for spinning up Bitcoin and Lightning nodes, etc.). testkit
depends on rpc
and eclair-rpc
, and both rpc
and eclair-rpc
tests depend on testkit
. This causes the compiler and build system to have a hard time. We solve this problem by publishing testkit
as a standalone project in Bintray.
Now, what happens when you need to make changes in testkit
, and have either rpc
or eclair-rpc
utilize these changes?
- Change the value of
version in ThisBuild
inversion.sbt
by adding-SNAPSHOT
to it. If the file previously wasversion in ThisBuild := "0.0.1"
, change it toversion in ThisBuild := "0.0.1-SNAPSHOT"
- If you have an active sbt shell/session, do
sbt reload
- Compile the project:
sbt compile
- Publish the freshly compiled project locally:
sbt publishLocal
. This places the newly compiled files into~/.ivy2/local/org.bitcoins
, where sbt will find them. - Change the value of
Deps.V.bitcoinsV
inproject/Deps.scala
to the version number you previously changed inversion.sbt
. In our case,"0.0.1-SNAPSHOT"
. - If you have an active sbt shell/session, do
sbt reload
again - You should now be good to go, and both
sbt test:compile
andsbt test
should work without issues.
Testing
Property based testing
This library aims to achieve high level of correctness via property based testing. At the simplest level, you can think of property based testing as specifying a invariant that must always hold true. Here 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 (see TransactionSignatureCreatorSpec
line 29-34). First we generate a supposedly validly signed multisig transaction with TransactionGenerators.signedMultiSigTransaction
(line 102-108). These transactions have varying m
of n
requirements. An interesting corner case if when you have 0 of n
signatures, which means no signature is required. Property based testing is really good at fleshing out these corner cases. We check to see if this transaction is valid by running it through our ScriptInterpreter
. 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
}
Running tests
To run the entire test suite all you need to do is run the following command
$ sbt test
[info] Elapsed time: 4 min 36.760 sec
[info] ScalaCheck
[info] Passed: Total 149, Failed 0, Errors 0, Passed 149
[info] ScalaTest
[info] Run completed in 4 minutes, 55 seconds.
[info] Total number of tests run: 744
[info] Suites: completed 97, aborted 0
[info] Tests: succeeded 744, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
[info] Passed: Total 909, Failed 0, Errors 0, Passed 909
[success] Total time: 297 s, completed Jul 20, 2017 10:34:16 AM
To run a specific suite of tests you can specify the suite name in the following way
$ sbt testOnly *ScriptInterpreterTest*
[info] ScriptInterpreterTest:
[info] ScriptInterpreter
[info] - must evaluate all the scripts from the bitcoin core script_tests.json
[info] Run completed in 8 seconds, 208 milliseconds.
[info] Total number of tests run: 1
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
[info] All tests passed.
The command sbt testQuick
can also be handy. It runs tests that either:
- Failed previously
- Has not been run previously
- Either the test or one of its dependencies has been recompiled
For more information on testQuick
, see the offical sbt docs.