mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-13 19:37:30 +01:00
Bump Scala versions (#697)
* Bump Scala versions Support Scala 2.12.9 and 2.13.0. To make this easier, we delete the `scripts` project. Everything that was in here was covered by content on the website. We also delete the `doc` folder, as that was a remnant from when `scripts` was called `doc`. * Crib uPickle akka-http support while we wait for publish * Fix compiler warnings * Add note on test logging to contribution guide * Reduce duplication in Blockchain implementation * Use Scala 2.12 for website * Introduce compat package object for collections converters * Fix Either compiler warnings * Add sync-chain and create-wallet docs from deleted scripts * Fix rebase goofup
This commit is contained in:
parent
f0c9432601
commit
a303818c1e
71 changed files with 877 additions and 941 deletions
|
@ -16,7 +16,8 @@ os:
|
|||
|
||||
scala:
|
||||
- 2.11.12
|
||||
- 2.12.8
|
||||
- 2.12.9
|
||||
- 2.13.0
|
||||
|
||||
# These directories are cached to S3 at the end of the build
|
||||
cache:
|
||||
|
@ -70,7 +71,7 @@ jobs:
|
|||
include:
|
||||
- stage: test
|
||||
name: Compile website
|
||||
script: sbt docs/mdoc
|
||||
script: sbt ++2.12.9 docs/mdoc
|
||||
# run ci-release only if previous stages passed
|
||||
- stage: release
|
||||
jdk: openjdk8
|
||||
|
@ -79,5 +80,5 @@ jobs:
|
|||
# run website push only if previous stages passed
|
||||
# we use custom sbt task that first compiles Scaladocs
|
||||
# and then calls the docusaurusPublishGhpages task
|
||||
- script: sbt docs/publishWebsite
|
||||
- script: sbt ++2.12.9 docs/publishWebsite
|
||||
name: Publish website
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name := "bitcoin-s-cli"
|
||||
|
||||
libraryDependencies ++= Deps.cli
|
||||
libraryDependencies ++= Deps.cli(scalaVersion.value)
|
||||
|
||||
graalVMNativeImageOptions ++= Seq(
|
||||
"-H:EnableURLProtocols=http",
|
||||
|
|
|
@ -5,8 +5,6 @@ import org.bitcoins.core.config.NetworkParameters
|
|||
import org.bitcoins.core.protocol._
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.core.config.Networks
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
|
||||
/** scopt readers for parsing CLI params and options */
|
||||
object CliReaders {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
name := "bitcoin-s-app-picklers"
|
||||
|
||||
libraryDependencies ++= Deps.picklers
|
||||
libraryDependencies ++= Deps.picklers(scalaVersion.value)
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.bitcoins
|
|||
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.currency.Bitcoins
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
import upickle.default._
|
||||
|
||||
package object picklers {
|
||||
|
|
|
@ -4,4 +4,4 @@ name := "bitcoin-s-server"
|
|||
// when server is quit
|
||||
Compile / fork := true
|
||||
|
||||
libraryDependencies ++= Deps.server
|
||||
libraryDependencies ++= Deps.server(scalaVersion.value)
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2015 Heiko Seeberger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package de.heikoseeberger.akkahttpupickle
|
||||
|
||||
import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller}
|
||||
import akka.http.scaladsl.model.ContentTypeRange
|
||||
import akka.http.scaladsl.model.MediaType
|
||||
import akka.http.scaladsl.model.MediaTypes.`application/json`
|
||||
import akka.http.scaladsl.unmarshalling.{FromEntityUnmarshaller, Unmarshaller}
|
||||
import akka.util.ByteString
|
||||
import upickle.default.{Reader, Writer, read, write}
|
||||
import scala.collection.immutable.Seq
|
||||
|
||||
/**
|
||||
* Automatic to and from JSON marshalling/unmarshalling using *upickle* protocol.
|
||||
*/
|
||||
object UpickleSupport extends UpickleSupport
|
||||
|
||||
/**
|
||||
* Automatic to and from JSON marshalling/unmarshalling using *upickle* protocol.
|
||||
*/
|
||||
trait UpickleSupport {
|
||||
|
||||
def unmarshallerContentTypes: Seq[ContentTypeRange] =
|
||||
mediaTypes.map(ContentTypeRange.apply)
|
||||
|
||||
def mediaTypes: Seq[MediaType.WithFixedCharset] =
|
||||
List(`application/json`)
|
||||
|
||||
private val jsonStringUnmarshaller =
|
||||
Unmarshaller.byteStringUnmarshaller
|
||||
.forContentTypes(unmarshallerContentTypes: _*)
|
||||
.mapWithCharset {
|
||||
case (ByteString.empty, _) => throw Unmarshaller.NoContentException
|
||||
case (data, charset) => data.decodeString(charset.nioCharset.name)
|
||||
}
|
||||
|
||||
private val jsonStringMarshaller =
|
||||
Marshaller.oneOf(mediaTypes: _*)(Marshaller.stringMarshaller)
|
||||
|
||||
/**
|
||||
* HTTP entity => `A`
|
||||
*
|
||||
* @tparam A type to decode
|
||||
* @return unmarshaller for `A`
|
||||
*/
|
||||
implicit def unmarshaller[A: Reader]: FromEntityUnmarshaller[A] =
|
||||
jsonStringUnmarshaller.map(read(_))
|
||||
|
||||
/**
|
||||
* `A` => HTTP entity
|
||||
*
|
||||
* @tparam A type to encode
|
||||
* @return marshaller for any `A` value
|
||||
*/
|
||||
implicit def marshaller[A: Writer]: ToEntityMarshaller[A] =
|
||||
jsonStringMarshaller.compose(write(_))
|
||||
}
|
|
@ -4,11 +4,9 @@ import upickle.default._
|
|||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.currency.Bitcoins
|
||||
|
||||
import org.bitcoins.picklers._
|
||||
import scala.util.Failure
|
||||
import scala.util.Try
|
||||
import scala.util.Success
|
||||
import akka.io.Udp.Send
|
||||
|
||||
// TODO ID?
|
||||
case class ServerCommand(method: String, params: ujson.Arr)
|
||||
|
|
|
@ -138,10 +138,15 @@ trait TransactionRpc { self: Client =>
|
|||
comment: String = "",
|
||||
subtractFeeFrom: Vector[BitcoinAddress] = Vector.empty): Future[
|
||||
DoubleSha256DigestBE] = {
|
||||
val jsonOutputs: JsValue = Json.toJson {
|
||||
amounts.map {
|
||||
case (addr, curr) => addr -> Bitcoins(curr.satoshis)
|
||||
}
|
||||
}
|
||||
bitcoindCall[DoubleSha256DigestBE](
|
||||
"sendmany",
|
||||
List(JsString(""),
|
||||
Json.toJson(amounts.mapValues(curr => Bitcoins(curr.satoshis))),
|
||||
jsonOutputs,
|
||||
JsNumber(minconf),
|
||||
JsString(comment),
|
||||
Json.toJson(subtractFeeFrom))
|
||||
|
|
|
@ -47,12 +47,17 @@ trait V17PsbtRpc { self: Client =>
|
|||
outputs: Map[BitcoinAddress, CurrencyUnit],
|
||||
locktime: Int = 0,
|
||||
replacable: Boolean = false): Future[String] = {
|
||||
bitcoindCall[String](
|
||||
"createpsbt",
|
||||
List(Json.toJson(inputs),
|
||||
Json.toJson(outputs.mapValues(curr => Bitcoins(curr.satoshis))),
|
||||
JsNumber(locktime),
|
||||
JsBoolean(replacable)))
|
||||
val outputsJson =
|
||||
Json.toJson {
|
||||
outputs.map {
|
||||
case (addr, curr) => addr -> Bitcoins(curr.satoshis)
|
||||
}
|
||||
}
|
||||
bitcoindCall[String]("createpsbt",
|
||||
List(Json.toJson(inputs),
|
||||
outputsJson,
|
||||
JsNumber(locktime),
|
||||
JsBoolean(replacable)))
|
||||
}
|
||||
|
||||
def combinePsbt(psbts: Vector[String]): Future[String] = {
|
||||
|
@ -72,15 +77,20 @@ trait V17PsbtRpc { self: Client =>
|
|||
locktime: Int = 0,
|
||||
options: WalletCreateFundedPsbtOptions = WalletCreateFundedPsbtOptions(),
|
||||
bip32derivs: Boolean = false
|
||||
): Future[WalletCreateFundedPsbtResult] =
|
||||
): Future[WalletCreateFundedPsbtResult] = {
|
||||
val jsonOutputs =
|
||||
Json.toJson {
|
||||
outputs.map { case (addr, curr) => addr -> Bitcoins(curr.satoshis) }
|
||||
}
|
||||
bitcoindCall[WalletCreateFundedPsbtResult](
|
||||
"walletcreatefundedpsbt",
|
||||
List(Json.toJson(inputs),
|
||||
Json.toJson(outputs.mapValues(curr => Bitcoins(curr.satoshis))),
|
||||
jsonOutputs,
|
||||
JsNumber(locktime),
|
||||
Json.toJson(options),
|
||||
Json.toJson(bip32derivs))
|
||||
)
|
||||
}
|
||||
|
||||
def walletProcessPsbt(
|
||||
psbt: String,
|
||||
|
|
|
@ -20,7 +20,7 @@ sealed trait BitcoindAuthCredentials {
|
|||
}
|
||||
|
||||
object BitcoindAuthCredentials extends BitcoinSLogger {
|
||||
import scala.collection.JavaConverters._
|
||||
import org.bitcoins.core.compat.JavaConverters._
|
||||
|
||||
/**
|
||||
* Authenticate by providing a username and password.
|
||||
|
|
|
@ -299,7 +299,7 @@ object BitcoindConfig extends BitcoinSLogger {
|
|||
* by splitting it on newlines
|
||||
*/
|
||||
def apply(config: String, datadir: File): BitcoindConfig =
|
||||
apply(config.split("\n"), datadir)
|
||||
apply(config.split("\n").toList, datadir)
|
||||
|
||||
/** Reads the given path and construct a `bitcoind` config from it */
|
||||
def apply(config: Path): BitcoindConfig =
|
||||
|
@ -307,7 +307,7 @@ object BitcoindConfig extends BitcoinSLogger {
|
|||
|
||||
/** Reads the given file and construct a `bitcoind` config from it */
|
||||
def apply(config: File, datadir: File = DEFAULT_DATADIR): BitcoindConfig = {
|
||||
import scala.collection.JavaConverters._
|
||||
import org.bitcoins.core.compat.JavaConverters._
|
||||
val lines = Files
|
||||
.readAllLines(config.toPath)
|
||||
.iterator()
|
||||
|
@ -369,7 +369,7 @@ object BitcoindConfig extends BitcoinSLogger {
|
|||
Files.createDirectories(datadir.toPath)
|
||||
val confFile = datadir.toPath.resolve("bitcoin.conf")
|
||||
|
||||
if (datadir == DEFAULT_DATADIR && confFile == DEFAULT_CONF_FILE) {
|
||||
if (datadir == DEFAULT_DATADIR && confFile == DEFAULT_CONF_FILE.toPath) {
|
||||
logger.warn(
|
||||
s"We will not overrwrite the existing bitcoin.conf in default datadir")
|
||||
} else {
|
||||
|
|
|
@ -2,7 +2,8 @@ package org.bitcoins.rpc.serializers
|
|||
|
||||
import java.io.File
|
||||
import java.net.{InetAddress, URI}
|
||||
import java.time.{LocalDateTime, ZoneId, ZoneOffset}
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
|
||||
|
@ -30,6 +31,7 @@ import org.bitcoins.rpc.serializers.JsonSerializers._
|
|||
import play.api.libs.json._
|
||||
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
import org.bitcoins.core.config._
|
||||
|
||||
object JsonReaders {
|
||||
|
@ -42,11 +44,14 @@ object JsonReaders {
|
|||
implicit readsK: Reads[K],
|
||||
readsV: Reads[V]): JsResult[Map[K, V]] = {
|
||||
js.validate[JsObject].flatMap { jsObj =>
|
||||
val jsResults: Seq[(JsResult[K], JsResult[V])] = jsObj.fields.map {
|
||||
case (key, value) => JsString(key).validate[K] -> value.validate[V]
|
||||
}
|
||||
val jsResults: scala.collection.Seq[(JsResult[K], JsResult[V])] =
|
||||
jsObj.fields.map {
|
||||
case (key, value) => JsString(key).validate[K] -> value.validate[V]
|
||||
}
|
||||
|
||||
val allErrors: Seq[(JsPath, Seq[JsonValidationError])] =
|
||||
val allErrors: scala.collection.Seq[(
|
||||
JsPath,
|
||||
scala.collection.Seq[JsonValidationError])] =
|
||||
jsResults.collect {
|
||||
case (JsError(keyErrors), _) => keyErrors
|
||||
case (_, JsError(valueErrors)) => valueErrors
|
||||
|
@ -70,7 +75,7 @@ object JsonReaders {
|
|||
|
||||
implicit object BigIntReads extends Reads[BigInt] {
|
||||
override def reads(json: JsValue): JsResult[BigInt] =
|
||||
SerializerUtil.processJsNumber[BigInt](_.toBigInt())(json)
|
||||
SerializerUtil.processJsNumber[BigInt](_.toBigInt)(json)
|
||||
}
|
||||
|
||||
implicit object Sha256DigestReads extends Reads[Sha256Digest] {
|
||||
|
@ -122,7 +127,7 @@ object JsonReaders {
|
|||
implicit object Int32Reads extends Reads[Int32] {
|
||||
override def reads(json: JsValue): JsResult[Int32] = json match {
|
||||
case JsNumber(n) =>
|
||||
n.toBigIntExact() match {
|
||||
n.toBigIntExact match {
|
||||
case Some(num) => JsSuccess(Int32(num))
|
||||
case None => SerializerUtil.buildErrorMsg("Int32", n)
|
||||
}
|
||||
|
@ -135,7 +140,7 @@ object JsonReaders {
|
|||
implicit object UInt32Reads extends Reads[UInt32] {
|
||||
override def reads(json: JsValue): JsResult[UInt32] = json match {
|
||||
case JsNumber(n) =>
|
||||
n.toBigIntExact() match {
|
||||
n.toBigIntExact match {
|
||||
case Some(num) =>
|
||||
if (num >= 0) {
|
||||
JsSuccess(UInt32(num))
|
||||
|
@ -153,7 +158,7 @@ object JsonReaders {
|
|||
implicit object UInt64Reads extends Reads[UInt64] {
|
||||
override def reads(json: JsValue): JsResult[UInt64] = json match {
|
||||
case JsNumber(n) =>
|
||||
n.toBigIntExact() match {
|
||||
n.toBigIntExact match {
|
||||
case Some(num) =>
|
||||
if (num >= 0) {
|
||||
JsSuccess(UInt64(num))
|
||||
|
@ -353,9 +358,10 @@ object JsonReaders {
|
|||
case None => JsError("error.expected.balance")
|
||||
}
|
||||
bitcoinResult.flatMap { bitcoins =>
|
||||
val jsStrings: IndexedSeq[String] = array.value
|
||||
.filter(_.isInstanceOf[JsString])
|
||||
.map(_.asInstanceOf[JsString].value)
|
||||
val jsStrings =
|
||||
array.value
|
||||
.filter(_.isInstanceOf[JsString])
|
||||
.map(_.asInstanceOf[JsString].value)
|
||||
val addressResult = jsStrings.find(BitcoinAddress.isValid) match {
|
||||
case Some(s) =>
|
||||
BitcoinAddress.fromString(s) match {
|
||||
|
@ -518,7 +524,7 @@ object JsonReaders {
|
|||
implicit object BitcoinFeeUnitReads extends Reads[BitcoinFeeUnit] {
|
||||
override def reads(json: JsValue): JsResult[BitcoinFeeUnit] =
|
||||
SerializerUtil.processJsNumber[BitcoinFeeUnit](num =>
|
||||
SatoshisPerByte(Satoshis(Int64((num * 100000).toBigInt()))))(json)
|
||||
SatoshisPerByte(Satoshis(Int64((num * 100000).toBigInt))))(json)
|
||||
}
|
||||
|
||||
implicit object FileReads extends Reads[File] {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.bitcoins.rpc.serializers
|
||||
|
||||
import org.bitcoins.core.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit}
|
||||
import org.bitcoins.core.currency.Bitcoins
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.ln.currency.MilliSatoshis
|
||||
|
@ -101,7 +101,7 @@ object JsonWriters {
|
|||
|
||||
def addToMapIfDefined[T](key: String, opt: Option[T])(
|
||||
implicit writes: Writes[T]): Unit =
|
||||
opt.foreach(o => jsOpts + (key -> Json.toJson(o)))
|
||||
opt.foreach(o => jsOpts += (key -> Json.toJson(o)))
|
||||
|
||||
addToMapIfDefined("changeAddress", opts.changeAddress)
|
||||
addToMapIfDefined("changePosition", opts.changePosition)
|
||||
|
|
|
@ -7,7 +7,7 @@ sealed abstract class SerializerUtil {
|
|||
def processJsNumberBigInt[T](numFunc: BigInt => T)(
|
||||
json: JsValue): JsResult[T] = json match {
|
||||
case JsNumber(nDecimal) =>
|
||||
val nOpt = nDecimal.toBigIntExact()
|
||||
val nOpt = nDecimal.toBigIntExact
|
||||
nOpt match {
|
||||
case Some(t) => JsSuccess(numFunc(t))
|
||||
case None =>
|
||||
|
|
73
build.sbt
73
build.sbt
|
@ -11,31 +11,40 @@ Test / flywayClean / aggregate := true
|
|||
|
||||
lazy val commonCompilerOpts = {
|
||||
List(
|
||||
"-Xmax-classfile-name",
|
||||
"128",
|
||||
"-Xsource:2.12",
|
||||
"-target:jvm-1.8"
|
||||
)
|
||||
}
|
||||
|
||||
//https://docs.scala-lang.org/overviews/compiler-options/index.html
|
||||
lazy val compilerOpts = Seq(
|
||||
"-encoding",
|
||||
"UTF-8",
|
||||
"-unchecked",
|
||||
"-feature",
|
||||
"-deprecation",
|
||||
"-Xfuture",
|
||||
"-Ywarn-dead-code",
|
||||
"-Ywarn-unused-import",
|
||||
"-Ywarn-value-discard",
|
||||
"-Ywarn-unused",
|
||||
"-unchecked",
|
||||
"-deprecation",
|
||||
"-feature"
|
||||
) ++ commonCompilerOpts
|
||||
val scala2_13CompilerOpts = Seq("-Xlint:unused")
|
||||
|
||||
lazy val testCompilerOpts = commonCompilerOpts
|
||||
val nonScala2_13CompilerOpts = Seq(
|
||||
"-Xmax-classfile-name",
|
||||
"128",
|
||||
"-Ywarn-unused",
|
||||
"-Ywarn-unused-import"
|
||||
)
|
||||
|
||||
//https://docs.scala-lang.org/overviews/compiler-options/index.html
|
||||
def compilerOpts(scalaVersion: String) =
|
||||
Seq(
|
||||
"-encoding",
|
||||
"UTF-8",
|
||||
"-unchecked",
|
||||
"-feature",
|
||||
"-deprecation",
|
||||
"-Ywarn-dead-code",
|
||||
"-Ywarn-value-discard",
|
||||
"-Ywarn-unused",
|
||||
"-unchecked",
|
||||
"-deprecation",
|
||||
"-feature"
|
||||
) ++ commonCompilerOpts ++ {
|
||||
if (scalaVersion.startsWith("2.13")) scala2_13CompilerOpts
|
||||
else nonScala2_13CompilerOpts
|
||||
}
|
||||
|
||||
val testCompilerOpts = commonCompilerOpts
|
||||
|
||||
lazy val isCI = {
|
||||
sys.props
|
||||
|
@ -67,7 +76,7 @@ lazy val commonSettings = List(
|
|||
apiURL := homepage.value.map(_.toString + "/api").map(url(_)),
|
||||
// scaladoc settings end
|
||||
////
|
||||
scalacOptions in Compile := compilerOpts,
|
||||
scalacOptions in Compile := compilerOpts(scalaVersion.value),
|
||||
scalacOptions in Test := testCompilerOpts,
|
||||
Compile / compile / javacOptions ++= {
|
||||
if (isCI) {
|
||||
|
@ -146,7 +155,6 @@ lazy val `bitcoin-s` = project
|
|||
walletServer,
|
||||
walletServerTest,
|
||||
testkit,
|
||||
scripts,
|
||||
zmq
|
||||
)
|
||||
.settings(commonSettings: _*)
|
||||
|
@ -381,7 +389,7 @@ lazy val bitcoindRpc = project
|
|||
lazy val bitcoindRpcTest = project
|
||||
.in(file("bitcoind-rpc-test"))
|
||||
.settings(commonTestSettings: _*)
|
||||
.settings(libraryDependencies ++= Deps.bitcoindRpcTest,
|
||||
.settings(libraryDependencies ++= Deps.bitcoindRpcTest(scalaVersion.value),
|
||||
name := "bitcoin-s-bitcoind-rpc-test")
|
||||
.dependsOn(core % testAndCompile, testkit)
|
||||
|
||||
|
@ -486,7 +494,7 @@ lazy val wallet = project
|
|||
.settings(walletDbSettings: _*)
|
||||
.settings(
|
||||
name := "bitcoin-s-wallet",
|
||||
libraryDependencies ++= Deps.wallet
|
||||
libraryDependencies ++= Deps.wallet(scalaVersion.value)
|
||||
)
|
||||
.dependsOn(core, dbCommons)
|
||||
.enablePlugins(FlywayPlugin)
|
||||
|
@ -502,25 +510,6 @@ lazy val walletTest = project
|
|||
.dependsOn(core % testAndCompile, testkit, wallet)
|
||||
.enablePlugins(FlywayPlugin)
|
||||
|
||||
lazy val scripts = project
|
||||
.in(file("scripts"))
|
||||
.settings(commonTestSettings: _*)
|
||||
.settings(
|
||||
name := "bitcoin-s-scripts",
|
||||
libraryDependencies ++= Deps.scripts
|
||||
)
|
||||
.dependsOn(
|
||||
bitcoindRpc,
|
||||
chain,
|
||||
core % testAndCompile,
|
||||
eclairRpc,
|
||||
node,
|
||||
secp256k1jni,
|
||||
testkit,
|
||||
wallet,
|
||||
zmq
|
||||
)
|
||||
|
||||
/** Given a database name, returns the appropriate
|
||||
* Flyway settings we apply to a project (chain, node, wallet) */
|
||||
def dbFlywaySettings(dbName: String): List[Setting[_]] = {
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package org.bitcoins.chain.blockchain
|
||||
|
||||
import org.bitcoins.chain.models.BlockHeaderDb
|
||||
|
||||
import scala.collection.{IndexedSeqLike, mutable}
|
||||
|
||||
/** @inheritdoc */
|
||||
case class Blockchain(headers: Vector[BlockHeaderDb])
|
||||
extends IndexedSeqLike[BlockHeaderDb, Vector[BlockHeaderDb]]
|
||||
with BaseBlockChain {
|
||||
|
||||
protected[blockchain] def compObjectfromHeaders(
|
||||
headers: scala.collection.immutable.Seq[BlockHeaderDb]): Blockchain =
|
||||
Blockchain.fromHeaders(headers)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def newBuilder: mutable.Builder[
|
||||
BlockHeaderDb,
|
||||
Vector[BlockHeaderDb]] = Vector.newBuilder[BlockHeaderDb]
|
||||
|
||||
/** @inheritdoc */
|
||||
override def seq: IndexedSeq[BlockHeaderDb] = headers
|
||||
}
|
||||
|
||||
object Blockchain extends BaseBlockChainCompObject {
|
||||
|
||||
def fromHeaders(
|
||||
headers: scala.collection.immutable.Seq[BlockHeaderDb]): Blockchain =
|
||||
Blockchain(headers.toVector)
|
||||
}
|
|
@ -4,10 +4,6 @@ import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb}
|
|||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
*
|
||||
* @param blockHeaderDAO
|
||||
*/
|
||||
case class BlockchainBuilder(blockHeaderDAO: BlockHeaderDAO)
|
||||
extends mutable.Builder[BlockHeaderDb, Blockchain] {
|
||||
private val internal = Vector.newBuilder[BlockHeaderDb]
|
|
@ -0,0 +1,32 @@
|
|||
package org.bitcoins.chain.blockchain
|
||||
|
||||
import org.bitcoins.chain.models.BlockHeaderDb
|
||||
|
||||
import scala.collection.{IndexedSeqLike, mutable}
|
||||
|
||||
/** @inheritdoc */
|
||||
case class Blockchain(headers: Vector[BlockHeaderDb])
|
||||
extends IndexedSeqLike[BlockHeaderDb, Vector[BlockHeaderDb]]
|
||||
with BaseBlockChain {
|
||||
|
||||
protected[blockchain] def compObjectfromHeaders(
|
||||
headers: scala.collection.immutable.Seq[BlockHeaderDb]): Blockchain =
|
||||
Blockchain.fromHeaders(headers)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def newBuilder: mutable.Builder[
|
||||
BlockHeaderDb,
|
||||
Vector[BlockHeaderDb]] = Vector.newBuilder[BlockHeaderDb]
|
||||
|
||||
/** @inheritdoc */
|
||||
override def seq: IndexedSeq[BlockHeaderDb] = headers
|
||||
|
||||
}
|
||||
|
||||
object Blockchain extends BaseBlockChainCompObject {
|
||||
|
||||
def fromHeaders(
|
||||
headers: scala.collection.immutable.Seq[BlockHeaderDb]): Blockchain =
|
||||
Blockchain(headers.toVector)
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.bitcoins.chain.blockchain
|
||||
|
||||
import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb}
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
case class BlockchainBuilder(blockHeaderDAO: BlockHeaderDAO)
|
||||
extends mutable.Builder[BlockHeaderDb, Blockchain] {
|
||||
private val internal = Vector.newBuilder[BlockHeaderDb]
|
||||
|
||||
override def result(): Blockchain = {
|
||||
Blockchain.fromHeaders(internal.result().reverse)
|
||||
}
|
||||
|
||||
override def +=(blockHeaderDb: BlockHeaderDb): this.type = {
|
||||
internal.+=(blockHeaderDb)
|
||||
this
|
||||
}
|
||||
|
||||
override def clear(): Unit = internal.clear()
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package org.bitcoins.chain.blockchain
|
||||
|
||||
import org.bitcoins.chain.models.BlockHeaderDb
|
||||
|
||||
import scala.collection.immutable.IndexedSeq
|
||||
|
||||
/** @inheritdoc */
|
||||
case class Blockchain(headers: Vector[BlockHeaderDb])
|
||||
extends IndexedSeq[BlockHeaderDb]
|
||||
with BaseBlockChain {
|
||||
|
||||
protected[blockchain] def compObjectfromHeaders(
|
||||
headers: scala.collection.immutable.Seq[BlockHeaderDb]) =
|
||||
Blockchain.fromHeaders(headers)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def seq = this
|
||||
|
||||
}
|
||||
|
||||
object Blockchain extends BaseBlockChainCompObject {
|
||||
|
||||
override def fromHeaders(
|
||||
headers: scala.collection.immutable.Seq[BlockHeaderDb]): Blockchain =
|
||||
Blockchain(headers.toVector)
|
||||
}
|
|
@ -1,13 +1,23 @@
|
|||
package org.bitcoins.chain.blockchain
|
||||
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.chain.models.BlockHeaderDb
|
||||
import org.bitcoins.chain.validation.{TipUpdateResult, TipValidation}
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.chain.ChainVerificationLogger
|
||||
|
||||
import org.bitcoins.chain.validation.TipUpdateResult
|
||||
import org.bitcoins.chain.validation.TipValidation
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.{IndexedSeqLike, mutable}
|
||||
|
||||
// INTERNAL NOTE: Due to changes in the Scala collections in 2.13 this
|
||||
// class and its companion object
|
||||
// has to be implemented separetely for the different Scala versions.
|
||||
// The general idea is that all three implement a collection, but slightly
|
||||
// different ones (the one that the 2.12 and 2.11 versions implement got
|
||||
// removed in 2.13). The most interesting method is `compObjectFromHeaders`.
|
||||
// This is a method that's meant to represent a `fromHeaders` method on the
|
||||
// companion object. Because Scala has restrictions on where to place companion
|
||||
// objects (they have to be in the same file as the trait/class), this was
|
||||
// the least ugly workaround I could come up with.
|
||||
|
||||
/**
|
||||
* In memory implementation of a blockchain
|
||||
|
@ -21,23 +31,23 @@ import scala.collection.{IndexedSeqLike, mutable}
|
|||
* }}}
|
||||
*
|
||||
*/
|
||||
case class Blockchain(headers: Vector[BlockHeaderDb])
|
||||
extends IndexedSeqLike[BlockHeaderDb, Vector[BlockHeaderDb]] {
|
||||
private[blockchain] trait BaseBlockChain {
|
||||
|
||||
protected[blockchain] def compObjectfromHeaders(
|
||||
headers: scala.collection.immutable.Seq[BlockHeaderDb]): Blockchain
|
||||
|
||||
val tip: BlockHeaderDb = headers.head
|
||||
|
||||
/** @inheritdoc */
|
||||
override def newBuilder: mutable.Builder[
|
||||
BlockHeaderDb,
|
||||
Vector[BlockHeaderDb]] = Vector.newBuilder[BlockHeaderDb]
|
||||
/** The height of the chain */
|
||||
val height: Int = tip.height
|
||||
|
||||
/** @inheritdoc */
|
||||
override def seq: IndexedSeq[BlockHeaderDb] = headers
|
||||
val length: Int = headers.length
|
||||
|
||||
/** @inheritdoc */
|
||||
override def length: Int = headers.length
|
||||
def apply(idx: Int): BlockHeaderDb = headers(idx)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def apply(idx: Int): BlockHeaderDb = headers.apply(idx)
|
||||
def headers: Vector[BlockHeaderDb]
|
||||
|
||||
def find(predicate: BlockHeaderDb => Boolean): Option[BlockHeaderDb]
|
||||
|
||||
/** Finds a block header at a given height */
|
||||
def findAtHeight(height: Int): Option[BlockHeaderDb] =
|
||||
|
@ -48,7 +58,7 @@ case class Blockchain(headers: Vector[BlockHeaderDb])
|
|||
val headerIdxOpt = headers.zipWithIndex.find(_._1 == header)
|
||||
headerIdxOpt.map {
|
||||
case (header, idx) =>
|
||||
val newChain = Blockchain.fromHeaders(headers.splitAt(idx)._2)
|
||||
val newChain = this.compObjectfromHeaders(headers.splitAt(idx)._2)
|
||||
require(newChain.tip == header)
|
||||
newChain
|
||||
}
|
||||
|
@ -58,17 +68,13 @@ case class Blockchain(headers: Vector[BlockHeaderDb])
|
|||
def fromValidHeader(header: BlockHeaderDb): Blockchain = {
|
||||
fromHeader(header).get
|
||||
}
|
||||
|
||||
/** The height of the chain */
|
||||
def height: Int = tip.height
|
||||
|
||||
}
|
||||
|
||||
object Blockchain extends ChainVerificationLogger {
|
||||
private[blockchain] trait BaseBlockChainCompObject
|
||||
extends ChainVerificationLogger {
|
||||
|
||||
def fromHeaders(headers: Vector[BlockHeaderDb]): Blockchain = {
|
||||
Blockchain(headers)
|
||||
}
|
||||
def fromHeaders(
|
||||
headers: scala.collection.immutable.Seq[BlockHeaderDb]): Blockchain
|
||||
|
||||
/**
|
||||
* Attempts to connect the given block header with the given blockchain
|
||||
|
@ -204,4 +210,5 @@ object Blockchain extends ChainVerificationLogger {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -46,7 +46,7 @@ case class ChainAppConfig(
|
|||
case Success(bool) =>
|
||||
logger.debug(s"Chain project is initialized")
|
||||
p.success(bool)
|
||||
case Failure(err) =>
|
||||
case Failure(_) =>
|
||||
logger.info(s"Chain project is not initialized")
|
||||
p.success(false)
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class Bech32Spec extends Properties("Bech32Spec") {
|
|||
val idx = rand % old.length
|
||||
val (f, l) = old.splitAt(idx)
|
||||
val replacementChar = pickReplacementChar(l.head)
|
||||
val replaced = f ++ Seq(replacementChar) ++ l.tail
|
||||
val replaced = s"$f$replacementChar${l.tail}"
|
||||
//should fail because we replaced a char in the addr, so checksum invalid
|
||||
Bech32Address.fromString(replaced).isFailure
|
||||
}
|
||||
|
@ -76,10 +76,14 @@ class Bech32Spec extends Properties("Bech32Spec") {
|
|||
val (f, l) = addr.splitAt(idx)
|
||||
if (l.head.isDigit) {
|
||||
switchCaseRandChar(addr)
|
||||
} else if (l.head.isUpper) {
|
||||
f ++ Seq(l.head.toLower) ++ l.tail
|
||||
} else {
|
||||
f ++ Seq(l.head.toUpper) ++ l.tail
|
||||
val middle =
|
||||
if (l.head.isUpper) {
|
||||
l.head.toLower
|
||||
} else {
|
||||
l.head.toUpper
|
||||
}
|
||||
s"$f$middle${l.tail}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,6 @@ testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck,
|
|||
"-verbosity",
|
||||
"2")
|
||||
|
||||
//test in assembly := {}
|
||||
|
||||
//testOptions in Test += Tests.Argument("-oF")
|
||||
|
||||
//parallelExecution in Test := false
|
||||
|
||||
coverageExcludedPackages := ".*gen"
|
||||
|
||||
coverageMinimum := 90
|
||||
|
||||
coverageFailOnMinimum := true
|
|
@ -0,0 +1,17 @@
|
|||
package org.bitcoins.core
|
||||
|
||||
/** This package provides
|
||||
* compatability functionality
|
||||
* for compiling Scala 2.11, 2.12
|
||||
* and 2.13, ideally without leading
|
||||
* to any compiler warnings in any of
|
||||
* the versions.
|
||||
*/
|
||||
package object compat {
|
||||
|
||||
/** Provides imports that allow converting
|
||||
* Java collections to Scala collections
|
||||
*/
|
||||
val JavaConverters = scala.collection.JavaConverters
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.bitcoins.core
|
||||
|
||||
/** This package provides
|
||||
* compatability functionality
|
||||
* for compiling Scala 2.11, 2.12
|
||||
* and 2.13, ideally without leading
|
||||
* to any compiler warnings in any of
|
||||
* the versions.
|
||||
*/
|
||||
package object compat {
|
||||
|
||||
/** Provides imports that allow converting
|
||||
* Java collections to Scala collections
|
||||
*/
|
||||
val JavaConverters = scala.collection.JavaConverters
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.bitcoins.core
|
||||
|
||||
/** This package provides
|
||||
* compatability functionality
|
||||
* for compiling Scala 2.11, 2.12
|
||||
* and 2.13, ideally without leading
|
||||
* to any compiler warnings in any of
|
||||
* the versions.
|
||||
*/
|
||||
package object compat {
|
||||
|
||||
/** Provides imports that allow converting
|
||||
* Java collections to Scala collections
|
||||
*/
|
||||
val JavaConverters = scala.jdk.CollectionConverters
|
||||
|
||||
}
|
|
@ -284,10 +284,11 @@ sealed abstract class BloomFilter extends NetworkElement {
|
|||
private def murmurConstant = UInt32("fba4c795")
|
||||
|
||||
/** Adds a sequence of byte vectors to our bloom filter then returns that new filter*/
|
||||
def insertByteVectors(bytes: Seq[ByteVector]): BloomFilter = {
|
||||
def insertByteVectors(
|
||||
bytes: scala.collection.Seq[ByteVector]): BloomFilter = {
|
||||
@tailrec
|
||||
def loop(
|
||||
remainingByteVectors: Seq[ByteVector],
|
||||
remainingByteVectors: scala.collection.Seq[ByteVector],
|
||||
accumBloomFilter: BloomFilter): BloomFilter = {
|
||||
if (remainingByteVectors.isEmpty) accumBloomFilter
|
||||
else
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package org.bitcoins.core.compat
|
||||
|
||||
import scala.util.Try
|
||||
import scala.util.Success
|
||||
import scala.util.Failure
|
||||
|
||||
/** This is an implementation of (parts of)
|
||||
* `scala.util.Either`, compatible with Scala 2.11,
|
||||
* 2.12 and 2.13. It is in large parts cribbed from
|
||||
* the Scala 2.12 standard library.
|
||||
*/
|
||||
sealed private[bitcoins] trait CompatEither[A, B] {
|
||||
|
||||
protected val underlying: Either[A, B]
|
||||
|
||||
/** The given function is applied if this is a `Right`.
|
||||
*
|
||||
* {{{
|
||||
* Right(12).map(x => "flower") // Result: Right("flower")
|
||||
* Left(12).map(x => "flower") // Result: Left(12)
|
||||
* }}}
|
||||
*/
|
||||
def map[B1](f: B => B1): CompatEither[A, B1] = underlying match {
|
||||
case Right(b) => CompatRight(f(b))
|
||||
case _ => this.asInstanceOf[CompatEither[A, B1]]
|
||||
}
|
||||
|
||||
/** Binds the given function across `Right`.
|
||||
*
|
||||
* @param f The function to bind across `Right`.
|
||||
*/
|
||||
def flatMap[A1 >: A, B1](f: B => CompatEither[A1, B1]): CompatEither[A1, B1] =
|
||||
underlying match {
|
||||
case Right(b) =>
|
||||
f(b) match {
|
||||
case CompatLeft(value) => CompatLeft(value)
|
||||
case CompatRight(value) => CompatRight(value)
|
||||
|
||||
}
|
||||
case Left(l) => CompatLeft(l)
|
||||
}
|
||||
|
||||
def toTry(implicit ev: A <:< Throwable): Try[B] = underlying match {
|
||||
case Right(b) => Success(b)
|
||||
case Left(a) => Failure(a)
|
||||
}
|
||||
|
||||
/** Applies `fa` if this is a `Left` or `fb` if this is a `Right`.
|
||||
*
|
||||
* @example {{{
|
||||
* val result = util.Try("42".toInt).toEither
|
||||
* result.fold(
|
||||
* e => s"Operation failed with $e",
|
||||
* v => s"Operation produced value: $v"
|
||||
* )
|
||||
* }}}
|
||||
*
|
||||
* @param fa the function to apply if this is a `Left`
|
||||
* @param fb the function to apply if this is a `Right`
|
||||
* @return the results of applying the function
|
||||
*/
|
||||
def fold[C](fa: A => C, fb: B => C): C = underlying match {
|
||||
case Right(b) => fb(b)
|
||||
case Left(a) => fa(a)
|
||||
}
|
||||
}
|
||||
|
||||
object CompatEither {
|
||||
|
||||
/** Converts the given `scala.util.Either` to a `CompatEither` */
|
||||
def apply[A, B](either: Either[A, B]): CompatEither[A, B] = either match {
|
||||
case Left(value) => CompatLeft(value)
|
||||
case Right(value) => CompatRight(value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Analogous to `scala.util.Left` */
|
||||
case class CompatLeft[A, B](value: A) extends CompatEither[A, B] {
|
||||
val underlying = scala.util.Left[A, B](value)
|
||||
|
||||
}
|
||||
|
||||
/** Analogous to `scala.util.Right` */
|
||||
case class CompatRight[A, B](value: B) extends CompatEither[A, B] {
|
||||
val underlying = scala.util.Right[A, B](value)
|
||||
}
|
|
@ -267,7 +267,10 @@ object ExtPrivateKey extends Factory[ExtPrivateKey] {
|
|||
* [[https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#master-key-generation BIP32]]
|
||||
*/
|
||||
private val BIP32_KEY: ByteVector =
|
||||
ByteVector.encodeAscii("Bitcoin seed").right.get
|
||||
ByteVector.encodeAscii("Bitcoin seed") match {
|
||||
case Left(exception) => throw exception
|
||||
case Right(bytevec) => bytevec
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a master private key
|
||||
|
|
|
@ -26,7 +26,10 @@ sealed abstract class LnHumanReadablePart extends Bech32HumanReadablePart {
|
|||
}
|
||||
|
||||
lazy val bytes: ByteVector =
|
||||
ByteVector.encodeAscii(chars).right.get
|
||||
ByteVector.encodeAscii(chars) match {
|
||||
case Left(exc) => throw exc
|
||||
case Right(bytevec) => bytevec
|
||||
}
|
||||
|
||||
override lazy val toString: String = chars
|
||||
}
|
||||
|
|
|
@ -36,9 +36,7 @@ sealed abstract class LnPolicy {
|
|||
|
||||
private def calc(mul: LnMultiplier): BigInt = {
|
||||
maxPicoBitcoins /
|
||||
(mul.multiplier / LnMultiplier.Pico.multiplier)
|
||||
.toBigIntExact()
|
||||
.get
|
||||
(mul.multiplier / LnMultiplier.Pico.multiplier).toBigIntExact.get
|
||||
}
|
||||
|
||||
val DEFAULT_LN_P2P_PORT = 9735
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.bitcoins.core.protocol.ln
|
|||
|
||||
import org.bitcoins.core.number.{UInt5, UInt8}
|
||||
import org.bitcoins.core.protocol.NetworkElement
|
||||
import org.bitcoins.core.protocol.ln.LnTag.PaymentHashTag
|
||||
import org.bitcoins.core.protocol.ln.util.LnUtil
|
||||
import org.bitcoins.core.util.Bech32
|
||||
import scodec.bits.ByteVector
|
||||
|
@ -92,10 +91,12 @@ object LnTaggedFields {
|
|||
val (description, descriptionHash): (
|
||||
Option[LnTag.DescriptionTag],
|
||||
Option[LnTag.DescriptionHashTag]) = {
|
||||
if (descriptionOrHash.isLeft) {
|
||||
(descriptionOrHash.left.toOption, None)
|
||||
} else {
|
||||
(None, descriptionOrHash.right.toOption)
|
||||
|
||||
descriptionOrHash match {
|
||||
case Left(description) =>
|
||||
(Some(description), None)
|
||||
case Right(hash) =>
|
||||
(None, Some(hash))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ sealed abstract class LnCurrencyUnit
|
|||
* would appear as "100p"
|
||||
*/
|
||||
def toEncodedString: String = {
|
||||
toBigInt + character.toString
|
||||
toBigInt.toString + character.toString
|
||||
}
|
||||
|
||||
override def toString(): String = s"$underlying ${character}BTC"
|
||||
|
|
|
@ -47,7 +47,7 @@ trait ScriptFlagFactory {
|
|||
|
||||
/** Parses a list of [[ScriptFlag]]s that is separated by commas. */
|
||||
def fromList(str: String): Seq[ScriptFlag] = {
|
||||
fromList(str.split(","))
|
||||
fromList(str.split(",").toList)
|
||||
}
|
||||
|
||||
/** Empty script flag. */
|
||||
|
|
|
@ -758,7 +758,7 @@ sealed abstract class ScriptInterpreter {
|
|||
calcOpCount(opCount, OP_CHECKSEQUENCEVERIFY))
|
||||
//no more script operations to run, return whether the program is valid and the final state of the program
|
||||
case Nil => loop(ScriptProgram.toExecutedProgram(p), opCount)
|
||||
case h :: _ => throw new RuntimeException(h + " was unmatched")
|
||||
case h :: _ => throw new RuntimeException(s"$h was unmatched")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ case class SatoshisPerKiloByte(currencyUnit: CurrencyUnit)
|
|||
extends BitcoinFeeUnit {
|
||||
|
||||
def toSatPerByte: SatoshisPerByte = {
|
||||
val conversionOpt = (currencyUnit.toBigDecimal * 0.001).toBigIntExact()
|
||||
val conversionOpt = (currencyUnit.toBigDecimal * 0.001).toBigIntExact
|
||||
conversionOpt match {
|
||||
case Some(conversion) =>
|
||||
val sat = Satoshis(Int64(conversion))
|
||||
|
|
|
@ -98,7 +98,7 @@ abstract class DbCommonsColumnMappers {
|
|||
},
|
||||
//this has the potential to throw
|
||||
{ bigDec: BigDecimal =>
|
||||
UInt64(bigDec.toBigIntExact().get)
|
||||
UInt64(bigDec.toBigIntExact.get)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
## Ammonite scripts
|
||||
|
||||
This project contain [Ammonite](https://ammonite.io) scripts that demonstrate
|
||||
functionality of `bitcoin-s`.
|
||||
|
||||
#### Running them with sbt:
|
||||
|
||||
```bash
|
||||
$ sbt "doc/run path/to/script.sc" # this is very slow, not recommended
|
||||
```
|
||||
|
||||
#### Running them with the [Bloop CLI](https://scalacenter.github.io/bloop/):
|
||||
|
||||
```bash
|
||||
$ bloop run doc --args path/to/script.sc # much faster than through sbt
|
||||
```
|
|
@ -1,40 +0,0 @@
|
|||
# bitcoin-s configuration
|
||||
|
||||
bitcoin-s uses [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md)
|
||||
to configure various parts of the application the library offers. HOCON is a
|
||||
superset of JSON, that is, all valid JSON is valid HOCON.
|
||||
|
||||
All configuration for bitcoin-s is under the `bitcoin-s` key. The most interesting
|
||||
configurable parts right now are `datadir` and `network`. See
|
||||
[`db-commons/src/main/resources/reference.conf`](../db-commons/src/main/resources/reference.conf)
|
||||
for more information. In the future there will be separate keys under `bitcoin-s`
|
||||
for the `wallet`, `chain` and `node` modules.
|
||||
|
||||
If you have a file `application.conf` anywhere on your classpath when using
|
||||
bitcoin-s, the values there take precedence over the ones found in our
|
||||
`reference.conf`.
|
||||
|
||||
The resolved configuration gets parsed by
|
||||
[`AppConfig`](../db-commons/src/main/scala/org/bitcoins/db/AppConfig.scala).
|
||||
You can call the `.withOverrides` on this to override any value in the
|
||||
bitcoin-s configuration. An example of this would be:
|
||||
|
||||
```scala
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import com.typesafe.config.ConfigFactory
|
||||
|
||||
val myConfig = ConfigFactory.parseString("bitcoin-s.network = testnet3")
|
||||
val walletConfig = WalletAppConfig.withOverrides(myConfig)
|
||||
```
|
||||
|
||||
You can pass as many configs as you'd like into `withOverrides`. If any
|
||||
keys appear multiple times the last one encountered. takes precedence.
|
||||
|
||||
|
||||
## Internal configuration
|
||||
|
||||
Database connections are also configured by using HOCON. This is done in
|
||||
[`db.conf`](../db-commons/src/main/resources/db.conf)
|
||||
(as well as [`application.conf`](../testkit/src/main/resources/application.conf)
|
||||
in `testkit` for running tests). The options exposed here are **not** intended to
|
||||
be used by users of bitcoin-s, and are internal only.
|
|
@ -1,13 +0,0 @@
|
|||
## bitcoin-s databases
|
||||
|
||||
### node project
|
||||
|
||||
This contains information related to peer to peer networking and chainstate for the bitcoin-s project. You can see configuration for these databases [here](../node/src/main/resources/reference.conf)
|
||||
|
||||
Database names:
|
||||
|
||||
- `nodedb` - the mainnet database
|
||||
- `nodedb-testnet3` - the testnet3 database
|
||||
- `nodedb-regtest` - the regtest database
|
||||
- `nodedb-unittest` - the database used by unit tests.
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.crypto.ECPrivateKey
|
||||
import org.bitcoins.core.currency.Satoshis
|
||||
import org.bitcoins.core.number.{Int32, Int64, UInt32}
|
||||
import org.bitcoins.core.protocol.script.P2PKHScriptPubKey
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
BaseTransaction,
|
||||
Transaction,
|
||||
TransactionOutPoint,
|
||||
TransactionOutput
|
||||
}
|
||||
import org.bitcoins.core.script.crypto.HashType
|
||||
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||
import org.bitcoins.core.wallet.utxo.BitcoinUTXOSpendingInfo
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.Future
|
||||
|
||||
class TxBuilderExample extends FlatSpec with MustMatchers {
|
||||
|
||||
behavior of "TxBuilderExample"
|
||||
|
||||
it must "build a signed tx" in {
|
||||
|
||||
//This is a documented example of how to create a signed bitcoin transaction
|
||||
//with bitcoin-s. You can run this test case with the following sbt command
|
||||
|
||||
//$ sbt "doc/testOnly *TxBuilderExample -- -z signed"
|
||||
|
||||
//generate a fresh private key that we are going to use in the scriptpubkey
|
||||
val privKey = ECPrivateKey.freshPrivateKey
|
||||
|
||||
//this is the script that the TxBuilder is going to create a
|
||||
//script signature that validly spends this scriptPubKey
|
||||
val creditingSpk = P2PKHScriptPubKey(pubKey = privKey.publicKey)
|
||||
val amount = Satoshis(Int64(10000))
|
||||
|
||||
//this is the utxo we are going to be spending
|
||||
val utxo =
|
||||
TransactionOutput(currencyUnit = amount, scriptPubKey = creditingSpk)
|
||||
|
||||
//the private key that locks the funds for the script we are spending too
|
||||
val destinationPrivKey = ECPrivateKey.freshPrivateKey
|
||||
|
||||
//the amount we are sending -- 5000 satoshis -- to the destinationSPK
|
||||
val destinationAmount = Satoshis(Int64(5000))
|
||||
|
||||
//the script that corresponds to destination private key, this is what is protecting the money
|
||||
val destinationSPK =
|
||||
P2PKHScriptPubKey(pubKey = destinationPrivKey.publicKey)
|
||||
|
||||
//this is where we are sending money too
|
||||
//we could add more destinations here if we
|
||||
//wanted to batch transactions
|
||||
val destinations = {
|
||||
val destination1 = TransactionOutput(currencyUnit = destinationAmount,
|
||||
scriptPubKey = destinationSPK)
|
||||
|
||||
List(destination1)
|
||||
}
|
||||
|
||||
//we have to fabricate a transaction that contains the
|
||||
//utxo we are trying to spend. If this were a real blockchain
|
||||
//we would need to reference the utxo set
|
||||
val creditingTx = BaseTransaction(version = Int32.one,
|
||||
inputs = List.empty,
|
||||
outputs = List(utxo),
|
||||
lockTime = UInt32.zero)
|
||||
|
||||
//this is the information we need from the crediting tx
|
||||
//to properly "link" it in the transaction we are creating
|
||||
val outPoint = TransactionOutPoint(creditingTx.txId, UInt32.zero)
|
||||
|
||||
// this contains all the information we need to
|
||||
// validly sign the utxo above
|
||||
val utxoSpendingInfo = BitcoinUTXOSpendingInfo(outPoint = outPoint,
|
||||
output = utxo,
|
||||
signers = List(privKey),
|
||||
redeemScriptOpt = None,
|
||||
scriptWitnessOpt = None,
|
||||
hashType =
|
||||
HashType.sigHashAll)
|
||||
|
||||
//all of the utxo spending information, since we are only
|
||||
//spending one utxo, this is just one element
|
||||
val utxos: List[BitcoinUTXOSpendingInfo] = List(utxoSpendingInfo)
|
||||
|
||||
//this is how much we are going to pay as a fee to the network
|
||||
//for this example, we are going to pay 1 satoshi per byte
|
||||
val feeRate = SatoshisPerByte(Satoshis.one)
|
||||
|
||||
val changePrivKey = ECPrivateKey.freshPrivateKey
|
||||
val changeSPK = P2PKHScriptPubKey(pubKey = changePrivKey.publicKey)
|
||||
|
||||
// the network we are on, for this example we are using
|
||||
// the regression test network. This is a network you control
|
||||
// on your own machine
|
||||
val networkParams = RegTest
|
||||
|
||||
//yay! Now we have a TxBuilder object that we can use
|
||||
//to sign the tx.
|
||||
val txBuilder: Future[BitcoinTxBuilder] = {
|
||||
BitcoinTxBuilder(
|
||||
destinations = destinations,
|
||||
utxos = utxos,
|
||||
feeRate = feeRate,
|
||||
changeSPK = changeSPK,
|
||||
network = networkParams
|
||||
)
|
||||
}
|
||||
|
||||
txBuilder.failed.foreach { case err => println(err.getMessage) }
|
||||
|
||||
//let's finally produce a validly signed tx
|
||||
//The 'sign' method is going produce a validly signed transaction
|
||||
//This is going to iterate through each of the 'utxos' and use
|
||||
//the corresponding 'UTXOSpendingInfo' to produce a validly
|
||||
//signed input. This tx has a
|
||||
//
|
||||
//1 input
|
||||
//2 outputs (destination and change outputs)
|
||||
//3 a fee rate of 1 satoshi/byte
|
||||
val signedTxF: Future[Transaction] = txBuilder.flatMap(_.sign)
|
||||
|
||||
//let's print these things out so you can example them
|
||||
signedTxF.map { tx =>
|
||||
println("\nInputs:")
|
||||
tx.inputs.foreach(println)
|
||||
|
||||
println("\nOutputs:")
|
||||
tx.outputs.foreach(println)
|
||||
|
||||
//here is the fully signed serialized tx that
|
||||
//you COULD broadcast to a cryptocurrency p2p network
|
||||
println(s"\nFully signed tx in hex:")
|
||||
|
||||
println(s"${tx.hex}")
|
||||
}
|
||||
|
||||
//The output from the print statements should read something like this
|
||||
|
||||
//Inputs:
|
||||
//TransactionInputImpl(TransactionOutPointImpl(DoubleSha256DigestImpl(43c75d1d59e6f13f2ad3baf6e124685ba0919bccdbdf89c362fe2f30fee4bdfc),UInt32Impl(0)),P2PKHScriptSignature(6a4730440220573a7bbbd59192c4bf01b8f1dcafe981d11ab8528fead9d66d702c1b72e5dc76022007946a423073c949e85a4ca3901ab10a2d6b72873a347d2a55ef873016adae8601210356d581971934349333066ed933cdea45ae9c72829ce34d8dd6a758d56967e4cb),UInt32Impl(0))
|
||||
//
|
||||
//Outputs:
|
||||
//TransactionOutputImpl(SatoshisImpl(Int64Impl(5000)),P2PKHScriptPubKeyImpl(1976a914dbdadae42124c46a00d81181e5d9ab28fbf546ed88ac))
|
||||
//TransactionOutputImpl(SatoshisImpl(Int64Impl(4774)),P2PKHScriptPubKeyImpl(1976a914a95eb0d284593f0c8f818f64a55fa6e3852012a688ac))
|
||||
//
|
||||
//Fully signed tx in hex:
|
||||
//020000000143c75d1d59e6f13f2ad3baf6e124685ba0919bccdbdf89c362fe2f30fee4bdfc000000006a4730440220573a7bbbd59192c4bf01b8f1dcafe981d11ab8528fead9d66d702c1b72e5dc76022007946a423073c949e85a4ca3901ab10a2d6b72873a347d2a55ef873016adae8601210356d581971934349333066ed933cdea45ae9c72829ce34d8dd6a758d56967e4cb000000000288130000000000001976a914dbdadae42124c46a00d81181e5d9ab28fbf546ed88aca6120000000000001976a914a95eb0d284593f0c8f818f64a55fa6e3852012a688ac00000000
|
||||
|
||||
//remember, you can call .hex on any bitcoin-s data structure to get the hex representation!
|
||||
}
|
||||
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
import org.bitcoins.rpc.config._
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.chain.db._
|
||||
import org.bitcoins.chain.config._
|
||||
import org.bitcoins.chain.blockchain._
|
||||
import org.bitcoins.chain.blockchain.sync._
|
||||
import org.bitcoins.chain.models._
|
||||
|
||||
import org.bitcoins.core.protocol.blockchain._
|
||||
import org.bitcoins.rpc.client.common._
|
||||
import org.bitcoins.testkit.chain._
|
||||
import org.bitcoins.wallet._
|
||||
import org.bitcoins.wallet.api._
|
||||
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent._
|
||||
import scala.concurrent.duration.DurationInt
|
||||
import scala.util._
|
||||
|
||||
//the goal for this script is to create a chain and sync it
|
||||
//to disk after creation
|
||||
|
||||
//we should be able to read this chain on subsequent runs
|
||||
//assuming we are connected to the same bitcoind instance
|
||||
|
||||
//you can run this script with
|
||||
//$ sbt "doc/run doc/src/main/scala/org/bitcoins/doc/chain/sync-chain.sc"
|
||||
|
||||
|
||||
//boring config stuff
|
||||
val logger = LoggerFactory.getLogger("org.bitcoins.doc.chain.SyncChain")
|
||||
val time = System.currentTimeMillis()
|
||||
implicit val system = ActorSystem(s"sync-chain-${time}")
|
||||
import system.dispatcher
|
||||
|
||||
//first we are assuming that a bitcoind regtest node is running in
|
||||
//the background, you can see 'connect_bitcoind.sc' script
|
||||
//to see how to bind to a local/remote bitcoind node
|
||||
//This script assumes that you have a bitcoind instance running in the
|
||||
//background and that you have ~/.bitcoin/bitcoin.conf setup.
|
||||
//you need to have 'rpcuser' and 'rpcpassword' set in that bitcoin.conf file
|
||||
//You can pass in an alternative datadir if you wish by construct a new java.io.File()
|
||||
val bitcoindInstance = BitcoindInstance.fromDatadir()
|
||||
val rpcCli = new BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
logger.info(s"Done configuring rpc client")
|
||||
//next we need to create a way to monitor the chain
|
||||
val getBestBlockHash = ChainTestUtil.bestBlockHashFnRpc(Future.successful(rpcCli))
|
||||
|
||||
val getBlockHeader = ChainTestUtil.getBlockHeaderFnRpc(Future.successful(rpcCli))
|
||||
|
||||
val chainDbConfig = ChainDbConfig.RegTestDbConfig
|
||||
val chainAppConfig = ChainAppConfig(chainDbConfig)
|
||||
|
||||
logger.info(s"Creating chain tables")
|
||||
//initialize chain tables in bitcoin-s if they do not exist
|
||||
val chainProjectInitF = ChainTestUtil.initializeIfNeeded(chainAppConfig)
|
||||
|
||||
val blockHeaderDAO = BlockHeaderDAO(appConfig = chainAppConfig)
|
||||
|
||||
val chainHandler = ChainHandler(blockHeaderDAO, chainAppConfig)
|
||||
|
||||
val syncedChainApiF = chainProjectInitF.flatMap { _ =>
|
||||
logger.info(s"Beginning sync to bitcoin-s chain state")
|
||||
ChainSync.sync(chainHandler, getBlockHeader, getBestBlockHash)
|
||||
}
|
||||
|
||||
val syncResultF = syncedChainApiF.flatMap { chainApi =>
|
||||
chainApi.getBlockCount.map(count => logger.info(s"chain api blockcount=${count}"))
|
||||
|
||||
rpcCli.getBlockCount.map(count => logger.info(s"bitcoind blockcount=${count}"))
|
||||
}
|
||||
|
||||
syncResultF.onComplete { case result =>
|
||||
|
||||
logger.info(s"Sync result=${result}")
|
||||
system.terminate()
|
||||
}
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
import java.io.File
|
||||
|
||||
import org.bitcoins.chain.blockchain.{Blockchain, ChainHandler}
|
||||
import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb, BlockHeaderDbHelper}
|
||||
import org.bitcoins.core.protocol.blockchain.{Block, RegTestNetChainParams}
|
||||
import org.bitcoins.wallet.Wallet
|
||||
import org.bitcoins.wallet.api.InitializeWalletSuccess
|
||||
import scodec.bits.ByteVector
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.chain.api.ChainApi
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import org.bitcoins.chain.db.ChainDbManagement
|
||||
import org.bitcoins.chain.db.ChainDbConfig
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.chain.blockchain.sync.ChainSync
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.number._
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import org.bitcoins.rpc.client.v17.BitcoindV17RpcClient
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.util.RpcUtil
|
||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.bitcoins.wallet.db.WalletDbManagement
|
||||
import org.bitcoins.wallet.db.WalletDbConfig
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
|
||||
import org.bitcoins.zmq.ZMQSubscriber
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent._
|
||||
import scala.concurrent.duration.DurationInt
|
||||
import scala.util._
|
||||
/**
|
||||
* This is for example purposes only!
|
||||
* This shows how to peer a bitcoin-s wallet
|
||||
* with a bitcoind instance that is relaying
|
||||
* information about what is happening on the blockchain
|
||||
* to the bitcoin-s wallet.
|
||||
*
|
||||
* This is useful if you want more flexible signing
|
||||
* procedures in the JVM ecosystem and more
|
||||
* granular control over your utxos with
|
||||
* popular databases like postgres, sqlite etc
|
||||
*/
|
||||
|
||||
//you can run this script with the following command
|
||||
//$ sbt "doc/run doc/src/main/scala/org/bitcoins/doc/wallet/create-wallet.sc"
|
||||
|
||||
val logger = LoggerFactory.getLogger("org.bitcoins.doc.wallet.CreateWallet")
|
||||
val time = System.currentTimeMillis()
|
||||
//boiler plate config
|
||||
implicit val system = ActorSystem(s"wallet-scala-sheet-${time}")
|
||||
import system.dispatcher
|
||||
|
||||
val chainDbConfig = ChainDbConfig.RegTestDbConfig
|
||||
val chainAppConfig = ChainAppConfig(chainDbConfig)
|
||||
implicit val chainParams = chainAppConfig.chain
|
||||
|
||||
val walletDbConfig = WalletDbConfig.RegTestDbConfig
|
||||
val walletAppConfig = WalletAppConfig(walletDbConfig)
|
||||
|
||||
val datadir = new File(s"/tmp/bitcoin-${time}/")
|
||||
val bitcoinConf = new File(datadir.getAbsolutePath + "/bitcoin.conf")
|
||||
|
||||
logger.info(s"bitcoin.conf location=${bitcoinConf.getAbsolutePath}")
|
||||
datadir.mkdirs()
|
||||
bitcoinConf.createNewFile()
|
||||
|
||||
val config = BitcoindRpcTestUtil.standardConfig
|
||||
val _ = BitcoindRpcTestUtil.writeConfigToFile(config,datadir)
|
||||
|
||||
//construct bitcoind
|
||||
val instance = BitcoindInstance.fromConfig(config = config, datadir)
|
||||
val bitcoind = new BitcoindRpcClient(instance = instance)
|
||||
|
||||
//start bitcoind, this may take a little while
|
||||
//generate 101 blocks so we have money in our wallet
|
||||
val bitcoindF = bitcoind.start().map(_ => bitcoind)
|
||||
|
||||
//create a native chain handler for bitcoin-s
|
||||
val blockHeaderDAO: BlockHeaderDAO = BlockHeaderDAO(appConfig = chainAppConfig)
|
||||
val genesisHeader = BlockHeaderDbHelper.fromBlockHeader(
|
||||
height = 0,
|
||||
bh = chainAppConfig.chain.genesisBlock.blockHeader)
|
||||
|
||||
|
||||
val blockHeaderTableF = {
|
||||
//drop regtest table if it exists
|
||||
val dropTableF = ChainDbManagement.dropHeaderTable(chainDbConfig)
|
||||
|
||||
//recreate the table
|
||||
val createdTableF = dropTableF.flatMap(_ => ChainDbManagement.createHeaderTable(chainDbConfig))
|
||||
|
||||
createdTableF
|
||||
}
|
||||
val createdGenHeaderF = blockHeaderTableF.flatMap(_ => blockHeaderDAO.create(genesisHeader))
|
||||
|
||||
val chainF = createdGenHeaderF.map(h => Vector(h))
|
||||
|
||||
val blockchainF = chainF.map(chain => Blockchain(chain))
|
||||
|
||||
val chainHandlerF = blockchainF.map(blockchain => ChainHandler(blockHeaderDAO, chainAppConfig))
|
||||
|
||||
val chainApi101BlocksF = sync(chainHandlerF, 101)
|
||||
|
||||
val bitcoinsLogF = chainApi101BlocksF.flatMap { chainApi =>
|
||||
chainApi.getBlockCount.map(count => logger.info(s"bitcoin-s blockcount=${count}"))
|
||||
}
|
||||
|
||||
val walletF = bitcoinsLogF.flatMap { _ =>
|
||||
//create tables
|
||||
val dropTablesF = WalletDbManagement.dropAll(walletDbConfig)
|
||||
val createTablesF = dropTablesF.flatMap(_ => WalletDbManagement.createAll(walletDbConfig))
|
||||
createTablesF.flatMap { _ =>
|
||||
Wallet.initialize(walletAppConfig)
|
||||
.collect{ case success: InitializeWalletSuccess => success.wallet }
|
||||
}
|
||||
}
|
||||
|
||||
val bitcoinsAddrF = walletF.flatMap(_.getNewAddress())
|
||||
|
||||
//send money to our wallet with bitcoind
|
||||
val amt = Bitcoins.one
|
||||
val transactionOutputIndexF: Future[(Transaction,Int)] = for {
|
||||
bitcoind <- bitcoindF
|
||||
bitcoinsAddr <- bitcoinsAddrF
|
||||
txid <- bitcoind.sendToAddress(bitcoinsAddr, amt)
|
||||
tx <- bitcoind.getRawTransactionRaw(txid)
|
||||
} yield {
|
||||
logger.info(s"Sending ${amt} to address ${bitcoinsAddr.value}")
|
||||
val Some((output,index)) = tx.outputs.zipWithIndex.find { case (output,index) =>
|
||||
output.scriptPubKey == bitcoinsAddr.scriptPubKey
|
||||
}
|
||||
|
||||
(tx,index)
|
||||
}
|
||||
|
||||
//add the utxo that was just created by bitcoind to our wallet
|
||||
val addUtxoF = for {
|
||||
wallet <- walletF
|
||||
(tx,index) <- transactionOutputIndexF
|
||||
addUtxo <- wallet.addUtxo(tx,UInt32(index))
|
||||
} yield {
|
||||
logger.info(s"Add utxo result=${addUtxo}")
|
||||
addUtxo
|
||||
}
|
||||
|
||||
//bury the utxo with enough proof of work to make it confirmed
|
||||
val chainApi6BlocksF = for {
|
||||
addUtxo <- addUtxoF
|
||||
(tx,_) <- transactionOutputIndexF
|
||||
chainApi <- sync(chainApi101BlocksF,6)
|
||||
} yield {
|
||||
logger.info(s"txid=${tx.txId.flip.hex}")
|
||||
}
|
||||
|
||||
//check balance & clean everything up
|
||||
chainApi6BlocksF.onComplete { chainApi =>
|
||||
val balanceF = walletF.flatMap(_.getBalance)
|
||||
|
||||
balanceF.onComplete(balance => logger.info(s"bitcoin-s walllet balance=${balance}"))
|
||||
|
||||
balanceF.flatMap(_ => cleanup())
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Syncs the give number of blocks to our chain */
|
||||
def sync(chainHandlerF: Future[ChainApi], numBlocks: Int)(implicit ec: ExecutionContext): Future[ChainApi] = {
|
||||
//we need a way to connect bitcoin-s to our running bitcoind, we are going to do this via rpc for now
|
||||
//we need to implement the 'getBestBlockHashFunc' and 'getBlockHeaderFunc' functions
|
||||
//to be able to sync our internal bitcoin-s chain with our external bitcoind chain
|
||||
val getBestBlockHashFunc = { () =>
|
||||
bitcoindF.flatMap(_.getBestBlockHash)
|
||||
}
|
||||
|
||||
val getBlockHeaderFunc = { hash: DoubleSha256DigestBE =>
|
||||
bitcoindF.flatMap(_.getBlockHeader(hash).map(_.blockHeader))
|
||||
}
|
||||
|
||||
|
||||
//now that we have bitcoind setup correctly and have rpc linked to
|
||||
//the bitcoin-s chain project, let's generate some blocks so
|
||||
//we have money to spend in our bitcoind wallet!
|
||||
//we need to generate 101 blocks to give us 50 btc to spend
|
||||
val genBlocksF = chainHandlerF.flatMap { _ =>
|
||||
bitcoindF.flatMap(_.generate(numBlocks))
|
||||
}
|
||||
|
||||
//now we need to sync those blocks into bitcoin-s
|
||||
val chainSyncF = genBlocksF.flatMap { _ =>
|
||||
chainHandlerF.flatMap { ch =>
|
||||
ChainSync.sync(
|
||||
ch.asInstanceOf[ChainHandler],
|
||||
getBlockHeaderFunc,
|
||||
getBestBlockHashFunc)
|
||||
}
|
||||
}
|
||||
|
||||
chainSyncF
|
||||
}
|
||||
|
||||
def cleanup(): Future[Unit] = {
|
||||
logger.info("Beginning clean up of create wallet script")
|
||||
val bitcoindStopF = {
|
||||
bitcoindF.flatMap { bitcoind =>
|
||||
val stopF = bitcoind.stop()
|
||||
stopF
|
||||
}
|
||||
}
|
||||
datadir.delete()
|
||||
logger.debug("cleaning up chain, wallet, and system")
|
||||
val chainCleanupF = ChainDbManagement.dropAll(chainDbConfig)
|
||||
val walletCleanupF = WalletDbManagement.dropAll(walletDbConfig)
|
||||
|
||||
val doneWithCleanupF = for {
|
||||
_ <- bitcoindStopF
|
||||
_ <- chainCleanupF
|
||||
_ <- walletCleanupF
|
||||
_ <- system.terminate()
|
||||
} yield {
|
||||
logger.info(s"Done cleaning up")
|
||||
}
|
||||
|
||||
doneWithCleanupF
|
||||
}
|
||||
|
||||
|
50
docs/applications/configuration.md
Normal file
50
docs/applications/configuration.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
id: configuration
|
||||
title: Application configuration
|
||||
---
|
||||
|
||||
Bitcoin-S uses [HOCON](https://github.com/lightbend/config/blob/master/HOCON.md)
|
||||
to configure various parts of the application the library offers. HOCON is a
|
||||
superset of JSON, that is, all valid JSON is valid HOCON.
|
||||
|
||||
All configuration for Bitcoin-S is under the `bitcoin-s` key.
|
||||
|
||||
If you have a file `application.conf` anywhere on your classpath when using
|
||||
bitcoin-s, the values there take precedence over the ones found in our
|
||||
`reference.conf`. We also look for the file `bitcoin-s.conf` in the current
|
||||
Bitcoin-S data directory.
|
||||
|
||||
The resolved configuration gets parsed by
|
||||
[`AppConfig`](../../db-commons/src/main/scala/org/bitcoins/db/AppConfig.scala).
|
||||
`AppConfig` is an abstract class that's implemented by corresponding case
|
||||
classes in the `wallet`, `chain` and `node` projects. Here's some examples of how to
|
||||
construct a wallet configuration:
|
||||
|
||||
```scala mdoc:compile-only
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import java.nio.file.Paths
|
||||
import scala.util.Properties
|
||||
|
||||
// reads $HOME/.bitcoin-s/
|
||||
val defaultConfig = WalletAppConfig.fromDefaultDatadir()
|
||||
|
||||
|
||||
// reads a custom data directory
|
||||
val customDirectory = Paths.get(Properties.userHome, "custom-bitcoin-s-directory")
|
||||
val configFromCustomDatadir = WalletAppConfig(customDirectory)
|
||||
|
||||
// reads a custom data directory and overrides the network to be testnet3
|
||||
val customOverride = ConfigFactory.parseString("bitcoin-s.network = testnet3")
|
||||
val configFromCustomDirAndOverride = WalletAppConfig(customDirectory, customOverride)
|
||||
```
|
||||
|
||||
You can pass as many `com.typesafe.config.Config`s as you'd like. If any
|
||||
keys appear multiple times the last one encountered takes precedence.
|
||||
|
||||
## Internal configuration
|
||||
|
||||
Database connections are also configured by using HOCON. This is done in
|
||||
[`db.conf`](../../db-commons/src/main/resources/db.conf). The options
|
||||
exposed here are **not** intended to
|
||||
be used by users of Bitcoin-S, and are internal only.
|
84
docs/chain/sync-chain.md
Normal file
84
docs/chain/sync-chain.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
---
|
||||
id: sync-chain
|
||||
title: Syncing block headers with Bitcoin-S
|
||||
---
|
||||
|
||||
Using the `chain` module of Bitcoin-S it's possible to
|
||||
sync and verify block headers from the Bitcoin blockchain. In this document
|
||||
we demonstrate how to do this, while persisting it to disk. We should be
|
||||
able to read this chain on subsequent runs, assuming we are connected
|
||||
to the same `bitcoind` instance.
|
||||
|
||||
```scala mdoc:compile-only
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.chain.blockchain._
|
||||
import org.bitcoins.chain.blockchain.sync._
|
||||
import org.bitcoins.chain.models._
|
||||
|
||||
import org.bitcoins.rpc.client.common._
|
||||
import org.bitcoins.testkit.chain._
|
||||
|
||||
import scala.concurrent._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val exectionContext = system.dispatcher
|
||||
|
||||
// We are assuming that a `bitcoind` regtest node is running the background.
|
||||
// You can see our `bitcoind` guides to see how to connect
|
||||
// to a local or remote `bitcoind` node.
|
||||
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
|
||||
val bitcoindInstance = BitcoindInstance.fromDatadir()
|
||||
val rpcCli = new BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
// Next, we need to create a way to monitor the chain:
|
||||
|
||||
val getBestBlockHash = ChainTestUtil.bestBlockHashFnRpc(Future.successful(rpcCli))
|
||||
|
||||
val getBlockHeader = ChainTestUtil.getBlockHeaderFnRpc(Future.successful(rpcCli))
|
||||
|
||||
|
||||
// set a data directory
|
||||
import java.nio.file.Files
|
||||
val datadir = Files.createTempDirectory("bitcoin-s-test")
|
||||
|
||||
// set the currenet network to regtest
|
||||
import com.typesafe.config.ConfigFactory
|
||||
val config = ConfigFactory.parseString {
|
||||
"""
|
||||
| bitcoin-s {
|
||||
| network = regtest
|
||||
| }
|
||||
|""".stripMargin
|
||||
}
|
||||
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
implicit val chainConfig = ChainAppConfig(datadir, config)
|
||||
|
||||
// Initialize the needed database tables if they don't exist:
|
||||
val chainProjectInitF = chainConfig.initialize()
|
||||
val blockHeaderDAO = BlockHeaderDAO()
|
||||
|
||||
// Now, do the actual syncing:
|
||||
val chainHandlerF = ChainHandler.fromDatabase(blockHeaderDAO)
|
||||
|
||||
val syncedChainApiF = for {
|
||||
_ <- chainProjectInitF
|
||||
handler <- chainHandlerF
|
||||
synced <- ChainSync.sync(handler, getBlockHeader, getBestBlockHash)
|
||||
} yield synced
|
||||
|
||||
|
||||
val syncResultF = syncedChainApiF.flatMap { chainApi =>
|
||||
chainApi.getBlockCount.map(count => println(s"chain api blockcount=${count}"))
|
||||
|
||||
rpcCli.getBlockCount.map(count => println(s"bitcoind blockcount=${count}"))
|
||||
}
|
||||
|
||||
syncResultF.onComplete { case result =>
|
||||
println(s"Sync result=${result}")
|
||||
system.terminate()
|
||||
}
|
||||
```
|
|
@ -49,6 +49,21 @@ file you should edit would be `$HOME/.bitcoin-s/bitcoin-s.conf`.
|
|||
You can place configuration files in the data directory that tests are being run in,
|
||||
but you can also edit [`reference.conf`](https://github.com/bitcoin-s/bitcoin-s/blob/master/db-commons/src/main/resources/reference.conf).
|
||||
|
||||
## Logging when working on Bitcoin-S tests
|
||||
|
||||
When working on various parts of Bitcoin-S the need to log what's going on arises
|
||||
pretty quickly. There's two way of doing this:
|
||||
|
||||
1. Using the way described in the section above, "Working on Bitcoin-S applications".
|
||||
You could either use traits (like `HTTPLogger` or `P2PLogger`) that exposes a
|
||||
field `logger`, or acquire the logger directly through the traits companion
|
||||
object.
|
||||
2. Use the standard `BitcoinSLogger`, which is also available as both a trait and
|
||||
a companion object with a field you can access (`BitcoinSLogger.logger`). Note
|
||||
that by default all logging from this logger is turned off in tests, to make
|
||||
output less noisy. You can tune this by changing the level found in
|
||||
`core-test/src/test/resources/logback-test.xml`.
|
||||
|
||||
## Developer productivity
|
||||
|
||||
### Bloop
|
||||
|
|
|
@ -99,7 +99,6 @@ handling could look:
|
|||
|
||||
```scala mdoc:compile-only
|
||||
import org.bitcoins.rpc.client.common._
|
||||
import org.bitcoins.rpc.BitcoindException
|
||||
import org.bitcoins.rpc.BitcoindWalletException
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.protocol._
|
||||
|
|
121
docs/wallet/create-wallet.md
Normal file
121
docs/wallet/create-wallet.md
Normal file
|
@ -0,0 +1,121 @@
|
|||
---
|
||||
id: create-wallet
|
||||
title: Creating a Bitcoin-S wallet
|
||||
---
|
||||
|
||||
This guide shows how to create a Bitcoin-S wallet and then
|
||||
peer it with a `bitcoind` instance that relays
|
||||
information about what is happening on the blockchain
|
||||
through the P2P network.
|
||||
|
||||
This is useful if you want more flexible signing procedures in
|
||||
the JVM ecosystem and more granular control over your
|
||||
UTXOs with popular database like Postgres, SQLite, etc.
|
||||
|
||||
This code snippet you have a running `bitcoind` instance, locally
|
||||
on regtest.
|
||||
|
||||
```scala mdoc:compile-only
|
||||
import akka.actor.ActorSystem
|
||||
implicit val system = ActorSystem()
|
||||
import system.dispatcher
|
||||
|
||||
import com.typesafe.config.ConfigFactory
|
||||
val config = ConfigFactory.parseString {
|
||||
"""
|
||||
| bitcoin-s {
|
||||
| network = regtest
|
||||
| }
|
||||
""".stripMargin
|
||||
}
|
||||
|
||||
import java.nio.file.Files
|
||||
val datadir = Files.createTempDirectory("bitcoin-s-wallet")
|
||||
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
implicit val walletConfig = WalletAppConfig(datadir, config)
|
||||
|
||||
// we also need to store chain state for syncing purposes
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
implicit val chainConfig = ChainAppConfig(datadir, config)
|
||||
|
||||
// when this future completes, we have
|
||||
// created the necessary directories and
|
||||
// databases for managing both chain state
|
||||
// and wallet state
|
||||
import scala.concurrent._
|
||||
val configF: Future[Unit] = for {
|
||||
_ <- walletConfig.initialize()
|
||||
_ <- chainConfig.initialize()
|
||||
} yield ()
|
||||
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
val bitcoindInstance = BitcoindInstance.fromDatadir()
|
||||
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
val bitcoind = new BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
// when this future completes, we have
|
||||
// synced our chain handler to our bitcoind
|
||||
// peer
|
||||
import org.bitcoins.chain.api.ChainApi
|
||||
val syncF: Future[ChainApi] = configF.flatMap { _ =>
|
||||
val getBestBlockHashFunc = { () =>
|
||||
bitcoind.getBestBlockHash
|
||||
}
|
||||
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
val getBlockHeaderFunc = { hash: DoubleSha256DigestBE =>
|
||||
bitcoind.getBlockHeader(hash).map(_.blockHeader)
|
||||
}
|
||||
|
||||
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
val blockHeaderDAO = BlockHeaderDAO()
|
||||
val chainHandler = ChainHandler(
|
||||
blockHeaderDAO,
|
||||
blockchains = Vector.empty)
|
||||
|
||||
import org.bitcoins.chain.blockchain.sync.ChainSync
|
||||
ChainSync.sync(chainHandler, getBlockHeaderFunc, getBestBlockHashFunc)
|
||||
}
|
||||
|
||||
// once this future completes, we have a initialized
|
||||
// wallet
|
||||
import org.bitcoins.wallet.api.LockedWalletApi
|
||||
import org.bitcoins.wallet.api.InitializeWalletSuccess
|
||||
import org.bitcoins.wallet.Wallet
|
||||
val walletF: Future[LockedWalletApi] = configF.flatMap { _ =>
|
||||
Wallet.initialize().collect {
|
||||
case InitializeWalletSuccess(wallet) => wallet
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// when this future completes, ww have sent a transaction
|
||||
// from bitcoind to the Bitcoin-S wallet
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.core.currency._
|
||||
val transactionF: Future[Transaction] = for {
|
||||
wallet <- walletF
|
||||
address <- wallet.getNewAddress()
|
||||
txid <- bitcoind.sendToAddress(address, 3.bitcoin)
|
||||
transaction <- bitcoind.getRawTransaction(txid)
|
||||
} yield transaction.hex
|
||||
|
||||
// when this future completes, we have processed
|
||||
// the transaction from bitcoind, and we have
|
||||
// queried our balance for the current balance
|
||||
val balanceF: Future[CurrencyUnit] = for {
|
||||
wallet <- walletF
|
||||
tx <- transactionF
|
||||
_ <- wallet.processTransaction(tx, confirmations = 0)
|
||||
balance <- wallet.getBalance
|
||||
} yield balance
|
||||
|
||||
balanceF.foreach { balance =>
|
||||
println(s"Bitcoin-S wallet balance: $balance")
|
||||
}
|
||||
|
||||
```
|
|
@ -243,7 +243,7 @@ trait EclairApi {
|
|||
* Documented by not implemented in Eclair
|
||||
*/
|
||||
def sendToRoute(
|
||||
route: TraversableOnce[NodeId],
|
||||
route: scala.collection.immutable.Seq[NodeId],
|
||||
amountMsat: MilliSatoshis,
|
||||
paymentHash: Sha256Digest,
|
||||
finalCltvExpiry: Long): Future[PaymentId]
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.bitcoins.core.protocol.ln.{
|
|||
ShortChannelId
|
||||
}
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.util.{BitcoinSUtil, FutureUtil}
|
||||
import org.bitcoins.core.util.BitcoinSUtil
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||
import org.bitcoins.eclair.rpc.api.EclairApi
|
||||
import org.bitcoins.eclair.rpc.config.EclairInstance
|
||||
|
@ -371,7 +371,7 @@ class EclairRpcClient(val instance: EclairInstance)(
|
|||
|
||||
//register callback that publishes a payment to our actor system's
|
||||
//event stream,
|
||||
receivedInfoF.map {
|
||||
receivedInfoF.foreach {
|
||||
case None =>
|
||||
if (attempts.incrementAndGet() >= maxAttempts) {
|
||||
// too many tries to get info about a payment
|
||||
|
@ -480,13 +480,13 @@ class EclairRpcClient(val instance: EclairInstance)(
|
|||
}
|
||||
|
||||
def sendToRoute(
|
||||
route: TraversableOnce[NodeId],
|
||||
route: scala.collection.immutable.Seq[NodeId],
|
||||
amountMsat: MilliSatoshis,
|
||||
paymentHash: Sha256Digest,
|
||||
finalCltvExpiry: Long): Future[PaymentId] = {
|
||||
eclairCall[PaymentId](
|
||||
"sendtoroute",
|
||||
"route" -> route.mkString(","),
|
||||
"route" -> route.iterator.mkString(","),
|
||||
"amountMsat" -> amountMsat.toBigDecimal.toString,
|
||||
"paymentHash" -> paymentHash.hex,
|
||||
"finalCltvExpiry" -> finalCltvExpiry.toString
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
val scala2_11 = "2.11.12"
|
||||
val scala2_12 = "2.12.8"
|
||||
val scala2_12 = "2.12.9"
|
||||
val scala2_13 = "2.13.0"
|
||||
|
||||
scalaVersion in ThisBuild := scala2_12
|
||||
scalaVersion in ThisBuild := scala2_13
|
||||
|
||||
crossScalaVersions in ThisBuild := List(scala2_12, scala2_11)
|
||||
crossScalaVersions in ThisBuild := List(scala2_13, scala2_12, scala2_11)
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package org.bitcoins.node.models
|
||||
|
||||
trait ColumnMappers {
|
||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||
}
|
||||
|
||||
object ColumnMappers extends ColumnMappers
|
|
@ -106,8 +106,9 @@ case class P2PClientActor(
|
|||
handleCommand(cmd, peerOpt = None)
|
||||
|
||||
case connected: Tcp.Connected =>
|
||||
Await.result(handleEvent(connected, unalignedBytes = ByteVector.empty),
|
||||
timeout)
|
||||
val _ = Await.result(
|
||||
handleEvent(connected, unalignedBytes = ByteVector.empty),
|
||||
timeout)
|
||||
case msg: NetworkMessage =>
|
||||
self.forward(msg.payload)
|
||||
case payload: NetworkPayload =>
|
||||
|
|
|
@ -8,7 +8,7 @@ object Deps {
|
|||
val scalacheck = "1.14.0"
|
||||
val scalaTest = "3.0.8"
|
||||
val slf4j = "1.7.28"
|
||||
val spray = "1.3.4"
|
||||
val spray = "1.3.5"
|
||||
val zeromq = "0.5.1"
|
||||
val akkav = "10.1.9"
|
||||
val akkaStreamv = "2.5.25"
|
||||
|
@ -17,9 +17,11 @@ object Deps {
|
|||
val junitV = "0.11"
|
||||
val nativeLoaderV = "2.3.4"
|
||||
val typesafeConfigV = "1.3.4"
|
||||
val ammoniteV = "1.6.7"
|
||||
|
||||
val asyncV = "0.9.7"
|
||||
// async dropped Scala 2.11 in 0.10.0
|
||||
val asyncOldScalaV = "0.9.7"
|
||||
val asyncNewScalaV = "0.10.0"
|
||||
|
||||
val postgresV = "9.4.1210"
|
||||
val akkaActorV = akkaStreamv
|
||||
val slickV = "3.3.2"
|
||||
|
@ -27,9 +29,21 @@ object Deps {
|
|||
val scalameterV = "0.17"
|
||||
|
||||
// Wallet/node/chain server deps
|
||||
val uPickleV = "0.7.4"
|
||||
val akkaHttpUpickleV = "1.27.0"
|
||||
val uJsonV = uPickleV // Li Haoyi ecosystem does common versioning
|
||||
val oldMicroPickleV = "0.7.4"
|
||||
val oldMicroJsonV = oldMicroPickleV
|
||||
|
||||
val newMicroPickleV = "0.7.5"
|
||||
val newMicroJsonV = newMicroPickleV
|
||||
|
||||
// akka-http-upickle is not yet published
|
||||
// to Maven central. There's a PR for adding
|
||||
// suport, https://github.com/hseeberger/akka-http-json/pull/314.
|
||||
// Until that's merged, you'll have to pull down
|
||||
// that PR, do `sbt publishLocal` and replace the
|
||||
// value here with whatever is in there. This
|
||||
// obviously has to be changed before this is
|
||||
// merged.
|
||||
|
||||
val sourcecodeV = "0.1.7"
|
||||
|
||||
// CLI deps
|
||||
|
@ -38,6 +52,7 @@ object Deps {
|
|||
}
|
||||
|
||||
object Compile {
|
||||
|
||||
val bouncycastle = "org.bouncycastle" % "bcprov-jdk15on" % V.bouncyCastle withSources () withJavadoc ()
|
||||
val scodec = "org.scodec" %% "scodec-bits" % V.scodecV withSources () withJavadoc ()
|
||||
val slf4j = "org.slf4j" % "slf4j-api" % V.slf4j % "provided" withSources () withJavadoc ()
|
||||
|
@ -53,24 +68,26 @@ object Deps {
|
|||
|
||||
//for loading secp256k1 natively
|
||||
val nativeLoader = "org.scijava" % "native-lib-loader" % V.nativeLoaderV withSources () withJavadoc ()
|
||||
val ammonite = "com.lihaoyi" %% "ammonite" % V.ammoniteV cross CrossVersion.full
|
||||
|
||||
//node deps
|
||||
val slick = "com.typesafe.slick" %% "slick" % V.slickV withSources () withJavadoc ()
|
||||
val slickHikari = "com.typesafe.slick" %% "slick-hikaricp" % V.slickV
|
||||
val sqlite = "org.xerial" % "sqlite-jdbc" % V.sqliteV
|
||||
val postgres = "org.postgresql" % "postgresql" % V.postgresV
|
||||
val uJson = "com.lihaoyi" %% "ujson" % V.uJsonV
|
||||
|
||||
// serializing to and from JSON
|
||||
val uPickle = "com.lihaoyi" %% "upickle" % V.uPickleV
|
||||
// zero dep JSON library. Have to use different versiont to juggle
|
||||
// Scala 2.11/12/13
|
||||
val oldMicroJson = "com.lihaoyi" %% "ujson" % V.oldMicroJsonV
|
||||
val newMicroJson = "com.lihaoyi" %% "ujson" % V.newMicroJsonV
|
||||
|
||||
// serializing to and from JSON Have to use different versiont to juggle
|
||||
// Scala 2.11/12/13
|
||||
val oldMicroPickle = "com.lihaoyi" %% "upickle" % V.oldMicroPickleV
|
||||
val newMicroPickle = "com.lihaoyi" %% "upickle" % V.newMicroPickleV
|
||||
|
||||
// get access to reflection data at compile-time
|
||||
val sourcecode = "com.lihaoyi" %% "sourcecode" % V.sourcecodeV
|
||||
|
||||
// make akka-http play nice with upickle
|
||||
val akkaHttpUpickle = "de.heikoseeberger" %% "akka-http-upickle" % V.akkaHttpUpickleV
|
||||
|
||||
// parsing of CLI opts and args
|
||||
val scopt = "com.github.scopt" %% "scopt" % V.scoptV
|
||||
|
||||
|
@ -82,7 +99,8 @@ object Deps {
|
|||
}
|
||||
|
||||
object Test {
|
||||
val async = "org.scala-lang.modules" %% "scala-async" % V.asyncV % "test" withSources () withJavadoc ()
|
||||
val oldAsync = "org.scala-lang.modules" %% "scala-async" % V.asyncOldScalaV % "test" withSources () withJavadoc ()
|
||||
val newAsync = "org.scala-lang.modules" %% "scala-async" % V.asyncNewScalaV % "test" withSources () withJavadoc ()
|
||||
val junitInterface = "com.novocode" % "junit-interface" % V.junitV % "test" withSources () withJavadoc ()
|
||||
val logback = Compile.logback % "test"
|
||||
val scalacheck = Compile.scalacheck % "test"
|
||||
|
@ -136,13 +154,13 @@ object Deps {
|
|||
Compile.typesafeConfig
|
||||
)
|
||||
|
||||
val bitcoindRpcTest = List(
|
||||
def bitcoindRpcTest(scalaVersion: String) = List(
|
||||
Test.akkaHttp,
|
||||
Test.akkaStream,
|
||||
Test.logback,
|
||||
Test.scalaTest,
|
||||
Test.scalacheck,
|
||||
Test.async
|
||||
if (scalaVersion.startsWith("2.11")) Test.oldAsync else Test.newAsync
|
||||
)
|
||||
|
||||
val bench = List(
|
||||
|
@ -158,19 +176,22 @@ object Deps {
|
|||
Compile.slickHikari
|
||||
)
|
||||
|
||||
val cli = List(
|
||||
def cli(scalaVersion: String) = List(
|
||||
Compile.sttp,
|
||||
Compile.uPickle,
|
||||
if (scalaVersion.startsWith("2.11")) Compile.oldMicroPickle
|
||||
else Compile.newMicroPickle,
|
||||
Compile.logback,
|
||||
Compile.scopt
|
||||
)
|
||||
|
||||
val picklers = List(
|
||||
Compile.uPickle
|
||||
def picklers(scalaVersion: String) = List(
|
||||
if (scalaVersion.startsWith("2.11")) Compile.oldMicroPickle
|
||||
else Compile.newMicroPickle
|
||||
)
|
||||
|
||||
val server = List(
|
||||
Compile.akkaHttpUpickle,
|
||||
Compile.uPickle,
|
||||
def server(scalaVersion: String) = List(
|
||||
if (scalaVersion.startsWith("2.11")) Compile.oldMicroPickle
|
||||
else Compile.newMicroPickle,
|
||||
Compile.logback,
|
||||
Compile.akkaHttp
|
||||
)
|
||||
|
@ -210,13 +231,9 @@ object Deps {
|
|||
Test.akkaTestkit
|
||||
)
|
||||
|
||||
val scripts = List(
|
||||
Compile.ammonite,
|
||||
Compile.logback
|
||||
)
|
||||
|
||||
val wallet = List(
|
||||
Compile.uJson,
|
||||
def wallet(scalaVersion: String) = List(
|
||||
if (scalaVersion.startsWith("2.11")) Compile.oldMicroJson
|
||||
else Compile.newMicroJson,
|
||||
Compile.logback
|
||||
)
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
## Ammonite scripts
|
||||
|
||||
This project contain [Ammonite](https://ammonite.io) scripts that demonstrate
|
||||
functionality of `bitcoin-s`.
|
||||
|
||||
#### Running them with sbt:
|
||||
|
||||
```bash
|
||||
$ sbt "scripts/run path/to/script.sc" # this is very slow, not recommended
|
||||
```
|
||||
|
||||
#### Running them with the [Bloop CLI](https://scalacenter.github.io/bloop/):
|
||||
|
||||
```bash
|
||||
$ bloop run scripts --args path/to/script.sc # much faster than through sbt
|
||||
```
|
|
@ -1,43 +0,0 @@
|
|||
package org.bitcoins.doc
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import scala.util.Properties
|
||||
|
||||
object amm extends App {
|
||||
|
||||
/** Gets all files ending with .sc in dir or subdirs */
|
||||
def getScripts(dir: Path): Seq[Path] = {
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
Files
|
||||
.walk(dir)
|
||||
.iterator()
|
||||
.asScala
|
||||
.filter(Files.isRegularFile(_))
|
||||
.filter(_.toString.endsWith(".sc"))
|
||||
.toList
|
||||
}
|
||||
|
||||
if (args.isEmpty || args.headOption.forall(_.isEmpty)) {
|
||||
import System.err.{println => printerr}
|
||||
|
||||
printerr("No script name provided!")
|
||||
printerr()
|
||||
|
||||
val cwd = Paths.get(Properties.userDir)
|
||||
val scripts = getScripts(cwd)
|
||||
|
||||
if (scripts.nonEmpty) {
|
||||
printerr("Available scripts:")
|
||||
scripts.foreach { script =>
|
||||
printerr(s" ${cwd.relativize(script)}")
|
||||
}
|
||||
} else {
|
||||
printerr("No .sc scripts found!")
|
||||
}
|
||||
sys.exit(1)
|
||||
} else {
|
||||
ammonite.Main.main(args)
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.core.config._
|
||||
import org.bitcoins.rpc.config._
|
||||
import org.bitcoins.rpc.client.common._
|
||||
import java.net.URI
|
||||
|
||||
//this script shows you have to connect to a remote bitcoind
|
||||
//instance via an ssh tunnel with bitcoin-s
|
||||
//first we need to create the ssh tunnel
|
||||
|
||||
//$ ssh -L 8332:localhost:8332 my-cool-user@my-cool-website.com
|
||||
|
||||
//note: the port number '8332' is for mainnet, if you want to
|
||||
//conect to a testnet rpc client you will need to do '18332'
|
||||
|
||||
//now we have a secure connection between our remote bitcoind
|
||||
//node that is running on 'my-cool-website.com' under the
|
||||
//username 'my-cool-user'
|
||||
|
||||
//it should be noted, that the steps above can be skipped and you
|
||||
//can connect to a running local bitcoind instance as well
|
||||
|
||||
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(s"connnect-bitcoind-ssh-${System.currentTimeMillis()}")
|
||||
implicit val ec = system.dispatcher
|
||||
val rpcCli = new BitcoindRpcClient(bitcoindInstance)
|
||||
|
||||
rpcCli.getBalance.onComplete { case balance =>
|
||||
println(s"Wallet balance=${balance}")
|
||||
system.terminate()
|
||||
}
|
|
@ -19,7 +19,8 @@ abstract class BloomFilterGenerator {
|
|||
} yield BloomFilter(size, falsePositiveRate, tweak, flags)
|
||||
|
||||
/** Loads a generic bloom filter with the given byte vectors and returns it */
|
||||
def bloomFilter(byteVectors: Seq[ByteVector]): Gen[BloomFilter] =
|
||||
def bloomFilter(
|
||||
byteVectors: scala.collection.Seq[ByteVector]): Gen[BloomFilter] =
|
||||
for {
|
||||
filter <- bloomFilter
|
||||
} yield filter.insertByteVectors(byteVectors)
|
||||
|
|
|
@ -43,8 +43,11 @@ abstract class MerkleGenerator {
|
|||
* false negatives.
|
||||
* @return
|
||||
*/
|
||||
def merkleBlockCreatedWithBloomFilter: Gen[
|
||||
(MerkleBlock, Block, Seq[DoubleSha256Digest], BloomFilter)] =
|
||||
def merkleBlockCreatedWithBloomFilter: Gen[(
|
||||
MerkleBlock,
|
||||
Block,
|
||||
scala.collection.Seq[DoubleSha256Digest],
|
||||
BloomFilter)] =
|
||||
for {
|
||||
block <- BlockchainElementsGenerator.block
|
||||
//choose some random txs in the block to put in the bloom filter
|
||||
|
|
|
@ -42,7 +42,7 @@ import org.bitcoins.rpc.config.BitcoindAuthCredentials
|
|||
*
|
||||
*/
|
||||
trait EclairRpcTestUtil extends BitcoinSLogger {
|
||||
import collection.JavaConverters._
|
||||
import org.bitcoins.core.compat.JavaConverters._
|
||||
|
||||
def randomDirName: String =
|
||||
0.until(5).map(_ => scala.util.Random.alphanumeric.head).mkString
|
||||
|
|
|
@ -466,7 +466,7 @@ trait BitcoindRpcTestUtil extends BitcoinSLogger {
|
|||
_ <- start1F
|
||||
_ <- start2F
|
||||
} yield {
|
||||
clientAccum += (client1, client2)
|
||||
clientAccum ++= List(client1, client2)
|
||||
(client1, client2)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.bitcoins.testkit.util
|
||||
|
||||
import org.scalactic.anyvals.PosInt
|
||||
import org.scalatest.prop.PropertyChecks
|
||||
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
|
||||
import org.scalatest.{FlatSpec, MustMatchers}
|
||||
import org.slf4j.{Logger, LoggerFactory}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import org.slf4j.{Logger, LoggerFactory}
|
|||
abstract class BitcoinSUnitTest
|
||||
extends FlatSpec
|
||||
with MustMatchers
|
||||
with PropertyChecks {
|
||||
with ScalaCheckPropertyChecks {
|
||||
|
||||
protected lazy val logger: Logger = LoggerFactory.getLogger(getClass)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import java.nio.file.Files
|
|||
import akka.compat.Future
|
||||
import akka.compat.Future
|
||||
import scala.concurrent.Future
|
||||
import scala.collection.JavaConverters._
|
||||
import org.bitcoins.core.compat.JavaConverters._
|
||||
import java.nio.file.Path
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import java.nio.file.Paths
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package org.bitcoins.wallet
|
||||
|
||||
import org.bitcoins.core.compat._
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.{Try, Success, Failure}
|
||||
|
||||
/**
|
||||
* @define liftBiasedFut Given a [[scala.Either Either]] that contains a
|
||||
|
@ -16,22 +16,25 @@ object EitherUtil {
|
|||
* where the right hand side of an either is asynchronous.
|
||||
*/
|
||||
def flattenFutureE[L, R](
|
||||
either: Either[L, Future[Either[L, R]]]
|
||||
): Future[Either[L, R]] = {
|
||||
either: CompatEither[L, Future[CompatEither[L, R]]]
|
||||
): Future[CompatEither[L, R]] = {
|
||||
|
||||
def ifLeft(left: L): Future[Either[L, R]] = Future.successful(Left(left))
|
||||
def ifRight(rightF: Future[Either[L, R]]): Future[Either[L, R]] = rightF
|
||||
def ifLeft(left: L): Future[CompatEither[L, R]] =
|
||||
Future.successful(CompatLeft(left))
|
||||
def ifRight(
|
||||
rightF: Future[CompatEither[L, R]]): Future[CompatEither[L, R]] =
|
||||
rightF
|
||||
|
||||
either.fold(ifLeft, ifRight)
|
||||
}
|
||||
|
||||
/** $liftBiasedFut */
|
||||
def liftRightBiasedFutureE[L, R](
|
||||
either: Either[L, Future[R]]
|
||||
)(implicit ec: ExecutionContext): Future[Either[L, R]] =
|
||||
either: CompatEither[L, Future[R]]
|
||||
)(implicit ec: ExecutionContext): Future[CompatEither[L, R]] =
|
||||
either match {
|
||||
case Right(fut) => fut.map(Right(_))
|
||||
case Left(l) => Future.successful(Left(l))
|
||||
case CompatRight(fut) => fut.map(elem => CompatRight(elem))
|
||||
case CompatLeft(l) => Future.successful(CompatLeft(l))
|
||||
}
|
||||
|
||||
/** $liftBiasedFut */
|
||||
|
@ -43,46 +46,4 @@ object EitherUtil {
|
|||
case Right(l) => Future.successful(Right(l))
|
||||
}
|
||||
|
||||
object EitherOps {
|
||||
import scala.language.implicitConversions
|
||||
implicit def either2EnhancedEither[A, B](
|
||||
either: Either[A, B]
|
||||
): EnchancedEither[A, B] = EnchancedEither(either)
|
||||
|
||||
implicit def enchancedEither2Either[A, B](
|
||||
enhanced: EnchancedEither[A, B]): Either[A, B] = enhanced.underlying
|
||||
}
|
||||
|
||||
/** The methods here are copied directly from the 2.12 stdlib */
|
||||
case class EnchancedEither[A, B](
|
||||
private[EitherUtil] val underlying: Either[A, B]) {
|
||||
|
||||
/** The given function is applied if this is a `Right`.
|
||||
*
|
||||
* {{{
|
||||
* Right(12).map(x => "flower") // Result: Right("flower")
|
||||
* Left(12).map(x => "flower") // Result: Left(12)
|
||||
* }}}
|
||||
*/
|
||||
def map[B1](f: B => B1): EnchancedEither[A, B1] = underlying match {
|
||||
case Right(b) => EnchancedEither(Right(f(b)))
|
||||
case _ => EnchancedEither(this.asInstanceOf[Either[A, B1]])
|
||||
}
|
||||
|
||||
/** Binds the given function across `Right`.
|
||||
*
|
||||
* @param f The function to bind across `Right`.
|
||||
*/
|
||||
def flatMap[A1 >: A, B1](f: B => Either[A1, B1]): EnchancedEither[A1, B1] =
|
||||
underlying match {
|
||||
case Right(b) => EnchancedEither(f(b))
|
||||
case _ => EnchancedEither(underlying.asInstanceOf[Either[A1, B1]])
|
||||
}
|
||||
|
||||
def toTry(implicit ev: A <:< Throwable): Try[B] = underlying match {
|
||||
case Right(b) => Success(b)
|
||||
case Left(a) => Failure(a)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.bitcoins.wallet
|
||||
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.compat.CompatEither
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
@ -8,9 +9,9 @@ import scala.util.{Failure, Success, Try}
|
|||
case class EncryptedMnemonic(value: AesEncryptedData, salt: AesSalt) {
|
||||
|
||||
def toMnemonic(password: AesPassword): Try[MnemonicCode] = {
|
||||
import EitherUtil.EitherOps._
|
||||
val key = password.toKey(salt)
|
||||
AesCrypt.decrypt(value, key).toTry.flatMap { decrypted =>
|
||||
val either = AesCrypt.decrypt(value, key)
|
||||
CompatEither(either).toTry.flatMap { decrypted =>
|
||||
decrypted.decodeUtf8 match {
|
||||
case Left(_) =>
|
||||
// when failing to decode this to a UTF-8 string
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.bitcoins.wallet
|
||||
|
||||
import org.bitcoins.core.compat._
|
||||
import org.bitcoins.core.config.BitcoinNetwork
|
||||
import org.bitcoins.core.crypto._
|
||||
import org.bitcoins.core.currency._
|
||||
|
@ -161,26 +162,28 @@ object Wallet extends CreateWalletApi with KeyHandlingLogger {
|
|||
override def initializeWithEntropy(entropy: BitVector)(
|
||||
implicit config: WalletAppConfig,
|
||||
ec: ExecutionContext): Future[InitializeWalletResult] = {
|
||||
import EitherUtil.EitherOps._
|
||||
|
||||
logger.info(s"Initializing wallet on chain ${config.network}")
|
||||
|
||||
val mnemonicT = Try(MnemonicCode.fromEntropy(entropy))
|
||||
val mnemonicE: Either[InitializeWalletError, MnemonicCode] =
|
||||
val mnemonicE: CompatEither[InitializeWalletError, MnemonicCode] =
|
||||
mnemonicT match {
|
||||
case Success(mnemonic) =>
|
||||
logger.trace(s"Created mnemonic from entropy")
|
||||
Right(mnemonic)
|
||||
CompatEither(Right(mnemonic))
|
||||
case Failure(err) =>
|
||||
logger.error(s"Could not create mnemonic from entropy! $err")
|
||||
Left(InitializeWalletError.BadEntropy)
|
||||
|
||||
CompatEither(Left(InitializeWalletError.BadEntropy))
|
||||
}
|
||||
|
||||
val encryptedMnemonicE: Either[InitializeWalletError, EncryptedMnemonic] =
|
||||
val encryptedMnemonicE: CompatEither[
|
||||
InitializeWalletError,
|
||||
EncryptedMnemonic] =
|
||||
mnemonicE.map { EncryptedMnemonicHelper.encrypt(_, badPassphrase) }
|
||||
|
||||
val biasedFinalEither: Either[InitializeWalletError, Future[WalletImpl]] =
|
||||
val biasedFinalEither: CompatEither[
|
||||
InitializeWalletError,
|
||||
Future[WalletImpl]] =
|
||||
for {
|
||||
mnemonic <- mnemonicE
|
||||
encrypted <- encryptedMnemonicE
|
||||
|
@ -216,14 +219,14 @@ object Wallet extends CreateWalletApi with KeyHandlingLogger {
|
|||
} yield wallet
|
||||
}
|
||||
|
||||
val finalEither: Future[Either[InitializeWalletError, WalletImpl]] =
|
||||
val finalEither: Future[CompatEither[InitializeWalletError, WalletImpl]] =
|
||||
EitherUtil.liftRightBiasedFutureE(biasedFinalEither)
|
||||
|
||||
finalEither.map {
|
||||
case Right(wallet) =>
|
||||
case CompatRight(wallet) =>
|
||||
logger.debug(s"Successfully initialized wallet")
|
||||
InitializeWalletSuccess(wallet)
|
||||
case Left(err) => err
|
||||
case CompatLeft(err) => err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.bitcoins.wallet
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import org.bitcoins.core.compat.JavaConverters._
|
||||
|
||||
import org.bitcoins.core.compat._
|
||||
import org.bitcoins.core.crypto.AesPassword
|
||||
import java.nio.file.Files
|
||||
import org.bitcoins.core.crypto.MnemonicCode
|
||||
|
@ -68,9 +70,9 @@ object WalletStorage extends KeyHandlingLogger {
|
|||
|
||||
val foundMnemonicOpt: Option[EncryptedMnemonic] =
|
||||
readEncryptedMnemonicFromDisk() match {
|
||||
case Left(_) =>
|
||||
case CompatLeft(_) =>
|
||||
None
|
||||
case Right(mnemonic) => Some(mnemonic)
|
||||
case CompatRight(mnemonic) => Some(mnemonic)
|
||||
}
|
||||
|
||||
foundMnemonicOpt match {
|
||||
|
@ -104,7 +106,7 @@ object WalletStorage extends KeyHandlingLogger {
|
|||
* performing no decryption
|
||||
*/
|
||||
private def readEncryptedMnemonicFromDisk()(
|
||||
implicit config: WalletAppConfig): Either[
|
||||
implicit config: WalletAppConfig): CompatEither[
|
||||
ReadMnemonicError,
|
||||
EncryptedMnemonic] = {
|
||||
|
||||
|
@ -112,7 +114,7 @@ object WalletStorage extends KeyHandlingLogger {
|
|||
config.datadir.resolve(ENCRYPTED_SEED_FILE_NAME)
|
||||
}
|
||||
|
||||
val jsonE: Either[ReadMnemonicError, ujson.Value] = {
|
||||
val jsonE: CompatEither[ReadMnemonicError, ujson.Value] = {
|
||||
if (Files.isRegularFile(path)) {
|
||||
val rawJson = Files.readAllLines(path).asScala.mkString("\n")
|
||||
logger.debug(s"Read raw encrypted mnemonic from $path")
|
||||
|
@ -121,24 +123,23 @@ object WalletStorage extends KeyHandlingLogger {
|
|||
ujson.read(rawJson)
|
||||
} match {
|
||||
case Failure(ujson.ParseException(clue, _, _, _)) =>
|
||||
Left(ReadMnemonicError.JsonParsingError(clue))
|
||||
CompatLeft(ReadMnemonicError.JsonParsingError(clue))
|
||||
case Failure(exception) => throw exception
|
||||
|
||||
case Success(value) =>
|
||||
logger.debug(s"Parsed $path into valid json")
|
||||
Right(value)
|
||||
CompatRight(value)
|
||||
}
|
||||
} else {
|
||||
logger.error(s"Encrypted mnemonic not found at $path")
|
||||
Left(ReadMnemonicError.NotFoundError)
|
||||
CompatLeft(ReadMnemonicError.NotFoundError)
|
||||
}
|
||||
}
|
||||
|
||||
import EitherUtil.EitherOps._
|
||||
import MnemonicJsonKeys._
|
||||
import ReadMnemonicError._
|
||||
|
||||
val readJsonTupleEither: Either[
|
||||
val readJsonTupleEither: CompatEither[
|
||||
ReadMnemonicError,
|
||||
(String, String, String)] = jsonE.flatMap { json =>
|
||||
logger.trace(s"Read encrypted mnemonic JSON: $json")
|
||||
|
@ -148,15 +149,15 @@ object WalletStorage extends KeyHandlingLogger {
|
|||
val rawSaltString = json(SALT).str
|
||||
(ivString, cipherTextString, rawSaltString)
|
||||
} match {
|
||||
case Success(value) => Right(value)
|
||||
case Success(value) => CompatRight(value)
|
||||
case Failure(value: ujson.Value.InvalidData) =>
|
||||
logger.error(s"Error when parsing JSON file $path: ${value.msg}")
|
||||
Left(JsonParsingError(value.msg))
|
||||
CompatLeft(JsonParsingError(value.msg))
|
||||
case Failure(exception) => throw exception
|
||||
}
|
||||
}
|
||||
|
||||
val encryptedEither: Either[ReadMnemonicError, EncryptedMnemonic] =
|
||||
val encryptedEither: CompatEither[ReadMnemonicError, EncryptedMnemonic] =
|
||||
readJsonTupleEither.flatMap {
|
||||
case (rawIv, rawCipherText, rawSalt) =>
|
||||
val encryptedOpt = for {
|
||||
|
@ -167,10 +168,12 @@ object WalletStorage extends KeyHandlingLogger {
|
|||
logger.debug(s"Parsed contents of $path into an EncryptedMnemonic")
|
||||
EncryptedMnemonic(AesEncryptedData(cipherText, iv), salt)
|
||||
}
|
||||
encryptedOpt
|
||||
.map(Right(_))
|
||||
.getOrElse(
|
||||
Left(JsonParsingError("JSON contents was not hex strings")))
|
||||
val toRight: Option[
|
||||
CompatRight[ReadMnemonicError, EncryptedMnemonic]] = encryptedOpt
|
||||
.map(CompatRight(_))
|
||||
|
||||
toRight.getOrElse(
|
||||
CompatLeft(JsonParsingError("JSON contents was not hex strings")))
|
||||
}
|
||||
encryptedEither
|
||||
}
|
||||
|
@ -185,22 +188,21 @@ object WalletStorage extends KeyHandlingLogger {
|
|||
|
||||
val encryptedEither = readEncryptedMnemonicFromDisk()
|
||||
|
||||
import EitherUtil.EitherOps._
|
||||
val decryptedEither: Either[ReadMnemonicError, MnemonicCode] =
|
||||
val decryptedEither: CompatEither[ReadMnemonicError, MnemonicCode] =
|
||||
encryptedEither.flatMap { encrypted =>
|
||||
encrypted.toMnemonic(passphrase) match {
|
||||
case Failure(exc) =>
|
||||
logger.error(s"Error when decrypting $encrypted: $exc")
|
||||
Left(ReadMnemonicError.DecryptionError)
|
||||
CompatLeft(ReadMnemonicError.DecryptionError)
|
||||
case Success(value) =>
|
||||
logger.debug(s"Decrypted $encrypted successfully")
|
||||
Right(value)
|
||||
CompatRight(value)
|
||||
}
|
||||
}
|
||||
|
||||
decryptedEither match {
|
||||
case Left(value) => value
|
||||
case Right(value) => ReadMnemonicSuccess(value)
|
||||
case CompatLeft(value) => value
|
||||
case CompatRight(value) => ReadMnemonicSuccess(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.bitcoins.wallet.internal
|
||||
|
||||
import org.bitcoins.core.compat._
|
||||
import org.bitcoins.wallet.LockedWallet
|
||||
import org.bitcoins.core.protocol.transaction.TransactionOutput
|
||||
import org.bitcoins.core.protocol.transaction.TransactionOutPoint
|
||||
|
@ -42,14 +43,14 @@ private[wallet] trait UtxoHandling extends KeyHandlingLogger {
|
|||
* it in our address table
|
||||
*/
|
||||
private def findAddress(
|
||||
spk: ScriptPubKey): Future[Either[AddUtxoError, AddressDb]] =
|
||||
spk: ScriptPubKey): Future[CompatEither[AddUtxoError, AddressDb]] =
|
||||
BitcoinAddress.fromScriptPubKey(spk, networkParameters) match {
|
||||
case Success(address) =>
|
||||
addressDAO.findAddress(address).map {
|
||||
case Some(addrDb) => Right(addrDb)
|
||||
case None => Left(AddUtxoError.AddressNotFound)
|
||||
case Some(addrDb) => CompatRight(addrDb)
|
||||
case None => CompatLeft(AddUtxoError.AddressNotFound)
|
||||
}
|
||||
case Failure(_) => Future.successful(Left(AddUtxoError.BadSPK))
|
||||
case Failure(_) => Future.successful(CompatLeft(AddUtxoError.BadSPK))
|
||||
}
|
||||
|
||||
/** Constructs a DB level representation of the given UTXO, and persist it to disk */
|
||||
|
@ -103,7 +104,6 @@ private[wallet] trait UtxoHandling extends KeyHandlingLogger {
|
|||
confirmations: Int,
|
||||
spent: Boolean): Future[AddUtxoResult] = {
|
||||
import AddUtxoError._
|
||||
import EitherUtil.EitherOps._
|
||||
|
||||
logger.info(s"Adding UTXO to wallet: ${transaction.txId.hex}:${vout.toInt}")
|
||||
|
||||
|
@ -127,12 +127,12 @@ private[wallet] trait UtxoHandling extends KeyHandlingLogger {
|
|||
|
||||
// second check: do we have an address associated with the provided
|
||||
// output in our DB?
|
||||
val addressDbEitherF: Future[Either[AddUtxoError, AddressDb]] =
|
||||
val addressDbEitherF: Future[CompatEither[AddUtxoError, AddressDb]] =
|
||||
findAddress(output.scriptPubKey)
|
||||
|
||||
// insert the UTXO into the DB
|
||||
addressDbEitherF.flatMap { addressDbE =>
|
||||
val biasedE: Either[AddUtxoError, Future[SpendingInfoDb]] = for {
|
||||
val biasedE: CompatEither[AddUtxoError, Future[SpendingInfoDb]] = for {
|
||||
addressDb <- addressDbE
|
||||
} yield
|
||||
writeUtxo(txid = transaction.txIdBE,
|
||||
|
@ -144,8 +144,8 @@ private[wallet] trait UtxoHandling extends KeyHandlingLogger {
|
|||
|
||||
EitherUtil.liftRightBiasedFutureE(biasedE)
|
||||
} map {
|
||||
case Right(utxo) => AddUtxoSuccess(utxo)
|
||||
case Left(e) => e
|
||||
case CompatRight(utxo) => AddUtxoSuccess(utxo)
|
||||
case CompatLeft(e) => e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,10 @@
|
|||
"rpc/rpc-eclair",
|
||||
"rpc/rpc-bitcoind"
|
||||
],
|
||||
"Chain": ["chain/sync-chain"],
|
||||
"Wallet": ["wallet/create-wallet"],
|
||||
"Contributing": ["contributing", "contributing-website"],
|
||||
"Applications": ["applications/configuration"],
|
||||
"Security": ["security"]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue