WIP: Docusaurus website (#465)

* Docs: Introduce new Docusaurus-based website

This commit is the result of running
npx docusaurus-init, and nothing more.
Further changes will happen on top of
this, to make it easier to review changes
and update to newer versions of Docusaurus
in the future.

* WIP: Add Bitcoin-S website

Change the default Docusaurus template to a custom website. Goes
off of existing documentation, and moves it into the new docs and
website directories. Deletes some unused files, such as
BUILD_README.md

* Initial mdoc support

* Add Scaladoc to website

* Add SVG assets

* Change colors, flesh out pages, correct Scaladoc links

* Rename doc project to scripts, move security doc to website

* Add copy buttons to website code snippets

* Add doc and tasks for publishing website

* Refactor how paths get copied after generating Scaladocs

* Add Get Started button

* Replace bitcoin-s logo with white text

* Add Montserrat font for headers

* flesh out user showcase and landing page

* Change Scaladoc URL to bitcoins package
This commit is contained in:
Torkel Rogstad 2019-05-15 01:05:14 +02:00 committed by Chris Stewart
parent 0841995ab0
commit 6bc6378f96
58 changed files with 8087 additions and 541 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
*/node_modules
*.log

5
.gitignore vendored
View File

@ -68,3 +68,8 @@ build-aux/compile
build-aux/test-driver
src/stamp-h1
libsecp256k1.pc
# Docusaurs
node_modules
website/build
website/static/api

View File

@ -1,188 +1 @@
# 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](https://join.slack.com/t/suredbits/shared_invite/enQtNDEyMjY3MTg1MTg3LTYyYjkwOGUzMDQ4NDAwZjE1M2I3MmQyNWNlZjNlYjg4OGRjYTRjNWUwNjRjNjg4Y2NjZjAxYjU1N2JjMTU1YWM) - 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](https://gitter.im/bitcoin-s-core/)
- [#bitcoin-scala](https://webchat.freenode.net/?channels=bitcoin-scala) on IRC Freenode
## Developer productivity
### Bloop
If you're tired of waiting around for sbt all day, there's a new,
cool kid on the block. It is called [Bloop](https://scalacenter.github.io/bloop/),
and it makes compilations in general faster, and in particular
incremental, small compilation units (which greatly help editor
performance). Bloop is a server that runs in the background of
your computer, and keeps several "hot" JVMs running at all
times. These JVMs serve compilation requests. Because the JVMs
are running in the background you avoid the startup lag, and you
also get code that's already [JIT compiled](https://en.wikipedia.org/wiki/Just-in-time_compilation)
for you.
The documentation on Bloops [site](https://scalacenter.github.io/bloop/) is good, but here is the highlights:
1. Install Bloop by doing step 1 & 2 in the [official guide](https://scalacenter.github.io/bloop/setup#universal)
2. Enable the Bloop background daemon
1. macOS:
```bash
$ brew services start bloop
```
2. Ubuntu:
```bash
$ systemctl --user enable $HOME/.bloop/systemd/bloop.service
$ systemctl --user daemon-reload
$ systemctl --user start bloop
```
3. Enable shell completion for the Bloop CLI
1. Bash:
```bash
$ echo '. $HOME/.bloop/bash/bloop' >> $HOME/.bash_profile
```
2. Zsh:
```bash
$ echo 'autoload -U compinit' >> $HOME/.zshrc
$ echo 'fpath=($HOME/.bloop/zsh $fpath)' >> $HOME/.bashrc
$ echo 'compinit' >> $HOME/.bashrc
```
3. Fish:
```bash
$ ln -s $HOME/.bloop/fish/bloop.fish ~/.config/fish/completions/bloop.fish
```
4. Generate configuration files
```bash
$ sbt bloopInstall
```
5. Import Bitcoin-S into IntelliJ again, as a bsp (Build Server Protocol) project (instead of a sbt project). Make sure you're running on the most recent IntelliJ and Scala plugin. See [official docs](https://scalacenter.github.io/bloop/docs/ides/intellij) for details.
6. _(Bonus step):_ Lightning fast recompilations on file save:
```bash
$ bloop compile --project <name of module your're working on> --watch
```
Your editor should now be much faster and require less resources :tada:
## 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](https://docs.scala-lang.org/overviews/scaladoc/for-library-authors.html)
- [Guidelines](https://docs.scala-lang.org/style/scaladoc.html) 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:
```bash
$ sbt previewSite
```
### Publishing Bitcoin-S site
```bash
$ sbt ghpagesPushSite
```
Read more on the [`sbt-ghpages`](https://github.com/sbt/sbt-ghpages) sbt plugin.
> Note: some setup is required before doing this the first time
> From the `sbt-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:
```bash
# 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
```
## 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](https://github.com/bitcoin-s/bitcoin-s-core/blob/89fbf35d78046b7ed21fd93fec05bb57cba023bb/src/test/scala/org/bitcoins/core/protocol/transaction/TransactionSpec.scala#L13-L17) is an example of a property in the bitcoin-s-core test suite
```scala
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`](testkit/src/main/scala/org/bitcoins/core/gen/TransactionGenerators.scala) 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`](core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureCreatorSpec.scala) line 29-34). First we generate a _supposedly_ validly signed multisig transaction with [`TransactionGenerators.signedMultiSigTransaction`](testkit/src/main/scala/org/bitcoins/core/gen/TransactionGenerators.scala) (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`](core/src/main/scala/org/bitcoins/core/script/interpreter/ScriptInterpreter.scala). If we have built our functionality correctly the ScriptInterpreter should always return [`ScriptOk`](core/src/main/scala/org/bitcoins/core/script/result/ScriptResult.scala) indicating the script was valid.
```scala
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
```scala
$ 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
```scala
$ 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:
1. Failed previously
2. Has not been run previously
3. Either the test or one of its dependencies has been recompiled
For more information on `testQuick`, see the offical [sbt docs](https://www.scala-sbt.org/1.x/docs/Testing.html#testQuick).
See the contribution guide on the Bitcoin-S [website](https://bitcoin-s.org/docs/contributing)

10
Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM node:8.11.4
WORKDIR /app/website
EXPOSE 3000 35729
COPY ./docs /app/docs
COPY ./website /app/website
RUN yarn install
CMD ["yarn", "start"]

View File

@ -1,58 +1,2 @@
[ ![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)
> Note: `bitcoin-s-bitcoind-rpc` requires you to have `bitcoind` (Bitcoin Core daemon) installed. Grab this at [bitcoincore.org](https://bitcoincore.org/en/download/)
## Usage
The Bitcoin Core RPC client in Bitcoin-S currently supports the Bitcoin Core 0.16 and 0.17
version lines. It can be set up to work with both local and remote Bitcoin Core servers.
### Basic example
```scala
import org.bitcoins.rpc
import rpc.client.common._
import rpc.config.BitcoindInstance
import akka.actor.ActorSystem
// data directory defaults to ~/.bitcoin on Linux and
// ~/Library/Application Support/Bitcoin on macOS
val bitcoindInstance = BitcoindInstance.fromDatadir()
// alternative:
import java.io.File
val dataDir = new File("/my/bitcoin/data/dir")
val otherInstance = BitcoindInstance.fromDatadir(dataDir)
implicit val actorSystem: ActorSystem = ActorSystem.create()
val client = new BitcoindRpcClient(bitcoindInstance)
for {
_ <- client.start()
balance <- client.getBalance
} yield balance
```
### Advanced example
TODO: How to connect to remote bitcoind
## Testing
To test the Bitcoin-S RPC project you need both version 0.16 and 0.17 of Bitcoin Core. A list of current and previous releases can be found [here](https://bitcoincore.org/en/releases/).
You then need to set environment variables to indicate where Bitcoin-S can find the different versions:
```bash
$ export BITCOIND_V16_PATH=/path/to/v16/bitcoind
$ export BITCOIND_V17_PATH=/path/to/v17/bitcoind
```
If you just run tests testing common functionality it's enough to have either version 0.16 or 0.17 on your `PATH`.
To run all RPC related tests:
```bash
$ bash sbt bitcoindRpcTest/test
```
See the `bitcoind`/Bitcoin Core section on the
Bitcoin-S [website](https://bitcoin-s.org/docs/rpc/rpc-bitcoind).

159
build.sbt
View File

@ -16,6 +16,7 @@ lazy val commonCompilerOpts = {
"128"
)
}
//https://docs.scala-lang.org/overviews/compiler-options/index.html
lazy val compilerOpts = Seq(
"-target:jvm-1.8",
@ -36,13 +37,9 @@ lazy val compilerOpts = Seq(
lazy val testCompilerOpts = commonCompilerOpts
lazy val commonSettings = List(
organization := "org.bitcoin-s",
homepage := Some(url("https://bitcoin-s.org")),
developers := List(
Developer(
"christewart",
@ -51,23 +48,15 @@ lazy val commonSettings = List(
url("https://twitter.com/Chris_Stewart_5")
)
),
scalacOptions in Compile := compilerOpts,
scalacOptions in Test := testCompilerOpts,
//show full stack trace of failed tests
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-oF"),
//show duration of tests
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-oD"),
assemblyOption in assembly := (assemblyOption in assembly).value
.copy(includeScala = false),
licenses += ("MIT", url("http://opensource.org/licenses/MIT")),
/**
* Adding Ammonite REPL to test scope, can access both test and compile
* sources. Docs: http://ammonite.io/#Ammonite-REPL
@ -87,7 +76,7 @@ lazy val commonSettings = List(
)
lazy val commonTestSettings = Seq(
publish / skip := true,
publish / skip := true
) ++ commonSettings
lazy val commonProdSettings = Seq(
@ -107,20 +96,84 @@ lazy val bitcoins = project
eclairRpc,
eclairRpcTest,
testkit,
doc
scripts
)
.settings(commonSettings: _*)
.settings(crossScalaVersions := Nil)
.settings(libraryDependencies ++= Deps.root)
.enablePlugins(ScalaUnidocPlugin, GhpagesPlugin, GitVersioning)
.enablePlugins(ScalaUnidocPlugin, GitVersioning)
.settings(
// scaladoc settings
// TODO this is not working properly
inTask(unidoc)(
scalacOptions in Compile ++= List(
"-doc-title",
"Bitcoin-S",
"-doc-version",
version.value
)),
// we modify the unidoc task to move the generated Scaladocs into the
// website directory afterwards
Compile / unidoc := {
import java.nio.file._
import scala.collection.JavaConverters._
val logger = streams.value.log
def cleanPath(path: Path, isRoot: Boolean = true): Unit = {
if (Files.isDirectory(path)) {
path.toFile.list().map { file =>
val toClean = path.resolve(file)
cleanPath(toClean, isRoot = false)
}
if (isRoot) ()
else Files.deleteIfExists(path)
} else if (isRoot) {
()
} else if (path.toString.endsWith(".gitkeep")) {
()
} else {
Files.deleteIfExists(path)
}
}
val websiteScaladocDir =
Paths.get("website", "static", "api").toAbsolutePath
logger.info(s"Cleaning website Scaladoc directory $websiteScaladocDir")
cleanPath(websiteScaladocDir)
// returned value is a list of files,
// list has one element
val generatedDir = (Compile / unidoc).value.head
logger.info(s"Moving files in $generatedDir to $websiteScaladocDir")
try {
Files
.walk(generatedDir.toPath)
.iterator()
.asScala
.drop(1) // skip the root directory
.foreach { child =>
val pathDiff = generatedDir.toPath.relativize(child)
Files.copy(child,
websiteScaladocDir.resolve(pathDiff),
StandardCopyOption.REPLACE_EXISTING)
}
} catch {
case e: Throwable =>
logger.err(
"Error when copying Scaladocs to website folder: ${e.toString}")
throw e
}
Seq(generatedDir)
}
)
.settings(
name := "bitcoin-s",
ScalaUnidoc / siteSubdirName := "latest/api",
addMappingsToSiteDir(ScalaUnidoc / packageDoc / mappings, ScalaUnidoc / siteSubdirName),
gitRemoteRepo := "git@github.com:bitcoin-s/bitcoin-s-core.git"
)
lazy val secp256k1jni = project
.in(file("secp256k1jni"))
.settings(commonSettings: _*)
@ -139,33 +192,34 @@ lazy val core = project
.settings(commonProdSettings: _*)
.dependsOn(
secp256k1jni
).enablePlugins(GitVersioning)
)
.enablePlugins(GitVersioning)
lazy val coreTest = project
.in(file("core-test"))
.settings(commonTestSettings: _*)
.settings(
name := "bitcoin-s-core-test"
).dependsOn(
)
.dependsOn(
core,
testkit,
).enablePlugins()
testkit
)
.enablePlugins()
lazy val zmq = project
.in(file("zmq"))
.settings(commonSettings: _*)
.settings(
name := "bitcoin-s-zmq",
libraryDependencies ++= Deps.bitcoindZmq)
.settings(name := "bitcoin-s-zmq", libraryDependencies ++= Deps.bitcoindZmq)
.dependsOn(
core
).enablePlugins(GitVersioning)
)
.enablePlugins(GitVersioning)
lazy val bitcoindRpc = project
.in(file("bitcoind-rpc"))
.settings(commonProdSettings: _*)
.settings(
name := "bitcoin-s-bitcoind-rpc",
.settings(name := "bitcoin-s-bitcoind-rpc",
libraryDependencies ++= Deps.bitcoindRpc)
.dependsOn(core)
.enablePlugins(GitVersioning)
@ -173,15 +227,13 @@ lazy val bitcoindRpc = project
lazy val bitcoindRpcTest = project
.in(file("bitcoind-rpc-test"))
.settings(commonTestSettings: _*)
.settings(
libraryDependencies ++= Deps.bitcoindRpcTest,
.settings(libraryDependencies ++= Deps.bitcoindRpcTest,
name := "bitcoin-s-bitcoind-rpc-test")
.dependsOn(testkit)
.enablePlugins()
lazy val bench = project
.in(file("bench"))
.settings(commonSettings: _*)
.settings(assemblyOption in assembly := (assemblyOption in assembly).value
.copy(includeScala = true))
@ -196,20 +248,19 @@ lazy val bench = project
lazy val eclairRpc = project
.in(file("eclair-rpc"))
.settings(commonProdSettings: _*)
.settings(
name := "bitcoin-s-eclair-rpc",
.settings(name := "bitcoin-s-eclair-rpc",
libraryDependencies ++= Deps.eclairRpc)
.dependsOn(
core,
bitcoindRpc
).enablePlugins(GitVersioning)
)
.enablePlugins(GitVersioning)
lazy val eclairRpcTest = project
.in(file("eclair-rpc-test"))
.settings(commonTestSettings: _*)
.settings(libraryDependencies ++= Deps.eclairRpcTest,
name := "bitcoin-s-eclair-rpc-test",
)
name := "bitcoin-s-eclair-rpc-test")
.dependsOn(testkit)
.enablePlugins()
@ -220,15 +271,27 @@ lazy val testkit = project
core,
bitcoindRpc,
eclairRpc
).enablePlugins(GitVersioning)
)
.enablePlugins(GitVersioning)
lazy val publishWebsite = taskKey[Unit]("Publish website")
lazy val doc = project
.in(file("doc"))
lazy val docs = project
.in(file("bitcoin-s-docs")) // important: it must not be docs/
.settings(commonTestSettings: _*)
.settings(
name := "bitcoin-s-doc",
libraryDependencies ++= Deps.doc,
// come back to visit this setting later
mdocExtraArguments := List("--no-link-hygiene"),
name := "bitcoin-s-docs",
mdocVariables := Map(
"VERSION" -> version.value
),
publishWebsite := Def
.sequential(
bitcoins / Compile / unidoc,
Compile / docusaurusPublishGhpages
)
.value
)
.dependsOn(
bitcoindRpc,
@ -238,6 +301,17 @@ lazy val doc = project
testkit,
zmq
)
.enablePlugins(MdocPlugin, DocusaurusPlugin)
lazy val scripts = project
.in(file("scripts"))
.dependsOn(core, bitcoindRpc, eclairRpc, zmq)
.settings(commonTestSettings: _*)
.settings(
name := "bitcoin-s-scripts",
libraryDependencies ++= Deps.scripts
)
// Ammonite is invoked through running
// a main class it places in test sources
// for us. This makes it a bit less awkward
@ -252,6 +326,3 @@ lazy val doc = project
addCommandAlias("amm", "test:run")
publishArtifact in bitcoins := false
previewSite / aggregate := false
previewAuto / aggregate := false

View File

@ -1,205 +1,2 @@
[ ![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).
#### Generating a BIP39 mnemonic phrase and an `xpriv`
BIP39 mnemonic phrases are the most common way of creating backups of wallets.
They are between 12 and 24 words the user writes down, and can later be used to restore
their bitcoins. From the mnemonic phrase we generate a wallet seed, and that seed
can be used to generate what's called an extended private key
([`ExtPrivateKey`](src/main/scala/org/bitcoins/core/crypto/ExtKey.scala) in Bitcoin-S).
Here's an example:
```scala
import scodec.bits._
import org.bitcoins.core.crypto._
// 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
val mnemonicCode = MnemonicCode.fromEntropy(entropy)
mnemonicCode.words // 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")
val xpriv = ExtPrivateKey.fromBIP39Seed(ExtKeyVersion.SegWitMainNetPriv,
bip39Seed)
val xpub = xpriv.extPublicKey
// 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")
// alternatively:
val otherSegwitPath =
SegWitHDPath(HDCoinType.Bitcoin,
accountIndex = 0,
HDChainType.External,
addressIndex = 0)
segwitPath == otherSegwitPath // true
// there's also paths available for legacy
// addresses (LegacyHDPath) as well as nested
// segwit paths (NestedSegWitPath)
```
### 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
See [`core-test/README.md`](../core-test/README.md).
See the core module section on the
Bitcoin-S [website](https://bitcoin-s.org/docs/core/core-intro).

18
docker-compose.yml Normal file
View File

@ -0,0 +1,18 @@
version: "3"
services:
docusaurus:
build: .
ports:
- 3000:3000
- 35729:35729
volumes:
- ./docs:/app/docs
- ./website/blog:/app/website/blog
- ./website/core:/app/website/core
- ./website/i18n:/app/website/i18n
- ./website/pages:/app/website/pages
- ./website/static:/app/website/static
- ./website/sidebars.json:/app/website/sidebars.json
- ./website/siteConfig.js:/app/website/siteConfig.js
working_dir: /app/website

View File

@ -0,0 +1,82 @@
---
id: contributing-website
title: Contributing to the website
---
This website is built using [Docusaurus](https://docusaurus.io/).
For simple changes to the documentation, click on the `Edit` button at the top
of each page and submit those changes directly on GitHub.
## Scaladoc
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](https://docs.scala-lang.org/overviews/scaladoc/for-library-authors.html)
- [Guidelines](https://docs.scala-lang.org/style/scaladoc.html) used by the official Scala language Scaladoc
- [Alvin Alexander guide](https://alvinalexander.com/scala/how-to-generate-scala-documentation-scaladoc-command-examples) on writing Scaladoc
To generate Scaladocs:
```bash
$ sbt
> unidoc
```
This gets placed in `website/static/api`. When viewing the Docusaurus site the generated Scaladocs
appear under the API tab in the header bar,
or in the "API reference" link in the footer.
## Running the site locally
For running the website locally, you'll need:
- `yarn` (https://yarnpkg.com/lang/en/docs/install-ci/)
- `sbt` (https://www.scala-sbt.org/1.0/docs/Setup.html)
> In case you want to contribute substantial structural changes to the website,
> we suggest to read
> [Docusaurus' documentation](https://docusaurus.io/docs/en/installation.html)
> first.
You can now build and launch the website using
these commands:
```sh
cd website
yarn install # only the first time, to install the dependencies
yarn start
```
In a separate shell:
```bash
$ sbt
> doc/mdoc --watch
```
The above commands compiles our Mdoc Markdown files every time you change
them, and puts them in the correct directory for Docusaurus to find them.
Now visit http://localhost:3000/ and you should see a local version of
the website.
## Adding a new page
Whenever you add a new markdown page to the documentation, you'll have to
manually include it in the side menu.
You can do this by editing the `website/sidebars.json` file. The name to use is
the `id` specified in the page metadata (see the existing pages for an example).
## Publishing the site
```bash
$ sbt
> docs/publishWebsite
```
This command first generates Scaladocs, then invokes
`docs/docusaurusPublishGhPages`, which in turn compile our mdoc
files, build the site and push them to GH pages.

161
docs/contributing.md Normal file
View File

@ -0,0 +1,161 @@
---
id: contributing
title: Contributing
---
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](https://join.slack.com/t/suredbits/shared_invite/enQtNDEyMjY3MTg1MTg3LTYyYjkwOGUzMDQ4NDAwZjE1M2I3MmQyNWNlZjNlYjg4OGRjYTRjNWUwNjRjNjg4Y2NjZjAxYjU1N2JjMTU1YWM) - 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](https://gitter.im/bitcoin-s-core/)
- [#bitcoin-scala](https://webchat.freenode.net/?channels=bitcoin-scala) on IRC Freenode
## Developer productivity
### Bloop
If you're tired of waiting around for sbt all day, there's a new,
cool kid on the block. It is called [Bloop](https://scalacenter.github.io/bloop/),
and it makes compilations in general faster, and in particular
incremental, small compilation units (which greatly help editor
performance). Bloop is a server that runs in the background of
your computer, and keeps several "hot" JVMs running at all
times. These JVMs serve compilation requests. Because the JVMs
are running in the background you avoid the startup lag, and you
also get code that's already [JIT compiled](https://en.wikipedia.org/wiki/Just-in-time_compilation)
for you.
The documentation on Bloops [site](https://scalacenter.github.io/bloop/) is good, but here is the highlights:
1. Install Bloop by doing step 1 & 2 in the [official guide](https://scalacenter.github.io/bloop/setup#universal)
2. Enable the Bloop background daemon
1. macOS:
```bash
$ brew services start bloop
```
2. Ubuntu:
```bash
$ systemctl --user enable $HOME/.bloop/systemd/bloop.service
$ systemctl --user daemon-reload
$ systemctl --user start bloop
```
3. Enable shell completion for the Bloop CLI
1. Bash:
```bash
$ echo '. $HOME/.bloop/bash/bloop' >> $HOME/.bash_profile
```
2. Zsh:
```bash
$ echo 'autoload -U compinit' >> $HOME/.zshrc
$ echo 'fpath=($HOME/.bloop/zsh $fpath)' >> $HOME/.bashrc
$ echo 'compinit' >> $HOME/.bashrc
```
3. Fish:
```bash
$ ln -s $HOME/.bloop/fish/bloop.fish ~/.config/fish/completions/bloop.fish
```
4. Generate configuration files
```bash
$ sbt bloopInstall
```
5. Import Bitcoin-S into IntelliJ again, as a bsp (Build Server Protocol) project (instead of a sbt project). Make sure you're running on the most recent IntelliJ and Scala plugin. See [official docs](https://scalacenter.github.io/bloop/docs/ides/intellij) for details.
6. _(Bonus step):_ Lightning fast recompilations on file save:
```bash
$ bloop compile --project <name of module your're working on> --watch
```
Your editor should now be much faster and require less resources :tada:
## 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](https://github.com/bitcoin-s/bitcoin-s-core/blob/89fbf35d78046b7ed21fd93fec05bb57cba023bb/src/test/scala/org/bitcoins/core/protocol/transaction/TransactionSpec.scala#L13-L17)
is an example of a property in the bitcoin-s-core test suite
```scala
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`](/api/org/bitcoins/core/gen/TransactionGenerators)
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
multisignature transaction was signed correctly (see
[`TransactionSignatureCreatorSpec`](core-test/src/test/scala/org/bitcoins/core/crypto/TransactionSignatureCreatorSpec.scala)
line 29-34). First we generate a _supposedly_ validly signed multisig
transaction with [`TransactionGenerators.signedMultiSigTransaction`](/api/org/bitcoins/testkit/core/gen/TransactionGenerators)
(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`](/api/org/bitcoins/core/script/interpreter/ScriptInterpreter).
If we have built our functionality correctly the `ScriptInterpreter` should
always return [`ScriptOk`](/api/org/bitcoins/core/script/result/ScriptResult)
indicating the script was valid.
```scala
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:
> This takes a long time, and runs a lot of tests that require IO. It may hog your computer at times.
```scala
$ 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
```scala
$ 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:
1. Failed previously
2. Has not been run previously
3. Either the test or one of its dependencies has been recompiled
For more information on `testQuick`, see the offical
[sbt docs](https://www.scala-sbt.org/1.x/docs/Testing.html#testQuick).

188
docs/core/core-intro.md Normal file
View File

@ -0,0 +1,188 @@
---
id: core-intro
title: Core module
---
The `core` module is the core (duh!) functionality of Bitcoin-S. The goal is to provide basic
data structures that are found in the Bitcoin and Lightning protocols while
minimizing external 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 heavily in this library to ensure high quality of code.
## The basics
Every bitcoin protocol data structure (and some other data structures) extends [`NetworkElement`](/api/org/bitcoins/core/protocol/NetworkElement). `NetworkElement` provides methods to convert the data structure to hex or byte representation. When paired with [`Factory`](/api/org/bitcoins/core/util/Factory) we can easily serialize and deserialize data structures.
Most data structures have companion objects that extends `Factory` to be able to easily create protocol data structures. An example of this is the [`ScriptPubKey`](/api/org/bitcoins/core/protocol/script/ScriptPubKey) companion object. You can use this companion object to create a `ScriptPubKey` from a hex string or a byte array.
## Main modules in `core`
1. [`protocol`](/api/org/bitcoins/core/protocol) - basic protocol data structures. Useful for serializing/deserializing things
1. [`crypto`](/api/org/bitcoins/core/crypto) - cryptograhic functionality used in Bitcoin and Lightning
1. [`script`](/api/org/bitcoins/core/script) - an implementation of [Script](https://en.bitcoin.it/wiki/Script) - the programming language in Bitcoin
1. [`wallet`](/api/org/bitcoins/core/wallet) - implements signing logic for Bitcoin transactions. This module is not named well as there is **NO** functionality to persist wallet state to disk as it stands. This will most likely be renamed in the future.
1. [`config`](/api/org/bitcoins/core/config) - Contains information about a chain's genesis block and DNS seeds
1. [`number`](/api/org/bitcoins/core/number) - Implements number types that are native in C, i.e. `UInt8`, `UInt32`, `UInt64`, etc.
1. [`currency`](/api/org/bitcoins/core/currency) - Implements currency units in the Bitcoin protocol
1. [`bloom`](/api/org/bitcoins/core/bloom) - Implements [Bloom filters](https://en.wikipedia.org/wiki/Bloom_filter) and [merkle blocks](https://bitcoin.org/en/glossary/merkle-block) needed for [BIP37](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki)
1. [`hd`](/api/org/bitcoins/core/hd) - Contains implementations of hierarchical deterministic (HD) paths, that when combined with `ExtPrivKey` and `ExtPubKey` in `crypto` can implement BIP32, BIP44, BIP49 and BIP84.
## Examples
### Serializing and deserializing a `Transaction`
Here is an example scala console session with bitcoins-core
```scala mdoc
import org.bitcoins.core.protocol.transaction._
val hexTx = "0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f8$9c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1$a02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e$90c865c2f6f7a9710a474154ab1423abb5b9288ac00000000"
// val tx = Transaction.fromHex(hexTx)
// val hexAgain = tx.hex
```
This gives us an example of a hex encoded Bitcoin transaction that is deserialized to a native Scala object called a [`Transaction`](/api/org/bitcoins/core/protocol/transaction/Transaction). You could also serialize the transaction to bytes using `tx.bytes` instead of `tx.hex`. These methods are available on every data structure that extends NetworkElement, like [`ECPrivateKey`](/api/org/bitcoins/core/crypto/ECPrivateKey), [`ScriptPubKey`](/api/org/bitcoins/core/protocol/script/ScriptPubKey), [`ScriptWitness`](/api/org/bitcoins/core/protocol/script/ScriptWitness), and [`Block`](/api/org/bitcoins/core/protocol/blockchain/Block).
#### Generating a BIP39 mnemonic phrase and an `xpriv`
BIP39 mnemonic phrases are the most common way of creating backups of wallets.
They are between 12 and 24 words the user writes down, and can later be used to restore
their bitcoins. From the mnemonic phrase we generate a wallet seed, and that seed
can be used to generate what's called an extended private key
([`ExtPrivateKey`](/api/org/bitcoins/core/crypto/ExtPrivateKey) in Bitcoin-S).
Here's an example:
```scala mdoc:to-string
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
val mnemonicCode = MnemonicCode.fromEntropy(entropy)
mnemonicCode.words // 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")
val xpriv = ExtPrivateKey.fromBIP39Seed(ExtKeyVersion.SegWitMainNetPriv,
bip39Seed)
val xpub = xpriv.extPublicKey
// 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")
// alternatively:
val otherSegwitPath =
SegWitHDPath(HDCoinType.Bitcoin,
accountIndex = 0,
HDChainType.External,
addressIndex = 0)
segwitPath == otherSegwitPath
// there's also paths available for legacy
// addresses (LegacyHDPath) as well as nested
// segwit paths (NestedSegWitPath)
```
### Building a signed transaction
Bitcoin Core supports building unsigned transactions and then signing them with a set of private keys. The first important thing to look at is [`UTXOSpendingInfo`](/api/org/bitcoins/core/wallet/utxo/UTXOSpendingInfo). This contains all of the information needed to create a validly signed [`ScriptSignature`](/api/org/bitcoins/core/protocol/script/ScriptSignature) that spends this output.
Our [`TxBuilder`](/api/org/bitcoins/core/wallet/builder/TxBuilder) class requires you to provide the following:
1. `destinations` - the places we are sending bitcoin to. These are [TransactionOutputs](/api/org/bitcoins/core/protocol/transaction/TransactionOutput) you are sending coins too
2. `utxos` - these are the [UTXOs](/api/org/bitcoins/core/wallet/utxo/UTXOSpendingInfo) used to fund your transaction. These must exist in your wallet, and you must know how to spend them (i.e. have the private key)
3. `feeRate` - the fee rate you want to pay for this transaction
4. `changeSPK` - where the change (i.e. `creditingAmount - destinationAmount - fee`) from the transaction will be sent
5. `network` - the network(/api/org/bitcoins/core/config/NetworkParameters) we are transacting on
After providing this information, you can generate a validly signed bitcoin transaction by calling the `sign` method.
To see a complete example of this, see [our `TxBuilder` example](txbuilder.md)
### The [`Sign` API](/api/org/bitcoins/core/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 [`core/src/main/scala/org/bitcoins/core/crypto/Sign.scala`](/api/org/bitcoins/core/crypto/Sign):
```scala mdoc
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 [`ECKey`](/api/org/bitcoins/core/crypto/ECKey) 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.
### 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 mdoc:silent
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.script._
import org.bitcoins.core.script.interpreter._
import org.bitcoins.core.policy._
import org.bitcoins.core.number._
import org.bitcoins.core.crypto._
import org.bitcoins.core.currency._
val spendingTx = Transaction.fromHex("0100000001ccf318f0cbac588a680bbad075aebdda1f211c94ba28125b0f627f9248310db3000000006b4830450221008337ce3ce0c6ac0ab72509f889c1d52701817a2362d6357457b63e3bdedc0c0602202908963b9cf1a095ab3b34b95ce2bc0d67fb0f19be1cc5f7b3de0b3a325629bf01210241d746ca08da0a668735c3e01c1fa02045f2f399c5937079b6434b5a31dfe353ffffffff0210335d05000000001976a914b1d7591b69e9def0feb13254bace942923c7922d88ac48030000000000001976a9145e690c865c2f6f7a9710a474154ab1423abb5b9288ac00000000")
val scriptPubKey = ScriptPubKey.fromAsmHex("76a91431a420903c05a0a7de2de40c9f02ebedbacdc17288ac")
val output = TransactionOutput(CurrencyUnits.zero, scriptPubKey)
val inputIndex = UInt32.zero
val btxsc = BaseTxSigComponent(spendingTx,inputIndex,output,Policy.standardScriptVerifyFlags)
val preExecution = PreExecutionScriptProgram(btxsc)
```
```scala mdoc
val result = ScriptInterpreter.run(preExecution)
```

132
docs/core/txbuilder.md Normal file
View File

@ -0,0 +1,132 @@
---
id: txbuilder
title: TxBuilder example
---
Bitcoin-S features a transaction buidlder that constructs and signs Bitcoin
transactions. Here's an example of how to use it
```scala mdoc:silent
import scala.concurrent._
import scala.concurrent.duration._
import org.bitcoins.core._
import number._
import config._
import currency._
import crypto._
import script.crypto._
import protocol.transaction._
import protocol.script._
import wallet.builder._
import wallet.fee._
import wallet.utxo._
implicit val ec: ExecutionContext = ExecutionContext.Implicits.global
// generate a fresh private key that we are going to use in the scriptpubkey
val privKey = ECPrivateKey.freshPrivateKey
val pubKey = privKey.publicKey
// 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 = 10000.satoshis
// 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 = 5000.satoshis
// 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(1.satoshi)
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: BitcoinTxBuilder = {
val builderF = BitcoinTxBuilder(
destinations = destinations,
utxos = utxos,
feeRate = feeRate,
changeSPK = changeSPK,
network = networkParams)
Await.result(builderF, 30.seconds)
}
// 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 UTXO has:
// 1: one input
// 2: outputs (destination and change outputs)
// 3: a fee rate of 1 satoshi/byte
val signedTx: Transaction = {
val signF = txBuilder.sign
Await.result(signF, 30.seconds)
}
```
```scala mdoc
signedTx.inputs.length
signedTx.outputs.length
//remember, you can call .hex on any bitcoin-s data structure to get the hex representation!
signedTx.hex
```

51
docs/getting-started.md Normal file
View File

@ -0,0 +1,51 @@
---
id: getting-started
title: Add Bitcoin-S to your project
---
## REPL
You can try out Bitcoin-S in a REPL in a matter of seconds. Run the provided
["try bitcoin-s"](https://github.com/bitcoin-s/bitcoin-s-core/blob/master/try-bitcoin-s.sh)
script, which has no dependencies other than an installed JDK. The script
downloads and installs [Coursier](https://get-coursier.io/) and uses it to
fetch the [Ammonite](https://ammonite.io) REPL and the latest version of
Bitcoin-S. It then drops you into immediately into a REPL session.
```
% curl -s https://raw.githubusercontent.com/bitcoin-s/bitcoin-s-core/master/try-bitcoin-s.sh | bash
Loading...
Welcome to the Ammonite Repl 1.0.3
(Scala 2.12.4 Java 1.8.0_152)
If you like Ammonite, please support our development at www.patreon.com/lihaoyi
@ 23 :: "foo" :: true :: HNil
res0: Int :: String :: Boolean :: HNil = 23 :: "foo" :: true :: HNil
%
```
## Build tools
If you want to add Bitcoin-S to your project, follow the
instructions for your build tool
### sbt
Add this to your `build.sbt`:
```scala
libraryDependencies +="org.bitcoins" % "bitcoin-s-secp256k1jni" % "@VERSION@"
libraryDependencies += "org.bitcoins" %% "bitcoin-s-core" % "@VERSION@" withSources() withJavadoc()
libraryDependencies += "org.bitcoins" %% "bitcoin-s-bitcoind-rpc" % "@VERSION@" withSources() withJavadoc()
libraryDependencies += "org.bitcoins" %% "bitcoin-s-eclair-rpc" % "@VERSION@" withSources() withJavadoc()
libraryDependencies += "org.bitcoins" %% "bitcoin-s-testkit" % "@VERSION@" withSources() withJavadoc()
libraryDependencies += "org.bitcoins" %% "bitcoin-s-zmq" % "@VERSION@" withSources() withJavadoc()
```
### Mill
TODO

116
docs/rpc/bitcoind.md Normal file
View File

@ -0,0 +1,116 @@
---
id: rpc-bitcoind
title: bitcoind/Bitcoin Core
---
> Note: `bitcoin-s-bitcoind-rpc` requires you to have `bitcoind` (Bitcoin Core daemon) installed. Grab this at [bitcoincore.org](https://bitcoincore.org/en/download/)
The Bitcoin Core RPC client in Bitcoin-S currently supports the Bitcoin Core 0.16 and 0.17
version lines. It can be set up to work with both local and remote Bitcoin Core servers.
## Connecting to a local `bitcoind` instance
```scala mdoc:compile-only
import scala.concurrent._
import akka.actor.ActorSystem
import org.bitcoins.{rpc, core}
import core.currency.Bitcoins
import rpc.client.common._
import rpc.config.BitcoindInstance
implicit val actorSystem: ActorSystem = ActorSystem.create()
implicit val ec: ExecutionContext = actorSystem.dispatcher
// data directory defaults to ~/.bitcoin on Linux and
// ~/Library/Application Support/Bitcoin on macOS
val bitcoindInstance = BitcoindInstance.fromDatadir()
// alternative:
import java.io.File
val dataDir = new File("/my/bitcoin/data/dir")
val otherInstance = BitcoindInstance.fromDatadir(dataDir)
val client = new BitcoindRpcClient(bitcoindInstance)
val balance: Future[Bitcoins] = for {
_ <- client.start()
balance <- client.getBalance
} yield balance
```
## Connecting to a remote `bitcoind`
First, we create a secure connection to our `bitcoind` instance by setting
up a SSH tunnel:
```bash
$ ssh -L 8332:localhost:8332 \
my-cool-user@my-cool-website.com
```
> Note: the port number '8332' is the default for mainnet. If you want to
> connect to a testnet `bitcoind`, the default port is '18332'
Now that we have a secure connection between our remote `bitcoind`, we're
ready to create the connection with our RPC client
```scala mdoc:compile-only
import akka.actor.ActorSystem
import java.net.URI
import scala.concurrent._
import org.bitcoins.core.config._
import org.bitcoins.rpc.config._
import org.bitcoins.rpc.client.common._
val username = "FILL_ME_IN" //this username comes from 'rpcuser' in your bitcoin.conf file
val password = "FILL_ME_IN" //this password comes from your 'rpcpassword' in your bitcoin.conf file
val rpcPort = 8332 //this is default port for mainnet, 18332 for testnet/regtest
val authCredentials = BitcoindAuthCredentials(
username = username,
password = password,
rpcPort = rpcPort
)
val bitcoindInstance = {
BitcoindInstance (
network = MainNet,
uri = new URI(s"http://localhost:${authCredentials.rpcPort + 1}"),
rpcUri = new URI(s"http://localhost:${authCredentials.rpcPort}"),
authCredentials = authCredentials
)
}
implicit val system: ActorSystem = ActorSystem.create()
implicit val ec: ExecutionContext = system.dispatcher
val rpcCli = new BitcoindRpcClient(bitcoindInstance)
rpcCli.getBalance.onComplete { case balance =>
println(s"Wallet balance=${balance}")
system.terminate()
}
```
## Testing
To test the Bitcoin-S RPC project you need both version 0.16 and 0.17 of Bitcoin Core. A list of current and previous releases can be found [here](https://bitcoincore.org/en/releases/).
You then need to set environment variables to indicate where Bitcoin-S can find the different versions:
```bash
$ export BITCOIND_V16_PATH=/path/to/v16/bitcoind
$ export BITCOIND_V17_PATH=/path/to/v17/bitcoind
```
If you just run tests testing common functionality it's enough to have either version 0.16 or 0.17 on your `PATH`.
To run all RPC related tests:
```bash
$ bash sbt bitcoindRpcTest/test
```

27
docs/rpc/eclair.md Normal file
View File

@ -0,0 +1,27 @@
---
id: rpc-eclair
title: Eclair
---
This is a RPC client for [Eclair](https://github.com/acinq/eclair). It assumes that a bitcoind instance is running.
Currently this RPC client is written for [v0.2-beta8](https://github.com/ACINQ/eclair/releases/tag/v0.2-beta8) version of Eclair.
## Configuration of Eclair
Please see the configuration secion of the
[Eclair README](https://github.com/acinq/eclair#configuring-eclair).
## Starting the jar
You need to download the jar from the [Eclair GitHub](https://github.com/ACINQ/eclair/releases/tag/v0.2-beta8).
To run Eclair you can use this command:
```bash
$ java -jar eclair-node-0.2-beta8-52821b8.jar &
```
Alternatively you can set the `ECLAIR_PATH` env variable and then you can start Eclair with the `start` method on `EclairRpcClient`.
**YOU NEED TO SET `ECLAIR_PATH` CORRECTLY TO BE ABLE TO RUN THE UNIT TESTS**

View File

@ -0,0 +1,6 @@
---
id: rpc-clients-intro
title: Introduction
---
Bitcoin-S contains RPC clients for interacting with both Bitcoin Core and Eclair.

View File

@ -1,6 +1,17 @@
---
id: security
title: Security
---
The Bitcoin-S developers take security very seriously. This library has
very few dependencies (at least in the `core` module), which is for
of security reasons.
## Disclosure
Please send an email to stewart.chris1234@gmail.com encrypted with this gpg key
If you have any security disclosures related to Bitcoin-S, please send an
email to [stewart.chris1234@gmail.com](mailto:stewart.chris1234@gmail.com?subject=Bitcoin-S%20Security%20Disclosure)
encrypted with this GPG key:
```
-----BEGIN PGP PUBLIC KEY BLOCK-----

View File

@ -1,29 +1,2 @@
[ ![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.
Currently this RPC client is written for the latest official version of eclair which is [v0.2-beta8](https://github.com/ACINQ/eclair/releases/tag/v0.2-beta8)
## Configuration eclair
Please see the configuration secion of the
[Eclair README](https://github.com/acinq/eclair#configuring-eclair).
## Starting the jar
You need to download the jar from the [Eclair GitHub](https://github.com/ACINQ/eclair/releases/tag/v0.2-beta8).
To run Eclair you can use this command:
```bash
$ java -jar eclair-node-0.2-beta8-52821b8.jar &
```
Alternatively you can set the `ECLAIR_PATH` env variable and then you can start Eclair with the `start` method on `EclairRpcClient`.
**YOU NEED TO SET `ECLAIR_PATH` CORRECTLY TO BE ABLE TO RUN THE UNIT TESTS**
See the Eclair section on the
Bitcoin-S [website](https://bitcoin-s.org/docs/rpc/rpc-eclair).

View File

@ -139,9 +139,9 @@ object Deps {
Test.ammonite
)
val doc = List(
val scripts = List(
Compile.ammonite,
"ch.qos.logback" % "logback-classic" % V.logback withSources () withJavadoc (),
Compile.logback,
Test.scalaTest,
Test.logback
)

View File

@ -13,12 +13,6 @@ addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15")
// sbt plugin to unify scaladoc/javadoc across multiple projects
addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.2")
// make static site through sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.2")
// publish said site to GitHub pages
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2")
// ensure proper linkage across libraries in Scaladoc
addSbtPlugin(
"com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "latest.release")
@ -29,3 +23,6 @@ addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.2.5")
//tool to publish snapshots to sonatype after CI builds finish
//https://github.com/olafurpg/sbt-ci-release
addSbtPlugin("com.geirsson" % "sbt-ci-release" % "1.2.6")
// write markdown files with type-checked Scala
addSbtPlugin("org.scalameta" % "sbt-mdoc" % "1.3.0" )

19
try-bitcoin-s.sh Normal file
View File

@ -0,0 +1,19 @@
#!/bin/sh
# this script is cribbed from Shapeless
# https://github.com/milessabin/shapeless/blob/master/scripts/try-shapeless.sh
COURSIER_URL=https://git.io/coursier-cli
# check if coursier exists
# TODO: check version? only new-ish works with latest.version
test -e ~/.coursier/coursier || \
# if not, download latest coursier-cli
(mkdir -p ~/.coursier && curl -L -s --output ~/.coursier/coursier $COURSIER_URL && chmod +x ~/.coursier/coursier)
MSG="Welcome the Bitcoin-S REPL, powered by Ammonite
Check out our documentation at https://bitcoin-s.org"
# launch Ammonite with bitcoin-s on the classpath
~/.coursier/coursier launch -q -P \
com.lihaoyi:ammonite_2.12.4:latest.release \
org.bitcoin-s:core.12:latest.release \
-- --predef-code 'import org.bitcoins.core._' --banner $MSG < /dev/tty

86
website/core/Footer.js Normal file
View File

@ -0,0 +1,86 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require("react");
class Footer extends React.Component {
docUrl(doc, language) {
const baseUrl = this.props.config.baseUrl;
const docsUrl = this.props.config.docsUrl;
const docsPart = `${docsUrl ? `${docsUrl}/` : ""}`;
const langPart = `${language ? `${language}/` : ""}`;
return `${baseUrl}${docsPart}${langPart}${doc}`;
}
pageUrl(doc, language) {
const baseUrl = this.props.config.baseUrl;
return baseUrl + (language ? `${language}/` : "") + doc;
}
render() {
return (
<footer className="nav-footer" id="footer">
<section className="sitemap">
<a href={this.props.config.baseUrl} className="nav-home">
{this.props.config.footerIcon && (
<img
src={this.props.config.baseUrl + this.props.config.footerIcon}
alt={this.props.config.title}
width="66"
height="58"
/>
)}
</a>
<div>
<h5>Docs</h5>
<a href={this.docUrl("getting-started", this.props.language)}>
Getting Started
</a>
<a href={this.docUrl("core/core-intro", this.props.language)}>
Guides
</a>
<a href={this.props.config.scaladocUrl}>API Reference</a>
</div>
<div>
<h5>Community</h5>
<a href={this.pageUrl("users.html", this.props.language)}>
User Showcase
</a>
<a
href={this.props.config.suredbitsSlack}
target="_blank"
rel="noreferrer noopener"
>
Slack
</a>
<a href={this.props.config.gitterUrl}>Gitter chat</a>
</div>
<div>
<h5>More</h5>
<a href={`${this.props.config.baseUrl}blog`}>Blog</a>
<a href={this.props.config.repoUrl}>GitHub</a>
<a
className="github-button"
href={this.props.config.repoUrl}
data-icon="octicon-star"
data-count-href="/bitcoin-s/bitcoin-s-core/stargazers"
data-show-count="true"
data-count-aria-label="# stargazers on GitHub"
aria-label="Star this project on GitHub"
>
Star
</a>
</div>
</section>
<section className="copyright">{this.props.config.copyright}</section>
</footer>
);
}
}
module.exports = Footer;

15
website/core/Image.js Normal file
View File

@ -0,0 +1,15 @@
const React = require("react");
const Image = ({ src, style }) => (
<img
style={{
maxWidth: "50%",
// center the image
margin: "0 25%",
...style
}}
src={src}
/>
);
module.exports = Image;

50
website/i18n/en.json Normal file
View File

@ -0,0 +1,50 @@
{
"_comment": "This file is auto-generated by write-translations.js",
"localized-strings": {
"next": "Next",
"previous": "Previous",
"tagline": "Bitcoin implementation in Scala",
"docs": {
"contributing-website": {
"title": "Contributing to the website"
},
"contributing": {
"title": "Contributing"
},
"core/core-intro": {
"title": "Core module"
},
"core/txbuilder": {
"title": "TxBuilder example"
},
"getting-started": {
"title": "Add Bitcoin-S to your project"
},
"rpc/rpc-bitcoind": {
"title": "bitcoind/Bitcoin Core"
},
"rpc/rpc-eclair": {
"title": "Eclair"
},
"rpc/rpc-clients-intro": {
"title": "Introduction"
}
},
"links": {
"Docs": "Docs",
"Help": "Help",
"Blog": "Blog"
},
"categories": {
"Getting started": "Getting started",
"Core module": "Core module",
"RPC clients": "RPC clients",
"Contributing": "Contributing"
}
},
"pages-strings": {
"Help Translate|recruit community translators for your project": "Help Translate",
"Edit this Doc|recruitment message asking to edit the doc source": "Edit",
"Translate this Doc|recruitment message asking to translate the docs": "Translate"
}
}

15
website/package.json Normal file
View File

@ -0,0 +1,15 @@
{
"license": "MIT",
"scripts": {
"examples": "docusaurus-examples",
"start": "docusaurus-start",
"build": "docusaurus-build",
"publish-gh-pages": "docusaurus-publish",
"write-translations": "docusaurus-write-translations",
"version": "docusaurus-version",
"rename-version": "docusaurus-rename-version"
},
"devDependencies": {
"docusaurus": "^1.9.0"
}
}

67
website/pages/en/help.js Normal file
View File

@ -0,0 +1,67 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require("react");
const CompLibrary = require("../../core/CompLibrary.js");
const Image = require(process.cwd() + "/core/Image.js");
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;
function Help(props) {
const { config: siteConfig, language = "" } = props;
const { baseUrl, docsUrl } = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ""}`;
const langPart = `${language ? `${language}/` : ""}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
const supportLinks = [
{
content: `Read the [guides and docs on this site.](${docUrl(
"core/core-intro"
)})`,
title: "Browse Docs"
},
{
content: `Explore the [Scaladocs](${
siteConfig.scaladocUrl
}) for the complete guide to how Bitcoin-S work`,
title: "Browse API reference"
},
{
content: [
"If you have any questions about either the documentation or",
"Bitcoin-S itself, the easiest way to get in touch with the",
`developers is the \`#bitcoin-s\` channel in the [Suredbits Slack](${
siteConfig.suredbitsSlack
}).`,
`There's also a [Gitter room](${
siteConfig.gitterUrl
}) you can join, although there's less`,
"activity there."
].join(" "),
title: "Join the community"
}
];
return (
<div className="docMainWrapper wrapper">
<Container className="mainContainer documentContainer postContainer">
<div className="post">
<header className="postHeader">
<h1>Need help?</h1>
</header>
<Image src={baseUrl + "img/undraw_questions_75e0.svg"} />
<GridBlock contents={supportLinks} layout="threeColumn" />
</div>
</Container>
</div>
);
}
module.exports = Help;

248
website/pages/en/index.js Normal file
View File

@ -0,0 +1,248 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require("react");
const CompLibrary = require("../../core/CompLibrary.js");
const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */
const Container = CompLibrary.Container;
const GridBlock = CompLibrary.GridBlock;
const Logo = props => (
<div className="projectLogo">
<img src={props.img_src} alt="Project Logo" />
</div>
);
class HomeSplash extends React.Component {
render() {
const { siteConfig, language = "" } = this.props;
const { baseUrl, docsUrl } = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ""}`;
const langPart = `${language ? `${language}/` : ""}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
const SplashContainer = props => (
<div className="homeContainer">
<div className="homeSplashFade">
<div className="wrapper homeWrapper">{props.children}</div>
</div>
</div>
);
const ProjectTitle = () => (
<h2 className="projectTitle">
<small>{siteConfig.tagline}</small>
</h2>
);
const PromoSection = props => (
<div className="section promoSection">
<div className="promoRow">
<div className="pluginRowBlock">{props.children}</div>
</div>
</div>
);
return (
<SplashContainer>
<div className="inner">
<ProjectTitle siteConfig={siteConfig} />
<PromoSection>
{/*
<Button href="#try">Try It Out</Button>
<Button href={docUrl("doc1.html")}>Example Link</Button>
<Button href={docUrl("doc2.html")}>Example Link 2</Button> */}
</PromoSection>
</div>
</SplashContainer>
);
}
}
class Index extends React.Component {
render() {
const { config: siteConfig, language = "" } = this.props;
const { baseUrl, docsUrl } = siteConfig;
const docsPart = `${docsUrl ? `${docsUrl}/` : ""}`;
const langPart = `${language ? `${language}/` : ""}`;
const docUrl = doc => `${baseUrl}${docsPart}${langPart}${doc}`;
const Block = props => (
<Container
padding={["bottom", "top"]}
id={props.id}
background={props.background}
>
<GridBlock
align="center"
contents={props.children}
layout={props.layout}
/>
</Container>
);
const Button = props => (
<div style={props.style} className="pluginWrapper buttonWrapper">
<a className="dark-button" href={props.href} target={props.target}>
{props.children}
</a>
</div>
);
// cribbed from Bloop site, https://github.com/scalacenter/bloop/blob/6b5384241d1bba4143315e66f668876d65a2e34f/website/pages/en/index.js#L92
const Hero = ({ siteConfig }) => (
<div className="hero">
<div className="hero__container">
<Logo img_src={`${siteConfig.baseUrl}img/bitcoin-s-logo.svg`} />
<h1>{siteConfig.tagline}</h1>
<Button href={docUrl("getting-started")}>Get started</Button>
</div>
</div>
);
const TryOut = () => (
<Block id="try">
{[
{
content: [
"Use our RPC clients for `bitcoind`/Bitcoin Core and Eclair, and get powerful",
"static typing baked into your RPC calls. All returned values you get from `bitcoind`",
"and Eclair are converted into native Bitcoin/Lightning data structures for you.",
"Is that raw hex string you've been passing around a transaction or a Lightning invoice?",
"With Bitcoin-S you get both confidence in your code _and_ powerful methods available",
"on your data"
].join(" "),
image: `${baseUrl}img/undraw_target_kriv.svg`,
imageAlign: "left",
title: "Super-powered RPC clients"
}
]}
</Block>
);
const Description = () => (
<Block background="dark">
{[
{
content: [
"Bitcoin-S is used in production today, facilitating transactions and systems handling",
"millions of dollars each day. It has a very high degree of code coverage,",
"with unit tests, property based tests and integration tests."
].join(" "),
image: `${baseUrl}img/undraw_Security_on_s9ym.svg`,
imageAlign: "right",
title: "Battle tested, high quality code"
}
]}
</Block>
);
const LearnHow = () => (
<Block background="light">
{[
{
content: [
"We provide solid APIs for constructing and signing transactions.",
"From small-scale 1-in 2-out transactions, to custom logic powering exchange withdrawals, we've got you covered.",
"Check out our [`TxBuilder` example](docs/txbuilder) to see how."
].join(" "),
image: `${baseUrl}img/undraw_transfer_money_rywa.svg`,
imageAlign: "right",
title: "Construct and sign bitcoin transactions"
}
]}
</Block>
);
const Features = () => (
<Block layout="fourColumn">
{[
{
content: [
"Bitcoin-S allows you to interact with data structures found in the",
"Bitcoin and Lightning protocols as first-class citizens of your app.",
"Go back and forth between hex, byte and JVM representation trivially,",
"letting our abstractions focus on what you want to build"
].join(" "),
image: `${baseUrl}img/undraw_digital_currency_qpak.svg`,
imageAlign: "top",
title: "Deep protocol understanding"
},
{
content: [
"Code with confidence, knowing your data won't change under you. All",
"data structures in Bitcoin-S are immutable. This eliminates a big",
"range of bugs right away, and enable you to write concurrent code",
"much easier"
].join(" "),
image: `${baseUrl}img/undraw_code_review_l1q9.svg`,
imageAlign: "top",
title: "Immutable data structures"
},
{
content: [
"Get the compiler to work for you, ensuring your logic covers all cases.",
"Modelling your application with mathematically founded types enables greater confidence in the correctness of your code"
].join(" "),
image: `${baseUrl}img/undraw_mathematics_4otb.svg`,
imageAlign: "top",
title: "Algebraic data types"
}
]}
</Block>
);
const Showcase = () => {
if ((siteConfig.users || []).length === 0) {
return null;
}
const showcase = siteConfig.users
.filter(user => user.pinned)
.map(user => (
<a href={user.infoLink} key={user.infoLink}>
<img src={user.image} alt={user.caption} title={user.caption} />
</a>
));
const pageUrl = page => baseUrl + (language ? `${language}/` : "") + page;
return (
<div className="productShowcaseSection paddingBottom">
<h2>Who is using Bitcoin-S?</h2>
<p>
Bitcoin-S is used in production applications, by both small and
large companies
</p>
<div className="logos">{showcase}</div>
<div className="more-users">
<a className="button" href={pageUrl("users.html")}>
Read more
</a>
</div>
</div>
);
};
return (
<div>
<Hero siteConfig={siteConfig} />
<div className="mainContainer">
<Features />
<LearnHow />
<TryOut />
<Description />
<Showcase />
</div>
</div>
);
}
}
module.exports = Index;

99
website/pages/en/users.js Normal file
View File

@ -0,0 +1,99 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const React = require("react");
const CompLibrary = require("../../core/CompLibrary.js");
const Markdown = CompLibrary.MarkdownBlock;
const Image = require(process.cwd() + "/core/Image.js");
const Container = CompLibrary.Container;
class Users extends React.Component {
render() {
const { config: siteConfig } = this.props;
if ((siteConfig.users || []).length === 0) {
return null;
}
const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js`;
const showcase = siteConfig.users.map(user => (
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between"
}}
>
<a href={user.infoLink} key={user.infoLink}>
<img src={user.image} alt={user.caption} title={user.caption} />
</a>
{user.description ? (
<Container className="showcase-user-container">
<Markdown>{user.description}</Markdown>
</Container>
) : null}
</div>
));
return (
<div className="mainContainer">
<Container padding={["bottom"]}>
<div className="showcaseSection">
<div className="prose">
<h1>What is Bitcoin-S good for?</h1>
<Image
style={{ margin: "1em 0" }}
src={siteConfig.baseUrl + "img/undraw_bitcoin2_ave7.svg"}
/>
<p>
Bitcoin-S is a versatile and feature-rich Bitcoin framework that
can power a vast array of Bitcoin and cryptocurrency
applications. Some examples of what Bitcoin-S is used for in
production today:
</p>
<ul>
<li>
Construct and sign transactions for withdrawals from a crypto
exchange
</li>
<li>
Spend to and from{" "}
<a href="https://en.bitcoin.it/wiki/Bech32">
Bech32 addresses
</a>
, enabling full SegWit support for your application
</li>
<li>
Parse{" "}
<a href="https://suredbits.com/lightning-101-what-is-a-lightning-invoice/">
Lightning Invoices
</a>{" "}
and other Lightning Network-native data structures
</li>
<li>
Interact with the{" "}
<a href="https://github.com/ACINQ/eclair">Eclair</a> Lightning
client, fast-tracking your application onto the Lightning
Network
</li>
</ul>
</div>
<h4>Here are some examples of companies using Bitcoin-S:</h4>
<div className="logos">{showcase}</div>
<p style={{ textAlign: "center" }}>Are you using this project?</p>
<a href={editUrl} className="button">
Add your company
</a>
</div>
</Container>
</div>
);
}
}
module.exports = Users;

13
website/sidebars.json Normal file
View File

@ -0,0 +1,13 @@
{
"docs": {
"Getting started": ["getting-started"],
"Core module": ["core/core-intro", "core/txbuilder"],
"RPC clients": [
"rpc/rpc-clients-intro",
"rpc/rpc-eclair",
"rpc/rpc-bitcoind"
],
"Contributing": ["contributing", "contributing-website"],
"Security": ["security"]
}
}

147
website/siteConfig.js Normal file
View File

@ -0,0 +1,147 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// See https://docusaurus.io/docs/site-config for all the possible
// site configuration options.
const baseUrl = "/";
const scaladocUrl = baseUrl + "api/org/bitcoins";
// List of projects/orgs using your project for the users page.
const users = [
/*
This is how a user description should look. The description field is optional.
You can use markdown in your company description.
{
caption: "The name of your company",
image: "/img/your-company-logo.png",
description: "Describe how your company uses bitcoin-s",
pinned: true
},
*/
{
caption: "Suredbits",
image: `${baseUrl}img/suredbits-logo.png`,
infoLink: "https://suredbits.com",
description: "Suredbits uses Bitcoin-S to power their Lightning APIs.",
pinned: true
},
{
caption: "Gemini",
image: `${baseUrl}img/gemini-logo.png`,
infoLink: "https://gemini.com",
description: [
"Gemini uses Bitcoin-S to batch transactions and facilitate deposits and",
"withdrawals with full SegWit support.",
"Read more at [their blog](https://medium.com/gemini/gemini-upgrades-wallet-with-full-support-of-segwit-5bb8e4bc851b)"
].join(" "),
pinned: true
}
];
const siteConfig = {
title: "bitcoin-s", // Title for your website.
tagline: "Bitcoin implementation in Scala",
url: "https://bitcoin-s.org", // Your website URL
baseUrl, // Base URL for your project */
// For github.io type URLs, you would set the url and baseUrl like:
// url: 'https://facebook.github.io',
// baseUrl: '/test-site/',
// URL for editing docs, has to be present for the
// "Edit this Doc" button to appear
editUrl: "https://github.com/bitcoin-s/bitcoin-s-core/docs",
// Used for publishing and more
projectName: "bitcoin-s",
organizationName: "bitcoin-s",
// For top-level user or org sites, the organization is still the same.
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
// organizationName: 'JoelMarcey'
// For no header links in the top nav bar -> headerLinks: [],
headerLinks: [
{ doc: "core/core-intro", label: "Docs" },
{ href: scaladocUrl, label: "API" },
{ page: "help", label: "Help" },
{ blog: true, label: "Blog" }
],
// If you have users set above, you add it here:
users,
/* path to images for header/footer */
headerIcon: "img/favicon.ico",
footerIcon: "img/favicon.ico",
favicon: "img/favicon.ico",
/* Colors for website */
colors: {
primaryColor: "#1f7a8c", // teal
secondaryColor: "#bfdbf7" // light-ish blue
},
/* Custom fonts for website */
fonts: {
headerFont: ["Montserrat", "sans-serif"]
},
// This copyright info is used in /core/Footer.js and blog RSS/Atom feeds.
copyright: `Copyright © ${new Date().getFullYear()} Suredbits & the bitcoin-s developers`,
highlight: {
// Highlight.js theme to use for syntax highlighting in code blocks.
theme: "default"
},
// Add custom scripts here that would be placed in <script> tags.
scripts: [
"https://buttons.github.io/buttons.js",
"https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js",
"https://fonts.googleapis.com/css?family=Montserrat:500",
`${baseUrl}js/code-block-buttons.js`
],
stylesheets: [`${baseUrl}css/code-block-buttons.css`],
// On page navigation for the current documentation page.
onPageNav: "separate",
// No .html extensions for paths.
cleanUrl: true,
// Open Graph and Twitter card images.
ogImage: "img/undraw_online.svg",
twitterImage: "img/undraw_tweetstorm.svg",
// Show documentation's last contributor's name.
enableUpdateBy: true,
// Show documentation's last update time.
enableUpdateTime: true,
// don't use Docusarus CSS for Scaladocs,
// and don't let Scaladoc CSS influence
// Docusaurus
separateCss: ["api"],
// mdoc writes docs to this directory
customDocsPath: "bitcoin-s-docs/target/mdoc",
////////////////////
// custom keys begin
repoUrl: "https://github.com/bitcoin-s/bitcoin-s-core",
suredbitsSlack:
"https://join.slack.com/t/suredbits/shared_invite/enQtNDEyMjY3MTg1MTg3LTYyYjkwOGUzMDQ4NDAwZjE1M2I3MmQyNWNlZjNlYjg4OGRjYTRjNWUwNjRjNjg4Y2NjZjAxYjU1N2JjMTU1YWM",
gitterUrl: "https://gitter.im/bitcoin-s-core/",
// avoid showing "root" as default Scaladoc page
scaladocUrl
// custom keys end
//////////////////
};
module.exports = siteConfig;

View File

@ -0,0 +1,39 @@
/* "Copy" code block button */
pre {
position: relative;
}
pre .btnIcon {
position: absolute;
top: 4px;
z-index: 2;
cursor: pointer;
border: 1px solid transparent;
padding: 0;
color: $primaryColor;
background-color: transparent;
height: 30px;
transition: all 0.25s ease-out;
}
pre .btnIcon:hover {
text-decoration: none;
}
.btnIcon__body {
align-items: center;
display: flex;
}
.btnIcon svg {
fill: currentColor;
margin-right: 0.4em;
}
.btnIcon__label {
font-size: 11px;
}
.btnClipboard {
right: 10px;
}

View File

@ -0,0 +1,102 @@
/* your custom css */
@media only screen and (min-device-width: 360px) and (max-device-width: 736px) {
}
@media only screen and (min-width: 1024px) {
}
@media only screen and (max-width: 1023px) {
}
@media only screen and (min-width: 1400px) {
}
@media only screen and (min-width: 1500px) {
}
.showcase-user-container {
max-width: 15em;
justify-content: space-between;
}
.dark-button {
border: 1px solid white;
border-radius: 3px;
color: white;
display: inline-block;
font-size: 14px;
font-weight: 400;
line-height: 1.2em;
padding: 10px;
text-decoration: none !important;
text-transform: uppercase;
transition: background 0.3s, color 0.3s;
}
.dark-button:hover {
background-color: $secondaryColor;
color: white;
}
/* begin crib section from Bloop https://github.com/scalacenter/bloop/blob/d73e247b353a9a0df4a95528095811da391c4a12/website/static/css/index.css#L126 */
.hero {
background: $primaryColor;
color: #f9f9f9;
overflow: hidden;
padding: 2rem 1rem 2rem;
position: relative;
text-align: center;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 500;
font-family: $headerFont;
}
.hero .projectLogo {
display: block;
}
.hero h1 {
margin: 0;
}
.hero::before {
background: url(../img/babel-black.svg) no-repeat center center;
background-size: cover;
bottom: 0;
content: "";
display: block;
left: 0;
opacity: 0.3;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
.hero__container {
position: relative;
z-index: 2;
}
/* end crib section from bloop */
.showcaseSection h1 {
margin-top: 0;
}
.showcaseSection p {
text-align: justify;
}
.showcaseSection li {
text-align: left;
}
.showcase-user-container p {
text-align: center;
}

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg3952"
xml:space="preserve"
width="481.07999"
height="129.30467"
viewBox="0 0 481.07999 129.30467"
sodipodi:docname="bitcoin-s-white-background.svg"
inkscape:version="0.92.4 (33fec40, 2019-01-16)"><metadata
id="metadata3958"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs3956"><clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath3968"><path
d="M 0,96.978 H 360.81 V 0 H 0 Z"
id="path3966"
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1024"
inkscape:window-height="1088"
id="namedview3954"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="1.8001165"
inkscape:cx="240.53999"
inkscape:cy="64.652336"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:current-layer="g3960" /><g
id="g3960"
inkscape:groupmode="layer"
inkscape:label="Dan Smith - Surebits_logo"
transform="matrix(1.3333333,0,0,-1.3333333,0,129.30467)"><g
id="g3962"><g
id="g3964"
clip-path="url(#clipPath3968)"><g
id="g3970"
transform="translate(121.9384,32.058)"><path
d="m 0,0 c 1.676,1.814 2.514,4.138 2.514,6.972 0,2.832 -0.838,5.145 -2.514,6.941 -1.676,1.795 -3.83,2.692 -6.463,2.692 -2.673,0 -4.847,-0.897 -6.523,-2.692 -1.675,-1.796 -2.513,-4.109 -2.513,-6.941 0,-2.873 0.838,-5.207 2.513,-7.002 1.676,-1.795 3.85,-2.693 6.523,-2.693 2.633,0 4.787,0.907 6.463,2.723 m 3.201,21.063 c 2.294,-1.376 4.07,-3.3 5.326,-5.774 C 9.784,12.815 10.412,9.963 10.412,6.731 10.412,3.54 9.793,0.728 8.558,-1.706 7.32,-4.14 5.565,-6.035 3.291,-7.391 1.018,-8.747 -1.616,-9.425 -4.607,-9.425 c -2.394,0 -4.51,0.468 -6.344,1.406 -1.836,0.937 -3.351,2.304 -4.548,4.099 v -5.206 h -7.779 v 44.402 h 7.779 V 17.623 c 1.157,1.795 2.642,3.161 4.458,4.099 1.815,0.937 3.919,1.406 6.313,1.406 2.993,0 5.635,-0.688 7.929,-2.065"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3972"
inkscape:connector-curvature="0" /></g><g
id="g3974"
transform="translate(143.122,67.3939)"><path
d="m 0,0 c 0.838,-0.878 1.257,-1.995 1.257,-3.352 0,-1.316 -0.419,-2.414 -1.257,-3.291 -0.838,-0.878 -1.896,-1.316 -3.172,-1.316 -1.276,0 -2.333,0.438 -3.171,1.316 -0.838,0.877 -1.257,1.975 -1.257,3.291 0,1.357 0.419,2.474 1.257,3.352 0.838,0.877 1.895,1.316 3.171,1.316 C -1.896,1.316 -0.838,0.877 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3976"
inkscape:connector-curvature="0" /></g><path
d="M 136.121,54.947 H 143.9 V 22.932 h -7.779 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3978"
inkscape:connector-curvature="0" /><g
id="g3980"
transform="translate(169.2714,24.9066)"><path
d="m 0,0 c -2.553,-1.556 -5.227,-2.334 -8.019,-2.334 -2.752,0 -5.006,0.809 -6.761,2.424 -1.756,1.616 -2.633,3.999 -2.633,7.151 v 15.798 h -4.488 l -0.06,5.685 h 4.548 v 8.797 h 7.72 v -8.797 h 9.155 V 23.039 H -9.693 V 8.617 c 0,-1.476 0.278,-2.523 0.837,-3.141 0.558,-0.62 1.396,-0.928 2.513,-0.928 1.197,0 2.713,0.438 4.548,1.316 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3982"
inkscape:connector-curvature="0" /></g><g
id="g3984"
transform="translate(185.7284,48.3646)"><path
d="m 0,0 c -2.594,0 -4.697,-0.878 -6.313,-2.633 -1.616,-1.756 -2.424,-4.03 -2.424,-6.822 0,-2.872 0.808,-5.187 2.424,-6.941 1.616,-1.756 3.719,-2.633 6.313,-2.633 3.83,0 6.702,1.256 8.617,3.769 l 4.847,-4.009 c -1.436,-2.075 -3.331,-3.67 -5.685,-4.788 -2.354,-1.117 -5.087,-1.674 -8.198,-1.674 -3.152,0 -5.954,0.677 -8.407,2.034 -2.454,1.355 -4.36,3.261 -5.715,5.715 -1.357,2.453 -2.035,5.275 -2.035,8.467 0,3.232 0.688,6.083 2.064,8.557 1.377,2.474 3.291,4.389 5.745,5.745 2.454,1.356 5.276,2.034 8.468,2.034 2.911,0 5.515,-0.508 7.809,-1.525 2.294,-1.018 4.159,-2.464 5.595,-4.339 L 8.378,-3.531 C 6.224,-1.178 3.431,0 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3986"
inkscape:connector-curvature="0" /></g><g
id="g3988"
transform="translate(209.1259,45.8509)"><path
d="m 0,0 c -1.676,-1.795 -2.514,-4.129 -2.514,-7.001 0,-2.873 0.838,-5.206 2.514,-7.002 1.676,-1.795 3.869,-2.692 6.583,-2.692 2.633,0 4.787,0.897 6.463,2.692 1.675,1.796 2.513,4.129 2.513,7.002 0,2.872 -0.838,5.206 -2.513,7.001 C 11.37,1.796 9.216,2.693 6.583,2.693 3.869,2.693 1.676,1.796 0,0 m 15.35,7.301 c 2.532,-1.357 4.507,-3.262 5.923,-5.715 1.416,-2.453 2.124,-5.276 2.124,-8.468 0,-3.231 -0.708,-6.084 -2.124,-8.556 -1.416,-2.475 -3.391,-4.39 -5.923,-5.746 -2.535,-1.356 -5.456,-2.034 -8.767,-2.034 -3.352,0 -6.305,0.678 -8.856,2.034 -2.554,1.356 -4.539,3.271 -5.956,5.746 -1.416,2.472 -2.124,5.325 -2.124,8.556 0,3.192 0.708,6.015 2.124,8.468 1.417,2.453 3.402,4.358 5.956,5.715 2.551,1.355 5.504,2.034 8.856,2.034 3.311,0 6.232,-0.679 8.767,-2.034"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3990"
inkscape:connector-curvature="0" /></g><g
id="g3992"
transform="translate(243.2948,67.3939)"><path
d="m 0,0 c 0.838,-0.878 1.256,-1.995 1.256,-3.352 0,-1.316 -0.418,-2.414 -1.256,-3.291 -0.838,-0.878 -1.896,-1.316 -3.172,-1.316 -1.277,0 -2.334,0.438 -3.172,1.316 -0.838,0.877 -1.257,1.975 -1.257,3.291 0,1.357 0.419,2.474 1.257,3.352 0.838,0.877 1.895,1.316 3.172,1.316 C -1.896,1.316 -0.838,0.877 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3994"
inkscape:connector-curvature="0" /></g><path
d="m 236.293,54.947 h 7.779 V 22.932 h -7.779 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path3996"
inkscape:connector-curvature="0" /><g
id="g3998"
transform="translate(278.6601,51.9252)"><path
d="m 0,0 c 2.114,-2.214 3.171,-5.217 3.171,-9.006 v -19.987 h -7.839 v 17.713 c 0,2.233 -0.639,3.999 -1.915,5.296 -1.277,1.296 -3.013,1.945 -5.206,1.945 -2.594,-0.041 -4.638,-0.919 -6.134,-2.634 -1.496,-1.716 -2.244,-3.91 -2.244,-6.582 v -15.738 h -7.779 V 3.021 h 7.779 v -5.983 c 2.193,4.108 6.023,6.202 11.489,6.283 C -5.007,3.321 -2.115,2.214 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4000"
inkscape:connector-curvature="0" /></g><path
d="M 286.259,43.876 H 301.28 V 38.49 h -15.021 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4002"
inkscape:connector-curvature="0" /><g
id="g4004"
transform="translate(320.8769,48.4242)"><path
d="m 0,0 c -1.656,0.519 -3.182,0.778 -4.578,0.778 -1.317,0 -2.363,-0.23 -3.142,-0.688 -0.778,-0.459 -1.167,-1.167 -1.167,-2.124 0,-0.998 0.489,-1.766 1.466,-2.304 0.978,-0.539 2.524,-1.107 4.638,-1.706 2.234,-0.679 4.069,-1.347 5.506,-2.005 1.436,-0.658 2.682,-1.636 3.74,-2.931 1.057,-1.297 1.586,-3.003 1.586,-5.117 0,-3.112 -1.197,-5.506 -3.591,-7.181 -2.394,-1.676 -5.406,-2.513 -9.036,-2.513 -2.474,0 -4.888,0.389 -7.24,1.167 -2.355,0.777 -4.35,1.885 -5.985,3.32 l 2.693,5.447 c 1.436,-1.238 3.151,-2.205 5.146,-2.903 1.994,-0.699 3.89,-1.048 5.685,-1.048 1.436,0 2.583,0.249 3.441,0.748 0.858,0.499 1.286,1.248 1.286,2.245 0,1.116 -0.499,1.954 -1.496,2.513 -0.997,0.558 -2.613,1.176 -4.847,1.855 -2.154,0.637 -3.91,1.266 -5.265,1.885 -1.358,0.619 -2.534,1.556 -3.531,2.813 -0.997,1.257 -1.496,2.902 -1.496,4.936 0,3.152 1.146,5.555 3.441,7.211 2.293,1.656 5.176,2.484 8.646,2.484 2.115,0 4.199,-0.299 6.254,-0.898 C 4.208,5.386 6.033,4.548 7.63,3.471 L 4.817,-2.154 C 3.261,-1.237 1.654,-0.519 0,0"
style="fill:#ed5039;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4006"
inkscape:connector-curvature="0" /></g><g
id="g4008"
transform="translate(60.327,74.4056)"><path
d="m 0,0 c -15.708,0 -28.442,-12.734 -28.442,-28.442 0,-9.185 4.357,-17.347 11.112,-22.546 3.014,1.79 8.559,4.834 10.472,4.2 2.675,-0.886 5.147,-1.348 7.414,-1.385 2.139,-0.034 3.845,0.313 5.121,1.035 1.276,0.726 1.926,1.866 1.951,3.419 0.027,1.621 -0.746,2.881 -2.32,3.781 -1.573,0.901 -4.068,1.866 -7.487,2.893 -3.611,1.162 -6.574,2.295 -8.889,3.402 -2.316,1.107 -4.314,2.728 -5.998,4.861 -1.682,2.134 -2.496,4.919 -2.44,8.351 0.082,5.055 2.089,8.911 6.022,11.57 3.931,2.659 8.847,3.938 14.742,3.842 4.018,-0.065 7.928,-0.76 11.729,-2.086 3.804,-1.325 5.115,-14.326 5.115,-14.326 -2.3,2.047 -5.06,3.664 -8.283,4.85 -3.22,1.188 -6.289,1.805 -9.205,1.852 -2.333,0.038 -4.201,-0.336 -5.608,-1.124 -1.406,-0.788 -2.123,-1.992 -2.149,-3.611 -0.03,-1.813 0.759,-3.187 2.363,-4.122 1.606,-0.933 4.214,-1.978 7.824,-3.141 3.482,-1.092 6.317,-2.16 8.503,-3.202 2.187,-1.039 4.075,-2.592 5.66,-4.661 1.587,-2.067 2.354,-4.753 2.3,-8.057 -0.058,-3.559 -1.028,-6.5 -2.867,-8.856 7.147,5.166 11.802,13.567 11.802,23.061 C 28.442,-12.734 15.708,0 0,0"
style="fill:#ed5039;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path4010"
inkscape:connector-curvature="0" /></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,45 @@
window.addEventListener("load", function() {
function button(label, ariaLabel, icon, className) {
const btn = document.createElement("button");
btn.classList.add("btnIcon", className);
btn.setAttribute("type", "button");
btn.setAttribute("aria-label", ariaLabel);
btn.innerHTML =
'<div class="btnIcon__body">' +
icon +
'<strong class="btnIcon__label">' +
label +
"</strong>" +
"</div>";
return btn;
}
function addButtons(codeBlockSelector, btn) {
document.querySelectorAll(codeBlockSelector).forEach(function(code) {
code.parentNode.appendChild(btn.cloneNode(true));
});
}
const copyIcon =
'<svg width="12" height="12" viewBox="340 364 14 15" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M342 375.974h4v.998h-4v-.998zm5-5.987h-5v.998h5v-.998zm2 2.994v-1.995l-3 2.993 3 2.994v-1.996h5v-1.995h-5zm-4.5-.997H342v.998h2.5v-.997zm-2.5 2.993h2.5v-.998H342v.998zm9 .998h1v1.996c-.016.28-.11.514-.297.702-.187.187-.422.28-.703.296h-10c-.547 0-1-.452-1-.998v-10.976c0-.546.453-.998 1-.998h3c0-1.107.89-1.996 2-1.996 1.11 0 2 .89 2 1.996h3c.547 0 1 .452 1 .998v4.99h-1v-2.995h-10v8.98h10v-1.996zm-9-7.983h8c0-.544-.453-.996-1-.996h-1c-.547 0-1-.453-1-.998 0-.546-.453-.998-1-.998-.547 0-1 .452-1 .998 0 .545-.453.998-1 .998h-1c-.547 0-1 .452-1 .997z" fill-rule="evenodd"/></svg>';
addButtons(
".hljs",
button("Copy", "Copy code to clipboard", copyIcon, "btnClipboard")
);
const clipboard = new ClipboardJS(".btnClipboard", {
target: function(trigger) {
return trigger.parentNode.querySelector("code");
}
});
clipboard.on("success", function(event) {
event.clearSelection();
const textEl = event.trigger.querySelector(".btnIcon__label");
textEl.textContent = "Copied";
setTimeout(function() {
textEl.textContent = "Copy";
}, 2000);
});
});

6057
website/yarn.lock Normal file

File diff suppressed because it is too large Load Diff