mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-22 06:31:55 +01:00
Add configurable logging to data directory (#640)
* Add logging to data directory In this commit we add the ability for the node, chain and wallet projects (+ the server) to log to the users data directory instead of whatever directory the binaries was launched from. This is inherently a bit more complicated than our previous setup, because we need to read the user directory before we can create loggers. As a result of this, some files/methods were moved around, so the relevant app config could be found in scope. We also introduce several logging categories that can be tuned individually through user configuration. These logggers are exposed both as traits that give a field `logger`, or as methods that return the required logger. * Add datadir configuration to AppConfig In this commit we add support for AppConfig to pick up the data directory configuration file. We also add a section to the contributing guide on how to tune logging levels. * Pass data directories explicitly for configuration
This commit is contained in:
parent
dd6c86dc14
commit
a76f61f97c
50 changed files with 716 additions and 241 deletions
|
@ -1,4 +1,3 @@
|
|||
akka {
|
||||
loggers = ["akka.event.slf4j.Slf4jLogger"]
|
||||
loglevel = "DEBUG"
|
||||
loglevel = "INFO"
|
||||
}
|
13
app/server/src/main/resources/logback.xml
Normal file
13
app/server/src/main/resources/logback.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<configuration>
|
||||
<!-- see how long statements took to execute by setting to DEBUG -->
|
||||
<logger name="slick.jdbc.JdbcBackend.benchmark" level="INFO"/>
|
||||
|
||||
<!-- see what statements are executed by setting to DEBUG -->
|
||||
<logger name="slick.jdbc.JdbcBackend.statement" level="INFO"/>
|
||||
|
||||
<!-- see what slick is compiling to in sql -->
|
||||
<logger name="slick.compiler" level="INFO"/>
|
||||
|
||||
<!-- see what's returned by Slick -->
|
||||
<logger name="slick.jdbc.StatementInvoker.result" level="INFO"/>
|
||||
</configuration>
|
|
@ -6,6 +6,8 @@ import org.bitcoins.node.config.NodeAppConfig
|
|||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.concurrent.Future
|
||||
import java.nio.file.Path
|
||||
import org.bitcoins.db.AppConfig
|
||||
|
||||
/**
|
||||
* A unified config class for all submodules of Bitcoin-S
|
||||
|
@ -13,11 +15,16 @@ import scala.concurrent.Future
|
|||
* in this case class' companion object an instance
|
||||
* of this class can be passed in anywhere a wallet,
|
||||
* chain or node config is required.
|
||||
*
|
||||
* @param directory The data directory of this app configuration
|
||||
* @param confs A sequence of optional configuration overrides
|
||||
*/
|
||||
case class BitcoinSAppConfig(private val confs: Config*) {
|
||||
val walletConf = WalletAppConfig(confs: _*)
|
||||
val nodeConf = NodeAppConfig(confs: _*)
|
||||
val chainConf = ChainAppConfig(confs: _*)
|
||||
case class BitcoinSAppConfig(
|
||||
private val directory: Path,
|
||||
private val confs: Config*) {
|
||||
val walletConf = WalletAppConfig(directory, confs: _*)
|
||||
val nodeConf = NodeAppConfig(directory, confs: _*)
|
||||
val chainConf = ChainAppConfig(directory, confs: _*)
|
||||
|
||||
/** Initializes the wallet, node and chain projects */
|
||||
def initialize()(implicit ec: ExecutionContext): Future[Unit] = {
|
||||
|
@ -44,6 +51,13 @@ case class BitcoinSAppConfig(private val confs: Config*) {
|
|||
* to be passed in wherever a specializes one is required
|
||||
*/
|
||||
object BitcoinSAppConfig {
|
||||
|
||||
/** Constructs an app configuration from the default Bitcoin-S
|
||||
* data directory and given list of configuration overrides.
|
||||
*/
|
||||
def fromDefaultDatadir(confs: Config*): BitcoinSAppConfig =
|
||||
BitcoinSAppConfig(AppConfig.DEFAULT_BITCOIN_S_DATADIR, confs: _*)
|
||||
|
||||
import scala.language.implicitConversions
|
||||
|
||||
/** Converts the given implicit config to a wallet config */
|
||||
|
|
|
@ -4,14 +4,12 @@ import akka.actor.ActorSystem
|
|||
import akka.http.scaladsl.server._
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.stream.ActorMaterializer
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.chain.api.ChainApi
|
||||
|
||||
import org.bitcoins.picklers._
|
||||
|
||||
case class ChainRoutes(chain: ChainApi)(implicit system: ActorSystem)
|
||||
extends BitcoinSLogger
|
||||
with ServerRoute {
|
||||
extends ServerRoute {
|
||||
implicit val materializer = ActorMaterializer()
|
||||
import system.dispatcher
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.bitcoins.server
|
|||
|
||||
import org.bitcoins.rpc.config.BitcoindInstance
|
||||
import org.bitcoins.node.models.Peer
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||
import akka.actor.ActorSystem
|
||||
import scala.concurrent.Await
|
||||
|
@ -17,7 +16,6 @@ import org.bitcoins.wallet.api.InitializeWalletSuccess
|
|||
import org.bitcoins.wallet.api.InitializeWalletError
|
||||
import org.bitcoins.node.SpvNode
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.wallet.api.UnlockedWalletApi
|
||||
import org.bitcoins.wallet.api.UnlockWalletSuccess
|
||||
|
@ -25,17 +23,19 @@ import org.bitcoins.wallet.api.UnlockWalletError
|
|||
import org.bitcoins.node.networking.peer.DataMessageHandler
|
||||
import org.bitcoins.node.SpvNodeCallbacks
|
||||
import org.bitcoins.wallet.WalletStorage
|
||||
import org.bitcoins.db.AppLoggers
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
|
||||
object Main
|
||||
extends App
|
||||
// TODO we want to log to user data directory
|
||||
// how do we do this?
|
||||
with BitcoinSLogger {
|
||||
object Main extends App {
|
||||
implicit val conf = {
|
||||
// val custom = ConfigFactory.parseString("bitcoin-s.network = testnet3")
|
||||
BitcoinSAppConfig()
|
||||
BitcoinSAppConfig.fromDefaultDatadir()
|
||||
}
|
||||
|
||||
private val logger = AppLoggers.getHttpLogger(
|
||||
conf.walletConf // doesn't matter which one we pass in
|
||||
)
|
||||
|
||||
implicit val walletConf: WalletAppConfig = conf.walletConf
|
||||
implicit val nodeConf: NodeAppConfig = conf.nodeConf
|
||||
implicit val chainConf: ChainAppConfig = conf.chainConf
|
||||
|
@ -113,7 +113,7 @@ object Main
|
|||
SpvNodeCallbacks(onTxReceived = Seq(onTX))
|
||||
}
|
||||
val blockheaderDAO = BlockHeaderDAO()
|
||||
val chain = ChainHandler(blockheaderDAO, conf)
|
||||
val chain = ChainHandler(blockheaderDAO)
|
||||
SpvNode(peer, chain, bloom, callbacks).start()
|
||||
}
|
||||
_ = logger.info(s"Starting SPV node sync")
|
||||
|
@ -123,7 +123,9 @@ object Main
|
|||
val walletRoutes = WalletRoutes(wallet, node)
|
||||
val nodeRoutes = NodeRoutes(node)
|
||||
val chainRoutes = ChainRoutes(node.chainApi)
|
||||
val server = Server(Seq(walletRoutes, nodeRoutes, chainRoutes))
|
||||
val server =
|
||||
Server(nodeConf, // could use either of configurations
|
||||
Seq(walletRoutes, nodeRoutes, chainRoutes))
|
||||
server.start()
|
||||
}
|
||||
} yield start
|
||||
|
|
|
@ -4,12 +4,10 @@ import akka.actor.ActorSystem
|
|||
import akka.http.scaladsl.server._
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.stream.ActorMaterializer
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.node.SpvNode
|
||||
|
||||
case class NodeRoutes(node: SpvNode)(implicit system: ActorSystem)
|
||||
extends BitcoinSLogger
|
||||
with ServerRoute {
|
||||
extends ServerRoute {
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
def handleCommand: PartialFunction[ServerCommand, StandardRoute] = {
|
||||
|
|
|
@ -4,7 +4,6 @@ import upickle.{default => up}
|
|||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl._
|
||||
import akka.stream.ActorMaterializer
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server._
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
|
@ -12,9 +11,14 @@ import akka.http.scaladsl.server.Directives._
|
|||
import de.heikoseeberger.akkahttpupickle.UpickleSupport._
|
||||
import akka.http.scaladsl.server.directives.DebuggingDirectives
|
||||
import akka.event.Logging
|
||||
import org.bitcoins.db.HttpLogger
|
||||
import org.bitcoins.db.AppConfig
|
||||
|
||||
case class Server(conf: AppConfig, handlers: Seq[ServerRoute])(
|
||||
implicit system: ActorSystem)
|
||||
extends HttpLogger {
|
||||
implicit private val config: AppConfig = conf
|
||||
|
||||
case class Server(handlers: Seq[ServerRoute])(implicit system: ActorSystem)
|
||||
extends BitcoinSLogger {
|
||||
implicit val materializer = ActorMaterializer()
|
||||
import system.dispatcher
|
||||
|
||||
|
@ -83,7 +87,7 @@ case class Server(handlers: Seq[ServerRoute])(implicit system: ActorSystem)
|
|||
}
|
||||
}
|
||||
|
||||
object Server extends BitcoinSLogger {
|
||||
object Server {
|
||||
|
||||
// TODO id parameter
|
||||
case class Response(
|
||||
|
|
|
@ -7,7 +7,6 @@ import akka.http.scaladsl.server._
|
|||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.stream.ActorMaterializer
|
||||
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.wallet.api.UnlockedWalletApi
|
||||
import org.bitcoins.core.wallet.fee.SatoshisPerByte
|
||||
|
@ -19,8 +18,7 @@ import scala.util.Success
|
|||
|
||||
case class WalletRoutes(wallet: UnlockedWalletApi, node: SpvNode)(
|
||||
implicit system: ActorSystem)
|
||||
extends BitcoinSLogger
|
||||
with ServerRoute {
|
||||
extends ServerRoute {
|
||||
import system.dispatcher
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
|
|
|
@ -7,9 +7,12 @@ import com.typesafe.config.ConfigFactory
|
|||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.config.MainNet
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import java.nio.file.Files
|
||||
import ch.qos.logback.classic.Level
|
||||
|
||||
class ChainAppConfigTest extends BitcoinSUnitTest {
|
||||
val config = ChainAppConfig()
|
||||
val tempDir = Files.createTempDirectory("bitcoin-s")
|
||||
val config = ChainAppConfig(directory = tempDir)
|
||||
|
||||
it must "be overridable" in {
|
||||
assert(config.network == RegTest)
|
||||
|
@ -30,4 +33,29 @@ class ChainAppConfigTest extends BitcoinSUnitTest {
|
|||
assert(overriden.network == MainNet)
|
||||
|
||||
}
|
||||
|
||||
it must "have user data directory configuration take precedence" in {
|
||||
|
||||
val tempDir = Files.createTempDirectory("bitcoin-s")
|
||||
val tempFile = Files.createFile(tempDir.resolve("bitcoin-s.conf"))
|
||||
val confStr = """
|
||||
| bitcoin-s {
|
||||
| network = testnet3
|
||||
|
|
||||
| logging {
|
||||
| level = off
|
||||
|
|
||||
| p2p = warn
|
||||
| }
|
||||
| }
|
||||
""".stripMargin
|
||||
val _ = Files.write(tempFile, confStr.getBytes())
|
||||
|
||||
val appConfig = ChainAppConfig(directory = tempDir)
|
||||
|
||||
assert(appConfig.datadir == tempDir.resolve("testnet3"))
|
||||
assert(appConfig.network == TestNet3)
|
||||
assert(appConfig.logLevel == Level.OFF)
|
||||
assert(appConfig.p2pLogLevel == Level.WARN)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.bitcoins.chain.config.ChainAppConfig
|
|||
*/
|
||||
trait ChainApi {
|
||||
|
||||
def chainConfig: ChainAppConfig
|
||||
implicit private[chain] val chainConfig: ChainAppConfig
|
||||
|
||||
/**
|
||||
* Adds a block header to our chain project
|
||||
|
|
|
@ -3,10 +3,11 @@ package org.bitcoins.chain.blockchain
|
|||
import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb}
|
||||
import org.bitcoins.chain.validation.{TipUpdateResult, TipValidation}
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
|
||||
import scala.collection.{IndexedSeqLike, mutable}
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.db.ChainVerificationLogger
|
||||
|
||||
/**
|
||||
* In memory implementation of a blockchain
|
||||
|
@ -21,8 +22,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||
*
|
||||
*/
|
||||
case class Blockchain(headers: Vector[BlockHeaderDb])
|
||||
extends IndexedSeqLike[BlockHeaderDb, Vector[BlockHeaderDb]]
|
||||
with BitcoinSLogger {
|
||||
extends IndexedSeqLike[BlockHeaderDb, Vector[BlockHeaderDb]] {
|
||||
val tip: BlockHeaderDb = headers.head
|
||||
|
||||
/** @inheritdoc */
|
||||
|
@ -41,7 +41,7 @@ case class Blockchain(headers: Vector[BlockHeaderDb])
|
|||
|
||||
}
|
||||
|
||||
object Blockchain extends BitcoinSLogger {
|
||||
object Blockchain extends ChainVerificationLogger {
|
||||
|
||||
def fromHeaders(headers: Vector[BlockHeaderDb]): Blockchain = {
|
||||
Blockchain(headers)
|
||||
|
@ -61,7 +61,8 @@ object Blockchain extends BitcoinSLogger {
|
|||
* or [[org.bitcoins.chain.blockchain.BlockchainUpdate.Failed Failed]] to connect to a tip
|
||||
*/
|
||||
def connectTip(header: BlockHeader, blockHeaderDAO: BlockHeaderDAO)(
|
||||
implicit ec: ExecutionContext): Future[BlockchainUpdate] = {
|
||||
implicit ec: ExecutionContext,
|
||||
conf: ChainAppConfig): Future[BlockchainUpdate] = {
|
||||
|
||||
//get all competing chains we have
|
||||
val blockchainsF: Future[Vector[Blockchain]] =
|
||||
|
|
|
@ -5,20 +5,19 @@ import org.bitcoins.chain.config.ChainAppConfig
|
|||
import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb}
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import org.bitcoins.db.ChainVerificationLogger
|
||||
|
||||
/**
|
||||
* Chain Handler is meant to be the reference implementation
|
||||
* of [[org.bitcoins.chain.api.ChainApi ChainApi]], this is the entry point in to the
|
||||
* chain project.
|
||||
*/
|
||||
case class ChainHandler(
|
||||
blockHeaderDAO: BlockHeaderDAO,
|
||||
chainConfig: ChainAppConfig)
|
||||
extends ChainApi
|
||||
with BitcoinSLogger {
|
||||
case class ChainHandler(blockHeaderDAO: BlockHeaderDAO)(
|
||||
implicit private[chain] val chainConfig: ChainAppConfig
|
||||
) extends ChainApi
|
||||
with ChainVerificationLogger {
|
||||
|
||||
override def getBlockCount(implicit ec: ExecutionContext): Future[Long] = {
|
||||
logger.debug(s"Querying for block count")
|
||||
|
@ -43,7 +42,8 @@ case class ChainHandler(
|
|||
override def processHeader(header: BlockHeader)(
|
||||
implicit ec: ExecutionContext): Future[ChainHandler] = {
|
||||
|
||||
val blockchainUpdateF = Blockchain.connectTip(header, blockHeaderDAO)
|
||||
val blockchainUpdateF =
|
||||
Blockchain.connectTip(header, blockHeaderDAO)
|
||||
|
||||
val newHandlerF = blockchainUpdateF.flatMap {
|
||||
case BlockchainUpdate.Successful(_, updatedHeader) =>
|
||||
|
@ -53,7 +53,7 @@ case class ChainHandler(
|
|||
createdF.map { header =>
|
||||
logger.debug(
|
||||
s"Connected new header to blockchain, height=${header.height} hash=${header.hashBE}")
|
||||
ChainHandler(blockHeaderDAO, chainConfig)
|
||||
ChainHandler(blockHeaderDAO)
|
||||
}
|
||||
case BlockchainUpdate.Failed(_, _, reason) =>
|
||||
val errMsg =
|
||||
|
|
|
@ -5,11 +5,12 @@ import org.bitcoins.chain.blockchain.ChainHandler
|
|||
import org.bitcoins.chain.models.BlockHeaderDb
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.db.ChainVerificationLogger
|
||||
|
||||
trait ChainSync extends BitcoinSLogger {
|
||||
trait ChainSync extends ChainVerificationLogger {
|
||||
|
||||
/** This method checks if our chain handler has the tip of the blockchain as an external source
|
||||
* If we do not have the same chain, we sync our chain handler until we are at the same best block hash
|
||||
|
@ -20,9 +21,12 @@ trait ChainSync extends BitcoinSLogger {
|
|||
* @param ec
|
||||
* @return
|
||||
*/
|
||||
def sync(chainHandler: ChainHandler,
|
||||
def sync(
|
||||
chainHandler: ChainHandler,
|
||||
getBlockHeaderFunc: DoubleSha256DigestBE => Future[BlockHeader],
|
||||
getBestBlockHashFunc: () => Future[DoubleSha256DigestBE])(implicit ec: ExecutionContext): Future[ChainApi] = {
|
||||
getBestBlockHashFunc: () => Future[DoubleSha256DigestBE])(
|
||||
implicit ec: ExecutionContext,
|
||||
conf: ChainAppConfig): Future[ChainApi] = {
|
||||
val currentTipsF: Future[Vector[BlockHeaderDb]] = {
|
||||
chainHandler.blockHeaderDAO.chainTips
|
||||
}
|
||||
|
@ -50,7 +54,6 @@ trait ChainSync extends BitcoinSLogger {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Keeps walking backwards on the chain until we match one
|
||||
* of the tips we have in our chain
|
||||
|
@ -61,17 +64,22 @@ trait ChainSync extends BitcoinSLogger {
|
|||
* @param ec
|
||||
* @return
|
||||
*/
|
||||
private def syncTips(chainApi: ChainApi,
|
||||
private def syncTips(
|
||||
chainApi: ChainApi,
|
||||
tips: Vector[BlockHeaderDb],
|
||||
bestBlockHash: DoubleSha256DigestBE,
|
||||
getBlockHeaderFunc: DoubleSha256DigestBE => Future[BlockHeader])(implicit ec: ExecutionContext): Future[ChainApi] = {
|
||||
getBlockHeaderFunc: DoubleSha256DigestBE => Future[BlockHeader])(
|
||||
implicit ec: ExecutionContext,
|
||||
conf: ChainAppConfig): Future[ChainApi] = {
|
||||
require(tips.nonEmpty, s"Cannot sync without the genesis block")
|
||||
|
||||
//we need to walk backwards on the chain until we get to one of our tips
|
||||
|
||||
val tipsBH = tips.map(_.blockHeader)
|
||||
|
||||
def loop(lastHeaderF: Future[BlockHeader], accum: List[BlockHeader]): Future[List[BlockHeader]] = {
|
||||
def loop(
|
||||
lastHeaderF: Future[BlockHeader],
|
||||
accum: List[BlockHeader]): Future[List[BlockHeader]] = {
|
||||
lastHeaderF.flatMap { lastHeader =>
|
||||
if (tipsBH.contains(lastHeader)) {
|
||||
//means we have synced back to a block that we know
|
||||
|
@ -81,9 +89,10 @@ trait ChainSync extends BitcoinSLogger {
|
|||
logger.debug(s"Last header=${lastHeader.hashBE.hex}")
|
||||
//we don't know this block, so we need to keep walking backwards
|
||||
//to find a block a we know
|
||||
val newLastHeaderF = getBlockHeaderFunc(lastHeader.previousBlockHashBE)
|
||||
val newLastHeaderF =
|
||||
getBlockHeaderFunc(lastHeader.previousBlockHashBE)
|
||||
|
||||
loop(newLastHeaderF,lastHeader +: accum)
|
||||
loop(newLastHeaderF, lastHeader +: accum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +100,9 @@ trait ChainSync extends BitcoinSLogger {
|
|||
val bestHeaderF = getBlockHeaderFunc(bestBlockHash)
|
||||
|
||||
bestHeaderF.map { bestHeader =>
|
||||
logger.info(s"Best tip from third party=${bestHeader.hashBE.hex} currentTips=${tips.map(_.hashBE.hex)}")
|
||||
logger.info(
|
||||
s"Best tip from third party=${bestHeader.hashBE.hex} currentTips=${tips
|
||||
.map(_.hashBE.hex)}")
|
||||
}
|
||||
|
||||
//one sanity check to make sure we aren't _ahead_ of our data source
|
||||
|
@ -110,7 +121,8 @@ trait ChainSync extends BitcoinSLogger {
|
|||
|
||||
//now we are going to add them to our chain and return the chain api
|
||||
headersToSyncF.flatMap { headers =>
|
||||
logger.info(s"Attempting to sync ${headers.length} blockheader to our chainstate")
|
||||
logger.info(
|
||||
s"Attempting to sync ${headers.length} blockheader to our chainstate")
|
||||
chainApi.processHeaders(headers.toVector)
|
||||
}
|
||||
}
|
||||
|
@ -120,5 +132,4 @@ trait ChainSync extends BitcoinSLogger {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
object ChainSync extends ChainSync
|
|
@ -11,13 +11,24 @@ import scala.concurrent.Future
|
|||
import scala.concurrent.Promise
|
||||
import scala.util.Success
|
||||
import scala.util.Failure
|
||||
import java.nio.file.Path
|
||||
|
||||
case class ChainAppConfig(private val confs: Config*) extends AppConfig {
|
||||
override protected val configOverrides: List[Config] = confs.toList
|
||||
override protected val moduleName: String = "chain"
|
||||
override protected type ConfigType = ChainAppConfig
|
||||
override protected def newConfigOfType(configs: Seq[Config]): ChainAppConfig =
|
||||
ChainAppConfig(configs: _*)
|
||||
/** Configuration for the Bitcoin-S chain verification module
|
||||
* @param directory The data directory of the module
|
||||
* @param confs Optional sequence of configuration overrides
|
||||
*/
|
||||
case class ChainAppConfig(
|
||||
private val directory: Path,
|
||||
private val confs: Config*)
|
||||
extends AppConfig {
|
||||
override protected[bitcoins] def configOverrides: List[Config] = confs.toList
|
||||
override protected[bitcoins] val moduleName: String = "chain"
|
||||
override protected[bitcoins] type ConfigType = ChainAppConfig
|
||||
override protected[bitcoins] def newConfigOfType(
|
||||
configs: Seq[Config]): ChainAppConfig =
|
||||
ChainAppConfig(directory, configs: _*)
|
||||
|
||||
protected[bitcoins] def baseDatadir: Path = directory
|
||||
|
||||
/**
|
||||
* Checks whether or not the chain project is initialized by
|
||||
|
@ -70,3 +81,12 @@ case class ChainAppConfig(private val confs: Config*) extends AppConfig {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ChainAppConfig {
|
||||
|
||||
/** Constructs a chain verification configuration from the default Bitcoin-S
|
||||
* data directory and given list of configuration overrides.
|
||||
*/
|
||||
def fromDefaultDatadir(confs: Config*): ChainAppConfig =
|
||||
ChainAppConfig(AppConfig.DEFAULT_BITCOIN_S_DATADIR, confs: _*)
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||
* our chain project
|
||||
*/
|
||||
case class BlockHeaderDAO()(
|
||||
implicit override val ec: ExecutionContext,
|
||||
override val appConfig: ChainAppConfig)
|
||||
implicit ec: ExecutionContext,
|
||||
appConfig: ChainAppConfig)
|
||||
extends CRUD[BlockHeaderDb, DoubleSha256DigestBE] {
|
||||
|
||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||
|
|
|
@ -2,8 +2,9 @@ package org.bitcoins.chain.pow
|
|||
|
||||
import org.bitcoins.chain.models.{BlockHeaderDAO, BlockHeaderDb}
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.core.protocol.blockchain.{BlockHeader, ChainParams}
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, NumberUtil}
|
||||
import org.bitcoins.core.util.NumberUtil
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
|
@ -11,7 +12,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||
* Implements functions found inside of bitcoin core's
|
||||
* @see [[https://github.com/bitcoin/bitcoin/blob/35477e9e4e3f0f207ac6fa5764886b15bf9af8d0/src/pow.cpp pow.cpp]]
|
||||
*/
|
||||
sealed abstract class Pow extends BitcoinSLogger {
|
||||
sealed abstract class Pow {
|
||||
|
||||
/**
|
||||
* Gets the next proof of work requirement for a block
|
||||
|
@ -24,8 +25,9 @@ sealed abstract class Pow extends BitcoinSLogger {
|
|||
tip: BlockHeaderDb,
|
||||
newPotentialTip: BlockHeader,
|
||||
blockHeaderDAO: BlockHeaderDAO)(
|
||||
implicit ec: ExecutionContext): Future[UInt32] = {
|
||||
val chainParams = blockHeaderDAO.appConfig.chain
|
||||
implicit ec: ExecutionContext,
|
||||
config: ChainAppConfig): Future[UInt32] = {
|
||||
val chainParams = config.chain
|
||||
val currentHeight = tip.height
|
||||
|
||||
val powLimit = NumberUtil.targetCompression(bigInteger =
|
||||
|
|
|
@ -8,9 +8,11 @@ import org.bitcoins.chain.models.{
|
|||
import org.bitcoins.chain.pow.Pow
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, NumberUtil}
|
||||
import org.bitcoins.core.util.NumberUtil
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.db.ChainVerificationLogger
|
||||
|
||||
/**
|
||||
* Responsible for checking if we can connect two
|
||||
|
@ -18,7 +20,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||
* things like proof of work difficulty, if it
|
||||
* references the previous block header correctly etc.
|
||||
*/
|
||||
sealed abstract class TipValidation extends BitcoinSLogger {
|
||||
sealed abstract class TipValidation extends ChainVerificationLogger {
|
||||
|
||||
/** Checks if the given header can be connected to the current tip
|
||||
* This is the method where a [[org.bitcoins.core.protocol.blockchain.BlockHeader BlockHeader]] is transformed into a
|
||||
|
@ -30,7 +32,8 @@ sealed abstract class TipValidation extends BitcoinSLogger {
|
|||
newPotentialTip: BlockHeader,
|
||||
currentTip: BlockHeaderDb,
|
||||
blockHeaderDAO: BlockHeaderDAO)(
|
||||
implicit ec: ExecutionContext): Future[TipUpdateResult] = {
|
||||
implicit ec: ExecutionContext,
|
||||
conf: ChainAppConfig): Future[TipUpdateResult] = {
|
||||
val header = newPotentialTip
|
||||
logger.trace(
|
||||
s"Checking header=${header.hashBE.hex} to try to connect to currentTip=${currentTip.hashBE.hex} with height=${currentTip.height}")
|
||||
|
@ -67,7 +70,9 @@ sealed abstract class TipValidation extends BitcoinSLogger {
|
|||
/** Logs the result of [[org.bitcoins.chain.validation.TipValidation.checkNewTip() checkNewTip]] */
|
||||
private def logTipResult(
|
||||
connectTipResultF: Future[TipUpdateResult],
|
||||
currentTip: BlockHeaderDb)(implicit ec: ExecutionContext): Unit = {
|
||||
currentTip: BlockHeaderDb)(
|
||||
implicit ec: ExecutionContext,
|
||||
conf: ChainAppConfig): Unit = {
|
||||
connectTipResultF.map {
|
||||
case TipUpdateResult.Success(tipDb) =>
|
||||
logger.trace(
|
||||
|
@ -102,7 +107,8 @@ sealed abstract class TipValidation extends BitcoinSLogger {
|
|||
newPotentialTip: BlockHeader,
|
||||
currentTip: BlockHeaderDb,
|
||||
blockHeaderDAO: BlockHeaderDAO)(
|
||||
implicit ec: ExecutionContext): Future[UInt32] = {
|
||||
implicit ec: ExecutionContext,
|
||||
config: ChainAppConfig): Future[UInt32] = {
|
||||
Pow.getNetworkWorkRequired(tip = currentTip,
|
||||
newPotentialTip = newPotentialTip,
|
||||
blockHeaderDAO = blockHeaderDAO)
|
||||
|
|
|
@ -3,8 +3,8 @@ package org.bitcoins.core.p2p
|
|||
import org.bitcoins.testkit.core.gen.p2p.DataMessageGenerator
|
||||
import org.bitcoins.testkit.util.BitcoinSUnitTest
|
||||
import scodec.bits._
|
||||
import org.bitcoins.node.util.BitcoinSpvNodeUtil
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.node.networking.P2PClient
|
||||
|
||||
class MerkleBlockMessageTest extends BitcoinSUnitTest {
|
||||
it must "have serialization symmetry" in {
|
||||
|
@ -42,15 +42,4 @@ class MerkleBlockMessageTest extends BitcoinSUnitTest {
|
|||
assert(fourth == expectedFourth)
|
||||
}
|
||||
|
||||
// we had a bug where we didn't consume the right number of bytes
|
||||
// when parsing a merkle block message, thereby screwing up
|
||||
// the parsing of the remainder
|
||||
it must "parse a byte vector with three messages in it" in {
|
||||
val bytes =
|
||||
hex"fabfb5da6d65726b6c65626c6f636b0097000000b4b6e45d00000020387191f7d488b849b4080fdf105c71269fc841a2f0f2944fc5dc785c830c716e37f36373098aae06a668cc74e388caf50ecdcb5504ce936490b4b72940e08859548c305dffff7f20010000000200000002ecd1c722709bfc241f8b94fc64034dcba2c95409dc4cd1d7b864e1128a04e5b044133327b04ff8ac576e7748a4dae4111f0c765dacbfe0c5a9fddbeb8f60d5af0105fabfb5da747800000000000000000000cc0100004413332702000000065b7f0f3eec398047e921037815aa41709b6243a1897f1423194b7558399ae0300000000017160014008dc9d88d1797305f3fbd30d2b36d6bde984a09feffffffe9145055d671fd705a09f028033da614b619205b9926fe5ebe45e15ae8b3231e0100000017160014d74cfac04bb0e6838c35f1f4a0a60d13655be2fbfeffffff797f8ff9c10fa618b6254343a648be995410e82c03fd8accb0de2271a3fb1abd00000000171600143ee832c09db48eca28a64a358ed7a01dbe52d31bfeffffffc794dba971b9479dfcbc662a3aacd641553bdb2418b15c0221c5dfd4471a7a70000000001716001452c13ba0314f7718c234ed6adfea6422ce03a545feffffffb7c3bf1762b15f3b0e0eaa5beb46fe96a9e2829a7413fd900b9b7e0d192ab64800000000171600143ee832c09db48eca28a64a358ed7a01dbe52d31bfeffffffb6ced6cb8dfc2f7f5b37561938ead3bc5ca4036e2b45d9738cc086a10eed4e010100000017160014aebb17e245fe8c98a75f0b6717fcadca30e491e2feffffff02002a7515000000001976a9148374ff8beb55ea2945039881ca26071b5749fafe88ac485620000000000017a91405d36a2b0bdedf3fc58bed6f9e4026f8934a2716876b050000fabfb5da686561646572730000000000010000001406e05800"
|
||||
val (messages, leftover) = BitcoinSpvNodeUtil.parseIndividualMessages(bytes)
|
||||
assert(messages.length == 3)
|
||||
assert(leftover.isEmpty)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,41 @@ bitcoin-s {
|
|||
datadir = ${HOME}/.bitcoin-s
|
||||
network = regtest # regtest, testnet3, mainnet
|
||||
|
||||
logging {
|
||||
level = info # trace, debug, info, warn, error, off
|
||||
|
||||
# You can also tune specific module loggers.
|
||||
# They each take the same levels as above.
|
||||
# If they are commented out (as they are
|
||||
# by default), `logging.level` gets used
|
||||
# instead.
|
||||
# The available loggers are:
|
||||
|
||||
# incoming and outgoing P2P messages
|
||||
# p2p = info
|
||||
|
||||
# verification of block headers, merkle trees
|
||||
# chain-verification = info
|
||||
|
||||
# generation of addresses, signing of TXs
|
||||
# key-handling = info
|
||||
|
||||
# wallet operations not related to key management
|
||||
# wallet = info
|
||||
|
||||
# HTTP RPC server
|
||||
# http = info
|
||||
|
||||
# Database interactions
|
||||
# database = info
|
||||
|
||||
# whether or not to write to the log file
|
||||
disable-file = false
|
||||
|
||||
# whether or not to log to stdout
|
||||
disable-console = false
|
||||
}
|
||||
|
||||
# settings for wallet module
|
||||
wallet {
|
||||
defaultAccountType = legacy # legacy, segwit, nested-segwit
|
||||
|
|
|
@ -26,6 +26,7 @@ import scala.util.Properties
|
|||
import scala.util.matching.Regex
|
||||
import scala.concurrent.ExecutionContext
|
||||
import scala.concurrent.Future
|
||||
import ch.qos.logback.classic.Level
|
||||
|
||||
/**
|
||||
* Everything needed to configure functionality
|
||||
|
@ -51,15 +52,16 @@ abstract class AppConfig extends BitcoinSLogger {
|
|||
* the type of themselves, ensuring `withOverrides` return
|
||||
* the correct type
|
||||
*/
|
||||
protected type ConfigType <: AppConfig
|
||||
protected[bitcoins] type ConfigType <: AppConfig
|
||||
|
||||
/** Constructor to make a new instance of this config type */
|
||||
protected def newConfigOfType(configOverrides: Seq[Config]): ConfigType
|
||||
protected[bitcoins] def newConfigOfType(
|
||||
configOverrides: Seq[Config]): ConfigType
|
||||
|
||||
/** List of user-provided configs that should
|
||||
* override defaults
|
||||
*/
|
||||
protected val configOverrides: List[Config] = List.empty
|
||||
protected[bitcoins] def configOverrides: List[Config] = List.empty
|
||||
|
||||
/**
|
||||
* This method returns a new `AppConfig`, where every
|
||||
|
@ -121,7 +123,7 @@ abstract class AppConfig extends BitcoinSLogger {
|
|||
/**
|
||||
* Name of the module. `chain`, `wallet`, `node` etc.
|
||||
*/
|
||||
protected def moduleName: String
|
||||
protected[bitcoins] def moduleName: String
|
||||
|
||||
/**
|
||||
* The configuration details for connecting/using the database for our projects
|
||||
|
@ -206,6 +208,27 @@ abstract class AppConfig extends BitcoinSLogger {
|
|||
* rest of the fields in this class from
|
||||
*/
|
||||
private[bitcoins] lazy val config: Config = {
|
||||
|
||||
val datadirConfig = {
|
||||
val file = baseDatadir.resolve("bitcoin-s.conf")
|
||||
val config = if (Files.isReadable(file)) {
|
||||
ConfigFactory.parseFile(file.toFile())
|
||||
} else {
|
||||
ConfigFactory.empty()
|
||||
}
|
||||
|
||||
val withDatadir =
|
||||
ConfigFactory.parseString(s"bitcoin-s.datadir = $baseDatadir")
|
||||
withDatadir.withFallback(config)
|
||||
}
|
||||
|
||||
logger.trace(s"Data directory config:")
|
||||
if (datadirConfig.hasPath("bitcoin-s")) {
|
||||
logger.trace(datadirConfig.getConfig("bitcoin-s").asReadableJson)
|
||||
} else {
|
||||
logger.trace(ConfigFactory.empty().asReadableJson)
|
||||
}
|
||||
|
||||
// `load` tries to resolve substitions,
|
||||
// `parseResources` does not
|
||||
val dbConfig = ConfigFactory
|
||||
|
@ -226,9 +249,12 @@ abstract class AppConfig extends BitcoinSLogger {
|
|||
logger.trace(
|
||||
s"Classpath config: ${classPathConfig.getConfig("bitcoin-s").asReadableJson}")
|
||||
|
||||
// loads reference.conf as well as application.conf,
|
||||
// if the user has made one
|
||||
val unresolvedConfig = classPathConfig
|
||||
// we want the data directory configuration
|
||||
// to take preference over any bundled (classpath)
|
||||
// configurations
|
||||
// loads reference.conf (provided by Bitcoin-S)
|
||||
val unresolvedConfig = datadirConfig
|
||||
.withFallback(classPathConfig)
|
||||
.withFallback(dbConfig)
|
||||
|
||||
logger.trace(s"Unresolved bitcoin-s config:")
|
||||
|
@ -256,32 +282,92 @@ abstract class AppConfig extends BitcoinSLogger {
|
|||
unresolvedConfig
|
||||
}
|
||||
|
||||
val config = withOverrides
|
||||
val finalConfig = withOverrides
|
||||
.resolve()
|
||||
.getConfig("bitcoin-s")
|
||||
|
||||
logger.debug(s"Resolved bitcoin-s config:")
|
||||
logger.debug(config.asReadableJson)
|
||||
logger.debug(finalConfig.asReadableJson)
|
||||
|
||||
config
|
||||
finalConfig
|
||||
|
||||
}
|
||||
|
||||
/** The data directory used by bitcoin-s apps */
|
||||
lazy val datadir: Path = {
|
||||
val basedir = Paths.get(config.getString("datadir"))
|
||||
/** The base data directory. This is where we look for a configuration file */
|
||||
protected[bitcoins] def baseDatadir: Path
|
||||
|
||||
/** The network specific data directory. */
|
||||
val datadir: Path = {
|
||||
val lastDirname = network match {
|
||||
case MainNet => "mainnet"
|
||||
case TestNet3 => "testnet3"
|
||||
case RegTest => "regtest"
|
||||
}
|
||||
basedir.resolve(lastDirname)
|
||||
baseDatadir.resolve(lastDirname)
|
||||
}
|
||||
|
||||
private def stringToLogLevel(str: String): Level =
|
||||
str.toLowerCase() match {
|
||||
case "trace" => Level.TRACE
|
||||
case "debug" => Level.DEBUG
|
||||
case "info" => Level.INFO
|
||||
case "warn" => Level.WARN
|
||||
case "error" => Level.ERROR
|
||||
case "off" => Level.OFF
|
||||
case other: String => sys.error(s"Unknown logging level: $other")
|
||||
}
|
||||
|
||||
/** The default logging level */
|
||||
lazy val logLevel: Level = {
|
||||
val levelString = config.getString("logging.level")
|
||||
stringToLogLevel(levelString)
|
||||
}
|
||||
|
||||
/** Whether or not we should log to file */
|
||||
lazy val disableFileLogging = config.getBoolean("logging.disable-file")
|
||||
|
||||
/** Whether or not we should log to stdout */
|
||||
lazy val disableConsoleLogging = config.getBoolean("logging.disable-console")
|
||||
|
||||
private def levelOrDefault(key: String): Level =
|
||||
config
|
||||
.getStringOrNone(key)
|
||||
.map(stringToLogLevel)
|
||||
.getOrElse(logLevel)
|
||||
|
||||
/** The logging level for our P2P logger */
|
||||
lazy val p2pLogLevel: Level = levelOrDefault("logging.p2p")
|
||||
|
||||
/** The logging level for our chain verification logger */
|
||||
lazy val verificationLogLevel: Level =
|
||||
levelOrDefault("logging.chain-verification")
|
||||
|
||||
/** The logging level for our key handling logger */
|
||||
lazy val keyHandlingLogLevel: Level =
|
||||
levelOrDefault("logging.key-handling")
|
||||
|
||||
/** Logging level for wallet */
|
||||
lazy val walletLogLeveL: Level =
|
||||
levelOrDefault("logging.wallet")
|
||||
|
||||
/** Logging level for HTTP RPC server */
|
||||
lazy val httpLogLevel: Level = levelOrDefault("logging.http")
|
||||
|
||||
/** Logging level for database interactions */
|
||||
lazy val databaseLogLevel: Level = levelOrDefault("logging.database")
|
||||
|
||||
}
|
||||
|
||||
object AppConfig extends BitcoinSLogger {
|
||||
|
||||
/** The default data directory
|
||||
*
|
||||
* TODO: use different directories on Windows and Mac,
|
||||
* should probably mimic what Bitcoin Core does
|
||||
*/
|
||||
private[bitcoins] val DEFAULT_BITCOIN_S_DATADIR: Path =
|
||||
Paths.get(Properties.userHome, ".bitcoin-s")
|
||||
|
||||
/**
|
||||
* Matches the default data directory location
|
||||
* with a network appended,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.bitcoins.db
|
||||
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import slick.jdbc.SQLiteProfile.api._
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
@ -15,16 +14,16 @@ import org.bitcoins.core.config.MainNet
|
|||
* You are responsible for the create function. You also need to specify
|
||||
* the table and the database you are connecting to.
|
||||
*/
|
||||
abstract class CRUD[T, PrimaryKeyType] extends BitcoinSLogger {
|
||||
|
||||
def appConfig: AppConfig
|
||||
implicit val ec: ExecutionContext
|
||||
abstract class CRUD[T, PrimaryKeyType](
|
||||
implicit private val config: AppConfig,
|
||||
private val ec: ExecutionContext)
|
||||
extends DatabaseLogger {
|
||||
|
||||
/** The table inside our database we are inserting into */
|
||||
val table: TableQuery[_ <: Table[T]]
|
||||
|
||||
/** Binding to the actual database itself, this is what is used to run querys */
|
||||
def database: SafeDatabase = SafeDatabase(appConfig)
|
||||
def database: SafeDatabase = SafeDatabase(config)
|
||||
|
||||
/**
|
||||
* create a record in the database
|
||||
|
@ -33,7 +32,7 @@ abstract class CRUD[T, PrimaryKeyType] extends BitcoinSLogger {
|
|||
* @return the inserted record
|
||||
*/
|
||||
def create(t: T): Future[T] = {
|
||||
logger.trace(s"Writing $t to DB with config: ${appConfig.config}")
|
||||
logger.trace(s"Writing $t to DB with config: ${config.config}")
|
||||
createAll(Vector(t)).map(_.head)
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,7 @@ abstract class CRUD[T, PrimaryKeyType] extends BitcoinSLogger {
|
|||
* @return Option[T] - the record if found, else none
|
||||
*/
|
||||
def read(id: PrimaryKeyType): Future[Option[T]] = {
|
||||
logger.trace(s"Reading from DB with config: ${appConfig.config}")
|
||||
logger.trace(s"Reading from DB with config: ${config.config}")
|
||||
val query = findByPrimaryKey(id)
|
||||
val rows: Future[Seq[T]] = database.run(query.result)
|
||||
rows.map(_.headOption)
|
||||
|
@ -130,7 +129,8 @@ abstract class CRUD[T, PrimaryKeyType] extends BitcoinSLogger {
|
|||
|
||||
}
|
||||
|
||||
case class SafeDatabase(config: AppConfig) extends BitcoinSLogger {
|
||||
case class SafeDatabase(config: AppConfig) extends DatabaseLogger {
|
||||
implicit private val conf: AppConfig = config
|
||||
|
||||
import config.database
|
||||
|
||||
|
|
|
@ -4,8 +4,12 @@ import slick.dbio.Effect.Write
|
|||
import slick.jdbc.SQLiteProfile.api._
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.ExecutionContext
|
||||
|
||||
abstract class CRUDAutoInc[T <: DbRowAutoInc[T]] extends CRUD[T, Long] {
|
||||
abstract class CRUDAutoInc[T <: DbRowAutoInc[T]](
|
||||
implicit config: AppConfig,
|
||||
ec: ExecutionContext)
|
||||
extends CRUD[T, Long] {
|
||||
|
||||
/** The table inside our database we are inserting into */
|
||||
override val table: TableQuery[_ <: TableAutoInc[T]]
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package org.bitcoins.db
|
||||
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import slick.jdbc.SQLiteProfile.api._
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
abstract class DbManagement extends BitcoinSLogger {
|
||||
abstract class DbManagement extends DatabaseLogger {
|
||||
def allTables: List[TableQuery[_ <: Table[_]]]
|
||||
|
||||
/** Lists all tables in the given database */
|
||||
|
|
|
@ -11,6 +11,24 @@ package object db {
|
|||
val options = ConfigRenderOptions.concise().setFormatted(true)
|
||||
config.root().render(options)
|
||||
}
|
||||
|
||||
/** Returns the string at key or the given default value */
|
||||
def getStringOrElse(key: String, default: => String): String = {
|
||||
if (config.hasPath(key)) {
|
||||
config.getString(key)
|
||||
} else {
|
||||
default
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the string at the given key, if it exists */
|
||||
def getStringOrNone(key: String): Option[String] = {
|
||||
if (config.hasPath(key)) {
|
||||
Some(config.getString(key))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,42 @@ It's possible to communicate with other developers through a variety of communic
|
|||
- [Bitcoin-S Gitter](https://gitter.im/bitcoin-s-core/)
|
||||
- [#bitcoin-scala](https://webchat.freenode.net/?channels=bitcoin-scala) on IRC Freenode
|
||||
|
||||
## Working on Bitcoin-S applications
|
||||
|
||||
Bitcoin-S includes a couple of applications that can be run as standalone executables.
|
||||
This includes the node, wallet and (partial) blockchain verification modules, as well
|
||||
as the server that bundles these three together and the CLI used to communicate with
|
||||
the server. These applications are configured with HOCON files. The file
|
||||
[`reference.conf`](https://github.com/bitcoin-s/bitcoin-s/blob/master/db-commons/src/main/resources/reference.conf)
|
||||
is the basis configuration file, and every option read by Bitcoin-S should be present in
|
||||
this file. This means that you can copy sections from this file and edit them, to tune
|
||||
how the application runs on your machine.
|
||||
|
||||
One example of things you can tune is logging levels. Lets say you wanted general logging
|
||||
to happen at the `WARN` level, but the P2P message handling to be logged at `DEBUG`. Your
|
||||
configuration file would then look like:
|
||||
|
||||
```conf
|
||||
bitcoins-s {
|
||||
logging {
|
||||
level = warn
|
||||
|
||||
p2p = debug
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Running the applications
|
||||
|
||||
When running the applications configuration placed in `bitcoin-s.conf` in the current
|
||||
data directory gets picked up. For linux this is by default `$HOME/.bitcoin-s/`, so the
|
||||
file you should edit would be `$HOME/.bitcoin-s/bitcoin-s.conf`.
|
||||
|
||||
### Running tests for the applications
|
||||
|
||||
You can place configuration files in the data directory that tests are being run in,
|
||||
but you can also edit [`reference.conf`](https://github.com/bitcoin-s/bitcoin-s/blob/master/db-commons/src/main/resources/reference.conf).
|
||||
|
||||
## Developer productivity
|
||||
|
||||
### Bloop
|
||||
|
|
|
@ -61,7 +61,7 @@ class BroadcastTransactionTest extends BitcoinSWalletTest {
|
|||
val peer = Peer.fromBitcoind(rpc.instance)
|
||||
val chainHandler = {
|
||||
val bhDao = BlockHeaderDAO()
|
||||
ChainHandler(bhDao, config)
|
||||
ChainHandler(bhDao)
|
||||
}
|
||||
|
||||
val spv =
|
||||
|
|
|
@ -7,9 +7,12 @@ import com.typesafe.config.Config
|
|||
import com.typesafe.config.ConfigFactory
|
||||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.config.MainNet
|
||||
import ch.qos.logback.classic.Level
|
||||
import java.nio.file.Files
|
||||
|
||||
class NodeAppConfigTest extends BitcoinSUnitTest {
|
||||
val config = NodeAppConfig()
|
||||
val tempDir = Files.createTempDirectory("bitcoin-s")
|
||||
val config = NodeAppConfig(directory = tempDir)
|
||||
|
||||
it must "be overridable" in {
|
||||
assert(config.network == RegTest)
|
||||
|
@ -30,4 +33,29 @@ class NodeAppConfigTest extends BitcoinSUnitTest {
|
|||
assert(overriden.network == MainNet)
|
||||
|
||||
}
|
||||
|
||||
it must "have user data directory configuration take precedence" in {
|
||||
|
||||
val tempDir = Files.createTempDirectory("bitcoin-s")
|
||||
val tempFile = Files.createFile(tempDir.resolve("bitcoin-s.conf"))
|
||||
val confStr = """
|
||||
| bitcoin-s {
|
||||
| network = testnet3
|
||||
|
|
||||
| logging {
|
||||
| level = off
|
||||
|
|
||||
| p2p = warn
|
||||
| }
|
||||
| }
|
||||
""".stripMargin
|
||||
val _ = Files.write(tempFile, confStr.getBytes())
|
||||
|
||||
val appConfig = NodeAppConfig(directory = tempDir)
|
||||
|
||||
assert(appConfig.datadir == tempDir.resolve("testnet3"))
|
||||
assert(appConfig.network == TestNet3)
|
||||
assert(appConfig.logLevel == Level.OFF)
|
||||
assert(appConfig.p2pLogLevel == Level.WARN)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ class NodeWithWalletTest extends BitcoinSWalletTest {
|
|||
val peer = Peer.fromBitcoind(rpc.instance)
|
||||
val chainHandler = {
|
||||
val bhDao = BlockHeaderDAO()
|
||||
ChainHandler(bhDao, config)
|
||||
ChainHandler(bhDao)
|
||||
}
|
||||
|
||||
val spv =
|
||||
|
|
|
@ -12,14 +12,24 @@ import org.bitcoins.testkit.node.NodeTestUtil
|
|||
import org.bitcoins.testkit.rpc.BitcoindRpcTestUtil
|
||||
import org.bitcoins.testkit.util.BitcoindRpcTest
|
||||
import org.scalatest._
|
||||
import scodec.bits._
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.duration.DurationInt
|
||||
import org.bitcoins.core.p2p.HeadersMessage
|
||||
import org.bitcoins.core.protocol.CompactSizeUInt
|
||||
import org.bitcoins.core.number.UInt64
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.number.Int32
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.p2p.NetworkMessage
|
||||
import org.bitcoins.core.p2p.VersionMessage
|
||||
import org.bitcoins.core.config.TestNet3
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
|
||||
/**
|
||||
* Created by chris on 6/7/16.
|
||||
*/
|
||||
class ClientTest
|
||||
class P2PClientTest
|
||||
extends BitcoindRpcTest
|
||||
with MustMatchers
|
||||
with BeforeAndAfter
|
||||
|
@ -45,8 +55,76 @@ class ClientTest
|
|||
lazy val bitcoindPeer2F = bitcoindRpcF.map { bitcoind =>
|
||||
NodeTestUtil.getBitcoindPeer(bitcoind)
|
||||
}
|
||||
behavior of "parseIndividualMessages"
|
||||
|
||||
behavior of "Client"
|
||||
it must "block header message that is not aligned with a tcp frame" in {
|
||||
|
||||
val headersMsg = HeadersMessage(
|
||||
CompactSizeUInt(UInt64(2), 1),
|
||||
Vector(
|
||||
BlockHeader(
|
||||
Int32(315017594),
|
||||
DoubleSha256Digest(
|
||||
"177e777f078d2deeaa3ad4b82e78a00ad2f4738c5217f7a36d9cf3bd11e41817"),
|
||||
DoubleSha256Digest(
|
||||
"1dcaebebd620823bb344bd18a18276de508910d66b4e3cbb3426a14eced66224"),
|
||||
UInt32(2845833462L),
|
||||
UInt32(2626024374L),
|
||||
UInt32(2637850613L)
|
||||
),
|
||||
BlockHeader(
|
||||
Int32(1694049746),
|
||||
DoubleSha256Digest(
|
||||
"07b6d61809476830bc7ef862a983a7222997df3f639e0d2aa5902a5a48018430"),
|
||||
DoubleSha256Digest(
|
||||
"68c65f803b70b72563e86ac3e8e20ad11fbfa2eac3f9fddf4bc624d03a14f084"),
|
||||
UInt32(202993555),
|
||||
UInt32(4046619225L),
|
||||
UInt32(1231236881)
|
||||
)
|
||||
)
|
||||
)
|
||||
val networkMsg = NetworkMessage(np, headersMsg)
|
||||
//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 (firstHalf, secondHalf) = networkMsg.bytes.splitAt(randomIndex)
|
||||
val (firstHalfParseHeaders, remainingBytes) =
|
||||
P2PClient.parseIndividualMessages(firstHalf)
|
||||
firstHalfParseHeaders must be(empty)
|
||||
|
||||
val (secondHalfParsedHeaders, _) =
|
||||
P2PClient.parseIndividualMessages(remainingBytes ++ secondHalf)
|
||||
val parsedNetworkMsg = secondHalfParsedHeaders.head
|
||||
val parsedHeadersMsg = parsedNetworkMsg.payload.asInstanceOf[HeadersMessage]
|
||||
parsedNetworkMsg.header must be(networkMsg.header)
|
||||
parsedHeadersMsg.headers.head must be(headersMsg.headers.head)
|
||||
parsedHeadersMsg.headers(1) must be(parsedHeadersMsg.headers(1))
|
||||
|
||||
}
|
||||
|
||||
it must "return the entire byte array if a message is not aligned to a byte frame" in {
|
||||
val versionMessage =
|
||||
VersionMessage(TestNet3.dnsSeeds(0), np)
|
||||
val networkMsg = NetworkMessage(np, versionMessage)
|
||||
//remove last byte so the message is not aligned
|
||||
val bytes = networkMsg.bytes.slice(0, networkMsg.bytes.size - 1)
|
||||
val (_, unAlignedBytes) = P2PClient.parseIndividualMessages(bytes)
|
||||
|
||||
unAlignedBytes must be(bytes)
|
||||
}
|
||||
|
||||
// we had a bug where we didn't consume the right number of bytes
|
||||
// when parsing a merkle block message, thereby screwing up
|
||||
// the parsing of the remainder
|
||||
it must "parse a byte vector with three messages in it" in {
|
||||
val bytes =
|
||||
hex"fabfb5da6d65726b6c65626c6f636b0097000000b4b6e45d00000020387191f7d488b849b4080fdf105c71269fc841a2f0f2944fc5dc785c830c716e37f36373098aae06a668cc74e388caf50ecdcb5504ce936490b4b72940e08859548c305dffff7f20010000000200000002ecd1c722709bfc241f8b94fc64034dcba2c95409dc4cd1d7b864e1128a04e5b044133327b04ff8ac576e7748a4dae4111f0c765dacbfe0c5a9fddbeb8f60d5af0105fabfb5da747800000000000000000000cc0100004413332702000000065b7f0f3eec398047e921037815aa41709b6243a1897f1423194b7558399ae0300000000017160014008dc9d88d1797305f3fbd30d2b36d6bde984a09feffffffe9145055d671fd705a09f028033da614b619205b9926fe5ebe45e15ae8b3231e0100000017160014d74cfac04bb0e6838c35f1f4a0a60d13655be2fbfeffffff797f8ff9c10fa618b6254343a648be995410e82c03fd8accb0de2271a3fb1abd00000000171600143ee832c09db48eca28a64a358ed7a01dbe52d31bfeffffffc794dba971b9479dfcbc662a3aacd641553bdb2418b15c0221c5dfd4471a7a70000000001716001452c13ba0314f7718c234ed6adfea6422ce03a545feffffffb7c3bf1762b15f3b0e0eaa5beb46fe96a9e2829a7413fd900b9b7e0d192ab64800000000171600143ee832c09db48eca28a64a358ed7a01dbe52d31bfeffffffb6ced6cb8dfc2f7f5b37561938ead3bc5ca4036e2b45d9738cc086a10eed4e010100000017160014aebb17e245fe8c98a75f0b6717fcadca30e491e2feffffff02002a7515000000001976a9148374ff8beb55ea2945039881ca26071b5749fafe88ac485620000000000017a91405d36a2b0bdedf3fc58bed6f9e4026f8934a2716876b050000fabfb5da686561646572730000000000010000001406e05800"
|
||||
val (messages, leftover) = P2PClient.parseIndividualMessages(bytes)
|
||||
assert(messages.length == 3)
|
||||
assert(leftover.isEmpty)
|
||||
|
||||
}
|
||||
behavior of "P2PClient"
|
||||
|
||||
it must "establish a tcp connection with a bitcoin node" in {
|
||||
bitcoindPeerF.flatMap(remote => connectAndDisconnect(remote))
|
||||
|
@ -74,8 +152,12 @@ class ClientTest
|
|||
def connectAndDisconnect(peer: Peer): Future[Assertion] = {
|
||||
val probe = TestProbe()
|
||||
val remote = peer.socket
|
||||
val chainHandler = {
|
||||
val dao = BlockHeaderDAO()
|
||||
ChainHandler(dao)
|
||||
}
|
||||
val peerMessageReceiver =
|
||||
PeerMessageReceiver(state = Preconnection)
|
||||
PeerMessageReceiver(state = Preconnection, chainHandler)
|
||||
val client =
|
||||
TestActorRef(P2PClient.props(peer, peerMessageReceiver), probe.ref)
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@ package org.bitcoins.node
|
|||
|
||||
import akka.actor.ActorSystem
|
||||
import org.bitcoins.chain.api.ChainApi
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.node.models.Peer
|
||||
import org.bitcoins.node.networking.P2PClient
|
||||
import org.bitcoins.node.networking.peer.{
|
||||
|
@ -23,23 +20,22 @@ import org.bitcoins.node.models.BroadcastAbleTransactionDAO
|
|||
import slick.jdbc.SQLiteProfile
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
import org.bitcoins.db.P2PLogger
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
|
||||
case class SpvNode(
|
||||
peer: Peer,
|
||||
chainApi: ChainApi,
|
||||
bloomFilter: BloomFilter,
|
||||
callbacks: SpvNodeCallbacks = SpvNodeCallbacks.empty
|
||||
)(
|
||||
implicit system: ActorSystem,
|
||||
nodeAppConfig: NodeAppConfig,
|
||||
chainAppConfig: ChainAppConfig)
|
||||
extends BitcoinSLogger {
|
||||
)(implicit system: ActorSystem, nodeAppConfig: NodeAppConfig)
|
||||
extends P2PLogger {
|
||||
import system.dispatcher
|
||||
|
||||
private val txDAO = BroadcastAbleTransactionDAO(SQLiteProfile)
|
||||
|
||||
private val peerMsgRecv =
|
||||
PeerMessageReceiver.newReceiver(callbacks)
|
||||
PeerMessageReceiver.newReceiver(chainApi, callbacks)
|
||||
|
||||
private val client: P2PClient =
|
||||
P2PClient(context = system, peer = peer, peerMessageReceiver = peerMsgRecv)
|
||||
|
|
|
@ -7,13 +7,24 @@ import scala.concurrent.Future
|
|||
import org.bitcoins.node.db.NodeDbManagement
|
||||
import scala.util.Failure
|
||||
import scala.util.Success
|
||||
import java.nio.file.Path
|
||||
|
||||
case class NodeAppConfig(private val confs: Config*) extends AppConfig {
|
||||
override val configOverrides: List[Config] = confs.toList
|
||||
override protected def moduleName: String = "node"
|
||||
override protected type ConfigType = NodeAppConfig
|
||||
override protected def newConfigOfType(configs: Seq[Config]): NodeAppConfig =
|
||||
NodeAppConfig(configs: _*)
|
||||
/** Configuration for the Bitcoin-S node
|
||||
* @param directory The data directory of the node
|
||||
* @param confs Optional sequence of configuration overrides
|
||||
*/
|
||||
case class NodeAppConfig(
|
||||
private val directory: Path,
|
||||
private val confs: Config*)
|
||||
extends AppConfig {
|
||||
override protected[bitcoins] def configOverrides: List[Config] = confs.toList
|
||||
override protected[bitcoins] val moduleName: String = "node"
|
||||
override protected[bitcoins] type ConfigType = NodeAppConfig
|
||||
override protected[bitcoins] def newConfigOfType(
|
||||
configs: Seq[Config]): NodeAppConfig =
|
||||
NodeAppConfig(directory, configs: _*)
|
||||
|
||||
protected[bitcoins] def baseDatadir: Path = directory
|
||||
|
||||
/**
|
||||
* Ensures correct tables and other required information is in
|
||||
|
@ -31,3 +42,13 @@ case class NodeAppConfig(private val confs: Config*) extends AppConfig {
|
|||
initF
|
||||
}
|
||||
}
|
||||
|
||||
object NodeAppConfig {
|
||||
|
||||
/** Constructs a node configuration from the default Bitcoin-S
|
||||
* data directory and given list of configuration overrides.
|
||||
*/
|
||||
def fromDefaultDatadir(confs: Config*): NodeAppConfig =
|
||||
NodeAppConfig(AppConfig.DEFAULT_BITCOIN_S_DATADIR, confs: _*)
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import akka.io.{IO, Tcp}
|
|||
import akka.util.ByteString
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.p2p.NetworkMessage
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.p2p.NetworkPayload
|
||||
import org.bitcoins.node.models.Peer
|
||||
import org.bitcoins.node.networking.peer.PeerMessageReceiver
|
||||
|
@ -14,6 +13,9 @@ import org.bitcoins.node.util.BitcoinSpvNodeUtil
|
|||
import scodec.bits.ByteVector
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import akka.util.CompactByteString
|
||||
import scala.annotation.tailrec
|
||||
import scala.util._
|
||||
import org.bitcoins.db.P2PLogger
|
||||
|
||||
/**
|
||||
* This actor is responsible for creating a connection,
|
||||
|
@ -50,7 +52,7 @@ case class P2PClientActor(
|
|||
peerMsgHandlerReceiver: PeerMessageReceiver
|
||||
)(implicit config: NodeAppConfig)
|
||||
extends Actor
|
||||
with BitcoinSLogger {
|
||||
with P2PLogger {
|
||||
|
||||
/**
|
||||
* The manager is an actor that handles the underlying low level I/O resources (selectors, channels)
|
||||
|
@ -179,7 +181,7 @@ case class P2PClientActor(
|
|||
val bytes: ByteVector = unalignedBytes ++ byteVec
|
||||
logger.trace(s"Bytes for message parsing: ${bytes.toHex}")
|
||||
val (messages, newUnalignedBytes) =
|
||||
BitcoinSpvNodeUtil.parseIndividualMessages(bytes)
|
||||
P2PClient.parseIndividualMessages(bytes)
|
||||
|
||||
logger.debug({
|
||||
val length = messages.length
|
||||
|
@ -237,7 +239,7 @@ case class P2PClientActor(
|
|||
|
||||
case class P2PClient(actor: ActorRef, peer: Peer)
|
||||
|
||||
object P2PClient {
|
||||
object P2PClient extends P2PLogger {
|
||||
|
||||
def props(peer: Peer, peerMsgHandlerReceiver: PeerMessageReceiver)(
|
||||
implicit config: NodeAppConfig
|
||||
|
@ -256,4 +258,48 @@ object P2PClient {
|
|||
P2PClient(actorRef, peer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Akka sends messages as one byte stream. There is not a 1 to 1 relationship between byte streams received and
|
||||
* bitcoin protocol messages. This function parses our byte stream into individual network messages
|
||||
*
|
||||
* @param bytes the bytes that need to be parsed into individual messages
|
||||
* @return the parsed [[NetworkMessage]]'s and the unaligned bytes that did not parse to a message
|
||||
*/
|
||||
private[bitcoins] def parseIndividualMessages(bytes: ByteVector)(
|
||||
implicit conf: NodeAppConfig): (List[NetworkMessage], ByteVector) = {
|
||||
@tailrec
|
||||
def loop(
|
||||
remainingBytes: ByteVector,
|
||||
accum: List[NetworkMessage]): (List[NetworkMessage], ByteVector) = {
|
||||
if (remainingBytes.length <= 0) {
|
||||
(accum.reverse, remainingBytes)
|
||||
} else {
|
||||
val messageTry = Try(NetworkMessage(remainingBytes))
|
||||
messageTry match {
|
||||
case Success(message) =>
|
||||
if (message.header.payloadSize.toInt != message.payload.bytes.size) {
|
||||
//this means our tcp frame was not aligned, therefore put the message back in the
|
||||
//buffer and wait for the remaining bytes
|
||||
(accum.reverse, remainingBytes)
|
||||
} else {
|
||||
val newRemainingBytes = remainingBytes.slice(
|
||||
message.bytes.length,
|
||||
remainingBytes.length)
|
||||
loop(newRemainingBytes, message :: accum)
|
||||
}
|
||||
case Failure(exception) =>
|
||||
logger.error(
|
||||
"Failed to parse network message, could be because TCP frame isn't aligned",
|
||||
exception)
|
||||
//this case means that our TCP frame was not aligned with bitcoin protocol
|
||||
//return the unaligned bytes so we can apply them to the next tcp frame of bytes we receive
|
||||
//http://stackoverflow.com/a/37979529/967713
|
||||
(accum.reverse, remainingBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
val (messages, remainingBytes) = loop(bytes, Nil)
|
||||
(messages, remainingBytes)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package org.bitcoins.node.networking.peer
|
||||
|
||||
import org.bitcoins.chain.api.ChainApi
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, FutureUtil}
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.core.p2p.{DataPayload, HeadersMessage, InventoryMessage}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
@ -21,21 +18,20 @@ import slick.jdbc.SQLiteProfile
|
|||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.core.p2p.TypeIdentifier
|
||||
import org.bitcoins.core.p2p.MsgUnassigned
|
||||
import org.bitcoins.db.P2PLogger
|
||||
|
||||
/** This actor is meant to handle a [[org.bitcoins.core.p2p.DataPayload DataPayload]]
|
||||
* that a peer to sent to us on the p2p network, for instance, if we a receive a
|
||||
* [[org.bitcoins.core.p2p.HeadersMessage HeadersMessage]] we should store those headers in our database
|
||||
*/
|
||||
class DataMessageHandler(callbacks: SpvNodeCallbacks)(
|
||||
class DataMessageHandler(callbacks: SpvNodeCallbacks, chainHandler: ChainApi)(
|
||||
implicit ec: ExecutionContext,
|
||||
chainConf: ChainAppConfig,
|
||||
nodeConf: NodeAppConfig)
|
||||
extends BitcoinSLogger {
|
||||
appConfig: NodeAppConfig)
|
||||
extends P2PLogger {
|
||||
|
||||
private val callbackNum = callbacks.onBlockReceived.length + callbacks.onMerkleBlockReceived.length + callbacks.onTxReceived.length
|
||||
logger.debug(s"Given $callbackNum of callback(s)")
|
||||
|
||||
private val blockHeaderDAO: BlockHeaderDAO = BlockHeaderDAO()
|
||||
private val txDAO = BroadcastAbleTransactionDAO(SQLiteProfile)
|
||||
|
||||
def handleDataPayload(
|
||||
|
@ -76,9 +72,7 @@ class DataMessageHandler(callbacks: SpvNodeCallbacks)(
|
|||
logger.trace(
|
||||
s"Received headers message with ${headersMsg.count.toInt} headers")
|
||||
val headers = headersMsg.headers
|
||||
val chainApi: ChainApi =
|
||||
ChainHandler(blockHeaderDAO, chainConfig = chainConf)
|
||||
val chainApiF = chainApi.processHeaders(headers)
|
||||
val chainApiF = chainHandler.processHeaders(headers)
|
||||
|
||||
chainApiF.map { newApi =>
|
||||
val lastHeader = headers.last
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package org.bitcoins.node.networking.peer
|
||||
|
||||
import akka.actor.ActorRefFactory
|
||||
import org.bitcoins.chain.config.ChainAppConfig
|
||||
import org.bitcoins.core.p2p.NetworkMessage
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.core.p2p._
|
||||
import org.bitcoins.node.models.Peer
|
||||
|
@ -17,6 +15,8 @@ import org.bitcoins.node.networking.peer.PeerMessageReceiverState.{
|
|||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
import org.bitcoins.node.SpvNodeCallbacks
|
||||
import org.bitcoins.db.P2PLogger
|
||||
import org.bitcoins.chain.api.ChainApi
|
||||
|
||||
/**
|
||||
* Responsible for receiving messages from a peer on the
|
||||
|
@ -27,12 +27,10 @@ import org.bitcoins.node.SpvNodeCallbacks
|
|||
*/
|
||||
class PeerMessageReceiver(
|
||||
state: PeerMessageReceiverState,
|
||||
callbacks: SpvNodeCallbacks
|
||||
)(
|
||||
implicit ref: ActorRefFactory,
|
||||
nodeAppConfig: NodeAppConfig,
|
||||
chainAppConfig: ChainAppConfig)
|
||||
extends BitcoinSLogger {
|
||||
callbacks: SpvNodeCallbacks,
|
||||
chainHandler: ChainApi
|
||||
)(implicit ref: ActorRefFactory, nodeAppConfig: NodeAppConfig)
|
||||
extends P2PLogger {
|
||||
|
||||
import ref.dispatcher
|
||||
|
||||
|
@ -138,7 +136,7 @@ class PeerMessageReceiver(
|
|||
private def handleDataPayload(
|
||||
payload: DataPayload,
|
||||
sender: PeerMessageSender): Unit = {
|
||||
val dataMsgHandler = new DataMessageHandler(callbacks)
|
||||
val dataMsgHandler = new DataMessageHandler(callbacks, chainHandler)
|
||||
//else it means we are receiving this data payload from a peer,
|
||||
//we need to handle it
|
||||
dataMsgHandler.handleDataPayload(payload, sender)
|
||||
|
@ -233,18 +231,20 @@ object PeerMessageReceiver {
|
|||
|
||||
def apply(
|
||||
state: PeerMessageReceiverState,
|
||||
chainHandler: ChainApi,
|
||||
callbacks: SpvNodeCallbacks = SpvNodeCallbacks.empty)(
|
||||
implicit ref: ActorRefFactory,
|
||||
nodeAppConfig: NodeAppConfig,
|
||||
chainAppConfig: ChainAppConfig
|
||||
): PeerMessageReceiver = {
|
||||
new PeerMessageReceiver(state, callbacks)
|
||||
nodeAppConfig: NodeAppConfig): PeerMessageReceiver = {
|
||||
new PeerMessageReceiver(state, callbacks, chainHandler)
|
||||
}
|
||||
|
||||
def newReceiver(callbacks: SpvNodeCallbacks = SpvNodeCallbacks.empty)(
|
||||
def newReceiver(
|
||||
chainHandler: ChainApi,
|
||||
callbacks: SpvNodeCallbacks = SpvNodeCallbacks.empty)(
|
||||
implicit nodeAppConfig: NodeAppConfig,
|
||||
chainAppConfig: ChainAppConfig,
|
||||
ref: ActorRefFactory): PeerMessageReceiver = {
|
||||
new PeerMessageReceiver(state = PeerMessageReceiverState.fresh(), callbacks)
|
||||
new PeerMessageReceiver(state = PeerMessageReceiverState.fresh(),
|
||||
callbacks,
|
||||
chainHandler)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package org.bitcoins.node.networking.peer
|
||||
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.p2p.{VerAckMessage, VersionMessage}
|
||||
import org.bitcoins.node.networking.P2PClient
|
||||
|
||||
import scala.concurrent.{Future, Promise}
|
||||
|
||||
sealed abstract class PeerMessageReceiverState extends BitcoinSLogger {
|
||||
sealed abstract class PeerMessageReceiverState {
|
||||
|
||||
/** This promise gets completed when we receive a
|
||||
* [[akka.io.Tcp.Connected]] message from [[org.bitcoins.node.networking.P2PClient P2PClient]]
|
||||
|
|
|
@ -4,14 +4,14 @@ import akka.actor.ActorRef
|
|||
import akka.io.Tcp
|
||||
import org.bitcoins.core.crypto.DoubleSha256Digest
|
||||
import org.bitcoins.core.p2p.NetworkMessage
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.p2p._
|
||||
import org.bitcoins.node.networking.P2PClient
|
||||
import org.bitcoins.node.config.NodeAppConfig
|
||||
import org.bitcoins.core.protocol.transaction.Transaction
|
||||
import org.bitcoins.db.P2PLogger
|
||||
|
||||
case class PeerMessageSender(client: P2PClient)(implicit conf: NodeAppConfig)
|
||||
extends BitcoinSLogger {
|
||||
extends P2PLogger {
|
||||
private val socket = client.peer.socket
|
||||
|
||||
/** Initiates a connection with the given peer */
|
||||
|
|
|
@ -30,6 +30,7 @@ object Deps {
|
|||
val uPickleV = "0.7.4"
|
||||
val akkaHttpUpickleV = "1.27.0"
|
||||
val uJsonV = uPickleV // Li Haoyi ecosystem does common versioning
|
||||
val sourcecodeV = "0.1.7"
|
||||
|
||||
// CLI deps
|
||||
val scoptV = "4.0.0-RC2"
|
||||
|
@ -44,7 +45,6 @@ object Deps {
|
|||
val akkaHttp = "com.typesafe.akka" %% "akka-http" % V.akkav withSources () withJavadoc ()
|
||||
val akkaStream = "com.typesafe.akka" %% "akka-stream" % V.akkaStreamv withSources () withJavadoc ()
|
||||
val akkaActor = "com.typesafe.akka" %% "akka-actor" % V.akkaStreamv withSources () withJavadoc ()
|
||||
val akkaLog = "com.typesafe.akka" %% "akka-slf4j" % V.akkaStreamv
|
||||
|
||||
val playJson = "com.typesafe.play" %% "play-json" % V.playv withSources () withJavadoc ()
|
||||
val typesafeConfig = "com.typesafe" % "config" % V.typesafeConfigV withSources () withJavadoc ()
|
||||
|
@ -65,6 +65,9 @@ object Deps {
|
|||
// serializing to and from JSON
|
||||
val uPickle = "com.lihaoyi" %% "upickle" % V.uPickleV
|
||||
|
||||
// get access to reflection data at compile-time
|
||||
val sourcecode = "com.lihaoyi" %% "sourcecode" % V.sourcecodeV
|
||||
|
||||
// make akka-http play nice with upickle
|
||||
val akkaHttpUpickle = "de.heikoseeberger" %% "akka-http-upickle" % V.akkaHttpUpickleV
|
||||
|
||||
|
@ -93,12 +96,10 @@ object Deps {
|
|||
}
|
||||
|
||||
val chain = List(
|
||||
Compile.slf4j
|
||||
Compile.logback
|
||||
)
|
||||
|
||||
val chainTest = List(
|
||||
Test.logback
|
||||
)
|
||||
val chainTest = List()
|
||||
|
||||
val core = List(
|
||||
Compile.bouncycastle,
|
||||
|
@ -151,6 +152,8 @@ object Deps {
|
|||
|
||||
val dbCommons = List(
|
||||
Compile.slick,
|
||||
Compile.sourcecode,
|
||||
Compile.logback,
|
||||
Compile.sqlite,
|
||||
Compile.slickHikari
|
||||
)
|
||||
|
@ -169,7 +172,6 @@ object Deps {
|
|||
Compile.akkaHttpUpickle,
|
||||
Compile.uPickle,
|
||||
Compile.logback,
|
||||
Compile.akkaLog,
|
||||
Compile.akkaHttp
|
||||
)
|
||||
|
||||
|
@ -198,7 +200,6 @@ object Deps {
|
|||
|
||||
val nodeTest = List(
|
||||
Test.akkaTestkit,
|
||||
Test.logback,
|
||||
Test.scalaTest
|
||||
)
|
||||
|
||||
|
@ -215,11 +216,11 @@ object Deps {
|
|||
)
|
||||
|
||||
val wallet = List(
|
||||
Compile.uJson
|
||||
Compile.uJson,
|
||||
Compile.logback
|
||||
)
|
||||
|
||||
val walletTest = List(
|
||||
Test.logback,
|
||||
Test.akkaTestkit
|
||||
)
|
||||
|
||||
|
|
|
@ -13,15 +13,7 @@ object BitcoinSTestAppConfig {
|
|||
*/
|
||||
def getTestConfig(config: Config*): BitcoinSAppConfig = {
|
||||
val tmpDir = Files.createTempDirectory("bitcoin-s-")
|
||||
val confStr = s"""
|
||||
| bitcoin-s {
|
||||
| datadir = $tmpDir
|
||||
| }
|
||||
|
|
||||
|""".stripMargin
|
||||
val conf = ConfigFactory.parseString(confStr)
|
||||
val allConfs = conf +: config
|
||||
BitcoinSAppConfig(allConfs: _*)
|
||||
BitcoinSAppConfig(tmpDir, config: _*)
|
||||
}
|
||||
|
||||
sealed trait ProjectType
|
||||
|
|
|
@ -184,7 +184,7 @@ trait ChainUnitTest
|
|||
def createPopulatedChainHandler(): Future[ChainHandler] = {
|
||||
for {
|
||||
blockHeaderDAO <- ChainUnitTest.createPopulatedBlockHeaderDAO()
|
||||
} yield ChainHandler(blockHeaderDAO = blockHeaderDAO, appConfig)
|
||||
} yield ChainHandler(blockHeaderDAO = blockHeaderDAO)
|
||||
}
|
||||
|
||||
def withPopulatedChainHandler(test: OneArgAsyncTest): FutureOutcome = {
|
||||
|
@ -415,7 +415,7 @@ object ChainUnitTest extends BitcoinSLogger {
|
|||
ec: ExecutionContext): ChainHandler = {
|
||||
lazy val blockHeaderDAO = BlockHeaderDAO()
|
||||
|
||||
ChainHandler(blockHeaderDAO = blockHeaderDAO, appConfig)
|
||||
ChainHandler(blockHeaderDAO)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@ import org.scalatest.{
|
|||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||
import org.bitcoins.chain.blockchain.ChainHandler
|
||||
import org.bitcoins.chain.models.BlockHeaderDAO
|
||||
import org.bitcoins.node.SpvNodeCallbacks
|
||||
|
||||
trait NodeUnitTest
|
||||
extends BitcoinSFixture
|
||||
|
@ -67,8 +70,11 @@ trait NodeUnitTest
|
|||
lazy val bitcoindPeerF = startedBitcoindF.map(NodeTestUtil.getBitcoindPeer)
|
||||
|
||||
def buildPeerMessageReceiver(): PeerMessageReceiver = {
|
||||
|
||||
val dao = BlockHeaderDAO()
|
||||
val chainHandler = ChainHandler(dao)
|
||||
val receiver =
|
||||
PeerMessageReceiver.newReceiver()
|
||||
PeerMessageReceiver.newReceiver(chainHandler, SpvNodeCallbacks.empty)
|
||||
receiver
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,19 @@ import org.bitcoins.core.config.MainNet
|
|||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import java.nio.file.Paths
|
||||
import org.bitcoins.core.hd.HDPurposes
|
||||
import java.nio.file.Files
|
||||
import ch.qos.logback.classic.Level
|
||||
import java.nio.file.Path
|
||||
import scala.util.Properties
|
||||
|
||||
class WalletAppConfigTest extends BitcoinSUnitTest {
|
||||
val config = WalletAppConfig()
|
||||
|
||||
val tempDir = Files.createTempDirectory("bitcoin-s")
|
||||
val config = WalletAppConfig(directory = tempDir)
|
||||
|
||||
it must "resolve DB connections correctly " in {
|
||||
assert(config.dbPath.startsWith(Properties.tmpDir))
|
||||
}
|
||||
|
||||
it must "be overridable" in {
|
||||
assert(config.network == RegTest)
|
||||
|
@ -27,27 +37,25 @@ class WalletAppConfigTest extends BitcoinSUnitTest {
|
|||
}
|
||||
|
||||
it should "not matter how the overrides are passed in" in {
|
||||
val dir = Paths.get("/", "bar", "biz")
|
||||
val overrider = ConfigFactory.parseString(s"""
|
||||
|bitcoin-s {
|
||||
| datadir = $dir
|
||||
| network = mainnet
|
||||
|}
|
||||
|""".stripMargin)
|
||||
|
||||
val throughConstuctor = WalletAppConfig(overrider)
|
||||
val throughConstuctor = WalletAppConfig(tempDir, overrider)
|
||||
val throughWithOverrides = config.withOverrides(overrider)
|
||||
assert(throughWithOverrides.network == MainNet)
|
||||
assert(throughWithOverrides.network == throughConstuctor.network)
|
||||
|
||||
assert(throughWithOverrides.datadir.startsWith(dir))
|
||||
assert(throughWithOverrides.datadir == throughConstuctor.datadir)
|
||||
|
||||
}
|
||||
|
||||
it must "be overridable without screwing up other options" in {
|
||||
val dir = Paths.get("/", "foo", "bar")
|
||||
val otherConf = ConfigFactory.parseString(s"bitcoin-s.datadir = $dir")
|
||||
val otherConf = ConfigFactory.parseString(
|
||||
s"bitcoin-s.wallet.defaultAccountType = segwit"
|
||||
)
|
||||
val thirdConf = ConfigFactory.parseString(
|
||||
s"bitcoin-s.wallet.defaultAccountType = nested-segwit")
|
||||
|
||||
|
@ -55,9 +63,11 @@ class WalletAppConfigTest extends BitcoinSUnitTest {
|
|||
|
||||
val twiceOverriden = overriden.withOverrides(thirdConf)
|
||||
|
||||
assert(overriden.datadir.startsWith(dir))
|
||||
assert(twiceOverriden.datadir.startsWith(dir))
|
||||
assert(overriden.defaultAccountKind == HDPurposes.SegWit)
|
||||
assert(twiceOverriden.defaultAccountKind == HDPurposes.NestedSegWit)
|
||||
|
||||
assert(config.datadir == overriden.datadir)
|
||||
assert(twiceOverriden.datadir == overriden.datadir)
|
||||
}
|
||||
|
||||
it must "be overridable with multiple levels" in {
|
||||
|
@ -65,6 +75,30 @@ class WalletAppConfigTest extends BitcoinSUnitTest {
|
|||
val mainnet = ConfigFactory.parseString("bitcoin-s.network = mainnet")
|
||||
val overriden: WalletAppConfig = config.withOverrides(testnet, mainnet)
|
||||
assert(overriden.network == MainNet)
|
||||
}
|
||||
|
||||
it must "have user data directory configuration take precedence" in {
|
||||
|
||||
val tempDir = Files.createTempDirectory("bitcoin-s")
|
||||
val tempFile = Files.createFile(tempDir.resolve("bitcoin-s.conf"))
|
||||
val confStr = """
|
||||
| bitcoin-s {
|
||||
| network = testnet3
|
||||
|
|
||||
| logging {
|
||||
| level = off
|
||||
|
|
||||
| p2p = warn
|
||||
| }
|
||||
| }
|
||||
""".stripMargin
|
||||
val _ = Files.write(tempFile, confStr.getBytes())
|
||||
|
||||
val appConfig = WalletAppConfig(directory = tempDir)
|
||||
|
||||
assert(appConfig.datadir == tempDir.resolve("testnet3"))
|
||||
assert(appConfig.network == TestNet3)
|
||||
assert(appConfig.logLevel == Level.OFF)
|
||||
assert(appConfig.p2pLogLevel == Level.WARN)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ import scodec.bits.ByteVector
|
|||
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
case class EncryptedMnemonic(value: AesEncryptedData, salt: AesSalt)
|
||||
extends BitcoinSLogger {
|
||||
case class EncryptedMnemonic(value: AesEncryptedData, salt: AesSalt) {
|
||||
|
||||
def toMnemonic(password: AesPassword): Try[MnemonicCode] = {
|
||||
import org.bitcoins.core.util.EitherUtil.EitherOps._
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.bitcoins.core.currency._
|
|||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.util.{BitcoinSLogger, EitherUtil}
|
||||
import org.bitcoins.core.util.EitherUtil
|
||||
import org.bitcoins.core.wallet.builder.BitcoinTxBuilder
|
||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||
import org.bitcoins.core.wallet.utxo.BitcoinUTXOSpendingInfo
|
||||
|
@ -17,11 +17,9 @@ import scodec.bits.BitVector
|
|||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.{Failure, Success, Try}
|
||||
import org.bitcoins.db.KeyHandlingLogger
|
||||
|
||||
sealed abstract class Wallet
|
||||
extends LockedWallet
|
||||
with UnlockedWalletApi
|
||||
with BitcoinSLogger {
|
||||
sealed abstract class Wallet extends LockedWallet with UnlockedWalletApi {
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
|
@ -140,7 +138,7 @@ sealed abstract class Wallet
|
|||
}
|
||||
|
||||
// todo: create multiple wallets, need to maintain multiple databases
|
||||
object Wallet extends CreateWalletApi with BitcoinSLogger {
|
||||
object Wallet extends CreateWalletApi with KeyHandlingLogger {
|
||||
|
||||
private case class WalletImpl(
|
||||
mnemonicCode: MnemonicCode
|
||||
|
@ -235,6 +233,7 @@ object Wallet extends CreateWalletApi with BitcoinSLogger {
|
|||
private def createRootAccount(wallet: Wallet, purpose: HDPurpose)(
|
||||
implicit config: WalletAppConfig,
|
||||
ec: ExecutionContext): Future[AccountDb] = {
|
||||
|
||||
val coin =
|
||||
HDCoin(purpose, HDUtil.getCoinType(config.network))
|
||||
val account = HDAccount(coin, 0)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.bitcoins.wallet
|
||||
|
||||
import scala.collection.JavaConverters._
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.crypto.AesPassword
|
||||
import java.nio.file.Files
|
||||
import org.bitcoins.core.crypto.MnemonicCode
|
||||
|
@ -15,9 +14,10 @@ import java.nio.file.Path
|
|||
import scala.util.Try
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.core.crypto.AesIV
|
||||
import org.bitcoins.db.KeyHandlingLogger
|
||||
|
||||
// what do we do if seed exists? error if they aren't equal?
|
||||
object WalletStorage extends BitcoinSLogger {
|
||||
object WalletStorage extends KeyHandlingLogger {
|
||||
|
||||
/** Checks if a wallet seed exists in datadir */
|
||||
def seedExists()(implicit config: WalletAppConfig): Boolean = {
|
||||
|
@ -183,6 +183,7 @@ object WalletStorage extends BitcoinSLogger {
|
|||
def decryptMnemonicFromDisk(passphrase: AesPassword)(
|
||||
implicit
|
||||
config: WalletAppConfig): ReadMnemonicResult = {
|
||||
|
||||
val encryptedEither = readEncryptedMnemonicFromDisk()
|
||||
|
||||
import org.bitcoins.core.util.EitherUtil.EitherOps._
|
||||
|
|
|
@ -10,13 +10,24 @@ import java.nio.file.Files
|
|||
import org.bitcoins.core.hd.HDPurpose
|
||||
import org.bitcoins.core.hd.HDPurposes
|
||||
import org.bitcoins.core.hd.AddressType
|
||||
import java.nio.file.Path
|
||||
|
||||
case class WalletAppConfig(private val conf: Config*) extends AppConfig {
|
||||
override val configOverrides: List[Config] = conf.toList
|
||||
override def moduleName: String = "wallet"
|
||||
override type ConfigType = WalletAppConfig
|
||||
override def newConfigOfType(configs: Seq[Config]): WalletAppConfig =
|
||||
WalletAppConfig(configs: _*)
|
||||
/** Configuration for the Bitcoin-S wallet
|
||||
* @param directory The data directory of the wallet
|
||||
* @param confs Optional sequence of configuration overrides
|
||||
*/
|
||||
case class WalletAppConfig(
|
||||
private val directory: Path,
|
||||
private val conf: Config*)
|
||||
extends AppConfig {
|
||||
override protected[bitcoins] def configOverrides: List[Config] = conf.toList
|
||||
override protected[bitcoins] def moduleName: String = "wallet"
|
||||
override protected[bitcoins] type ConfigType = WalletAppConfig
|
||||
override protected[bitcoins] def newConfigOfType(
|
||||
configs: Seq[Config]): WalletAppConfig =
|
||||
WalletAppConfig(directory, configs: _*)
|
||||
|
||||
protected[bitcoins] def baseDatadir: Path = directory
|
||||
|
||||
lazy val defaultAccountKind: HDPurpose =
|
||||
config.getString("wallet.defaultAccountType") match {
|
||||
|
@ -63,3 +74,12 @@ case class WalletAppConfig(private val conf: Config*) extends AppConfig {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
object WalletAppConfig {
|
||||
|
||||
/** Constructs a wallet configuration from the default Bitcoin-S
|
||||
* data directory and given list of configuration overrides.
|
||||
*/
|
||||
def fromDefaultDatadir(confs: Config*): WalletAppConfig =
|
||||
WalletAppConfig(AppConfig.DEFAULT_BITCOIN_S_DATADIR, confs: _*)
|
||||
}
|
||||
|
|
|
@ -22,12 +22,14 @@ import org.bitcoins.core.protocol.script.ScriptPubKey
|
|||
import org.bitcoins.core.protocol.transaction.TransactionOutPoint
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.hd.AddressType
|
||||
import org.bitcoins.db.KeyHandlingLogger
|
||||
|
||||
/**
|
||||
* Provides functionality related to addresses. This includes
|
||||
* enumeratng and creating them, primarily.
|
||||
*/
|
||||
private[wallet] trait AddressHandling { self: LockedWallet =>
|
||||
private[wallet] trait AddressHandling extends KeyHandlingLogger {
|
||||
self: LockedWallet =>
|
||||
|
||||
override def listAddresses(): Future[Vector[AddressDb]] =
|
||||
addressDAO.findAll()
|
||||
|
|
|
@ -9,13 +9,15 @@ import org.bitcoins.wallet.api.AddUtxoSuccess
|
|||
import org.bitcoins.wallet.api.AddUtxoError
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.db.KeyHandlingLogger
|
||||
|
||||
/** Provides functionality for processing transactions. This
|
||||
* includes importing UTXOs spent to our wallet, updating
|
||||
* confirmation counts and marking UTXOs as spent when
|
||||
* spending from our wallet
|
||||
*/
|
||||
private[wallet] trait TransactionProcessing { self: LockedWallet =>
|
||||
private[wallet] trait TransactionProcessing extends KeyHandlingLogger {
|
||||
self: LockedWallet =>
|
||||
/////////////////////
|
||||
// Public facing API
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.bitcoins.core.protocol.BitcoinAddress
|
|||
import scala.util.Success
|
||||
import scala.util.Failure
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.db.KeyHandlingLogger
|
||||
|
||||
/**
|
||||
* Provides functionality related to handling UTXOs in our wallet.
|
||||
|
@ -29,7 +30,8 @@ import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
|||
* UTXOs in the wallet and importing a UTXO into the wallet for later
|
||||
* spending.
|
||||
*/
|
||||
private[wallet] trait UtxoHandling { self: LockedWallet =>
|
||||
private[wallet] trait UtxoHandling extends KeyHandlingLogger {
|
||||
self: LockedWallet =>
|
||||
|
||||
/** @inheritdoc */
|
||||
override def listUtxos(): Future[Vector[SpendingInfoDb]] =
|
||||
|
|
|
@ -15,8 +15,8 @@ import org.bitcoins.core.protocol.script.ScriptPubKey
|
|||
import org.bitcoins.core.hd.HDPurpose
|
||||
|
||||
case class AddressDAO()(
|
||||
implicit val ec: ExecutionContext,
|
||||
val appConfig: WalletAppConfig
|
||||
implicit ec: ExecutionContext,
|
||||
config: WalletAppConfig
|
||||
) extends CRUD[AddressDb, BitcoinAddress] {
|
||||
import org.bitcoins.db.DbCommonsColumnMappers._
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.bitcoins.core.hd.HDPath
|
|||
|
||||
import org.bitcoins.core.hd.SegWitHDPath
|
||||
import org.bitcoins.core.crypto.BIP39Seed
|
||||
import org.bitcoins.core.util.BitcoinSLogger
|
||||
import org.bitcoins.core.hd.LegacyHDPath
|
||||
import org.bitcoins.core.crypto.DoubleSha256DigestBE
|
||||
|
||||
|
@ -85,9 +84,7 @@ case class LegacySpendingInfo(
|
|||
* we need to derive the private keys, given
|
||||
* the root wallet seed.
|
||||
*/
|
||||
sealed trait SpendingInfoDb
|
||||
extends DbRowAutoInc[SpendingInfoDb]
|
||||
with BitcoinSLogger {
|
||||
sealed trait SpendingInfoDb extends DbRowAutoInc[SpendingInfoDb] {
|
||||
|
||||
protected type PathType <: HDPath
|
||||
|
||||
|
@ -141,13 +138,6 @@ sealed trait SpendingInfoDb
|
|||
|
||||
val sign: Sign = Sign(privKey.signFunction, pubAtPath)
|
||||
|
||||
logger.info({
|
||||
val shortStr = s"${outPoint.txId.hex}:${outPoint.vout.toInt}"
|
||||
val detailsStr =
|
||||
s"scriptPubKey=${output.scriptPubKey}, amount=${output.value}, keyPath=${privKeyPath}, pubKey=${pubAtPath}"
|
||||
s"Converting DB UTXO $shortStr ($detailsStr) to spending info"
|
||||
})
|
||||
|
||||
BitcoinUTXOSpendingInfo(outPoint,
|
||||
output,
|
||||
List(sign),
|
||||
|
|
Loading…
Add table
Reference in a new issue