mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-13 19:37:30 +01:00
Multi module configuration (#494)
* Replace AppConfig with more specific types * Rework database configuration In this commit we: 1) Introduce new database settings for all submodules, and at the same time remove the singular datbase setting for the entire project 2) Introduce a new method .initialize on all configs that is responsible for creating needed directories, databases and files 3) Introduce a new class BitcoinSAppConfig that wraps configs for all three other subproject config. We also provide implicit conversions that enable this super-config to be passed in wherever a specialized config is required. 4) Add more tests for our configuration setup. * Add Ammonite to Docs deps
This commit is contained in:
parent
46db2a57b7
commit
ceec55d458
50 changed files with 589 additions and 244 deletions
|
@ -353,7 +353,7 @@ lazy val nodeTest = {
|
||||||
|
|
||||||
lazy val testkit = project
|
lazy val testkit = project
|
||||||
.in(file("testkit"))
|
.in(file("testkit"))
|
||||||
.settings(commonProdSettings: _*)
|
.settings(commonSettings: _*)
|
||||||
.dependsOn(
|
.dependsOn(
|
||||||
core,
|
core,
|
||||||
chain,
|
chain,
|
||||||
|
@ -383,7 +383,8 @@ lazy val docs = project
|
||||||
bitcoins / Compile / unidoc,
|
bitcoins / Compile / unidoc,
|
||||||
Compile / docusaurusPublishGhpages
|
Compile / docusaurusPublishGhpages
|
||||||
)
|
)
|
||||||
.value
|
.value,
|
||||||
|
libraryDependencies ++= Deps.docs
|
||||||
)
|
)
|
||||||
.dependsOn(
|
.dependsOn(
|
||||||
bitcoindRpc,
|
bitcoindRpc,
|
||||||
|
|
|
@ -4,7 +4,6 @@ import akka.actor.ActorSystem
|
||||||
import org.bitcoins.chain.config.ChainAppConfig
|
import org.bitcoins.chain.config.ChainAppConfig
|
||||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||||
import org.bitcoins.core.protocol.blockchain.MainNetChainParams
|
import org.bitcoins.core.protocol.blockchain.MainNetChainParams
|
||||||
import org.bitcoins.db.AppConfig
|
|
||||||
import org.bitcoins.testkit.chain.fixture.{ChainFixture, ChainFixtureTag}
|
import org.bitcoins.testkit.chain.fixture.{ChainFixture, ChainFixtureTag}
|
||||||
import org.bitcoins.testkit.chain.{ChainTestUtil, ChainUnitTest}
|
import org.bitcoins.testkit.chain.{ChainTestUtil, ChainUnitTest}
|
||||||
import org.scalatest.FutureOutcome
|
import org.scalatest.FutureOutcome
|
||||||
|
@ -15,18 +14,18 @@ class BitcoinPowTest extends ChainUnitTest {
|
||||||
|
|
||||||
override type FixtureParam = ChainFixture
|
override type FixtureParam = ChainFixture
|
||||||
|
|
||||||
override lazy implicit val appConfig: ChainAppConfig = mainnetAppConfig
|
implicit override lazy val appConfig: ChainAppConfig = mainnetAppConfig
|
||||||
|
|
||||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||||
withChainFixture(test)
|
withChainFixture(test)
|
||||||
|
|
||||||
override implicit val system: ActorSystem = ActorSystem("BitcoinPowTest")
|
implicit override val system: ActorSystem = ActorSystem("BitcoinPowTest")
|
||||||
|
|
||||||
behavior of "BitcoinPow"
|
behavior of "BitcoinPow"
|
||||||
|
|
||||||
it must "NOT calculate a POW change when one is not needed" inFixtured {
|
it must "NOT calculate a POW change when one is not needed" inFixtured {
|
||||||
case ChainFixture.Empty =>
|
case ChainFixture.Empty =>
|
||||||
val blockHeaderDAO = BlockHeaderDAO(appConfig)
|
val blockHeaderDAO = BlockHeaderDAO()
|
||||||
val header1 = ChainTestUtil.ValidPOWChange.blockHeaderDb566494
|
val header1 = ChainTestUtil.ValidPOWChange.blockHeaderDb566494
|
||||||
val header2 = ChainTestUtil.ValidPOWChange.blockHeaderDb566495
|
val header2 = ChainTestUtil.ValidPOWChange.blockHeaderDb566495
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ import org.bitcoins.testkit.chain.{
|
||||||
import org.scalatest.{Assertion, FutureOutcome}
|
import org.scalatest.{Assertion, FutureOutcome}
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import org.bitcoins.db.AppConfig
|
|
||||||
import org.bitcoins.chain.config.ChainAppConfig
|
import org.bitcoins.chain.config.ChainAppConfig
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
|
@ -25,12 +24,12 @@ class TipValidationTest extends ChainUnitTest {
|
||||||
override type FixtureParam = BlockHeaderDAO
|
override type FixtureParam = BlockHeaderDAO
|
||||||
|
|
||||||
// we're working with mainnet data
|
// we're working with mainnet data
|
||||||
override lazy implicit val appConfig: ChainAppConfig = mainnetAppConfig
|
implicit override lazy val appConfig: ChainAppConfig = mainnetAppConfig
|
||||||
|
|
||||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||||
withBlockHeaderDAO(test)
|
withBlockHeaderDAO(test)
|
||||||
|
|
||||||
override implicit val system: ActorSystem = ActorSystem("TipValidationTest")
|
implicit override val system: ActorSystem = ActorSystem("TipValidationTest")
|
||||||
|
|
||||||
behavior of "TipValidation"
|
behavior of "TipValidation"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
bitcoin-s {
|
|
||||||
database {
|
|
||||||
name = "chaindb.sqlite"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +1,18 @@
|
||||||
package org.bitcoins.chain.api
|
package org.bitcoins.chain.api
|
||||||
|
|
||||||
import org.bitcoins.db._
|
|
||||||
import org.bitcoins.chain.models.BlockHeaderDb
|
import org.bitcoins.chain.models.BlockHeaderDb
|
||||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
import org.bitcoins.chain.config.ChainAppConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry api to the chain project for adding new things to our blockchain
|
* Entry api to the chain project for adding new things to our blockchain
|
||||||
*/
|
*/
|
||||||
trait ChainApi {
|
trait ChainApi {
|
||||||
|
|
||||||
def chainConfig: AppConfig
|
def chainConfig: ChainAppConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a block header to our chain project
|
* Adds a block header to our chain project
|
||||||
|
|
|
@ -54,7 +54,7 @@ object Blockchain extends BitcoinSLogger {
|
||||||
val nested: Vector[Future[BlockchainUpdate]] = blockchains.map {
|
val nested: Vector[Future[BlockchainUpdate]] = blockchains.map {
|
||||||
blockchain =>
|
blockchain =>
|
||||||
val tip = blockchain.tip
|
val tip = blockchain.tip
|
||||||
logger.info(
|
logger.debug(
|
||||||
s"Attempting to add new tip=${header.hashBE.hex} with prevhash=${header.previousBlockHashBE.hex} to chain with current tips=${tip.hashBE.hex}")
|
s"Attempting to add new tip=${header.hashBE.hex} with prevhash=${header.previousBlockHashBE.hex} to chain with current tips=${tip.hashBE.hex}")
|
||||||
val tipResultF = TipValidation.checkNewTip(newPotentialTip = header,
|
val tipResultF = TipValidation.checkNewTip(newPotentialTip = header,
|
||||||
currentTip = tip,
|
currentTip = tip,
|
||||||
|
|
|
@ -14,23 +14,29 @@ import scala.util.Failure
|
||||||
|
|
||||||
case class ChainAppConfig(val confs: Config*) extends AppConfig {
|
case class ChainAppConfig(val confs: Config*) extends AppConfig {
|
||||||
override protected val configOverrides: List[Config] = confs.toList
|
override protected val configOverrides: List[Config] = confs.toList
|
||||||
override protected val moduleConfigName: String = "chain.conf"
|
override protected val moduleName: String = "chain"
|
||||||
override protected type ConfigType = ChainAppConfig
|
override protected type ConfigType = ChainAppConfig
|
||||||
override protected def newConfigOfType(
|
override protected def newConfigOfType(
|
||||||
configs: List[Config]): ChainAppConfig = ChainAppConfig(configs: _*)
|
configs: List[Config]): ChainAppConfig = ChainAppConfig(configs: _*)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether or not the chain project is initialized by
|
||||||
|
* trying to read the genesis block header from our block
|
||||||
|
* header table
|
||||||
|
*/
|
||||||
def isInitialized()(implicit ec: ExecutionContext): Future[Boolean] = {
|
def isInitialized()(implicit ec: ExecutionContext): Future[Boolean] = {
|
||||||
val bhDAO = BlockHeaderDAO(this)
|
val bhDAO =
|
||||||
|
BlockHeaderDAO()(ec = implicitly[ExecutionContext], appConfig = this)
|
||||||
val p = Promise[Boolean]()
|
val p = Promise[Boolean]()
|
||||||
val isDefinedOptF = {
|
val isDefinedOptF = {
|
||||||
bhDAO.read(chain.genesisBlock.blockHeader.hashBE).map(_.isDefined)
|
bhDAO.read(chain.genesisBlock.blockHeader.hashBE).map(_.isDefined)
|
||||||
}
|
}
|
||||||
isDefinedOptF.onComplete {
|
isDefinedOptF.onComplete {
|
||||||
case Success(bool) =>
|
case Success(bool) =>
|
||||||
logger.info(s"Chain project is initialized")
|
logger.debug(s"Chain project is initialized")
|
||||||
p.success(bool)
|
p.success(bool)
|
||||||
case Failure(err) =>
|
case Failure(err) =>
|
||||||
logger.info(s"Failed to init chain app err=${err.getMessage}")
|
logger.info(s"Chain project is not initialized")
|
||||||
p.success(false)
|
p.success(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,8 +47,7 @@ case class ChainAppConfig(val confs: Config*) extends AppConfig {
|
||||||
* This creates the necessary tables for the chain project
|
* This creates the necessary tables for the chain project
|
||||||
* and inserts preliminary data like the genesis block header
|
* and inserts preliminary data like the genesis block header
|
||||||
* */
|
* */
|
||||||
def initialize(implicit ec: ExecutionContext): Future[Unit] = {
|
override def initialize()(implicit ec: ExecutionContext): Future[Unit] = {
|
||||||
val blockHeaderDAO = BlockHeaderDAO(this)
|
|
||||||
val isInitF = isInitialized()
|
val isInitF = isInitialized()
|
||||||
isInitF.flatMap { isInit =>
|
isInitF.flatMap { isInit =>
|
||||||
if (isInit) {
|
if (isInit) {
|
||||||
|
@ -53,9 +58,14 @@ case class ChainAppConfig(val confs: Config*) extends AppConfig {
|
||||||
BlockHeaderDbHelper.fromBlockHeader(height = 0,
|
BlockHeaderDbHelper.fromBlockHeader(height = 0,
|
||||||
bh =
|
bh =
|
||||||
chain.genesisBlock.blockHeader)
|
chain.genesisBlock.blockHeader)
|
||||||
|
val blockHeaderDAO =
|
||||||
|
BlockHeaderDAO()(ec = implicitly[ExecutionContext], appConfig = this)
|
||||||
val bhCreatedF =
|
val bhCreatedF =
|
||||||
createdF.flatMap(_ => blockHeaderDAO.create(genesisHeader))
|
createdF.flatMap(_ => blockHeaderDAO.create(genesisHeader))
|
||||||
bhCreatedF.flatMap(_ => FutureUtil.unit)
|
bhCreatedF.flatMap { _ =>
|
||||||
|
logger.info(s"Inserted genesis block header into DB")
|
||||||
|
FutureUtil.unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import org.bitcoins.db.{DbManagement}
|
||||||
import slick.lifted.TableQuery
|
import slick.lifted.TableQuery
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import org.bitcoins.chain.config.ChainAppConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for creating and destroying database
|
* Responsible for creating and destroying database
|
||||||
|
@ -19,11 +21,12 @@ sealed abstract class ChainDbManagement extends DbManagement {
|
||||||
override val allTables = List(chainTable)
|
override val allTables = List(chainTable)
|
||||||
|
|
||||||
def createHeaderTable(createIfNotExists: Boolean = true)(
|
def createHeaderTable(createIfNotExists: Boolean = true)(
|
||||||
implicit config: AppConfig): Future[Unit] = {
|
implicit config: ChainAppConfig,
|
||||||
|
ec: ExecutionContext): Future[Unit] = {
|
||||||
createTable(chainTable, createIfNotExists)
|
createTable(chainTable, createIfNotExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
def dropHeaderTable()(implicit config: AppConfig): Future[Unit] = {
|
def dropHeaderTable()(implicit config: ChainAppConfig): Future[Unit] = {
|
||||||
dropTable(chainTable)
|
dropTable(chainTable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,9 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||||
* to [[org.bitcoins.core.protocol.blockchain.BlockHeader]]s in
|
* to [[org.bitcoins.core.protocol.blockchain.BlockHeader]]s in
|
||||||
* our chain project
|
* our chain project
|
||||||
*/
|
*/
|
||||||
case class BlockHeaderDAO(appConfig: ChainAppConfig)(
|
case class BlockHeaderDAO()(
|
||||||
implicit override val ec: ExecutionContext)
|
implicit override val ec: ExecutionContext,
|
||||||
|
override val appConfig: ChainAppConfig)
|
||||||
extends CRUD[BlockHeaderDb, DoubleSha256DigestBE] {
|
extends CRUD[BlockHeaderDb, DoubleSha256DigestBE] {
|
||||||
|
|
||||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||||
|
|
|
@ -1,17 +1,43 @@
|
||||||
bitcoin-s {
|
common = {
|
||||||
database {
|
dataSourceClass = slick.jdbc.DatabaseUrlDataSource
|
||||||
dataSourceClass = slick.jdbc.DatabaseUrlDataSource
|
profile = "slick.jdbc.SQLiteProfile$"
|
||||||
profile = "slick.jdbc.SQLiteProfile$"
|
|
||||||
dbPath = ${bitcoin-s.datadir}/${bitcoin-s.network}/
|
|
||||||
|
|
||||||
|
db {
|
||||||
|
path = ${bitcoin-s.datadir}/${bitcoin-s.network}/
|
||||||
|
driver = org.sqlite.JDBC
|
||||||
|
|
||||||
|
# as long as we're on SQLite there's no point
|
||||||
|
# in doing connection pooling
|
||||||
|
connectionPool = disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitcoin-s {
|
||||||
|
wallet = ${common}
|
||||||
|
wallet {
|
||||||
# this config key is read by Slick
|
# this config key is read by Slick
|
||||||
db {
|
db {
|
||||||
driver = org.sqlite.JDBC
|
name = walletdb.sqlite
|
||||||
url = "jdbc:sqlite:"${bitcoin-s.database.dbPath}${bitcoin-s.database.name}
|
url = "jdbc:sqlite:"${bitcoin-s.wallet.db.path}${bitcoin-s.wallet.db.name}
|
||||||
|
|
||||||
# as long as we're on SQLite there's no point
|
}
|
||||||
# in doing connection pooling
|
}
|
||||||
connectionPool = disabled
|
|
||||||
|
node = ${common}
|
||||||
|
node {
|
||||||
|
# this config key is read by Slick
|
||||||
|
db {
|
||||||
|
name = nodedb.sqlite
|
||||||
|
url = "jdbc:sqlite:"${bitcoin-s.node.db.path}${bitcoin-s.node.db.name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chain = ${common}
|
||||||
|
chain {
|
||||||
|
# this config key is read by Slick
|
||||||
|
db {
|
||||||
|
name = chaindb.sqlite
|
||||||
|
url = "jdbc:sqlite:"${bitcoin-s.chain.db.path}${bitcoin-s.chain.db.name}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ import java.nio.file.Files
|
||||||
|
|
||||||
import scala.util.Properties
|
import scala.util.Properties
|
||||||
import scala.util.matching.Regex
|
import scala.util.matching.Regex
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Everything needed to configure functionality
|
* Everything needed to configure functionality
|
||||||
|
@ -34,6 +36,17 @@ import scala.util.matching.Regex
|
||||||
*/
|
*/
|
||||||
abstract class AppConfig extends BitcoinSLogger {
|
abstract class AppConfig extends BitcoinSLogger {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this project.
|
||||||
|
* After this future resolves, all operations should be
|
||||||
|
* able to be performed correctly.
|
||||||
|
*
|
||||||
|
* Initializing may include creating database tables,
|
||||||
|
* making directories or files needed latern or
|
||||||
|
* something else entirely.
|
||||||
|
*/
|
||||||
|
def initialize()(implicit ec: ExecutionContext): Future[Unit]
|
||||||
|
|
||||||
/** Sub members of AppConfig should override this type with
|
/** Sub members of AppConfig should override this type with
|
||||||
* the type of themselves, ensuring `withOverrides` return
|
* the type of themselves, ensuring `withOverrides` return
|
||||||
* the correct type
|
* the correct type
|
||||||
|
@ -92,24 +105,18 @@ abstract class AppConfig extends BitcoinSLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of module specific
|
* Name of the module. `chain`, `wallet`, `node` etc.
|
||||||
* config file. `wallet.conf`, `node.conf`,
|
|
||||||
* etc.
|
|
||||||
*/
|
*/
|
||||||
protected def moduleConfigName: String
|
protected def moduleName: String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configuration details for connecting/using the database for our projects
|
* The configuration details for connecting/using the database for our projects
|
||||||
* that require datbase connections
|
* that require datbase connections
|
||||||
*/
|
*/
|
||||||
lazy val dbConfig: DatabaseConfig[SQLiteProfile] = {
|
lazy val dbConfig: DatabaseConfig[SQLiteProfile] = {
|
||||||
//if we don't pass specific class, non-deterministic
|
|
||||||
//errors around the loaded configuration depending
|
|
||||||
//on the state of the default classLoader
|
|
||||||
//https://github.com/lightbend/config#debugging-your-configuration
|
|
||||||
val dbConfig = {
|
val dbConfig = {
|
||||||
Try {
|
Try {
|
||||||
DatabaseConfig.forConfig[SQLiteProfile](path = "database", config)
|
DatabaseConfig.forConfig[SQLiteProfile](path = moduleName, config)
|
||||||
} match {
|
} match {
|
||||||
case Success(value) =>
|
case Success(value) =>
|
||||||
value
|
value
|
||||||
|
@ -120,7 +127,7 @@ abstract class AppConfig extends BitcoinSLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.trace(s"Resolved DB config: ${dbConfig.config}")
|
logger.debug(s"Resolved DB config: ${dbConfig.config}")
|
||||||
|
|
||||||
val _ = createDbFileIfDNE()
|
val _ = createDbFileIfDNE()
|
||||||
|
|
||||||
|
@ -133,20 +140,33 @@ abstract class AppConfig extends BitcoinSLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The path where our DB is located */
|
/** The path where our DB is located */
|
||||||
// todo: what happens when to this if we
|
// todo: what happens to this if we
|
||||||
// dont use SQLite?
|
// dont use SQLite?
|
||||||
lazy val dbPath: Path = {
|
lazy val dbPath: Path = {
|
||||||
val pathStr = config.getString("database.dbPath")
|
val pathStr = config.getString(s"$moduleName.db.path")
|
||||||
val path = Paths.get(pathStr)
|
val path = Paths.get(pathStr)
|
||||||
logger.debug(s"DB path: $path")
|
logger.debug(s"DB path: $path")
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The name of our database */
|
||||||
|
// todo: what happens to this if we
|
||||||
|
// dont use SQLite?
|
||||||
|
lazy val dbName: String = {
|
||||||
|
config.getString(s"$moduleName.db.name")
|
||||||
|
}
|
||||||
|
|
||||||
private def createDbFileIfDNE(): Unit = {
|
private def createDbFileIfDNE(): Unit = {
|
||||||
//should add a check in here that we are using sqlite
|
//should add a check in here that we are using sqlite
|
||||||
if (!Files.exists(dbPath)) {
|
if (!Files.exists(dbPath)) {
|
||||||
logger.debug(s"Creating database directory=$dbPath")
|
val _ = {
|
||||||
val _ = Files.createDirectories(dbPath)
|
logger.debug(s"Creating database directory=$dbPath")
|
||||||
|
Files.createDirectories(dbPath)
|
||||||
|
val dbFilePath = dbPath.resolve(dbName)
|
||||||
|
logger.debug(s"Creating database file=$dbFilePath")
|
||||||
|
Files.createFile(dbFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,13 +191,7 @@ abstract class AppConfig extends BitcoinSLogger {
|
||||||
* The underlying config that we derive the
|
* The underlying config that we derive the
|
||||||
* rest of the fields in this class from
|
* rest of the fields in this class from
|
||||||
*/
|
*/
|
||||||
protected lazy val config: Config = {
|
private[bitcoins] lazy val config: Config = {
|
||||||
val moduleConfig =
|
|
||||||
ConfigFactory.load(moduleConfigName)
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
s"Module config: ${moduleConfig.getConfig("bitcoin-s").asReadableJson}")
|
|
||||||
|
|
||||||
// `load` tries to resolve substitions,
|
// `load` tries to resolve substitions,
|
||||||
// `parseResources` does not
|
// `parseResources` does not
|
||||||
val dbConfig = ConfigFactory
|
val dbConfig = ConfigFactory
|
||||||
|
@ -186,9 +200,14 @@ abstract class AppConfig extends BitcoinSLogger {
|
||||||
logger.trace(
|
logger.trace(
|
||||||
s"DB config: ${dbConfig.getConfig("bitcoin-s").asReadableJson}")
|
s"DB config: ${dbConfig.getConfig("bitcoin-s").asReadableJson}")
|
||||||
|
|
||||||
val classPathConfig =
|
// we want to NOT resolve substitutions in the configuraton until the user
|
||||||
ConfigFactory
|
// provided configs also has been loaded. .parseResources() does not do that
|
||||||
.load()
|
// whereas .load() does
|
||||||
|
val classPathConfig = {
|
||||||
|
val applicationConf = ConfigFactory.parseResources("application.conf")
|
||||||
|
val referenceConf = ConfigFactory.parseResources("reference.conf")
|
||||||
|
applicationConf.withFallback(referenceConf)
|
||||||
|
}
|
||||||
|
|
||||||
logger.trace(
|
logger.trace(
|
||||||
s"Classpath config: ${classPathConfig.getConfig("bitcoin-s").asReadableJson}")
|
s"Classpath config: ${classPathConfig.getConfig("bitcoin-s").asReadableJson}")
|
||||||
|
@ -196,7 +215,6 @@ abstract class AppConfig extends BitcoinSLogger {
|
||||||
// loads reference.conf as well as application.conf,
|
// loads reference.conf as well as application.conf,
|
||||||
// if the user has made one
|
// if the user has made one
|
||||||
val unresolvedConfig = classPathConfig
|
val unresolvedConfig = classPathConfig
|
||||||
.withFallback(moduleConfig)
|
|
||||||
.withFallback(dbConfig)
|
.withFallback(dbConfig)
|
||||||
|
|
||||||
logger.trace(s"Unresolved bitcoin-s config:")
|
logger.trace(s"Unresolved bitcoin-s config:")
|
||||||
|
@ -219,6 +237,7 @@ abstract class AppConfig extends BitcoinSLogger {
|
||||||
// in this order
|
// in this order
|
||||||
overrides.withFallback(unresolvedConfig)
|
overrides.withFallback(unresolvedConfig)
|
||||||
} else {
|
} else {
|
||||||
|
logger.trace(s"No user-provided overrides")
|
||||||
unresolvedConfig
|
unresolvedConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,10 @@ abstract class CRUD[T, PrimaryKeyType] extends BitcoinSLogger {
|
||||||
* @param t - the record to be inserted
|
* @param t - the record to be inserted
|
||||||
* @return the inserted record
|
* @return the inserted record
|
||||||
*/
|
*/
|
||||||
def create(t: T): Future[T] = createAll(Vector(t)).map(_.head)
|
def create(t: T): Future[T] = {
|
||||||
|
logger.trace(s"Writing $t to DB with config: ${appConfig.config}")
|
||||||
|
createAll(Vector(t)).map(_.head)
|
||||||
|
}
|
||||||
|
|
||||||
def createAll(ts: Vector[T]): Future[Vector[T]]
|
def createAll(ts: Vector[T]): Future[Vector[T]]
|
||||||
|
|
||||||
|
@ -41,6 +44,7 @@ abstract class CRUD[T, PrimaryKeyType] extends BitcoinSLogger {
|
||||||
* @return Option[T] - the record if found, else none
|
* @return Option[T] - the record if found, else none
|
||||||
*/
|
*/
|
||||||
def read(id: PrimaryKeyType): Future[Option[T]] = {
|
def read(id: PrimaryKeyType): Future[Option[T]] = {
|
||||||
|
logger.trace(s"Reading from DB with config: ${appConfig.config}")
|
||||||
val query = findByPrimaryKey(id)
|
val query = findByPrimaryKey(id)
|
||||||
val rows: Future[Seq[T]] = database.run(query.result)
|
val rows: Future[Seq[T]] = database.run(query.result)
|
||||||
rows.map(_.headOption)
|
rows.map(_.headOption)
|
||||||
|
|
|
@ -16,29 +16,38 @@ abstract class DbManagement extends BitcoinSLogger {
|
||||||
db.run(query)
|
db.run(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Lists all tables in the given database */
|
||||||
|
def listTables(db: SafeDatabase): Future[Vector[SQLiteTableInfo]] =
|
||||||
|
listTables(db.config.database)
|
||||||
|
|
||||||
def createAll()(
|
def createAll()(
|
||||||
implicit config: AppConfig,
|
implicit config: AppConfig,
|
||||||
ec: ExecutionContext): Future[List[Unit]] = {
|
ec: ExecutionContext): Future[Unit] = {
|
||||||
Future.sequence(allTables.map(createTable(_)))
|
Future.sequence(allTables.map(createTable(_))).map(_ => ())
|
||||||
}
|
}
|
||||||
|
|
||||||
def dropAll()(
|
def dropAll()(
|
||||||
implicit config: AppConfig,
|
implicit config: AppConfig,
|
||||||
ec: ExecutionContext): Future[List[Unit]] = {
|
ec: ExecutionContext): Future[Unit] = {
|
||||||
Future.sequence(allTables.reverse.map(dropTable(_)))
|
Future.sequence(allTables.reverse.map(dropTable(_))).map(_ => ())
|
||||||
}
|
}
|
||||||
|
|
||||||
def createTable(
|
def createTable(
|
||||||
table: TableQuery[_ <: Table[_]],
|
table: TableQuery[_ <: Table[_]],
|
||||||
createIfNotExists: Boolean = true)(
|
createIfNotExists: Boolean = true)(
|
||||||
implicit config: AppConfig): Future[Unit] = {
|
implicit config: AppConfig,
|
||||||
|
ec: ExecutionContext): Future[Unit] = {
|
||||||
|
val tableName = table.baseTableRow.tableName
|
||||||
|
logger.debug(
|
||||||
|
s"Creating table $tableName with DB config: ${config.dbConfig.config} ")
|
||||||
|
|
||||||
import config.database
|
import config.database
|
||||||
val result = if (createIfNotExists) {
|
val query = if (createIfNotExists) {
|
||||||
database.run(table.schema.createIfNotExists)
|
table.schema.createIfNotExists
|
||||||
} else {
|
} else {
|
||||||
database.run(table.schema.create)
|
table.schema.create
|
||||||
}
|
}
|
||||||
result
|
database.run(query).map(_ => logger.debug(s"Created table $tableName"))
|
||||||
}
|
}
|
||||||
|
|
||||||
def dropTable(
|
def dropTable(
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.scalatest._
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.concurrent.duration.DurationInt
|
import scala.concurrent.duration.DurationInt
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by chris on 6/7/16.
|
* Created by chris on 6/7/16.
|
||||||
|
@ -28,21 +29,22 @@ class ClientTest
|
||||||
implicit val system = ActorSystem(
|
implicit val system = ActorSystem(
|
||||||
s"Client-Test-System-${System.currentTimeMillis()}")
|
s"Client-Test-System-${System.currentTimeMillis()}")
|
||||||
|
|
||||||
private val appConfig = NodeTestUtil.nodeAppConfig
|
implicit private val config: BitcoinSAppConfig =
|
||||||
|
BitcoinSAppConfig.getTestConfig()
|
||||||
|
implicit private val chainConf = config.chainConf
|
||||||
|
implicit private val nodeConf = config.nodeConf
|
||||||
|
|
||||||
private val chainAppConfig = ChainAppConfig()
|
implicit val np = config.chainConf.network
|
||||||
|
|
||||||
implicit val np = appConfig.network
|
lazy val bitcoindRpcF = BitcoindRpcTestUtil.startedBitcoindRpcClient()
|
||||||
|
|
||||||
val bitcoindRpcF = BitcoindRpcTestUtil.startedBitcoindRpcClient()
|
lazy val bitcoindPeerF = bitcoindRpcF.map { bitcoind =>
|
||||||
|
|
||||||
val bitcoindPeerF = bitcoindRpcF.map { bitcoind =>
|
|
||||||
NodeTestUtil.getBitcoindPeer(bitcoind)
|
NodeTestUtil.getBitcoindPeer(bitcoind)
|
||||||
}
|
}
|
||||||
|
|
||||||
val bitcoindRpc2F = BitcoindRpcTestUtil.startedBitcoindRpcClient()
|
lazy val bitcoindRpc2F = BitcoindRpcTestUtil.startedBitcoindRpcClient()
|
||||||
|
|
||||||
val bitcoindPeer2F = bitcoindRpcF.map { bitcoind =>
|
lazy val bitcoindPeer2F = bitcoindRpcF.map { bitcoind =>
|
||||||
NodeTestUtil.getBitcoindPeer(bitcoind)
|
NodeTestUtil.getBitcoindPeer(bitcoind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,9 +77,7 @@ class ClientTest
|
||||||
val probe = TestProbe()
|
val probe = TestProbe()
|
||||||
val remote = peer.socket
|
val remote = peer.socket
|
||||||
val peerMessageReceiver =
|
val peerMessageReceiver =
|
||||||
PeerMessageReceiver(state = Preconnection,
|
PeerMessageReceiver(state = Preconnection)
|
||||||
nodeAppConfig = appConfig,
|
|
||||||
chainAppConfig = chainAppConfig)
|
|
||||||
val client =
|
val client =
|
||||||
TestActorRef(Client.props(peer, peerMessageReceiver), probe.ref)
|
TestActorRef(Client.props(peer, peerMessageReceiver), probe.ref)
|
||||||
|
|
||||||
|
|
|
@ -16,16 +16,17 @@ import org.bitcoins.node.messages.HeadersMessage
|
||||||
import org.bitcoins.node.messages.control.VersionMessage
|
import org.bitcoins.node.messages.control.VersionMessage
|
||||||
import org.bitcoins.node.messages.data.HeadersMessage
|
import org.bitcoins.node.messages.data.HeadersMessage
|
||||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig
|
||||||
|
import org.bitcoins.node.config.NodeAppConfig
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by chris on 9/6/16.
|
|
||||||
*/
|
|
||||||
class BitcoinSpvNodeUtilTest extends BitcoinSUnitTest {
|
class BitcoinSpvNodeUtilTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
|
lazy val config: NodeAppConfig = BitcoinSAppConfig.getTestConfig()
|
||||||
|
|
||||||
"BitcoinSpvNodeUtil" must "return the entire byte array if a message is not aligned to a byte frame" in {
|
"BitcoinSpvNodeUtil" must "return the entire byte array if a message is not aligned to a byte frame" in {
|
||||||
val versionMessage =
|
val versionMessage =
|
||||||
VersionMessage(TestNet3.dnsSeeds(0), Constants.networkParameters)
|
VersionMessage(TestNet3.dnsSeeds(0), config.network)
|
||||||
val networkMsg = NetworkMessage(Constants.networkParameters, versionMessage)
|
val networkMsg = NetworkMessage(config.network, versionMessage)
|
||||||
//remove last byte so the message is not aligned
|
//remove last byte so the message is not aligned
|
||||||
val bytes = networkMsg.bytes.slice(0, networkMsg.bytes.size - 1)
|
val bytes = networkMsg.bytes.slice(0, networkMsg.bytes.size - 1)
|
||||||
val (_, unAlignedBytes) = BitcoinSpvNodeUtil.parseIndividualMessages(bytes)
|
val (_, unAlignedBytes) = BitcoinSpvNodeUtil.parseIndividualMessages(bytes)
|
||||||
|
@ -60,7 +61,7 @@ class BitcoinSpvNodeUtilTest extends BitcoinSUnitTest {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val networkMsg = NetworkMessage(Constants.networkParameters, headersMsg)
|
val networkMsg = NetworkMessage(config.network, headersMsg)
|
||||||
//split the network msg at a random index to simulate a tcp frame not being aligned
|
//split the network msg at a random index to simulate a tcp frame not being aligned
|
||||||
val randomIndex = scala.util.Random.nextInt().abs % networkMsg.bytes.size
|
val randomIndex = scala.util.Random.nextInt().abs % networkMsg.bytes.size
|
||||||
val (firstHalf, secondHalf) = networkMsg.bytes.splitAt(randomIndex)
|
val (firstHalf, secondHalf) = networkMsg.bytes.splitAt(randomIndex)
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
bitcoin-s {
|
|
||||||
database {
|
|
||||||
name = "nodedb.sqlite"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -24,7 +24,7 @@ object Main extends App with BitcoinSLogger {
|
||||||
implicit val chainAppConfig = ChainAppConfig()
|
implicit val chainAppConfig = ChainAppConfig()
|
||||||
logger.info(s"Chain config: ${chainAppConfig.dbConfig.config}")
|
logger.info(s"Chain config: ${chainAppConfig.dbConfig.config}")
|
||||||
|
|
||||||
val bhDAO = BlockHeaderDAO(chainAppConfig)
|
val bhDAO = BlockHeaderDAO()
|
||||||
val chainApi = ChainHandler(bhDAO, chainAppConfig)
|
val chainApi = ChainHandler(bhDAO, chainAppConfig)
|
||||||
val table = TableQuery[BlockHeaderTable]
|
val table = TableQuery[BlockHeaderTable]
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ case class SpvNode(peer: Peer, chainApi: ChainApi)(
|
||||||
import system.dispatcher
|
import system.dispatcher
|
||||||
|
|
||||||
private val peerMsgRecv =
|
private val peerMsgRecv =
|
||||||
PeerMessageReceiver.newReceiver(nodeAppConfig, chainAppConfig)
|
PeerMessageReceiver.newReceiver
|
||||||
|
|
||||||
private val client: Client =
|
private val client: Client =
|
||||||
Client(context = system, peer = peer, peerMessageReceiver = peerMsgRecv)
|
Client(context = system, peer = peer, peerMessageReceiver = peerMsgRecv)
|
||||||
|
@ -35,21 +35,24 @@ case class SpvNode(peer: Peer, chainApi: ChainApi)(
|
||||||
|
|
||||||
/** Starts our spv node */
|
/** Starts our spv node */
|
||||||
def start(): Future[SpvNode] = {
|
def start(): Future[SpvNode] = {
|
||||||
peerMsgSender.connect()
|
for {
|
||||||
|
_ <- nodeAppConfig.initialize()
|
||||||
|
node <- {
|
||||||
|
peerMsgSender.connect()
|
||||||
|
|
||||||
val isInitializedF =
|
val isInitializedF =
|
||||||
AsyncUtil.retryUntilSatisfied(peerMsgRecv.isInitialized)
|
AsyncUtil.retryUntilSatisfied(peerMsgRecv.isInitialized)
|
||||||
|
|
||||||
isInitializedF.map { _ =>
|
isInitializedF.failed.foreach(err =>
|
||||||
logger.info(s"Our peer=${peer} has been initialized")
|
logger.error(s"Failed to conenct with peer=$peer with err=${err}"))
|
||||||
}
|
|
||||||
|
|
||||||
isInitializedF.failed.foreach { err =>
|
isInitializedF.map { _ =>
|
||||||
logger.error(s"Failed to conenct with peer=$peer with err=${err}")
|
logger.info(s"Our peer=${peer} has been initialized")
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
} yield node
|
||||||
|
|
||||||
isInitializedF.map(_ => this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Stops our spv node */
|
/** Stops our spv node */
|
||||||
|
|
|
@ -2,12 +2,32 @@ package org.bitcoins.node.config
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.db.AppConfig
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import scala.concurrent.Future
|
||||||
|
import org.bitcoins.node.db.NodeDbManagement
|
||||||
|
import scala.util.Failure
|
||||||
|
import scala.util.Success
|
||||||
|
|
||||||
case class NodeAppConfig(confs: Config*) extends AppConfig {
|
case class NodeAppConfig(confs: Config*) extends AppConfig {
|
||||||
override val configOverrides: List[Config] = confs.toList
|
override val configOverrides: List[Config] = confs.toList
|
||||||
override protected def moduleConfigName: String = "node.conf"
|
override protected def moduleName: String = "node"
|
||||||
override protected type ConfigType = NodeAppConfig
|
override protected type ConfigType = NodeAppConfig
|
||||||
override protected def newConfigOfType(configs: List[Config]): NodeAppConfig =
|
override protected def newConfigOfType(configs: List[Config]): NodeAppConfig =
|
||||||
NodeAppConfig(configs: _*)
|
NodeAppConfig(configs: _*)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures correct tables and other required information is in
|
||||||
|
* place for our node.
|
||||||
|
*/
|
||||||
|
override def initialize()(implicit ec: ExecutionContext): Future[Unit] = {
|
||||||
|
logger.debug(s"Initializing node setup")
|
||||||
|
val initF = NodeDbManagement.createAll()(config = this, ec)
|
||||||
|
initF.onComplete {
|
||||||
|
case Failure(err) =>
|
||||||
|
logger.error(s"Error when initializing node: ${err.getMessage}")
|
||||||
|
case Success(_) =>
|
||||||
|
logger.debug(s"Initializing node setup: done")
|
||||||
|
}
|
||||||
|
initF
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,14 @@
|
||||||
package org.bitcoins.node.constant
|
package org.bitcoins.node.constant
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import org.bitcoins.core.config.{MainNet, NetworkParameters, RegTest, TestNet3}
|
|
||||||
import org.bitcoins.core.protocol.blockchain.{
|
|
||||||
ChainParams,
|
|
||||||
MainNetChainParams,
|
|
||||||
RegTestNetChainParams,
|
|
||||||
TestNetChainParams
|
|
||||||
}
|
|
||||||
import org.bitcoins.node.config.NodeAppConfig
|
|
||||||
import org.bitcoins.node.versions.ProtocolVersion70013
|
import org.bitcoins.node.versions.ProtocolVersion70013
|
||||||
import slick.jdbc.PostgresProfile.api._
|
|
||||||
|
|
||||||
import scala.concurrent.duration.DurationInt
|
import scala.concurrent.duration.DurationInt
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
case object Constants {
|
case object Constants {
|
||||||
lazy val actorSystem = ActorSystem("BitcoinSpvNode")
|
val emptyConfig = ConfigFactory.parseString("")
|
||||||
def networkParameters: NetworkParameters = appConfig.network
|
lazy val actorSystem = ActorSystem("BitcoinSpvNode", emptyConfig)
|
||||||
def version = ProtocolVersion70013
|
def version = ProtocolVersion70013
|
||||||
|
|
||||||
def timeout = 5.seconds
|
def timeout = 5.seconds
|
||||||
|
@ -25,19 +17,4 @@ case object Constants {
|
||||||
/** This is the file where our block headers are stored */
|
/** This is the file where our block headers are stored */
|
||||||
def blockHeaderFile = new java.io.File("src/main/resources/block_headers.dat")
|
def blockHeaderFile = new java.io.File("src/main/resources/block_headers.dat")
|
||||||
|
|
||||||
lazy val appConfig: NodeAppConfig = NodeAppConfig()
|
|
||||||
|
|
||||||
/** The [[ChainParams]] for the blockchain we are currently connected to */
|
|
||||||
def chainParams: ChainParams = networkParameters match {
|
|
||||||
case MainNet => MainNetChainParams
|
|
||||||
case TestNet3 => TestNetChainParams
|
|
||||||
case RegTest => RegTestNetChainParams
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This is the database we are currently bound to, this
|
|
||||||
* should be the database that stores information corresponding to the network
|
|
||||||
* we are currently connected to inside of the [[networkParameters]] function
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
def database: Database = appConfig.database
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,12 @@ package org.bitcoins.node.models
|
||||||
import org.bitcoins.db.{CRUDAutoInc}
|
import org.bitcoins.db.{CRUDAutoInc}
|
||||||
import slick.jdbc.SQLiteProfile.api._
|
import slick.jdbc.SQLiteProfile.api._
|
||||||
|
|
||||||
import org.bitcoins.db.AppConfig
|
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
|
import org.bitcoins.node.config.NodeAppConfig
|
||||||
|
|
||||||
case class PeerDAO(appConfig: AppConfig)(
|
case class PeerDAO()(
|
||||||
implicit override val ec: ExecutionContext)
|
implicit override val ec: ExecutionContext,
|
||||||
|
override val appConfig: NodeAppConfig)
|
||||||
extends CRUDAutoInc[Peer] {
|
extends CRUDAutoInc[Peer] {
|
||||||
override val table = TableQuery[PeerTable]
|
override val table = TableQuery[PeerTable]
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ sealed abstract class BlockActor extends Actor with BitcoinSLogger {
|
||||||
val inv = Inventory(TypeIdentifier.MsgBlock, hash)
|
val inv = Inventory(TypeIdentifier.MsgBlock, hash)
|
||||||
val getDataMessage = GetDataMessage(inv)
|
val getDataMessage = GetDataMessage(inv)
|
||||||
val networkMessage =
|
val networkMessage =
|
||||||
NetworkMessage(Constants.networkParameters, getDataMessage)
|
NetworkMessage(network = ???, getDataMessage)
|
||||||
peerMsgHandler ! networkMessage
|
peerMsgHandler ! networkMessage
|
||||||
context.become(awaitBlockMsg)
|
context.become(awaitBlockMsg)
|
||||||
case blockHeader: BlockHeader =>
|
case blockHeader: BlockHeader =>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import akka.util.ByteString
|
||||||
import org.bitcoins.core.config.NetworkParameters
|
import org.bitcoins.core.config.NetworkParameters
|
||||||
import org.bitcoins.core.util.BitcoinSLogger
|
import org.bitcoins.core.util.BitcoinSLogger
|
||||||
import org.bitcoins.node.NetworkMessage
|
import org.bitcoins.node.NetworkMessage
|
||||||
import org.bitcoins.node.constant.Constants
|
import org.bitcoins.node.config.NodeAppConfig
|
||||||
import org.bitcoins.node.messages.NetworkPayload
|
import org.bitcoins.node.messages.NetworkPayload
|
||||||
import org.bitcoins.node.models.Peer
|
import org.bitcoins.node.models.Peer
|
||||||
import org.bitcoins.node.networking.peer.PeerMessageReceiver
|
import org.bitcoins.node.networking.peer.PeerMessageReceiver
|
||||||
|
@ -41,6 +41,8 @@ import scodec.bits.ByteVector
|
||||||
*/
|
*/
|
||||||
sealed abstract class ClientActor extends Actor with BitcoinSLogger {
|
sealed abstract class ClientActor extends Actor with BitcoinSLogger {
|
||||||
|
|
||||||
|
val config: NodeAppConfig
|
||||||
|
|
||||||
def peer: Peer
|
def peer: Peer
|
||||||
|
|
||||||
/** The place we send messages that we successfully parsed from our
|
/** The place we send messages that we successfully parsed from our
|
||||||
|
@ -61,7 +63,7 @@ sealed abstract class ClientActor extends Actor with BitcoinSLogger {
|
||||||
* i.e. [[org.bitcoins.core.config.MainNet]] or [[org.bitcoins.core.config.TestNet3]]
|
* i.e. [[org.bitcoins.core.config.MainNet]] or [[org.bitcoins.core.config.TestNet3]]
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
def network: NetworkParameters = Constants.networkParameters
|
def network: NetworkParameters = config.network
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This actor signifies the node we are connected to on the p2p network
|
* This actor signifies the node we are connected to on the p2p network
|
||||||
|
@ -223,16 +225,20 @@ case class Client(actor: ActorRef, peer: Peer)
|
||||||
object Client {
|
object Client {
|
||||||
private case class ClientActorImpl(
|
private case class ClientActorImpl(
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
peerMsgHandlerReceiver: PeerMessageReceiver)
|
peerMsgHandlerReceiver: PeerMessageReceiver)(
|
||||||
extends ClientActor
|
implicit override val config: NodeAppConfig
|
||||||
|
) extends ClientActor
|
||||||
|
|
||||||
def props(peer: Peer, peerMsgHandlerReceiver: PeerMessageReceiver): Props =
|
def props(peer: Peer, peerMsgHandlerReceiver: PeerMessageReceiver)(
|
||||||
Props(classOf[ClientActorImpl], peer, peerMsgHandlerReceiver)
|
implicit config: NodeAppConfig
|
||||||
|
): Props =
|
||||||
|
Props(classOf[ClientActorImpl], peer, peerMsgHandlerReceiver, config)
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
context: ActorRefFactory,
|
context: ActorRefFactory,
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
peerMessageReceiver: PeerMessageReceiver): Client = {
|
peerMessageReceiver: PeerMessageReceiver)(
|
||||||
|
implicit config: NodeAppConfig): Client = {
|
||||||
val actorRef = context.actorOf(
|
val actorRef = context.actorOf(
|
||||||
props(peer = peer, peerMsgHandlerReceiver = peerMessageReceiver),
|
props(peer = peer, peerMsgHandlerReceiver = peerMessageReceiver),
|
||||||
BitcoinSpvNodeUtil.createActorName(this.getClass))
|
BitcoinSpvNodeUtil.createActorName(this.getClass))
|
||||||
|
|
|
@ -49,7 +49,7 @@ sealed abstract class PaymentActor extends Actor with BitcoinSLogger {
|
||||||
BloomFilter(10, 0.0001, UInt32.zero, BloomUpdateNone).insert(hash)
|
BloomFilter(10, 0.0001, UInt32.zero, BloomUpdateNone).insert(hash)
|
||||||
val filterLoadMsg = FilterLoadMessage(bloomFilter)
|
val filterLoadMsg = FilterLoadMessage(bloomFilter)
|
||||||
val bloomFilterNetworkMsg =
|
val bloomFilterNetworkMsg =
|
||||||
NetworkMessage(Constants.networkParameters, filterLoadMsg)
|
NetworkMessage(network = ???, filterLoadMsg)
|
||||||
peerMsgHandler ! bloomFilterNetworkMsg
|
peerMsgHandler ! bloomFilterNetworkMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ sealed abstract class PaymentActor extends Actor with BitcoinSLogger {
|
||||||
Inventory(TypeIdentifier.MsgFilteredBlock, blockHashes.head)
|
Inventory(TypeIdentifier.MsgFilteredBlock, blockHashes.head)
|
||||||
val getDataMsg = GetDataMessage(merkleBlockInventory)
|
val getDataMsg = GetDataMessage(merkleBlockInventory)
|
||||||
val getDataNetworkMessage =
|
val getDataNetworkMessage =
|
||||||
NetworkMessage(Constants.networkParameters, getDataMsg)
|
NetworkMessage(network = ???, getDataMsg)
|
||||||
peerMessageHandler ! getDataNetworkMessage
|
peerMessageHandler ! getDataNetworkMessage
|
||||||
logger.debug("Switching to awaitMerkleBlockMessage")
|
logger.debug("Switching to awaitMerkleBlockMessage")
|
||||||
context.become(
|
context.become(
|
||||||
|
|
|
@ -17,11 +17,12 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||||
* that a peer to sent to us on the p2p network, for instance, if we a receive a
|
* that a peer to sent to us on the p2p network, for instance, if we a receive a
|
||||||
* [[HeadersMessage]] we should store those headers in our database
|
* [[HeadersMessage]] we should store those headers in our database
|
||||||
*/
|
*/
|
||||||
class DataMessageHandler(appConfig: ChainAppConfig)(
|
class DataMessageHandler()(
|
||||||
implicit ec: ExecutionContext)
|
implicit ec: ExecutionContext,
|
||||||
|
appConfig: ChainAppConfig)
|
||||||
extends BitcoinSLogger {
|
extends BitcoinSLogger {
|
||||||
|
|
||||||
private val blockHeaderDAO: BlockHeaderDAO = BlockHeaderDAO(appConfig)
|
private val blockHeaderDAO: BlockHeaderDAO = BlockHeaderDAO()
|
||||||
|
|
||||||
def handleDataPayload(
|
def handleDataPayload(
|
||||||
payload: DataPayload,
|
payload: DataPayload,
|
||||||
|
|
|
@ -30,6 +30,9 @@ class PeerMessageReceiver(
|
||||||
chainAppConfig: ChainAppConfig)(implicit ref: ActorRefFactory)
|
chainAppConfig: ChainAppConfig)(implicit ref: ActorRefFactory)
|
||||||
extends BitcoinSLogger {
|
extends BitcoinSLogger {
|
||||||
|
|
||||||
|
implicit private val nodeConfig = nodeAppConfig
|
||||||
|
implicit private val chainConfig = chainAppConfig
|
||||||
|
|
||||||
import ref.dispatcher
|
import ref.dispatcher
|
||||||
|
|
||||||
//TODO: Really bad to just modify this internal state
|
//TODO: Really bad to just modify this internal state
|
||||||
|
@ -136,7 +139,7 @@ class PeerMessageReceiver(
|
||||||
private def handleDataPayload(
|
private def handleDataPayload(
|
||||||
payload: DataPayload,
|
payload: DataPayload,
|
||||||
sender: PeerMessageSender): Unit = {
|
sender: PeerMessageSender): Unit = {
|
||||||
val dataMsgHandler = new DataMessageHandler(chainAppConfig)
|
val dataMsgHandler = new DataMessageHandler()
|
||||||
//else it means we are receiving this data payload from a peer,
|
//else it means we are receiving this data payload from a peer,
|
||||||
//we need to handle it
|
//we need to handle it
|
||||||
dataMsgHandler.handleDataPayload(payload, sender)
|
dataMsgHandler.handleDataPayload(payload, sender)
|
||||||
|
@ -227,16 +230,18 @@ object PeerMessageReceiver {
|
||||||
case class NetworkMessageReceived(msg: NetworkMessage, client: Client)
|
case class NetworkMessageReceived(msg: NetworkMessage, client: Client)
|
||||||
extends PeerMessageReceiverMsg
|
extends PeerMessageReceiverMsg
|
||||||
|
|
||||||
def apply(
|
def apply(state: PeerMessageReceiverState)(
|
||||||
state: PeerMessageReceiverState,
|
implicit ref: ActorRefFactory,
|
||||||
nodeAppConfig: NodeAppConfig,
|
nodeAppConfig: NodeAppConfig,
|
||||||
chainAppConfig: ChainAppConfig)(
|
chainAppConfig: ChainAppConfig
|
||||||
implicit ref: ActorRefFactory): PeerMessageReceiver = {
|
): PeerMessageReceiver = {
|
||||||
new PeerMessageReceiver(state, nodeAppConfig, chainAppConfig)(ref)
|
new PeerMessageReceiver(state, nodeAppConfig, chainAppConfig)(ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
def newReceiver(nodeAppConfig: NodeAppConfig, chainAppConfig: ChainAppConfig)(
|
def newReceiver(
|
||||||
implicit ref: ActorRefFactory): PeerMessageReceiver = {
|
implicit nodeAppConfig: NodeAppConfig,
|
||||||
|
chainAppConfig: ChainAppConfig,
|
||||||
|
ref: ActorRefFactory): PeerMessageReceiver = {
|
||||||
new PeerMessageReceiver(state = PeerMessageReceiverState.fresh(),
|
new PeerMessageReceiver(state = PeerMessageReceiverState.fresh(),
|
||||||
nodeAppConfig,
|
nodeAppConfig,
|
||||||
chainAppConfig)(ref)
|
chainAppConfig)(ref)
|
||||||
|
|
|
@ -210,7 +210,7 @@ object Deps {
|
||||||
Test.ammonite
|
Test.ammonite
|
||||||
)
|
)
|
||||||
|
|
||||||
val doc = List(
|
val docs = List(
|
||||||
Compile.ammonite,
|
Compile.ammonite,
|
||||||
Compile.logback,
|
Compile.logback,
|
||||||
Test.scalaTest,
|
Test.scalaTest,
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
bitcoin-s {
|
|
||||||
datadir = ${HOME}/.bitcoin-s/.unittest
|
|
||||||
|
|
||||||
database {
|
|
||||||
db {
|
|
||||||
# run against a file-based database
|
|
||||||
# url="jdbc:sqlite:"${dbPath}${dbName}
|
|
||||||
|
|
||||||
# run against an in-memory database
|
|
||||||
# cache=shared is needed to make it persist across DB connections
|
|
||||||
# (each query is one connection). DB is wiped on JVM being killed
|
|
||||||
# with this option set.
|
|
||||||
url = "jdbc:sqlite:file::memory:?cache=shared"
|
|
||||||
|
|
||||||
connectionPool = disabled
|
|
||||||
keepAliveConnection = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
package org.bitcoins.testkit
|
||||||
|
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
import org.bitcoins.node.config.NodeAppConfig
|
||||||
|
import org.bitcoins.chain.config.ChainAppConfig
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import scala.concurrent.Future
|
||||||
|
import java.nio.file.Files
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unified config class for all submodules of Bitcoin-S
|
||||||
|
* that accepts configuration. Thanks to implicit definitions
|
||||||
|
* in this case class' companion object an instance
|
||||||
|
* of this class can be passed in anywhere a wallet,
|
||||||
|
* chain or node config is required.
|
||||||
|
*/
|
||||||
|
case class BitcoinSAppConfig(confs: Config*) {
|
||||||
|
val walletConf = WalletAppConfig(confs: _*)
|
||||||
|
val nodeConf = NodeAppConfig(confs: _*)
|
||||||
|
val chainConf = ChainAppConfig(confs: _*)
|
||||||
|
|
||||||
|
/** Initializes the wallet, node and chain projects */
|
||||||
|
def initialize()(implicit ec: ExecutionContext): Future[Unit] = {
|
||||||
|
val futures = List(walletConf.initialize(),
|
||||||
|
nodeConf.initialize(),
|
||||||
|
chainConf.initialize())
|
||||||
|
|
||||||
|
Future.sequence(futures).map(_ => ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implicit conversions that allow a unified configuration
|
||||||
|
* to be passed in wherever a specializes one is required
|
||||||
|
*/
|
||||||
|
object BitcoinSAppConfig {
|
||||||
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
|
/** Converts the given implicit config to a wallet config */
|
||||||
|
implicit def implicitToWalletConf(
|
||||||
|
implicit conf: BitcoinSAppConfig): WalletAppConfig =
|
||||||
|
conf.walletConf
|
||||||
|
|
||||||
|
/** Converts the given config to a wallet config */
|
||||||
|
implicit def toWalletConf(conf: BitcoinSAppConfig): WalletAppConfig =
|
||||||
|
conf.walletConf
|
||||||
|
|
||||||
|
/** Converts the given implicit config to a chain config */
|
||||||
|
implicit def implicitToChainConf(
|
||||||
|
implicit conf: BitcoinSAppConfig): ChainAppConfig =
|
||||||
|
conf.chainConf
|
||||||
|
|
||||||
|
/** Converts the given config to a chain config */
|
||||||
|
implicit def toChainConf(conf: BitcoinSAppConfig): ChainAppConfig =
|
||||||
|
conf.chainConf
|
||||||
|
|
||||||
|
/** Converts the given implicit config to a node config */
|
||||||
|
implicit def implicitToNodeConf(
|
||||||
|
implicit conf: BitcoinSAppConfig): NodeAppConfig =
|
||||||
|
conf.nodeConf
|
||||||
|
|
||||||
|
/** Converts the given config to a node config */
|
||||||
|
implicit def toNodeConf(conf: BitcoinSAppConfig): NodeAppConfig =
|
||||||
|
conf.nodeConf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App configuration suitable for test purposes:
|
||||||
|
|
||||||
|
* 1) Data directory is set to user temp directory
|
||||||
|
* 2) All databases are in-memory
|
||||||
|
*/
|
||||||
|
def getTestConfig(config: Config*) = {
|
||||||
|
val tmpDir = Files.createTempDirectory("bitcoin-s-")
|
||||||
|
val confStr = s"""
|
||||||
|
| bitcoin-s {
|
||||||
|
| datadir = $tmpDir
|
||||||
|
|
|
||||||
|
| wallet.db {
|
||||||
|
| url = "jdbc:sqlite:file::memory:?cache=shared"
|
||||||
|
| connectionPool = disabled
|
||||||
|
| keepAliveConnection = true
|
||||||
|
| }
|
||||||
|
| node.db {
|
||||||
|
| url = "jdbc:sqlite:file::memory:?cache=shared"
|
||||||
|
| connectionPool = disabled
|
||||||
|
| keepAliveConnection = true
|
||||||
|
| }
|
||||||
|
| chain.db {
|
||||||
|
| url = "jdbc:sqlite:file::memory:?cache=shared"
|
||||||
|
| connectionPool = disabled
|
||||||
|
| keepAliveConnection = true
|
||||||
|
| }
|
||||||
|
| }
|
||||||
|
|
|
||||||
|
|""".stripMargin
|
||||||
|
val conf = ConfigFactory.parseString(confStr)
|
||||||
|
val allConfs = conf +: config
|
||||||
|
BitcoinSAppConfig(allConfs: _*)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,13 +14,13 @@ import org.bitcoins.chain.models.{
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader, ChainParams}
|
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader, ChainParams}
|
||||||
import org.bitcoins.core.util.BitcoinSLogger
|
import org.bitcoins.core.util.BitcoinSLogger
|
||||||
import org.bitcoins.db.AppConfig
|
|
||||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||||
import org.bitcoins.testkit.chain
|
import org.bitcoins.testkit.chain
|
||||||
import org.bitcoins.testkit.chain.fixture._
|
import org.bitcoins.testkit.chain.fixture._
|
||||||
import org.bitcoins.testkit.fixtures.BitcoinSFixture
|
import org.bitcoins.testkit.fixtures.BitcoinSFixture
|
||||||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||||
import org.bitcoins.zmq.ZMQSubscriber
|
import org.bitcoins.zmq.ZMQSubscriber
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig
|
||||||
import org.scalatest._
|
import org.scalatest._
|
||||||
import play.api.libs.json.{JsError, JsSuccess, Json}
|
import play.api.libs.json.{JsError, JsSuccess, Json}
|
||||||
import scodec.bits.ByteVector
|
import scodec.bits.ByteVector
|
||||||
|
@ -28,6 +28,7 @@ import scodec.bits.ByteVector
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
import org.bitcoins.db.AppConfig
|
||||||
|
|
||||||
trait ChainUnitTest
|
trait ChainUnitTest
|
||||||
extends org.scalatest.fixture.AsyncFlatSpec
|
extends org.scalatest.fixture.AsyncFlatSpec
|
||||||
|
@ -44,16 +45,16 @@ trait ChainUnitTest
|
||||||
|
|
||||||
implicit lazy val chainParam: ChainParams = appConfig.chain
|
implicit lazy val chainParam: ChainParams = appConfig.chain
|
||||||
|
|
||||||
implicit lazy val appConfig: ChainAppConfig = ChainAppConfig()
|
implicit lazy val appConfig: ChainAppConfig =
|
||||||
|
BitcoinSAppConfig.getTestConfig()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Behaves exactly like the default conf, execpt
|
* Behaves exactly like the default conf, execpt
|
||||||
* network is set to mainnet
|
* network is set to mainnet
|
||||||
*/
|
*/
|
||||||
lazy val mainnetAppConfig: ChainAppConfig = {
|
lazy val mainnetAppConfig: ChainAppConfig = {
|
||||||
val defaultConfig = ChainAppConfig()
|
|
||||||
val mainnetConf = ConfigFactory.parseString("bitcoin-s.network = mainnet")
|
val mainnetConf = ConfigFactory.parseString("bitcoin-s.network = mainnet")
|
||||||
defaultConfig.withOverrides(mainnetConf)
|
BitcoinSAppConfig.getTestConfig(mainnetConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
|
@ -378,7 +379,7 @@ object ChainUnitTest extends BitcoinSLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def destroyHeaderTable()(implicit appConfig: AppConfig): Future[Unit] = {
|
def destroyHeaderTable()(implicit appConfig: ChainAppConfig): Future[Unit] = {
|
||||||
ChainDbManagement.dropHeaderTable()
|
ChainDbManagement.dropHeaderTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,7 +390,8 @@ object ChainUnitTest extends BitcoinSLogger {
|
||||||
|
|
||||||
/** Creates the [[org.bitcoins.chain.models.BlockHeaderTable]] */
|
/** Creates the [[org.bitcoins.chain.models.BlockHeaderTable]] */
|
||||||
private def setupHeaderTable()(
|
private def setupHeaderTable()(
|
||||||
implicit appConfig: AppConfig): Future[Unit] = {
|
implicit appConfig: ChainAppConfig,
|
||||||
|
ec: ExecutionContext): Future[Unit] = {
|
||||||
ChainDbManagement.createHeaderTable(createIfNotExists = true)
|
ChainDbManagement.createHeaderTable(createIfNotExists = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +413,7 @@ object ChainUnitTest extends BitcoinSLogger {
|
||||||
def makeChainHandler()(
|
def makeChainHandler()(
|
||||||
implicit appConfig: ChainAppConfig,
|
implicit appConfig: ChainAppConfig,
|
||||||
ec: ExecutionContext): ChainHandler = {
|
ec: ExecutionContext): ChainHandler = {
|
||||||
lazy val blockHeaderDAO = BlockHeaderDAO(appConfig)
|
lazy val blockHeaderDAO = BlockHeaderDAO()
|
||||||
|
|
||||||
ChainHandler(blockHeaderDAO = blockHeaderDAO, appConfig)
|
ChainHandler(blockHeaderDAO = blockHeaderDAO, appConfig)
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,10 +71,9 @@ abstract class NodeTestUtil {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
def nodeAppConfig: NodeAppConfig = NodeAppConfig()
|
|
||||||
|
|
||||||
def client(peer: Peer, peerMsgReceiver: PeerMessageReceiver)(
|
def client(peer: Peer, peerMsgReceiver: PeerMessageReceiver)(
|
||||||
implicit ref: ActorRefFactory): Client = {
|
implicit ref: ActorRefFactory,
|
||||||
|
conf: NodeAppConfig): Client = {
|
||||||
Client.apply(ref, peer, peerMsgReceiver)
|
Client.apply(ref, peer, peerMsgReceiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,10 @@ package org.bitcoins.testkit.node
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import org.bitcoins.chain.config.ChainAppConfig
|
|
||||||
import org.bitcoins.core.config.NetworkParameters
|
import org.bitcoins.core.config.NetworkParameters
|
||||||
import org.bitcoins.core.util.BitcoinSLogger
|
import org.bitcoins.core.util.BitcoinSLogger
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.db.AppConfig
|
||||||
import org.bitcoins.node.SpvNode
|
import org.bitcoins.node.SpvNode
|
||||||
import org.bitcoins.node.config.NodeAppConfig
|
|
||||||
import org.bitcoins.node.models.Peer
|
import org.bitcoins.node.models.Peer
|
||||||
import org.bitcoins.node.networking.peer.{
|
import org.bitcoins.node.networking.peer.{
|
||||||
PeerHandler,
|
PeerHandler,
|
||||||
|
@ -31,6 +29,9 @@ import org.scalatest.{
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig._
|
||||||
|
|
||||||
trait NodeUnitTest
|
trait NodeUnitTest
|
||||||
extends BitcoinSFixture
|
extends BitcoinSFixture
|
||||||
with MustMatchers
|
with MustMatchers
|
||||||
|
@ -39,7 +40,7 @@ trait NodeUnitTest
|
||||||
with BeforeAndAfterAll {
|
with BeforeAndAfterAll {
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
AppConfig.throwIfDefaultDatadir(nodeAppConfig)
|
AppConfig.throwIfDefaultDatadir(config.nodeConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def afterAll(): Unit = {
|
override def afterAll(): Unit = {
|
||||||
|
@ -56,9 +57,11 @@ trait NodeUnitTest
|
||||||
|
|
||||||
val timeout: FiniteDuration = 10.seconds
|
val timeout: FiniteDuration = 10.seconds
|
||||||
|
|
||||||
implicit lazy val nodeAppConfig: NodeAppConfig = NodeAppConfig()
|
/** Wallet config with data directory set to user temp directory */
|
||||||
implicit lazy val chainAppConfig: ChainAppConfig = ChainAppConfig()
|
implicit protected lazy val config: BitcoinSAppConfig =
|
||||||
implicit val np: NetworkParameters = nodeAppConfig.network
|
BitcoinSAppConfig.getTestConfig()
|
||||||
|
|
||||||
|
implicit lazy val np: NetworkParameters = config.nodeConf.network
|
||||||
|
|
||||||
lazy val startedBitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient()
|
lazy val startedBitcoindF = BitcoindRpcTestUtil.startedBitcoindRpcClient()
|
||||||
|
|
||||||
|
@ -66,7 +69,7 @@ trait NodeUnitTest
|
||||||
|
|
||||||
def buildPeerMessageReceiver(): PeerMessageReceiver = {
|
def buildPeerMessageReceiver(): PeerMessageReceiver = {
|
||||||
val receiver =
|
val receiver =
|
||||||
PeerMessageReceiver.newReceiver(nodeAppConfig, chainAppConfig)
|
PeerMessageReceiver.newReceiver
|
||||||
receiver
|
receiver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +146,7 @@ trait NodeUnitTest
|
||||||
object NodeUnitTest {
|
object NodeUnitTest {
|
||||||
|
|
||||||
def destroySpvNode(spvNode: SpvNode)(
|
def destroySpvNode(spvNode: SpvNode)(
|
||||||
implicit appConfig: NodeAppConfig,
|
implicit config: BitcoinSAppConfig,
|
||||||
ec: ExecutionContext): Future[Unit] = {
|
ec: ExecutionContext): Future[Unit] = {
|
||||||
val stopF = spvNode.stop()
|
val stopF = spvNode.stop()
|
||||||
stopF.flatMap(_ => ChainUnitTest.destroyHeaderTable())
|
stopF.flatMap(_ => ChainUnitTest.destroyHeaderTable())
|
||||||
|
@ -152,7 +155,7 @@ object NodeUnitTest {
|
||||||
def destorySpvNodeConnectedWithBitcoind(
|
def destorySpvNodeConnectedWithBitcoind(
|
||||||
spvNodeConnectedWithBitcoind: SpvNodeConnectedWithBitcoind)(
|
spvNodeConnectedWithBitcoind: SpvNodeConnectedWithBitcoind)(
|
||||||
implicit system: ActorSystem,
|
implicit system: ActorSystem,
|
||||||
appConfig: NodeAppConfig): Future[Unit] = {
|
appConfig: BitcoinSAppConfig): Future[Unit] = {
|
||||||
import system.dispatcher
|
import system.dispatcher
|
||||||
val spvNode = spvNodeConnectedWithBitcoind.spvNode
|
val spvNode = spvNodeConnectedWithBitcoind.spvNode
|
||||||
val bitcoind = spvNodeConnectedWithBitcoind.bitcoind
|
val bitcoind = spvNodeConnectedWithBitcoind.bitcoind
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
package org.bitcoins.testkit.db
|
||||||
|
|
||||||
|
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig._
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import org.bitcoins.core.config.TestNet3
|
||||||
|
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
import scala.concurrent.ExecutionContext
|
||||||
|
import org.bitcoins.wallet.models.AccountDAO
|
||||||
|
import org.bitcoins.testkit.chain.ChainTestUtil
|
||||||
|
import org.bitcoins.chain.models.BlockHeaderDb
|
||||||
|
import org.bitcoins.chain.models.BlockHeaderDbHelper
|
||||||
|
import org.bitcoins.wallet.models.AccountDb
|
||||||
|
import org.bitcoins.core.hd.HDAccount
|
||||||
|
import org.bitcoins.core.hd.HDCoin
|
||||||
|
import org.bitcoins.core.hd.HDPurposes
|
||||||
|
import org.bitcoins.core.hd.HDCoinType
|
||||||
|
import org.bitcoins.testkit.core.gen.CryptoGenerators
|
||||||
|
import os.write
|
||||||
|
import org.bitcoins.node.db.NodeDbManagement
|
||||||
|
import org.bitcoins.db.DbManagement
|
||||||
|
import org.bitcoins.wallet.db.WalletDbManagement
|
||||||
|
import org.bitcoins.db.SQLiteTableInfo
|
||||||
|
import slick.jdbc.SQLiteProfile.api._
|
||||||
|
import org.bitcoins.db.CRUD
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
class AppConfigTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
|
val system = ActorSystem()
|
||||||
|
implicit val ec: ExecutionContext = system.dispatcher
|
||||||
|
|
||||||
|
behavior of "BitcoinSAppConfig"
|
||||||
|
|
||||||
|
it must "propagate values correctly to all sub configs" in {
|
||||||
|
val networkOverride =
|
||||||
|
ConfigFactory.parseString("bitcoin-s.network = testnet3")
|
||||||
|
|
||||||
|
val config = BitcoinSAppConfig.getTestConfig(networkOverride)
|
||||||
|
val chainConf = config.chainConf
|
||||||
|
val walletConf = config.walletConf
|
||||||
|
val nodeConf = config.nodeConf
|
||||||
|
|
||||||
|
assert(chainConf.datadir == walletConf.datadir)
|
||||||
|
assert(walletConf.datadir == nodeConf.datadir)
|
||||||
|
|
||||||
|
assert(chainConf.network == TestNet3)
|
||||||
|
assert(walletConf.network == TestNet3)
|
||||||
|
assert(nodeConf.network == TestNet3)
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "have the same DB path" in {
|
||||||
|
val conf = BitcoinSAppConfig.getTestConfig()
|
||||||
|
val chainConf = conf.chainConf
|
||||||
|
val walletConf = conf.walletConf
|
||||||
|
val nodeConf = conf.nodeConf
|
||||||
|
assert(chainConf.dbPath == walletConf.dbPath)
|
||||||
|
assert(walletConf.dbPath == nodeConf.dbPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "have distinct databases" in {
|
||||||
|
val conf = BitcoinSAppConfig.getTestConfig()
|
||||||
|
val chainConf = conf.chainConf
|
||||||
|
val walletConf = conf.walletConf
|
||||||
|
val nodeConf = conf.nodeConf
|
||||||
|
assert(chainConf.dbName != walletConf.dbName)
|
||||||
|
assert(walletConf.dbName != nodeConf.dbName)
|
||||||
|
}
|
||||||
|
|
||||||
|
it must "be able to write to distinct databases" in {
|
||||||
|
implicit val config = BitcoinSAppConfig.getTestConfig()
|
||||||
|
val chainConf = config.chainConf
|
||||||
|
val walletConf = config.walletConf
|
||||||
|
val nodeConf = config.nodeConf
|
||||||
|
val allConfs = List(chainConf, walletConf, nodeConf)
|
||||||
|
|
||||||
|
val bhDAO = BlockHeaderDAO()
|
||||||
|
val accountDAO = AccountDAO()
|
||||||
|
|
||||||
|
allConfs.foreach { conf =>
|
||||||
|
val fullDbPath = conf.dbPath.resolve(conf.dbName)
|
||||||
|
assert(!Files.exists(fullDbPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
val writeF = {
|
||||||
|
for {
|
||||||
|
_ <- config.initialize()
|
||||||
|
_ = {
|
||||||
|
allConfs.foreach { conf =>
|
||||||
|
val fullDbPath = conf.dbPath.resolve(conf.dbName)
|
||||||
|
assert(Files.isRegularFile(fullDbPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ <- {
|
||||||
|
bhDAO.create(ChainTestUtil.regTestGenesisHeaderDb)
|
||||||
|
}
|
||||||
|
_ <- {
|
||||||
|
val hdAccount =
|
||||||
|
HDAccount(HDCoin(HDPurposes.Legacy, HDCoinType.Bitcoin), 0)
|
||||||
|
val xpub = CryptoGenerators.extPublicKey.sample.get
|
||||||
|
val account = AccountDb(xpub, hdAccount)
|
||||||
|
accountDAO.create(account)
|
||||||
|
}
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
_ <- writeF
|
||||||
|
nodeTables <- NodeDbManagement.listTables(bhDAO.database)
|
||||||
|
walletTables <- WalletDbManagement.listTables(accountDAO.database)
|
||||||
|
} yield {
|
||||||
|
def hasTable(tables: Seq[SQLiteTableInfo], dao: CRUD[_, _]): Boolean =
|
||||||
|
tables.exists(_.name == dao.table.baseTableRow.tableName)
|
||||||
|
|
||||||
|
assert(hasTable(walletTables, accountDAO))
|
||||||
|
assert(!hasTable(walletTables, bhDAO))
|
||||||
|
|
||||||
|
assert(hasTable(nodeTables, bhDAO))
|
||||||
|
assert(!hasTable(nodeTables, accountDAO))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,15 +19,21 @@ import java.nio.file.Paths
|
||||||
import org.bitcoins.wallet.ReadMnemonicError.DecryptionError
|
import org.bitcoins.wallet.ReadMnemonicError.DecryptionError
|
||||||
import java.{util => ju}
|
import java.{util => ju}
|
||||||
import org.bitcoins.wallet.ReadMnemonicError.JsonParsingError
|
import org.bitcoins.wallet.ReadMnemonicError.JsonParsingError
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig._
|
||||||
|
import scala.concurrent.Await
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class WalletStorageTest
|
class WalletStorageTest
|
||||||
extends BitcoinSWalletTest
|
extends BitcoinSWalletTest
|
||||||
with BeforeAndAfterEach
|
with BeforeAndAfterEach
|
||||||
with EmptyFixture {
|
with EmptyFixture {
|
||||||
|
|
||||||
val datadir = appConfig.datadir
|
val datadir = config.walletConf.datadir
|
||||||
|
|
||||||
override def beforeEach(): Unit = {
|
override def beforeEach(): Unit = {
|
||||||
|
// make sure datadir is created for reading/writing mnemonics
|
||||||
|
Await.result(config.walletConf.initialize(), 5.seconds)
|
||||||
|
|
||||||
Files
|
Files
|
||||||
.walk(datadir)
|
.walk(datadir)
|
||||||
.iterator()
|
.iterator()
|
||||||
|
|
|
@ -6,10 +6,15 @@ import org.bitcoins.wallet.util.BitcoinSWalletTest
|
||||||
import org.scalatest._
|
import org.scalatest._
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
trait AccountDAOFixture extends fixture.AsyncFlatSpec with BitcoinSWalletTest {
|
trait AccountDAOFixture extends fixture.AsyncFlatSpec with BitcoinSWalletTest {
|
||||||
override final type FixtureParam = AccountDAO
|
override final type FixtureParam = AccountDAO
|
||||||
|
|
||||||
|
// to get around the config in `BitcoinSWalletTest` not resolving
|
||||||
|
// as an AppConfig
|
||||||
|
private implicit val walletConfig: WalletAppConfig = config.walletConf
|
||||||
|
|
||||||
override final def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
override final def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||||
makeDependentFixture(createAccountTable, dropAccountTable)(test)
|
makeDependentFixture(createAccountTable, dropAccountTable)(test)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.bitcoins.wallet.db.WalletDbManagement
|
||||||
import org.bitcoins.wallet.models.{AccountDAO, AddressDAO}
|
import org.bitcoins.wallet.models.{AccountDAO, AddressDAO}
|
||||||
import org.bitcoins.wallet.util.BitcoinSWalletTest
|
import org.bitcoins.wallet.util.BitcoinSWalletTest
|
||||||
import org.scalatest._
|
import org.scalatest._
|
||||||
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This fixture has a tuple of DAOs, because
|
* This fixture has a tuple of DAOs, because
|
||||||
|
@ -18,6 +19,10 @@ trait AddressDAOFixture extends fixture.AsyncFlatSpec with BitcoinSWalletTest {
|
||||||
override final def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
override final def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||||
makeDependentFixture(createTables, dropTables)(test)
|
makeDependentFixture(createTables, dropTables)(test)
|
||||||
|
|
||||||
|
// to get around the config in `BitcoinSWalletTest` not resolving
|
||||||
|
// as an AppConfig
|
||||||
|
private implicit val walletConfig: WalletAppConfig = config.walletConf
|
||||||
|
|
||||||
private def dropTables(daos: FixtureParam): Future[Unit] = {
|
private def dropTables(daos: FixtureParam): Future[Unit] = {
|
||||||
val (account, address) = daos
|
val (account, address) = daos
|
||||||
val dropAccountF = WalletDbManagement.dropTable(account.table)
|
val dropAccountF = WalletDbManagement.dropTable(account.table)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import slick.jdbc.SQLiteProfile.api._
|
||||||
|
|
||||||
import scala.language.reflectiveCalls
|
import scala.language.reflectiveCalls
|
||||||
import scala.concurrent.{Await, Future}
|
import scala.concurrent.{Await, Future}
|
||||||
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
private[fixtures] trait DAOFixture
|
private[fixtures] trait DAOFixture
|
||||||
extends fixture.AsyncFlatSpec
|
extends fixture.AsyncFlatSpec
|
||||||
|
@ -17,6 +18,10 @@ private[fixtures] trait DAOFixture
|
||||||
private[fixtures] val daoAccumulator =
|
private[fixtures] val daoAccumulator =
|
||||||
Vector.newBuilder[HasTable]
|
Vector.newBuilder[HasTable]
|
||||||
|
|
||||||
|
// to get around the config in `BitcoinSWalletTest` not resolving
|
||||||
|
// as an AppConfig
|
||||||
|
private implicit val walletConfig: WalletAppConfig = config.walletConf
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
val tables = daoAccumulator.result()
|
val tables = daoAccumulator.result()
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.bitcoins.wallet.util.BitcoinSWalletTest
|
||||||
import org.scalatest._
|
import org.scalatest._
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
trait UtxoDAOFixture extends fixture.AsyncFlatSpec with BitcoinSWalletTest {
|
trait UtxoDAOFixture extends fixture.AsyncFlatSpec with BitcoinSWalletTest {
|
||||||
|
|
||||||
|
@ -14,6 +15,10 @@ trait UtxoDAOFixture extends fixture.AsyncFlatSpec with BitcoinSWalletTest {
|
||||||
override final def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
override final def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||||
makeDependentFixture(createUtxoTable, dropUtxoTable)(test)
|
makeDependentFixture(createUtxoTable, dropUtxoTable)(test)
|
||||||
|
|
||||||
|
// to get around the config in `BitcoinSWalletTest` not resolving
|
||||||
|
// as an AppConfig
|
||||||
|
private implicit val walletConfig: WalletAppConfig = config.walletConf
|
||||||
|
|
||||||
private def dropUtxoTable(utxoDAO: FixtureParam): Future[Unit] = {
|
private def dropUtxoTable(utxoDAO: FixtureParam): Future[Unit] = {
|
||||||
WalletDbManagement.dropTable(utxoDAO.table)
|
WalletDbManagement.dropTable(utxoDAO.table)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ import org.scalatest._
|
||||||
import scala.concurrent.duration.{DurationInt, FiniteDuration}
|
import scala.concurrent.duration.{DurationInt, FiniteDuration}
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.db.AppConfig
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig
|
||||||
|
import org.bitcoins.testkit.BitcoinSAppConfig._
|
||||||
|
|
||||||
trait BitcoinSWalletTest
|
trait BitcoinSWalletTest
|
||||||
extends fixture.AsyncFlatSpec
|
extends fixture.AsyncFlatSpec
|
||||||
|
@ -31,7 +33,10 @@ trait BitcoinSWalletTest
|
||||||
implicit val ec: ExecutionContext = actorSystem.dispatcher
|
implicit val ec: ExecutionContext = actorSystem.dispatcher
|
||||||
|
|
||||||
protected lazy val chainParams: ChainParams = WalletTestUtil.chainParams
|
protected lazy val chainParams: ChainParams = WalletTestUtil.chainParams
|
||||||
protected implicit lazy val appConfig: WalletAppConfig = WalletAppConfig()
|
|
||||||
|
/** Wallet config with data directory set to user temp directory */
|
||||||
|
implicit protected lazy val config: BitcoinSAppConfig =
|
||||||
|
BitcoinSAppConfig.getTestConfig()
|
||||||
|
|
||||||
/** Timeout for async operations */
|
/** Timeout for async operations */
|
||||||
protected val timeout: FiniteDuration = 10.seconds
|
protected val timeout: FiniteDuration = 10.seconds
|
||||||
|
@ -43,19 +48,24 @@ trait BitcoinSWalletTest
|
||||||
}
|
}
|
||||||
|
|
||||||
override def beforeAll(): Unit = {
|
override def beforeAll(): Unit = {
|
||||||
AppConfig.throwIfDefaultDatadir(appConfig)
|
AppConfig.throwIfDefaultDatadir(config.walletConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
def destroyWallet(wallet: UnlockedWalletApi): Future[Unit] =
|
def destroyWallet(wallet: UnlockedWalletApi): Future[Unit] = {
|
||||||
WalletDbManagement.dropAll().map(_ => ())
|
WalletDbManagement
|
||||||
|
.dropAll()(config = config.walletConf, ec = implicitly[ExecutionContext])
|
||||||
|
.map(_ => ())
|
||||||
|
}
|
||||||
|
|
||||||
def createNewWallet(): Future[UnlockedWalletApi] = {
|
def createNewWallet(): Future[UnlockedWalletApi] = {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_ <- WalletDbManagement.createAll()
|
_ <- config.initialize()
|
||||||
wallet <- Wallet.initialize().map {
|
wallet <- Wallet.initialize().map {
|
||||||
case InitializeWalletSuccess(wallet) => wallet
|
case InitializeWalletSuccess(wallet) => wallet
|
||||||
case err: InitializeWalletError => fail(err)
|
case err: InitializeWalletError =>
|
||||||
|
logger.error(s"Could not initialize wallet: $err")
|
||||||
|
fail(err)
|
||||||
}
|
}
|
||||||
} yield wallet
|
} yield wallet
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
bitcoin-s {
|
|
||||||
database {
|
|
||||||
name = "walletdb.sqlite"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,13 +3,13 @@ package org.bitcoins.wallet
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
import org.bitcoins.core.crypto._
|
import org.bitcoins.core.crypto._
|
||||||
import org.bitcoins.core.config._
|
import org.bitcoins.core.config._
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
private[wallet] object HDUtil {
|
private[wallet] object HDUtil {
|
||||||
|
|
||||||
/** Gets the xpriv version required for the given HD purpose */
|
/** Gets the xpriv version required for the given HD purpose */
|
||||||
def getXprivVersion(hdPurpose: HDPurpose)(
|
def getXprivVersion(hdPurpose: HDPurpose)(
|
||||||
implicit config: AppConfig): ExtKeyPrivVersion = {
|
implicit config: WalletAppConfig): ExtKeyPrivVersion = {
|
||||||
import config.network
|
import config.network
|
||||||
import org.bitcoins.core.hd.HDPurposes._
|
import org.bitcoins.core.hd.HDPurposes._
|
||||||
import ExtKeyVersion._
|
import ExtKeyVersion._
|
||||||
|
@ -28,7 +28,7 @@ private[wallet] object HDUtil {
|
||||||
|
|
||||||
/** Gets the xpub version required for the given HD purpose */
|
/** Gets the xpub version required for the given HD purpose */
|
||||||
def getXpubVersion(hdPurpose: HDPurpose)(
|
def getXpubVersion(hdPurpose: HDPurpose)(
|
||||||
implicit config: AppConfig): ExtKeyPubVersion = {
|
implicit config: WalletAppConfig): ExtKeyPubVersion = {
|
||||||
import config.network
|
import config.network
|
||||||
import org.bitcoins.core.hd.HDPurposes._
|
import org.bitcoins.core.hd.HDPurposes._
|
||||||
import ExtKeyVersion._
|
import ExtKeyVersion._
|
||||||
|
|
|
@ -20,10 +20,10 @@ import org.bitcoins.wallet.models._
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.util.Success
|
import scala.util.Success
|
||||||
import scala.util.Failure
|
import scala.util.Failure
|
||||||
import org.bitcoins.db.AppConfig
|
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
import org.bitcoins.wallet.ReadMnemonicError.DecryptionError
|
import org.bitcoins.wallet.ReadMnemonicError.DecryptionError
|
||||||
import org.bitcoins.wallet.ReadMnemonicError.JsonParsingError
|
import org.bitcoins.wallet.ReadMnemonicError.JsonParsingError
|
||||||
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
abstract class LockedWallet extends LockedWalletApi with BitcoinSLogger {
|
abstract class LockedWallet extends LockedWalletApi with BitcoinSLogger {
|
||||||
|
|
||||||
|
@ -263,9 +263,11 @@ abstract class LockedWallet extends LockedWalletApi with BitcoinSLogger {
|
||||||
object LockedWallet {
|
object LockedWallet {
|
||||||
private case class LockedWalletImpl()(
|
private case class LockedWalletImpl()(
|
||||||
implicit val ec: ExecutionContext,
|
implicit val ec: ExecutionContext,
|
||||||
val walletConfig: AppConfig)
|
val walletConfig: WalletAppConfig)
|
||||||
extends LockedWallet
|
extends LockedWallet
|
||||||
|
|
||||||
def apply()(implicit ec: ExecutionContext, config: AppConfig): LockedWallet =
|
def apply()(
|
||||||
LockedWalletImpl()
|
implicit ec: ExecutionContext,
|
||||||
|
config: WalletAppConfig): LockedWallet = LockedWalletImpl()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import scodec.bits.BitVector
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
import scala.util.{Failure, Success, Try}
|
import scala.util.{Failure, Success, Try}
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
sealed abstract class Wallet
|
sealed abstract class Wallet
|
||||||
extends LockedWallet
|
extends LockedWallet
|
||||||
|
@ -108,7 +108,7 @@ object Wallet extends CreateWalletApi with BitcoinSLogger {
|
||||||
private case class WalletImpl(
|
private case class WalletImpl(
|
||||||
mnemonicCode: MnemonicCode
|
mnemonicCode: MnemonicCode
|
||||||
)(
|
)(
|
||||||
implicit override val walletConfig: AppConfig,
|
implicit override val walletConfig: WalletAppConfig,
|
||||||
override val ec: ExecutionContext)
|
override val ec: ExecutionContext)
|
||||||
extends Wallet {
|
extends Wallet {
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ object Wallet extends CreateWalletApi with BitcoinSLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply(mnemonicCode: MnemonicCode)(
|
def apply(mnemonicCode: MnemonicCode)(
|
||||||
implicit config: AppConfig,
|
implicit config: WalletAppConfig,
|
||||||
ec: ExecutionContext): Wallet =
|
ec: ExecutionContext): Wallet =
|
||||||
WalletImpl(mnemonicCode)
|
WalletImpl(mnemonicCode)
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ object Wallet extends CreateWalletApi with BitcoinSLogger {
|
||||||
|
|
||||||
// todo fix signature
|
// todo fix signature
|
||||||
override def initializeWithEntropy(entropy: BitVector)(
|
override def initializeWithEntropy(entropy: BitVector)(
|
||||||
implicit config: AppConfig,
|
implicit config: WalletAppConfig,
|
||||||
ec: ExecutionContext): Future[InitializeWalletResult] = {
|
ec: ExecutionContext): Future[InitializeWalletResult] = {
|
||||||
import org.bitcoins.core.util.EitherUtil.EitherOps._
|
import org.bitcoins.core.util.EitherUtil.EitherOps._
|
||||||
|
|
||||||
|
@ -182,6 +182,7 @@ object Wallet extends CreateWalletApi with BitcoinSLogger {
|
||||||
logger.debug(s"Saved encrypted wallet mnemonic to $mnemonicPath")
|
logger.debug(s"Saved encrypted wallet mnemonic to $mnemonicPath")
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
_ <- config.initialize()
|
||||||
_ <- wallet.accountDAO
|
_ <- wallet.accountDAO
|
||||||
.create(accountDb)
|
.create(accountDb)
|
||||||
.map(_ => logger.trace(s"Saved account to DB"))
|
.map(_ => logger.trace(s"Saved account to DB"))
|
||||||
|
|
|
@ -13,7 +13,7 @@ import scala.util.Success
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
// what do we do if seed exists? error if they aren't equal?
|
// what do we do if seed exists? error if they aren't equal?
|
||||||
object WalletStorage extends BitcoinSLogger {
|
object WalletStorage extends BitcoinSLogger {
|
||||||
|
@ -35,7 +35,7 @@ object WalletStorage extends BitcoinSLogger {
|
||||||
* the file name.
|
* the file name.
|
||||||
*/
|
*/
|
||||||
def writeMnemonicToDisk(mnemonic: EncryptedMnemonic)(
|
def writeMnemonicToDisk(mnemonic: EncryptedMnemonic)(
|
||||||
implicit config: AppConfig): Path = {
|
implicit config: WalletAppConfig): Path = {
|
||||||
import mnemonic.{value => encrypted}
|
import mnemonic.{value => encrypted}
|
||||||
|
|
||||||
val jsObject = {
|
val jsObject = {
|
||||||
|
@ -98,7 +98,7 @@ object WalletStorage extends BitcoinSLogger {
|
||||||
* performing no decryption
|
* performing no decryption
|
||||||
*/
|
*/
|
||||||
private def readEncryptedMnemonicFromDisk()(
|
private def readEncryptedMnemonicFromDisk()(
|
||||||
implicit config: AppConfig): Either[
|
implicit config: WalletAppConfig): Either[
|
||||||
ReadMnemonicError,
|
ReadMnemonicError,
|
||||||
EncryptedMnemonic] = {
|
EncryptedMnemonic] = {
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ object WalletStorage extends BitcoinSLogger {
|
||||||
*/
|
*/
|
||||||
def decryptMnemonicFromDisk(passphrase: AesPassword)(
|
def decryptMnemonicFromDisk(passphrase: AesPassword)(
|
||||||
implicit
|
implicit
|
||||||
config: AppConfig): ReadMnemonicResult = {
|
config: WalletAppConfig): ReadMnemonicResult = {
|
||||||
val encryptedEither = readEncryptedMnemonicFromDisk()
|
val encryptedEither = readEncryptedMnemonicFromDisk()
|
||||||
|
|
||||||
import org.bitcoins.core.util.EitherUtil.EitherOps._
|
import org.bitcoins.core.util.EitherUtil.EitherOps._
|
||||||
|
|
|
@ -4,7 +4,7 @@ import org.bitcoins.core.crypto.MnemonicCode
|
||||||
import scodec.bits.BitVector
|
import scodec.bits.BitVector
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @define initialize
|
* @define initialize
|
||||||
|
@ -25,7 +25,7 @@ trait CreateWalletApi {
|
||||||
|
|
||||||
private def initializeInternal()(
|
private def initializeInternal()(
|
||||||
implicit executionContext: ExecutionContext,
|
implicit executionContext: ExecutionContext,
|
||||||
appConfig: AppConfig): Future[InitializeWalletResult] =
|
config: WalletAppConfig): Future[InitializeWalletResult] =
|
||||||
initializeWithEntropy(entropy = MnemonicCode.getEntropy256Bits)
|
initializeWithEntropy(entropy = MnemonicCode.getEntropy256Bits)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,19 +33,19 @@ trait CreateWalletApi {
|
||||||
*/
|
*/
|
||||||
final def initialize()(
|
final def initialize()(
|
||||||
implicit executionContext: ExecutionContext,
|
implicit executionContext: ExecutionContext,
|
||||||
appConfig: AppConfig): Future[InitializeWalletResult] =
|
config: WalletAppConfig): Future[InitializeWalletResult] =
|
||||||
initializeInternal()
|
initializeInternal()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* $initializeWithEnt
|
* $initializeWithEnt
|
||||||
*/
|
*/
|
||||||
def initializeWithEntropy(entropy: BitVector)(
|
def initializeWithEntropy(entropy: BitVector)(
|
||||||
implicit config: AppConfig,
|
implicit config: WalletAppConfig,
|
||||||
executionContext: ExecutionContext): Future[InitializeWalletResult]
|
executionContext: ExecutionContext): Future[InitializeWalletResult]
|
||||||
|
|
||||||
// todo: scaladoc
|
// todo: scaladoc
|
||||||
final def initializeWithMnemonic(mnemonicCode: MnemonicCode)(
|
final def initializeWithMnemonic(mnemonicCode: MnemonicCode)(
|
||||||
implicit config: AppConfig,
|
implicit config: WalletAppConfig,
|
||||||
executionContext: ExecutionContext): Future[InitializeWalletResult] = {
|
executionContext: ExecutionContext): Future[InitializeWalletResult] = {
|
||||||
val entropy = mnemonicCode.toEntropy
|
val entropy = mnemonicCode.toEntropy
|
||||||
initializeWithEntropy(entropy)
|
initializeWithEntropy(entropy)
|
||||||
|
|
|
@ -14,7 +14,7 @@ import org.bitcoins.wallet.models.{AccountDb, AddressDb, UTXOSpendingInfoDb}
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API for the wallet project.
|
* API for the wallet project.
|
||||||
|
@ -25,7 +25,7 @@ import org.bitcoins.db.AppConfig
|
||||||
*/
|
*/
|
||||||
sealed trait WalletApi {
|
sealed trait WalletApi {
|
||||||
|
|
||||||
implicit val walletConfig: AppConfig
|
implicit val walletConfig: WalletAppConfig
|
||||||
implicit val ec: ExecutionContext
|
implicit val ec: ExecutionContext
|
||||||
|
|
||||||
def chainParams: ChainParams = walletConfig.chain
|
def chainParams: ChainParams = walletConfig.chain
|
||||||
|
|
|
@ -2,11 +2,37 @@ package org.bitcoins.wallet.config
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import org.bitcoins.db.AppConfig
|
import org.bitcoins.db.AppConfig
|
||||||
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
import org.bitcoins.wallet.db.WalletDbManagement
|
||||||
|
import scala.util.Failure
|
||||||
|
import scala.util.Success
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
case class WalletAppConfig(conf: Config*) extends AppConfig {
|
case class WalletAppConfig(conf: Config*) extends AppConfig {
|
||||||
override val configOverrides: List[Config] = conf.toList
|
override val configOverrides: List[Config] = conf.toList
|
||||||
override def moduleConfigName: String = "wallet.conf"
|
override def moduleName: String = "wallet"
|
||||||
override type ConfigType = WalletAppConfig
|
override type ConfigType = WalletAppConfig
|
||||||
override def newConfigOfType(configs: List[Config]): WalletAppConfig =
|
override def newConfigOfType(configs: List[Config]): WalletAppConfig =
|
||||||
WalletAppConfig(configs: _*)
|
WalletAppConfig(configs: _*)
|
||||||
|
|
||||||
|
override def initialize()(implicit ec: ExecutionContext): Future[Unit] = {
|
||||||
|
logger.debug(s"Initializing wallet setup")
|
||||||
|
|
||||||
|
if (Files.notExists(datadir)) {
|
||||||
|
Files.createDirectories(datadir)
|
||||||
|
}
|
||||||
|
|
||||||
|
val initF = {
|
||||||
|
WalletDbManagement.createAll()(this, ec)
|
||||||
|
}
|
||||||
|
initF.onComplete {
|
||||||
|
case Failure(exception) =>
|
||||||
|
logger.error(s"Error on wallet setup: ${exception.getMessage}")
|
||||||
|
case Success(_) =>
|
||||||
|
logger.debug(s"Initializing wallet setup: done")
|
||||||
|
}
|
||||||
|
|
||||||
|
initF
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,15 @@ import slick.jdbc.SQLiteProfile.api._
|
||||||
import scala.concurrent.{Future}
|
import scala.concurrent.{Future}
|
||||||
import org.bitcoins.db.CRUD
|
import org.bitcoins.db.CRUD
|
||||||
import org.bitcoins.db.SlickUtil
|
import org.bitcoins.db.SlickUtil
|
||||||
import org.bitcoins.db.AppConfig
|
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
|
|
||||||
case class AccountDAO()(implicit val ec: ExecutionContext)
|
case class AccountDAO()(
|
||||||
|
implicit val ec: ExecutionContext,
|
||||||
|
val appConfig: WalletAppConfig)
|
||||||
extends CRUD[AccountDb, (HDCoin, Int)] {
|
extends CRUD[AccountDb, (HDCoin, Int)] {
|
||||||
|
|
||||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||||
|
|
||||||
override def appConfig: WalletAppConfig = WalletAppConfig()
|
|
||||||
|
|
||||||
override val table: TableQuery[AccountTable] = TableQuery[AccountTable]
|
override val table: TableQuery[AccountTable] = TableQuery[AccountTable]
|
||||||
|
|
||||||
override def createAll(ts: Vector[AccountDb]): Future[Vector[AccountDb]] =
|
override def createAll(ts: Vector[AccountDb]): Future[Vector[AccountDb]] =
|
||||||
|
|
|
@ -9,16 +9,14 @@ import slick.sql.SqlAction
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
import org.bitcoins.core.hd.HDChainType
|
import org.bitcoins.core.hd.HDChainType
|
||||||
import org.bitcoins.db.AppConfig
|
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
case class AddressDAO()(
|
case class AddressDAO()(
|
||||||
implicit val ec: ExecutionContext
|
implicit val ec: ExecutionContext,
|
||||||
|
val appConfig: WalletAppConfig
|
||||||
) extends CRUD[AddressDb, BitcoinAddress] {
|
) extends CRUD[AddressDb, BitcoinAddress] {
|
||||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||||
|
|
||||||
override def appConfig: WalletAppConfig = WalletAppConfig()
|
|
||||||
|
|
||||||
override val table: TableQuery[AddressTable] = TableQuery[AddressTable]
|
override val table: TableQuery[AddressTable] = TableQuery[AddressTable]
|
||||||
|
|
||||||
override def createAll(ts: Vector[AddressDb]): Future[Vector[AddressDb]] =
|
override def createAll(ts: Vector[AddressDb]): Future[Vector[AddressDb]] =
|
||||||
|
|
|
@ -6,13 +6,12 @@ import slick.jdbc.SQLiteProfile.api._
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
import org.bitcoins.db.AppConfig
|
|
||||||
|
|
||||||
case class UTXOSpendingInfoDAO()(implicit val ec: ExecutionContext)
|
case class UTXOSpendingInfoDAO()(
|
||||||
|
implicit val ec: ExecutionContext,
|
||||||
|
val appConfig: WalletAppConfig)
|
||||||
extends CRUDAutoInc[UTXOSpendingInfoDb] {
|
extends CRUDAutoInc[UTXOSpendingInfoDb] {
|
||||||
|
|
||||||
override def appConfig: WalletAppConfig = WalletAppConfig()
|
|
||||||
|
|
||||||
/** The table inside our database we are inserting into */
|
/** The table inside our database we are inserting into */
|
||||||
override val table = TableQuery[UTXOSpendingInfoTable]
|
override val table = TableQuery[UTXOSpendingInfoTable]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue