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:
Torkel Rogstad 2019-06-05 17:48:15 +02:00 committed by Chris Stewart
parent 46db2a57b7
commit ceec55d458
50 changed files with 589 additions and 244 deletions

View file

@ -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,

View file

@ -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

View file

@ -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"

View file

@ -1,5 +0,0 @@
bitcoin-s {
database {
name = "chaindb.sqlite"
}
}

View file

@ -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

View file

@ -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,

View file

@ -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
}
} }
} }
} }

View file

@ -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)
} }
} }

View file

@ -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._

View file

@ -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}
} }
} }
} }

View file

@ -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
} }

View file

@ -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)

View file

@ -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(

View file

@ -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)

View file

@ -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)

View file

@ -1,5 +0,0 @@
bitcoin-s {
database {
name = "nodedb.sqlite"
}
}

View file

@ -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]

View file

@ -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 */

View file

@ -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
}
} }

View file

@ -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
} }

View file

@ -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]
} }

View file

@ -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 =>

View file

@ -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))

View file

@ -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(

View file

@ -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,

View file

@ -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)

View file

@ -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,

View file

@ -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
}
}
}

View file

@ -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: _*)
}
}

View file

@ -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)
} }

View file

@ -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)
} }

View file

@ -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

View file

@ -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))
}
}
}

View file

@ -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()

View file

@ -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)

View file

@ -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)

View file

@ -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()

View file

@ -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)
} }

View file

@ -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
} }

View file

@ -1,5 +0,0 @@
bitcoin-s {
database {
name = "walletdb.sqlite"
}
}

View file

@ -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._

View file

@ -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()
} }

View file

@ -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"))

View file

@ -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._

View file

@ -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)

View file

@ -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

View file

@ -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
}
} }

View file

@ -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]] =

View file

@ -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]] =

View file

@ -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]