2020 06 12 mv to appconfig (#1553)

* Move hasWallet() to WalletAppConfig

* Move helper methods to create data dstructures in a project to their respective AppConfig file
This commit is contained in:
Chris Stewart 2020-06-16 12:49:32 -05:00 committed by GitHub
parent 3912a02f53
commit dec503f561
4 changed files with 163 additions and 131 deletions

View File

@ -1,7 +1,6 @@
package org.bitcoins.server
import java.net.InetSocketAddress
import java.nio.file.{Files, Paths}
import java.nio.file.Paths
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
@ -14,22 +13,24 @@ import org.bitcoins.chain.models.{
CompactFilterHeaderDAO
}
import org.bitcoins.core.Core
import org.bitcoins.core.api.{ChainQueryApi, FeeRateApi}
import org.bitcoins.core.util.{BitcoinSLogger, FutureUtil}
import org.bitcoins.core.util.{BitcoinSLogger, FutureUtil, NetworkUtil}
import org.bitcoins.db.AppConfig
import org.bitcoins.feeprovider.BitcoinerLiveFeeRateProvider
import org.bitcoins.keymanager.KeyManagerInitializeError
import org.bitcoins.keymanager.bip39.{BIP39KeyManager, BIP39LockedKeyManager}
import org.bitcoins.node.config.NodeAppConfig
import org.bitcoins.node.models.Peer
import org.bitcoins.node._
import org.bitcoins.wallet.Wallet
import org.bitcoins.node.{
Node,
NodeCallbacks,
OnBlockHeadersReceived,
OnBlockReceived,
OnCompactFiltersReceived,
OnTxReceived,
SpvNode
}
import org.bitcoins.wallet.api._
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.models.AccountDAO
import scala.concurrent.duration._
import scala.concurrent.{Await, ExecutionContext, Future, Promise}
import scala.concurrent.{ExecutionContext, Future, Promise}
object Main extends App with BitcoinSLogger {
@ -69,7 +70,8 @@ object Main extends App with BitcoinSLogger {
implicit val chainConf: ChainAppConfig = conf.chainConf
val peerSocket =
parseInetSocketAddress(nodeConf.peers.head, nodeConf.network.port)
NetworkUtil.parseInetSocketAddress(nodeConf.peers.head,
nodeConf.network.port)
val peer = Peer.fromSocket(peerSocket)
val bip39PasswordOpt = None //todo need to prompt user for this
@ -83,7 +85,7 @@ object Main extends App with BitcoinSLogger {
//get a node that isn't started
val uninitializedNodeF = configInitializedF.flatMap { _ =>
createNode(peer)(nodeConf, chainConf, system)
nodeConf.createNode(peer)(chainConf, system)
}
//get our wallet
@ -91,10 +93,10 @@ object Main extends App with BitcoinSLogger {
_ <- configInitializedF
uninitializedNode <- uninitializedNodeF
chainApi <- chainApiF
wallet <- createWallet(uninitializedNode,
chainApi,
BitcoinerLiveFeeRateProvider(60),
bip39PasswordOpt)
wallet <- walletConf.createWallet(uninitializedNode,
chainApi,
BitcoinerLiveFeeRateProvider(60),
bip39PasswordOpt)
} yield wallet
//add callbacks to our unitialized node
@ -156,83 +158,6 @@ object Main extends App with BitcoinSLogger {
//start everything!
runMain()
/** Checks if the user already has a wallet */
private def hasWallet()(
implicit walletConf: WalletAppConfig,
ec: ExecutionContext): Future[Boolean] = {
val walletDB = walletConf.dbPath resolve walletConf.dbName
val hdCoin = walletConf.defaultAccount.coin
if (Files.exists(walletDB) && walletConf.seedExists()) {
AccountDAO().read((hdCoin, 0)).map(_.isDefined)
} else {
Future.successful(false)
}
}
private def createNode(peer: Peer)(
implicit nodeConf: NodeAppConfig,
chainConf: ChainAppConfig,
system: ActorSystem): Future[Node] = {
if (nodeConf.isSPVEnabled) {
Future.successful(SpvNode(peer, nodeConf, chainConf, system))
} else if (nodeConf.isNeutrinoEnabled) {
Future.successful(NeutrinoNode(peer, nodeConf, chainConf, system))
} else {
Future.failed(
new RuntimeException("Neither Neutrino nor SPV mode is enabled."))
}
}
private def createWallet(
nodeApi: Node,
chainQueryApi: ChainQueryApi,
feeRateApi: FeeRateApi,
bip39PasswordOpt: Option[String])(
implicit walletConf: WalletAppConfig,
system: ActorSystem): Future[WalletApi] = {
import system.dispatcher
hasWallet().flatMap { walletExists =>
if (walletExists) {
logger.info(s"Using pre-existing wallet")
// TODO change me when we implement proper password handling
BIP39LockedKeyManager.unlock(BIP39KeyManager.badPassphrase,
bip39PasswordOpt,
walletConf.kmParams) match {
case Right(km) =>
val wallet =
Wallet(km, nodeApi, chainQueryApi, feeRateApi, km.creationTime)
Future.successful(wallet)
case Left(err) =>
error(err)
}
} else {
logger.info(s"Initializing key manager")
val bip39PasswordOpt = None
val keyManagerE: Either[KeyManagerInitializeError, BIP39KeyManager] =
BIP39KeyManager.initialize(kmParams = walletConf.kmParams,
bip39PasswordOpt = bip39PasswordOpt)
val keyManager = keyManagerE match {
case Right(keyManager) => keyManager
case Left(err) =>
error(err)
}
logger.info(s"Creating new wallet")
val unInitializedWallet =
Wallet(keyManager,
nodeApi,
chainQueryApi,
feeRateApi,
keyManager.creationTime)
Wallet.initialize(wallet = unInitializedWallet,
bip39PasswordOpt = bip39PasswordOpt)
}
}
}
private def createCallbacks(wallet: WalletApi)(
implicit nodeConf: NodeAppConfig,
ec: ExecutionContext): Future[NodeCallbacks] = {
@ -287,41 +212,6 @@ object Main extends App with BitcoinSLogger {
}
}
/** Log the given message, shut down the actor system and quit. */
private def error(message: Any)(implicit system: ActorSystem): Nothing = {
logger.error(s"FATAL: $message")
logger.error(s"Shutting down actor system")
Await.result(system.terminate(), 10.seconds)
logger.error("Actor system terminated")
logger.error(s"Exiting")
sys.error(message.toString())
}
private def parseInetSocketAddress(
address: String,
defaultPort: Int): InetSocketAddress = {
def parsePort(port: String): Int = {
lazy val errorMsg = s"Invalid peer port: $address"
try {
val res = port.toInt
if (res < 0 || res > 0xffff) {
throw new RuntimeException(errorMsg)
}
res
} catch {
case _: NumberFormatException =>
throw new RuntimeException(errorMsg)
}
}
address.split(":") match {
case Array(host) => new InetSocketAddress(host, defaultPort)
case Array(host, port) => new InetSocketAddress(host, parsePort(port))
case _ => throw new RuntimeException(s"Invalid peer address: $address")
}
}
/** This is needed for migrations V2/V3 on the chain project to re-calculate the total work for the chain */
private def runChainWorkCalc()(
implicit chainAppConfig: ChainAppConfig,

View File

@ -0,0 +1,35 @@
package org.bitcoins.core.util
import java.net.InetSocketAddress
abstract class NetworkUtil {
private def parsePort(port: String): Int = {
lazy val errorMsg = s"Invalid peer port: $port"
try {
val res = port.toInt
if (res < 0 || res > 0xffff) {
throw new RuntimeException(errorMsg)
}
res
} catch {
case _: NumberFormatException =>
throw new RuntimeException(errorMsg)
}
}
/** Parses a string that looks like this to [[java.net.InetSocketAddress]]
* "neutrino.testnet3.suredbits.com:18333"
* */
def parseInetSocketAddress(
address: String,
defaultPort: Int): InetSocketAddress = {
address.split(":") match {
case Array(host) => new InetSocketAddress(host, defaultPort)
case Array(host, port) => new InetSocketAddress(host, parsePort(port))
case _ => throw new RuntimeException(s"Invalid peer address: $address")
}
}
}
object NetworkUtil extends NetworkUtil

View File

@ -2,10 +2,14 @@ package org.bitcoins.node.config
import java.nio.file.Path
import akka.actor.ActorSystem
import com.typesafe.config.Config
import org.bitcoins.chain.config.ChainAppConfig
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.db.{AppConfig, AppConfigFactory, JdbcProfileComponent}
import org.bitcoins.node.{NeutrinoNode, Node, SpvNode}
import org.bitcoins.node.db.NodeDbManagement
import org.bitcoins.node.models.Peer
import scala.concurrent.{ExecutionContext, Future}
@ -68,6 +72,11 @@ case class NodeAppConfig(
0.until(list.size())
.foldLeft(Vector.empty[String])((acc, i) => acc :+ list.get(i))
}
/** Creates either a neutrino node or a spv node based on the [[NodeAppConfig]] given */
def createNode(peer: Peer)(chainConf: ChainAppConfig, system: ActorSystem): Future[Node] = {
NodeAppConfig.createNode(peer)(this,chainConf,system)
}
}
object NodeAppConfig extends AppConfigFactory[NodeAppConfig] {
@ -81,4 +90,15 @@ object NodeAppConfig extends AppConfigFactory[NodeAppConfig] {
useLogbackConf,
confs: _*)
/** Creates either a neutrino node or a spv node based on the [[NodeAppConfig]] given */
def createNode(peer: Peer)(implicit nodeConf: NodeAppConfig, chainConf: ChainAppConfig, system: ActorSystem): Future[Node] = {
if (nodeConf.isSPVEnabled) {
Future.successful(SpvNode(peer, nodeConf, chainConf, system))
} else if (nodeConf.isNeutrinoEnabled) {
Future.successful(NeutrinoNode(peer, nodeConf, chainConf, system))
} else {
Future.failed(
new RuntimeException("Neither Neutrino nor SPV mode is enabled."))
}
}
}

View File

@ -4,11 +4,20 @@ import java.nio.file.{Files, Path}
import java.util.concurrent.TimeUnit
import com.typesafe.config.Config
import org.bitcoins.core.api.{ChainQueryApi, FeeRateApi, NodeApi}
import org.bitcoins.core.hd._
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.db.{AppConfig, AppConfigFactory, JdbcProfileComponent}
import org.bitcoins.keymanager.{KeyManagerParams, WalletStorage}
import org.bitcoins.keymanager.bip39.{BIP39KeyManager, BIP39LockedKeyManager}
import org.bitcoins.keymanager.{
KeyManagerInitializeError,
KeyManagerParams,
WalletStorage
}
import org.bitcoins.wallet.{Wallet, WalletLogger}
import org.bitcoins.wallet.api.WalletApi
import org.bitcoins.wallet.db.WalletDbManagement
import org.bitcoins.wallet.models.AccountDAO
import scala.concurrent.duration.{DurationInt, FiniteDuration}
import scala.concurrent.{ExecutionContext, Future}
@ -125,9 +134,38 @@ case class WalletAppConfig(
5.second
}
}
/** Checks if the following exist
* 1. A wallet exists
* 2. seed exists
* 3. The account exists */
def hasWallet()(implicit ec: ExecutionContext): Future[Boolean] = {
val walletDB = dbPath.resolve(dbName)
val hdCoin = defaultAccount.coin
if (Files.exists(walletDB) && seedExists()) {
AccountDAO()(ec, this).read((hdCoin, 0)).map(_.isDefined)
} else {
Future.successful(false)
}
}
/** Creates a wallet based on this [[WalletAppConfig]] */
def createWallet(
nodeApi: NodeApi,
chainQueryApi: ChainQueryApi,
feeRateApi: FeeRateApi,
bip39PasswordOpt: Option[String])(
implicit ec: ExecutionContext): Future[WalletApi] = {
WalletAppConfig.createWallet(nodeApi = nodeApi,
chainQueryApi = chainQueryApi,
feeRateApi = feeRateApi,
bip39PasswordOpt = bip39PasswordOpt)(this, ec)
}
}
object WalletAppConfig extends AppConfigFactory[WalletAppConfig] {
object WalletAppConfig
extends AppConfigFactory[WalletAppConfig]
with WalletLogger {
/** Constructs a wallet configuration from the default Bitcoin-S
* data directory and given list of configuration overrides.
@ -137,4 +175,53 @@ object WalletAppConfig extends AppConfigFactory[WalletAppConfig] {
useLogbackConf: Boolean,
confs: Vector[Config])(implicit ec: ExecutionContext): WalletAppConfig =
WalletAppConfig(datadir, useLogbackConf, confs: _*)
/** Creates a wallet based on the given [[WalletAppConfig]] */
def createWallet(
nodeApi: NodeApi,
chainQueryApi: ChainQueryApi,
feeRateApi: FeeRateApi,
bip39PasswordOpt: Option[String])(
implicit walletConf: WalletAppConfig,
ec: ExecutionContext): Future[WalletApi] = {
walletConf.hasWallet().flatMap { walletExists =>
if (walletExists) {
logger.info(s"Using pre-existing wallet")
// TODO change me when we implement proper password handling
BIP39LockedKeyManager.unlock(BIP39KeyManager.badPassphrase,
bip39PasswordOpt,
walletConf.kmParams) match {
case Right(km) =>
val wallet =
Wallet(km, nodeApi, chainQueryApi, feeRateApi, km.creationTime)
Future.successful(wallet)
case Left(err) =>
sys.error(s"Error initializing key manager, err=${err}")
}
} else {
logger.info(s"Initializing key manager")
val bip39PasswordOpt = None
val keyManagerE: Either[KeyManagerInitializeError, BIP39KeyManager] =
BIP39KeyManager.initialize(kmParams = walletConf.kmParams,
bip39PasswordOpt = bip39PasswordOpt)
val keyManager = keyManagerE match {
case Right(keyManager) => keyManager
case Left(err) =>
sys.error(s"Error initializing key manager, err=${err}")
}
logger.info(s"Creating new wallet")
val unInitializedWallet =
Wallet(keyManager,
nodeApi,
chainQueryApi,
feeRateApi,
keyManager.creationTime)
Wallet.initialize(wallet = unInitializedWallet,
bip39PasswordOpt = bip39PasswordOpt)
}
}
}
}