mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
2024 09 24 simplify wallet (#5685)
* WIP: Simplify wallet # Conflicts: # fee-provider/src/main/scala/org/bitcoins/feeprovider/FeeProviderFactory.scala # Conflicts: # wallet/src/main/scala/org/bitcoins/wallet/Wallet.scala * Get walletTest/test passing * Remove WalletApi.{start(),stop()} Conflicts: core/src/main/scala/org/bitcoins/core/api/wallet/WalletApi.scala wallet/src/main/scala/org/bitcoins/wallet/Wallet.scala wallet/src/main/scala/org/bitcoins/wallet/WalletHolder.scala * Cleanup RescanDLCTest * Move checkRootAccount into AccountHandling.scala * Fix rebase * Fix docs
This commit is contained in:
parent
7caea21b6a
commit
13a895efe9
@ -125,8 +125,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
|||||||
DLCWalletNeutrinoBackendLoader(
|
DLCWalletNeutrinoBackendLoader(
|
||||||
walletHolder,
|
walletHolder,
|
||||||
mockChainApi,
|
mockChainApi,
|
||||||
mockNode,
|
mockNode
|
||||||
feeRateApi
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +51,7 @@ class WalletRoutesSpec
|
|||||||
DLCWalletNeutrinoBackendLoader(
|
DLCWalletNeutrinoBackendLoader(
|
||||||
walletHolder,
|
walletHolder,
|
||||||
mockChainApi,
|
mockChainApi,
|
||||||
mockNode,
|
mockNode
|
||||||
feeRateApi
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val walletRoutes: WalletRoutes =
|
val walletRoutes: WalletRoutes =
|
||||||
|
@ -29,7 +29,6 @@ import org.bitcoins.core.util.TimeUtil
|
|||||||
import org.bitcoins.dlc.node.DLCNode
|
import org.bitcoins.dlc.node.DLCNode
|
||||||
import org.bitcoins.dlc.node.config.DLCNodeAppConfig
|
import org.bitcoins.dlc.node.config.DLCNodeAppConfig
|
||||||
import org.bitcoins.dlc.wallet.*
|
import org.bitcoins.dlc.wallet.*
|
||||||
import org.bitcoins.feeprovider.MempoolSpaceTarget.HourFeeTarget
|
|
||||||
import org.bitcoins.feeprovider.*
|
import org.bitcoins.feeprovider.*
|
||||||
import org.bitcoins.node.Node
|
import org.bitcoins.node.Node
|
||||||
import org.bitcoins.node.config.NodeAppConfig
|
import org.bitcoins.node.config.NodeAppConfig
|
||||||
@ -185,15 +184,6 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
|
|||||||
walletCreationTimeOpt = Some(creationTime)
|
walletCreationTimeOpt = Some(creationTime)
|
||||||
)(chainConf, system)
|
)(chainConf, system)
|
||||||
|
|
||||||
val defaultApi =
|
|
||||||
MempoolSpaceProvider(HourFeeTarget, network, torConf.socks5ProxyParams)
|
|
||||||
val feeProvider = FeeProviderFactory.getFeeProviderOrElse(
|
|
||||||
defaultApi,
|
|
||||||
conf.walletConf.feeProviderNameOpt,
|
|
||||||
conf.walletConf.feeProviderTargetOpt,
|
|
||||||
torConf.socks5ProxyParams,
|
|
||||||
network
|
|
||||||
)
|
|
||||||
// get our wallet
|
// get our wallet
|
||||||
val walletHolder = WalletHolder.empty
|
val walletHolder = WalletHolder.empty
|
||||||
val neutrinoWalletLoaderF = {
|
val neutrinoWalletLoaderF = {
|
||||||
@ -203,8 +193,7 @@ class BitcoinSServerMain(override val serverArgParser: ServerArgParser)(implicit
|
|||||||
val l = DLCWalletNeutrinoBackendLoader(
|
val l = DLCWalletNeutrinoBackendLoader(
|
||||||
walletHolder,
|
walletHolder,
|
||||||
chainApi,
|
chainApi,
|
||||||
nodeApi = node,
|
nodeApi = node
|
||||||
feeRateApi = feeProvider
|
|
||||||
)
|
)
|
||||||
walletLoaderApiOpt = Some(l)
|
walletLoaderApiOpt = Some(l)
|
||||||
l
|
l
|
||||||
|
@ -222,8 +222,7 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
|
|||||||
)
|
)
|
||||||
val pairedWallet = Wallet(
|
val pairedWallet = Wallet(
|
||||||
nodeApi = nodeApi,
|
nodeApi = nodeApi,
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind
|
||||||
feeRateApi = wallet.feeRateApi
|
|
||||||
)(wallet.walletConfig)
|
)(wallet.walletConfig)
|
||||||
|
|
||||||
walletCallbackP.success(pairedWallet)
|
walletCallbackP.success(pairedWallet)
|
||||||
@ -305,8 +304,7 @@ object BitcoindRpcBackendUtil extends BitcoinSLogger {
|
|||||||
walletCallbackP.future,
|
walletCallbackP.future,
|
||||||
chainCallbacksOpt
|
chainCallbacksOpt
|
||||||
),
|
),
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind
|
||||||
feeRateApi = wallet.feeRateApi
|
|
||||||
)(wallet.walletConfig, wallet.dlcConfig)
|
)(wallet.walletConfig, wallet.dlcConfig)
|
||||||
|
|
||||||
walletCallbackP.success(pairedWallet)
|
walletCallbackP.success(pairedWallet)
|
||||||
|
@ -50,7 +50,6 @@ sealed trait DLCWalletLoaderApi
|
|||||||
protected def loadWallet(
|
protected def loadWallet(
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi,
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
feeProviderApi: FeeRateApi,
|
|
||||||
walletNameOpt: Option[String],
|
walletNameOpt: Option[String],
|
||||||
aesPasswordOpt: Option[AesPassword]
|
aesPasswordOpt: Option[AesPassword]
|
||||||
)(implicit
|
)(implicit
|
||||||
@ -71,8 +70,7 @@ sealed trait DLCWalletLoaderApi
|
|||||||
_ <- dlcConfig.start()
|
_ <- dlcConfig.start()
|
||||||
dlcWallet <- dlcConfig.createDLCWallet(
|
dlcWallet <- dlcConfig.createDLCWallet(
|
||||||
nodeApi = nodeApi,
|
nodeApi = nodeApi,
|
||||||
chainQueryApi = chainQueryApi,
|
chainQueryApi = chainQueryApi
|
||||||
feeRateApi = feeProviderApi
|
|
||||||
)(walletConfig)
|
)(walletConfig)
|
||||||
} yield (dlcWallet, walletConfig, dlcConfig)
|
} yield (dlcWallet, walletConfig, dlcConfig)
|
||||||
}
|
}
|
||||||
@ -271,8 +269,7 @@ sealed trait DLCWalletLoaderApi
|
|||||||
case class DLCWalletNeutrinoBackendLoader(
|
case class DLCWalletNeutrinoBackendLoader(
|
||||||
walletHolder: WalletHolder,
|
walletHolder: WalletHolder,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi,
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit
|
)(implicit
|
||||||
override val conf: BitcoinSAppConfig,
|
override val conf: BitcoinSAppConfig,
|
||||||
override val system: ActorSystem
|
override val system: ActorSystem
|
||||||
@ -301,7 +298,6 @@ case class DLCWalletNeutrinoBackendLoader(
|
|||||||
(dlcWallet, walletConfig, dlcConfig) <- loadWallet(
|
(dlcWallet, walletConfig, dlcConfig) <- loadWallet(
|
||||||
chainQueryApi = chainQueryApi,
|
chainQueryApi = chainQueryApi,
|
||||||
nodeApi = nodeApi,
|
nodeApi = nodeApi,
|
||||||
feeProviderApi = feeRateApi,
|
|
||||||
walletNameOpt = walletNameOpt,
|
walletNameOpt = walletNameOpt,
|
||||||
aesPasswordOpt = aesPasswordOpt
|
aesPasswordOpt = aesPasswordOpt
|
||||||
)
|
)
|
||||||
@ -351,7 +347,6 @@ case class DLCWalletBitcoindBackendLoader(
|
|||||||
(dlcWallet, walletConfig, dlcConfig) <- loadWallet(
|
(dlcWallet, walletConfig, dlcConfig) <- loadWallet(
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind,
|
||||||
nodeApi = nodeApi,
|
nodeApi = nodeApi,
|
||||||
feeProviderApi = feeProvider,
|
|
||||||
walletNameOpt = walletNameOpt,
|
walletNameOpt = walletNameOpt,
|
||||||
aesPasswordOpt = aesPasswordOpt
|
aesPasswordOpt = aesPasswordOpt
|
||||||
)
|
)
|
||||||
|
@ -775,7 +775,7 @@ lazy val wallet = project
|
|||||||
name := "bitcoin-s-wallet",
|
name := "bitcoin-s-wallet",
|
||||||
libraryDependencies ++= Deps.wallet(scalaVersion.value)
|
libraryDependencies ++= Deps.wallet(scalaVersion.value)
|
||||||
)
|
)
|
||||||
.dependsOn(coreJVM, appCommons, dbCommons, keyManager, asyncUtilsJVM, tor)
|
.dependsOn(coreJVM, appCommons, dbCommons, keyManager, asyncUtilsJVM, feeProvider, tor)
|
||||||
|
|
||||||
lazy val walletTest = project
|
lazy val walletTest = project
|
||||||
.in(file("wallet-test"))
|
.in(file("wallet-test"))
|
||||||
|
@ -8,7 +8,6 @@ import org.bitcoins.core.currency.CurrencyUnit
|
|||||||
import org.bitcoins.core.hd.HDAccount
|
import org.bitcoins.core.hd.HDAccount
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
import org.bitcoins.core.protocol.transaction.Transaction
|
import org.bitcoins.core.protocol.transaction.Transaction
|
||||||
import org.bitcoins.core.util.StartStopAsync
|
|
||||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||||
import org.bitcoins.crypto.DoubleSha256DigestBE
|
import org.bitcoins.crypto.DoubleSha256DigestBE
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
* @see
|
* @see
|
||||||
* [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44]]
|
* [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44]]
|
||||||
*/
|
*/
|
||||||
trait WalletApi extends StartStopAsync[WalletApi] {
|
trait WalletApi {
|
||||||
def accountHandling: AccountHandlingApi
|
def accountHandling: AccountHandlingApi
|
||||||
def fundTxHandling: FundTransactionHandlingApi
|
def fundTxHandling: FundTransactionHandlingApi
|
||||||
def rescanHandling: RescanHandlingApi
|
def rescanHandling: RescanHandlingApi
|
||||||
@ -33,7 +32,7 @@ trait WalletApi extends StartStopAsync[WalletApi] {
|
|||||||
|
|
||||||
val nodeApi: NodeApi
|
val nodeApi: NodeApi
|
||||||
val chainQueryApi: ChainQueryApi
|
val chainQueryApi: ChainQueryApi
|
||||||
val feeRateApi: FeeRateApi
|
def feeRateApi: FeeRateApi
|
||||||
val creationTime: Instant
|
val creationTime: Instant
|
||||||
|
|
||||||
def broadcastTransaction(transaction: Transaction): Future[Unit] =
|
def broadcastTransaction(transaction: Transaction): Future[Unit] =
|
||||||
@ -41,10 +40,6 @@ trait WalletApi extends StartStopAsync[WalletApi] {
|
|||||||
|
|
||||||
def getFeeRate(): Future[FeeUnit] = feeRateApi.getFeeRate()
|
def getFeeRate(): Future[FeeUnit] = feeRateApi.getFeeRate()
|
||||||
|
|
||||||
def start(): Future[WalletApi]
|
|
||||||
|
|
||||||
def stop(): Future[WalletApi]
|
|
||||||
|
|
||||||
/** Gets the sum of all UTXOs in this wallet */
|
/** Gets the sum of all UTXOs in this wallet */
|
||||||
def getBalance()(implicit ec: ExecutionContext): Future[CurrencyUnit] = {
|
def getBalance()(implicit ec: ExecutionContext): Future[CurrencyUnit] = {
|
||||||
val confirmedF = getConfirmedBalance()
|
val confirmedF = getConfirmedBalance()
|
||||||
|
@ -6,8 +6,9 @@ import org.bitcoins.core.protocol.dlc.models.{
|
|||||||
DisjointUnionContractInfo,
|
DisjointUnionContractInfo,
|
||||||
SingleContractInfo
|
SingleContractInfo
|
||||||
}
|
}
|
||||||
|
import org.bitcoins.core.wallet.rescan.RescanState
|
||||||
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
import org.bitcoins.rpc.client.common.BitcoindRpcClient
|
||||||
import org.bitcoins.testkit.wallet.DLCWalletUtil._
|
import org.bitcoins.testkit.wallet.DLCWalletUtil.*
|
||||||
import org.bitcoins.testkit.wallet.{DLCWalletUtil, DualWalletTestCachedBitcoind}
|
import org.bitcoins.testkit.wallet.{DLCWalletUtil, DualWalletTestCachedBitcoind}
|
||||||
import org.scalatest.FutureOutcome
|
import org.scalatest.FutureOutcome
|
||||||
|
|
||||||
@ -55,7 +56,7 @@ class RescanDLCTest extends DualWalletTestCachedBitcoind {
|
|||||||
|
|
||||||
Vector(hash) <- bitcoind.generate(1)
|
Vector(hash) <- bitcoind.generate(1)
|
||||||
|
|
||||||
_ <- wallet.rescanHandling.rescanNeutrinoWallet(
|
rescanState <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||||
startOpt = None,
|
startOpt = None,
|
||||||
endOpt = Some(BlockHash(hash)),
|
endOpt = Some(BlockHash(hash)),
|
||||||
addressBatchSize = 20,
|
addressBatchSize = 20,
|
||||||
@ -64,6 +65,7 @@ class RescanDLCTest extends DualWalletTestCachedBitcoind {
|
|||||||
)
|
)
|
||||||
|
|
||||||
postStatus <- getDLCStatus(wallet)
|
postStatus <- getDLCStatus(wallet)
|
||||||
|
_ <- RescanState.awaitRescanDone(rescanState)
|
||||||
} yield assert(postStatus.state == DLCState.Claimed)
|
} yield assert(postStatus.state == DLCState.Claimed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +102,7 @@ class RescanDLCTest extends DualWalletTestCachedBitcoind {
|
|||||||
|
|
||||||
Vector(hash) <- bitcoind.generate(1)
|
Vector(hash) <- bitcoind.generate(1)
|
||||||
|
|
||||||
_ <- wallet.rescanHandling.rescanNeutrinoWallet(
|
rescanState <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||||
startOpt = None,
|
startOpt = None,
|
||||||
endOpt = Some(BlockHash(hash)),
|
endOpt = Some(BlockHash(hash)),
|
||||||
addressBatchSize = 20,
|
addressBatchSize = 20,
|
||||||
@ -109,6 +111,7 @@ class RescanDLCTest extends DualWalletTestCachedBitcoind {
|
|||||||
)
|
)
|
||||||
|
|
||||||
postStatus <- getDLCStatus(wallet)
|
postStatus <- getDLCStatus(wallet)
|
||||||
|
_ <- RescanState.awaitRescanDone(rescanState)
|
||||||
} yield assert(postStatus.state == DLCState.RemoteClaimed)
|
} yield assert(postStatus.state == DLCState.RemoteClaimed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import org.bitcoins.commons.config.{AppConfigFactoryBase, ConfigOps}
|
|||||||
import org.bitcoins.core.api.CallbackConfig
|
import org.bitcoins.core.api.CallbackConfig
|
||||||
import org.bitcoins.core.api.chain.ChainQueryApi
|
import org.bitcoins.core.api.chain.ChainQueryApi
|
||||||
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
|
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
|
||||||
import org.bitcoins.core.api.feeprovider.FeeRateApi
|
|
||||||
import org.bitcoins.core.api.node.NodeApi
|
import org.bitcoins.core.api.node.NodeApi
|
||||||
import org.bitcoins.core.protocol.dlc.models.DLCState.{
|
import org.bitcoins.core.protocol.dlc.models.DLCState.{
|
||||||
AdaptorSigComputationState,
|
AdaptorSigComputationState,
|
||||||
@ -144,13 +143,11 @@ case class DLCAppConfig(
|
|||||||
|
|
||||||
def createDLCWallet(
|
def createDLCWallet(
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit walletConf: WalletAppConfig): Future[DLCWallet] = {
|
)(implicit walletConf: WalletAppConfig): Future[DLCWallet] = {
|
||||||
DLCAppConfig.createDLCWallet(
|
DLCAppConfig.createDLCWallet(
|
||||||
nodeApi = nodeApi,
|
nodeApi = nodeApi,
|
||||||
chainQueryApi = chainQueryApi,
|
chainQueryApi = chainQueryApi
|
||||||
feeRateApi = feeRateApi
|
|
||||||
)(walletConf, this)
|
)(walletConf, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,8 +324,7 @@ object DLCAppConfig
|
|||||||
/** Creates a wallet based on the given [[WalletAppConfig]] */
|
/** Creates a wallet based on the given [[WalletAppConfig]] */
|
||||||
def createDLCWallet(
|
def createDLCWallet(
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit
|
)(implicit
|
||||||
walletConf: WalletAppConfig,
|
walletConf: WalletAppConfig,
|
||||||
dlcConf: DLCAppConfig
|
dlcConf: DLCAppConfig
|
||||||
@ -339,12 +335,12 @@ object DLCAppConfig
|
|||||||
if (walletExists) {
|
if (walletExists) {
|
||||||
logger.info(s"Using pre-existing wallet")
|
logger.info(s"Using pre-existing wallet")
|
||||||
val wallet =
|
val wallet =
|
||||||
DLCWallet(nodeApi, chainQueryApi, feeRateApi)
|
DLCWallet(nodeApi, chainQueryApi)
|
||||||
Future.successful(wallet)
|
Future.successful(wallet)
|
||||||
} else {
|
} else {
|
||||||
logger.info(s"Creating new wallet")
|
logger.info(s"Creating new wallet")
|
||||||
val unInitializedWallet =
|
val unInitializedWallet =
|
||||||
DLCWallet(nodeApi, chainQueryApi, feeRateApi)
|
DLCWallet(nodeApi, chainQueryApi)
|
||||||
|
|
||||||
Wallet
|
Wallet
|
||||||
.initialize(
|
.initialize(
|
||||||
|
@ -3,7 +3,6 @@ package org.bitcoins.dlc.wallet
|
|||||||
import org.bitcoins.core.api.chain.ChainQueryApi
|
import org.bitcoins.core.api.chain.ChainQueryApi
|
||||||
import org.bitcoins.core.api.dlc.wallet.DLCNeutrinoHDWalletApi
|
import org.bitcoins.core.api.dlc.wallet.DLCNeutrinoHDWalletApi
|
||||||
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
|
import org.bitcoins.core.api.dlc.wallet.db.DLCDb
|
||||||
import org.bitcoins.core.api.feeprovider.FeeRateApi
|
|
||||||
import org.bitcoins.core.api.node.NodeApi
|
import org.bitcoins.core.api.node.NodeApi
|
||||||
import org.bitcoins.core.api.wallet.db.*
|
import org.bitcoins.core.api.wallet.db.*
|
||||||
import org.bitcoins.core.currency.*
|
import org.bitcoins.core.currency.*
|
||||||
@ -2267,8 +2266,7 @@ object DLCWallet extends WalletLogger {
|
|||||||
|
|
||||||
private case class DLCWalletImpl(
|
private case class DLCWalletImpl(
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit
|
)(implicit
|
||||||
val walletConfig: WalletAppConfig,
|
val walletConfig: WalletAppConfig,
|
||||||
val dlcConfig: DLCAppConfig
|
val dlcConfig: DLCAppConfig
|
||||||
@ -2276,10 +2274,9 @@ object DLCWallet extends WalletLogger {
|
|||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit config: WalletAppConfig, dlcConfig: DLCAppConfig): DLCWallet = {
|
)(implicit config: WalletAppConfig, dlcConfig: DLCAppConfig): DLCWallet = {
|
||||||
DLCWalletImpl(nodeApi, chainQueryApi, feeRateApi)
|
DLCWalletImpl(nodeApi, chainQueryApi)
|
||||||
}
|
}
|
||||||
|
|
||||||
private object AcceptingOffersLatch {
|
private object AcceptingOffersLatch {
|
||||||
|
@ -188,7 +188,7 @@ val chainApi = new ChainQueryApi {
|
|||||||
|
|
||||||
// Finally, we can initialize our wallet with our own node api
|
// Finally, we can initialize our wallet with our own node api
|
||||||
val wallet =
|
val wallet =
|
||||||
Wallet(nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
Wallet(nodeApi = nodeApi, chainQueryApi = chainApi)
|
||||||
|
|
||||||
// Then to trigger one of the events we can run
|
// Then to trigger one of the events we can run
|
||||||
wallet.chainQueryApi.getFiltersBetweenHeights(100, 150)
|
wallet.chainQueryApi.getFiltersBetweenHeights(100, 150)
|
||||||
|
@ -95,7 +95,7 @@ val exampleCallback = createCallback(exampleProcessBlock)
|
|||||||
|
|
||||||
// Finally, we can initialize our wallet with our own node api
|
// Finally, we can initialize our wallet with our own node api
|
||||||
val wallet =
|
val wallet =
|
||||||
Wallet(nodeApi = nodeApi, chainQueryApi = chainApi, feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
Wallet(nodeApi = nodeApi, chainQueryApi = chainApi)
|
||||||
|
|
||||||
// Then to trigger the event we can run
|
// Then to trigger the event we can run
|
||||||
val exampleBlock = DoubleSha256DigestBE(
|
val exampleBlock = DoubleSha256DigestBE(
|
||||||
|
@ -70,8 +70,7 @@ val exampleCallbacks = WalletCallbacks(
|
|||||||
val wallet =
|
val wallet =
|
||||||
Wallet(
|
Wallet(
|
||||||
nodeApi = bitcoind,
|
nodeApi = bitcoind,
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind)
|
||||||
feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
|
||||||
|
|
||||||
// Finally, we can add the callbacks to our wallet config
|
// Finally, we can add the callbacks to our wallet config
|
||||||
walletConf.addCallbacks(exampleCallbacks)
|
walletConf.addCallbacks(exampleCallbacks)
|
||||||
|
@ -115,8 +115,7 @@ val genesisHashBEF = bitcoind.getBlockHash(0)
|
|||||||
//a fresh wallet
|
//a fresh wallet
|
||||||
implicit val walletAppConfig: WalletAppConfig = WalletAppConfig.fromDefaultDatadir()
|
implicit val walletAppConfig: WalletAppConfig = WalletAppConfig.fromDefaultDatadir()
|
||||||
|
|
||||||
val feeRateProvider: FeeRateApi = MempoolSpaceProvider.fromBlockTarget(6, proxyParams = None)
|
val wallet = Wallet(bitcoind, bitcoind)
|
||||||
val wallet = Wallet(bitcoind, bitcoind, feeRateProvider)
|
|
||||||
|
|
||||||
//yay! we have a synced wallet
|
//yay! we have a synced wallet
|
||||||
val syncedWalletF = genesisHashBEF.flatMap { genesisHash =>
|
val syncedWalletF = genesisHashBEF.flatMap { genesisHash =>
|
||||||
|
@ -150,7 +150,7 @@ val wallet = Wallet(new NodeApi {
|
|||||||
override def broadcastTransactions(txs: Vector[Transaction]): Future[Unit] = Future.successful(())
|
override def broadcastTransactions(txs: Vector[Transaction]): Future[Unit] = Future.successful(())
|
||||||
override def downloadBlocks(blockHashes: Vector[DoubleSha256DigestBE]): Future[Unit] = Future.successful(())
|
override def downloadBlocks(blockHashes: Vector[DoubleSha256DigestBE]): Future[Unit] = Future.successful(())
|
||||||
override def getConnectionCount: Future[Int] = Future.successful(0)
|
override def getConnectionCount: Future[Int] = Future.successful(0)
|
||||||
}, chainApi, ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
}, chainApi)
|
||||||
val walletF: Future[WalletApi] = configF.flatMap { _ =>
|
val walletF: Future[WalletApi] = configF.flatMap { _ =>
|
||||||
Wallet.initialize(wallet, wallet.accountHandling, None)
|
Wallet.initialize(wallet, wallet.accountHandling, None)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ object FeeProviderName extends StringFactory[FeeProviderName] {
|
|||||||
case object Random extends FeeProviderName
|
case object Random extends FeeProviderName
|
||||||
|
|
||||||
val all: Vector[FeeProviderName] =
|
val all: Vector[FeeProviderName] =
|
||||||
Vector(BitcoinerLive, BitGo, Constant, MempoolSpace)
|
Vector(BitcoinerLive, BitGo, Constant, MempoolSpace, Random)
|
||||||
|
|
||||||
override def fromStringOpt(str: String): Option[FeeProviderName] = {
|
override def fromStringOpt(str: String): Option[FeeProviderName] = {
|
||||||
all.find(_.toString.toLowerCase == str.toLowerCase)
|
all.find(_.toString.toLowerCase == str.toLowerCase)
|
||||||
|
@ -14,7 +14,7 @@ class RandomFeeProvider extends FeeRateApi {
|
|||||||
var lastFeeRate: Option[FeeUnit] = None
|
var lastFeeRate: Option[FeeUnit] = None
|
||||||
|
|
||||||
override def getFeeRate(): Future[FeeUnit] = {
|
override def getFeeRate(): Future[FeeUnit] = {
|
||||||
val raw = scala.util.Random.between(1, 10000)
|
val raw = scala.util.Random.between(1, 1000)
|
||||||
val feeRate = SatoshisPerVirtualByte(Satoshis(raw))
|
val feeRate = SatoshisPerVirtualByte(Satoshis(raw))
|
||||||
lastFeeRate = Some(feeRate)
|
lastFeeRate = Some(feeRate)
|
||||||
Future.successful(feeRate)
|
Future.successful(feeRate)
|
||||||
|
@ -84,6 +84,7 @@ object BitcoinSTestAppConfig {
|
|||||||
| relay = true
|
| relay = true
|
||||||
| enable-peer-discovery = false
|
| enable-peer-discovery = false
|
||||||
| }
|
| }
|
||||||
|
| fee-provider.name = "random"
|
||||||
| proxy.enabled = $torEnabled
|
| proxy.enabled = $torEnabled
|
||||||
| tor.enabled = $torEnabled
|
| tor.enabled = $torEnabled
|
||||||
| tor.use-random-ports = false
|
| tor.use-random-ports = false
|
||||||
|
@ -216,11 +216,8 @@ trait NodeTestWithCachedBitcoind extends BaseNodeTest with CachedTor {
|
|||||||
): Future[Unit] = {
|
): Future[Unit] = {
|
||||||
val node = nodeWithBitcoind.node
|
val node = nodeWithBitcoind.node
|
||||||
val destroyNodeF = tearDownNode(node, appConfig)
|
val destroyNodeF = tearDownNode(node, appConfig)
|
||||||
val destroyWalletF =
|
|
||||||
BitcoinSWalletTest.destroyWallet(nodeWithBitcoind.wallet)
|
|
||||||
for {
|
for {
|
||||||
_ <- destroyNodeF
|
_ <- destroyNodeF
|
||||||
_ <- destroyWalletF
|
|
||||||
_ <- BitcoinSWalletTest.destroyWalletAppConfig(appConfig.walletConf)
|
_ <- BitcoinSWalletTest.destroyWalletAppConfig(appConfig.walletConf)
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,7 @@ trait WalletLoaderFixtures
|
|||||||
// initialize the default wallet so it can be used in tests
|
// initialize the default wallet so it can be used in tests
|
||||||
_ <- config.walletConf.createHDWallet(
|
_ <- config.walletConf.createHDWallet(
|
||||||
nodeApi = bitcoind,
|
nodeApi = bitcoind,
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind
|
||||||
feeRateApi = bitcoind
|
|
||||||
)
|
)
|
||||||
|
|
||||||
walletHolder = WalletHolder.empty
|
walletHolder = WalletHolder.empty
|
||||||
|
@ -9,7 +9,6 @@ import org.bitcoins.core.api.node.NodeApi
|
|||||||
import org.bitcoins.core.api.wallet.WalletApi
|
import org.bitcoins.core.api.wallet.WalletApi
|
||||||
import org.bitcoins.core.currency.*
|
import org.bitcoins.core.currency.*
|
||||||
import org.bitcoins.dlc.wallet.{DLCAppConfig, DLCWallet}
|
import org.bitcoins.dlc.wallet.{DLCAppConfig, DLCWallet}
|
||||||
import org.bitcoins.feeprovider.RandomFeeProvider
|
|
||||||
import org.bitcoins.node.NodeCallbacks
|
import org.bitcoins.node.NodeCallbacks
|
||||||
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
import org.bitcoins.rpc.client.common.{BitcoindRpcClient, BitcoindVersion}
|
||||||
import org.bitcoins.server.BitcoinSAppConfig
|
import org.bitcoins.server.BitcoinSAppConfig
|
||||||
@ -75,9 +74,8 @@ trait BitcoinSWalletTest
|
|||||||
makeDependentFixture[Wallet](
|
makeDependentFixture[Wallet](
|
||||||
build =
|
build =
|
||||||
createNewWallet(nodeApi = nodeApi, chainQueryApi = chainQueryApi),
|
createNewWallet(nodeApi = nodeApi, chainQueryApi = chainQueryApi),
|
||||||
destroy = { wallet =>
|
destroy = { (_: WalletApi) =>
|
||||||
for {
|
for {
|
||||||
_ <- destroyWallet(wallet)
|
|
||||||
_ <- destroyWalletAppConfig(newWalletConf)
|
_ <- destroyWalletAppConfig(newWalletConf)
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
@ -93,9 +91,8 @@ trait BitcoinSWalletTest
|
|||||||
)(implicit walletAppConfig: WalletAppConfig): FutureOutcome = {
|
)(implicit walletAppConfig: WalletAppConfig): FutureOutcome = {
|
||||||
makeDependentFixture(
|
makeDependentFixture(
|
||||||
build = () => FundWalletUtil.createFundedWallet(nodeApi, chainQueryApi),
|
build = () => FundWalletUtil.createFundedWallet(nodeApi, chainQueryApi),
|
||||||
destroy = { (funded: FundedWallet) =>
|
destroy = { (_: FundedWallet) =>
|
||||||
for {
|
for {
|
||||||
_ <- destroyWallet(funded.wallet)
|
|
||||||
_ <- destroyWalletAppConfig(walletAppConfig)
|
_ <- destroyWalletAppConfig(walletAppConfig)
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
@ -107,9 +104,8 @@ trait BitcoinSWalletTest
|
|||||||
)(implicit walletAppConfig: WalletAppConfig): FutureOutcome = {
|
)(implicit walletAppConfig: WalletAppConfig): FutureOutcome = {
|
||||||
makeDependentFixture(
|
makeDependentFixture(
|
||||||
build = () => FundWalletUtil.createFundedWallet(nodeApi, chainQueryApi),
|
build = () => FundWalletUtil.createFundedWallet(nodeApi, chainQueryApi),
|
||||||
destroy = { (funded: FundedWallet) =>
|
destroy = { (_: FundedWallet) =>
|
||||||
for {
|
for {
|
||||||
_ <- destroyWallet(funded.wallet)
|
|
||||||
_ <- destroyWalletAppConfig(walletAppConfig)
|
_ <- destroyWalletAppConfig(walletAppConfig)
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
@ -152,9 +148,8 @@ trait BitcoinSWalletTest
|
|||||||
build = { () =>
|
build = { () =>
|
||||||
createDefaultWallet(nodeApi, chainQueryApi)
|
createDefaultWallet(nodeApi, chainQueryApi)
|
||||||
},
|
},
|
||||||
destroy = { wallet =>
|
destroy = { (_: WalletApi) =>
|
||||||
for {
|
for {
|
||||||
_ <- destroyWallet(wallet)
|
|
||||||
_ <- destroyWalletAppConfig(walletAppConfig)
|
_ <- destroyWalletAppConfig(walletAppConfig)
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
@ -168,7 +163,7 @@ trait BitcoinSWalletTest
|
|||||||
build = { () =>
|
build = { () =>
|
||||||
createWallet2Accounts(nodeApi, chainQueryApi)
|
createWallet2Accounts(nodeApi, chainQueryApi)
|
||||||
},
|
},
|
||||||
destroy = destroyWallet
|
destroy = { (_: WalletApi) => Future.unit }
|
||||||
)(test)
|
)(test)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +280,7 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||||||
|
|
||||||
walletConfig.start().flatMap { _ =>
|
walletConfig.start().flatMap { _ =>
|
||||||
val wallet =
|
val wallet =
|
||||||
Wallet(nodeApi, chainQueryApi, new RandomFeeProvider)(walletConfig)
|
Wallet(nodeApi, chainQueryApi)(walletConfig)
|
||||||
Wallet.initialize(wallet,
|
Wallet.initialize(wallet,
|
||||||
wallet.accountHandling,
|
wallet.accountHandling,
|
||||||
walletConfig.bip39PasswordOpt)
|
walletConfig.bip39PasswordOpt)
|
||||||
@ -310,7 +305,7 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||||||
|
|
||||||
initConfs.flatMap { _ =>
|
initConfs.flatMap { _ =>
|
||||||
val wallet =
|
val wallet =
|
||||||
DLCWallet(nodeApi, chainQueryApi, new RandomFeeProvider)(
|
DLCWallet(nodeApi, chainQueryApi)(
|
||||||
config.walletConf,
|
config.walletConf,
|
||||||
config.dlcConf
|
config.dlcConf
|
||||||
)
|
)
|
||||||
@ -351,8 +346,7 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||||||
walletWithCallback = Wallet(
|
walletWithCallback = Wallet(
|
||||||
nodeApi =
|
nodeApi =
|
||||||
SyncUtil.getNodeApiWalletCallback(bitcoind, walletCallbackP.future),
|
SyncUtil.getNodeApiWalletCallback(bitcoind, walletCallbackP.future),
|
||||||
chainQueryApi = bitcoind,
|
chainQueryApi = bitcoind
|
||||||
feeRateApi = new RandomFeeProvider
|
|
||||||
)(wallet.walletConfig)
|
)(wallet.walletConfig)
|
||||||
// complete the walletCallbackP so we can handle the callbacks when they are
|
// complete the walletCallbackP so we can handle the callbacks when they are
|
||||||
// called without hanging forever.
|
// called without hanging forever.
|
||||||
@ -523,23 +517,13 @@ object BitcoinSWalletTest extends WalletLogger {
|
|||||||
walletWithBitcoind: WalletWithBitcoind[_]
|
walletWithBitcoind: WalletWithBitcoind[_]
|
||||||
)(implicit ec: ExecutionContext): Future[Unit] = {
|
)(implicit ec: ExecutionContext): Future[Unit] = {
|
||||||
for {
|
for {
|
||||||
_ <- destroyWallet(walletWithBitcoind.wallet)
|
|
||||||
_ <- destroyWalletAppConfig(walletWithBitcoind.walletConfig)
|
_ <- destroyWalletAppConfig(walletWithBitcoind.walletConfig)
|
||||||
} yield ()
|
} yield ()
|
||||||
}
|
}
|
||||||
|
|
||||||
def destroyWallet(
|
|
||||||
wallet: WalletApi
|
|
||||||
)(implicit ec: ExecutionContext): Future[Unit] = {
|
|
||||||
for {
|
|
||||||
_ <- wallet.stop()
|
|
||||||
} yield ()
|
|
||||||
}
|
|
||||||
|
|
||||||
def destroyDLCWallet(wallet: DLCWallet): Future[Unit] = {
|
def destroyDLCWallet(wallet: DLCWallet): Future[Unit] = {
|
||||||
import wallet.ec
|
import wallet.ec
|
||||||
for {
|
for {
|
||||||
_ <- destroyWallet(wallet)
|
|
||||||
_ <- destroyWalletAppConfig(wallet.walletConfig)
|
_ <- destroyWalletAppConfig(wallet.walletConfig)
|
||||||
_ <- wallet.dlcConfig.stop()
|
_ <- wallet.dlcConfig.stop()
|
||||||
} yield ()
|
} yield ()
|
||||||
|
@ -6,9 +6,7 @@ import org.bitcoins.core.crypto.{ExtPublicKey, MnemonicCode}
|
|||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
import org.bitcoins.core.util.FutureUtil
|
import org.bitcoins.core.util.FutureUtil
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
|
||||||
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
|
import org.bitcoins.core.wallet.keymanagement.KeyManagerParams
|
||||||
import org.bitcoins.feeprovider.ConstantFeeRateProvider
|
|
||||||
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
import org.bitcoins.testkit.BitcoinSTestAppConfig
|
||||||
import org.bitcoins.testkit.chain.MockChainQueryApi
|
import org.bitcoins.testkit.chain.MockChainQueryApi
|
||||||
import org.bitcoins.testkit.fixtures.EmptyFixture
|
import org.bitcoins.testkit.fixtures.EmptyFixture
|
||||||
@ -153,8 +151,7 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
|||||||
wallet =
|
wallet =
|
||||||
Wallet(
|
Wallet(
|
||||||
MockNodeApi,
|
MockNodeApi,
|
||||||
MockChainQueryApi,
|
MockChainQueryApi
|
||||||
ConstantFeeRateProvider(SatoshisPerVirtualByte.one)
|
|
||||||
)(config)
|
)(config)
|
||||||
init <- Wallet.initialize(
|
init <- Wallet.initialize(
|
||||||
wallet = wallet,
|
wallet = wallet,
|
||||||
|
@ -204,7 +204,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||||||
val walletDiffKeyManagerF: Future[Wallet] = for {
|
val walletDiffKeyManagerF: Future[Wallet] = for {
|
||||||
_ <- startedF
|
_ <- startedF
|
||||||
} yield {
|
} yield {
|
||||||
Wallet(wallet.nodeApi, wallet.chainQueryApi, wallet.feeRateApi)(
|
Wallet(wallet.nodeApi, wallet.chainQueryApi)(
|
||||||
uniqueEntropyWalletConfig
|
uniqueEntropyWalletConfig
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -34,10 +34,10 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
abstract class Wallet extends NeutrinoHDWalletApi with WalletLogger {
|
abstract class Wallet extends NeutrinoHDWalletApi with WalletLogger {
|
||||||
|
|
||||||
def keyManager: BIP39KeyManager = {
|
def keyManager: BIP39KeyManager = {
|
||||||
walletConfig.kmConf.toBip39KeyManager
|
walletConfig.kmConf.toBip39KeyManager
|
||||||
}
|
}
|
||||||
|
def feeRateApi: FeeRateApi = walletConfig.feeRateApi
|
||||||
implicit val walletConfig: WalletAppConfig
|
implicit val walletConfig: WalletAppConfig
|
||||||
|
|
||||||
implicit val system: ActorSystem = walletConfig.system
|
implicit val system: ActorSystem = walletConfig.system
|
||||||
@ -137,44 +137,6 @@ abstract class Wallet extends NeutrinoHDWalletApi with WalletLogger {
|
|||||||
|
|
||||||
def walletCallbacks: WalletCallbacks = walletConfig.callBacks
|
def walletCallbacks: WalletCallbacks = walletConfig.callBacks
|
||||||
|
|
||||||
private def checkRootAccount: Future[Unit] = {
|
|
||||||
val coinType = HDUtil.getCoinType(keyManager.kmParams.network)
|
|
||||||
val coin =
|
|
||||||
HDCoin(purpose = keyManager.kmParams.purpose, coinType = coinType)
|
|
||||||
val account = HDAccount(coin = coin, index = 0)
|
|
||||||
// safe since we're deriving from a priv
|
|
||||||
val xpub = keyManager.deriveXPub(account).get
|
|
||||||
|
|
||||||
accountDAO.read((account.coin, account.index)).flatMap {
|
|
||||||
case Some(account) =>
|
|
||||||
if (account.xpub != xpub) {
|
|
||||||
val errorMsg =
|
|
||||||
s"Divergent xpubs for account=$account. Existing database xpub=${account.xpub}, key manager's xpub=$xpub. " +
|
|
||||||
s"It is possible we have a different key manager being used than expected, key manager=${keyManager.kmParams.seedPath.toAbsolutePath.toString}"
|
|
||||||
Future.failed(new RuntimeException(errorMsg))
|
|
||||||
} else {
|
|
||||||
Future.unit
|
|
||||||
}
|
|
||||||
case None =>
|
|
||||||
val errorMsg = s"Missing root xpub for account $account in database"
|
|
||||||
Future.failed(new RuntimeException(errorMsg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override def start(): Future[Wallet] = {
|
|
||||||
logger.info("Starting Wallet")
|
|
||||||
|
|
||||||
checkRootAccount.map { _ =>
|
|
||||||
walletConfig.startRebroadcastTxsScheduler(this)
|
|
||||||
startFeeRateCallbackScheduler()
|
|
||||||
this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override def stop(): Future[Wallet] = {
|
|
||||||
Future.successful(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def getNewAddress(): Future[BitcoinAddress] = {
|
override def getNewAddress(): Future[BitcoinAddress] = {
|
||||||
addressHandling.getNewAddress()
|
addressHandling.getNewAddress()
|
||||||
}
|
}
|
||||||
@ -353,18 +315,16 @@ object Wallet extends WalletLogger {
|
|||||||
|
|
||||||
private case class WalletImpl(
|
private case class WalletImpl(
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit
|
)(implicit
|
||||||
val walletConfig: WalletAppConfig
|
val walletConfig: WalletAppConfig
|
||||||
) extends Wallet
|
) extends Wallet
|
||||||
|
|
||||||
def apply(
|
def apply(
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit config: WalletAppConfig): Wallet = {
|
)(implicit config: WalletAppConfig): Wallet = {
|
||||||
WalletImpl(nodeApi, chainQueryApi, feeRateApi)
|
WalletImpl(nodeApi, chainQueryApi)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates the master xpub for the key manager in the database
|
/** Creates the master xpub for the key manager in the database
|
||||||
|
@ -70,26 +70,8 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
|||||||
newWallet: DLCNeutrinoHDWalletApi
|
newWallet: DLCNeutrinoHDWalletApi
|
||||||
): Future[DLCNeutrinoHDWalletApi] =
|
): Future[DLCNeutrinoHDWalletApi] =
|
||||||
synchronized {
|
synchronized {
|
||||||
val oldWalletOpt = walletOpt
|
walletOpt = Some(newWallet)
|
||||||
walletOpt = None
|
Future.successful(newWallet)
|
||||||
val res = for {
|
|
||||||
_ <- {
|
|
||||||
oldWalletOpt match {
|
|
||||||
case Some(oldWallet) => oldWallet.stop()
|
|
||||||
case None => Future.unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ <- newWallet.start()
|
|
||||||
} yield {
|
|
||||||
synchronized {
|
|
||||||
walletOpt = Some(newWallet)
|
|
||||||
newWallet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.failed.foreach(ex => logger.error("Cannot start wallet ", ex))
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def delegate[T]
|
private def delegate[T]
|
||||||
@ -116,19 +98,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
|||||||
override lazy val feeRateApi: FeeRateApi = wallet.feeRateApi
|
override lazy val feeRateApi: FeeRateApi = wallet.feeRateApi
|
||||||
override lazy val creationTime: Instant = wallet.creationTime
|
override lazy val creationTime: Instant = wallet.creationTime
|
||||||
|
|
||||||
override def start(): Future[WalletApi] = delegate(_.start())
|
|
||||||
|
|
||||||
override def stop(): Future[WalletApi] = {
|
|
||||||
val res = delegate(_.stop())
|
|
||||||
|
|
||||||
res.onComplete { _ =>
|
|
||||||
synchronized {
|
|
||||||
walletOpt = None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
override def getConfirmedBalance(): Future[CurrencyUnit] = delegate(
|
override def getConfirmedBalance(): Future[CurrencyUnit] = delegate(
|
||||||
_.getConfirmedBalance()
|
_.getConfirmedBalance()
|
||||||
)
|
)
|
||||||
|
@ -8,14 +8,16 @@ import org.bitcoins.core.api.CallbackConfig
|
|||||||
import org.bitcoins.core.api.chain.ChainQueryApi
|
import org.bitcoins.core.api.chain.ChainQueryApi
|
||||||
import org.bitcoins.core.api.feeprovider.FeeRateApi
|
import org.bitcoins.core.api.feeprovider.FeeRateApi
|
||||||
import org.bitcoins.core.api.node.NodeApi
|
import org.bitcoins.core.api.node.NodeApi
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd.*
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
import org.bitcoins.core.wallet.keymanagement._
|
import org.bitcoins.core.wallet.keymanagement.*
|
||||||
import org.bitcoins.crypto.AesPassword
|
import org.bitcoins.crypto.AesPassword
|
||||||
import org.bitcoins.db.DatabaseDriver.{PostgreSQL, SQLite}
|
import org.bitcoins.db.DatabaseDriver.{PostgreSQL, SQLite}
|
||||||
import org.bitcoins.db._
|
import org.bitcoins.db.*
|
||||||
import org.bitcoins.db.models.MasterXPubDAO
|
import org.bitcoins.db.models.MasterXPubDAO
|
||||||
import org.bitcoins.db.util.{DBMasterXPubApi, MasterXPubUtil}
|
import org.bitcoins.db.util.{DBMasterXPubApi, MasterXPubUtil}
|
||||||
|
import org.bitcoins.feeprovider.{FeeProviderFactory, MempoolSpaceProvider}
|
||||||
|
import org.bitcoins.feeprovider.MempoolSpaceTarget.HourFeeTarget
|
||||||
import org.bitcoins.keymanager.config.KeyManagerAppConfig
|
import org.bitcoins.keymanager.config.KeyManagerAppConfig
|
||||||
import org.bitcoins.tor.config.TorAppConfig
|
import org.bitcoins.tor.config.TorAppConfig
|
||||||
import org.bitcoins.wallet.callback.{
|
import org.bitcoins.wallet.callback.{
|
||||||
@ -29,7 +31,7 @@ import org.bitcoins.wallet.{Wallet, WalletLogger}
|
|||||||
|
|
||||||
import java.nio.file.{Files, Path, Paths}
|
import java.nio.file.{Files, Path, Paths}
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.concurrent._
|
import java.util.concurrent.*
|
||||||
import scala.concurrent.duration.{
|
import scala.concurrent.duration.{
|
||||||
Duration,
|
Duration,
|
||||||
DurationInt,
|
DurationInt,
|
||||||
@ -37,6 +39,7 @@ import scala.concurrent.duration.{
|
|||||||
FiniteDuration
|
FiniteDuration
|
||||||
}
|
}
|
||||||
import scala.concurrent.{Await, ExecutionContext, Future}
|
import scala.concurrent.{Await, ExecutionContext, Future}
|
||||||
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
/** Configuration for the Bitcoin-S wallet
|
/** Configuration for the Bitcoin-S wallet
|
||||||
* @param directory
|
* @param directory
|
||||||
@ -57,6 +60,19 @@ case class WalletAppConfig(
|
|||||||
|
|
||||||
implicit override val ec: ExecutionContext = system.dispatcher
|
implicit override val ec: ExecutionContext = system.dispatcher
|
||||||
|
|
||||||
|
private val defaultApi =
|
||||||
|
MempoolSpaceProvider(HourFeeTarget, network, torConf.socks5ProxyParams)
|
||||||
|
|
||||||
|
lazy val feeRateApi: FeeRateApi = {
|
||||||
|
FeeProviderFactory.getFeeProviderOrElse(
|
||||||
|
defaultApi,
|
||||||
|
feeProviderNameOpt,
|
||||||
|
feeProviderTargetOpt,
|
||||||
|
torConf.socks5ProxyParams,
|
||||||
|
network
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override protected[bitcoins] def moduleName: String =
|
override protected[bitcoins] def moduleName: String =
|
||||||
WalletAppConfig.moduleName
|
WalletAppConfig.moduleName
|
||||||
|
|
||||||
@ -65,7 +81,7 @@ case class WalletAppConfig(
|
|||||||
override protected[bitcoins] def newConfigOfType(
|
override protected[bitcoins] def newConfigOfType(
|
||||||
configs: Vector[Config]
|
configs: Vector[Config]
|
||||||
): WalletAppConfig =
|
): WalletAppConfig =
|
||||||
WalletAppConfig(baseDatadir, configs)
|
WalletAppConfig(baseDatadir, configs, kmConfOpt)
|
||||||
|
|
||||||
override def appConfig: WalletAppConfig = this
|
override def appConfig: WalletAppConfig = this
|
||||||
|
|
||||||
@ -216,6 +232,8 @@ case class WalletAppConfig(
|
|||||||
Files.createDirectories(datadir)
|
Files.createDirectories(datadir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startFeeRateCallbackScheduler()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_ <- super.start()
|
_ <- super.start()
|
||||||
_ <- kmConf.start()
|
_ <- kmConf.start()
|
||||||
@ -257,6 +275,7 @@ case class WalletAppConfig(
|
|||||||
stopCallbacksF.flatMap { _ =>
|
stopCallbacksF.flatMap { _ =>
|
||||||
clearCallbacks()
|
clearCallbacks()
|
||||||
stopRebroadcastTxsScheduler()
|
stopRebroadcastTxsScheduler()
|
||||||
|
stopFeeRateScheduler()
|
||||||
// this eagerly shuts down all scheduled tasks on the scheduler
|
// this eagerly shuts down all scheduled tasks on the scheduler
|
||||||
// in the future, we should actually cancel all things that are scheduled
|
// in the future, we should actually cancel all things that are scheduled
|
||||||
// manually, and then shutdown the scheduler
|
// manually, and then shutdown the scheduler
|
||||||
@ -325,41 +344,41 @@ case class WalletAppConfig(
|
|||||||
/** Creates a wallet based on this [[WalletAppConfig]] */
|
/** Creates a wallet based on this [[WalletAppConfig]] */
|
||||||
def createHDWallet(
|
def createHDWallet(
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit system: ActorSystem): Future[Wallet] = {
|
)(implicit system: ActorSystem): Future[Wallet] = {
|
||||||
WalletAppConfig.createHDWallet(
|
WalletAppConfig.createHDWallet(
|
||||||
nodeApi = nodeApi,
|
nodeApi = nodeApi,
|
||||||
chainQueryApi = chainQueryApi,
|
chainQueryApi = chainQueryApi
|
||||||
feeRateApi = feeRateApi
|
|
||||||
)(this, system)
|
)(this, system)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[this] var rebroadcastTransactionsCancelOpt
|
private[this] var rebroadcastTransactionsCancelOpt
|
||||||
: Option[ScheduledFuture[_]] = None
|
: Option[ScheduledFuture[?]] = None
|
||||||
|
private var feeRateCancelOpt: Option[ScheduledFuture[?]] = None
|
||||||
|
|
||||||
/** Starts the wallet's rebroadcast transaction scheduler */
|
/** Starts the wallet's rebroadcast transaction scheduler */
|
||||||
def startRebroadcastTxsScheduler(wallet: Wallet): Unit = synchronized {
|
private def startRebroadcastTxsScheduler(wallet: Wallet): Unit =
|
||||||
rebroadcastTransactionsCancelOpt match {
|
synchronized {
|
||||||
case Some(_) =>
|
rebroadcastTransactionsCancelOpt match {
|
||||||
// already scheduled, do nothing
|
case Some(_) =>
|
||||||
()
|
// already scheduled, do nothing
|
||||||
case None =>
|
()
|
||||||
logger.info(s"Starting wallet rebroadcast task")
|
case None =>
|
||||||
|
logger.info(s"Starting wallet rebroadcast task")
|
||||||
|
|
||||||
val interval = rebroadcastFrequency.toSeconds
|
val interval = rebroadcastFrequency.toSeconds
|
||||||
val initDelay = interval
|
val initDelay = interval
|
||||||
val future =
|
val future =
|
||||||
scheduler.scheduleAtFixedRate(
|
scheduler.scheduleAtFixedRate(
|
||||||
RebroadcastTransactionsRunnable(wallet),
|
RebroadcastTransactionsRunnable(wallet),
|
||||||
initDelay,
|
initDelay,
|
||||||
interval,
|
interval,
|
||||||
TimeUnit.SECONDS
|
TimeUnit.SECONDS
|
||||||
)
|
)
|
||||||
rebroadcastTransactionsCancelOpt = Some(future)
|
rebroadcastTransactionsCancelOpt = Some(future)
|
||||||
()
|
()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** Kills the wallet's rebroadcast transaction scheduler */
|
/** Kills the wallet's rebroadcast transaction scheduler */
|
||||||
def stopRebroadcastTxsScheduler(): Unit = synchronized {
|
def stopRebroadcastTxsScheduler(): Unit = synchronized {
|
||||||
@ -368,20 +387,60 @@ case class WalletAppConfig(
|
|||||||
if (!cancel.isCancelled) {
|
if (!cancel.isCancelled) {
|
||||||
logger.info(s"Stopping wallet rebroadcast task")
|
logger.info(s"Stopping wallet rebroadcast task")
|
||||||
cancel.cancel(true)
|
cancel.cancel(true)
|
||||||
} else {
|
|
||||||
rebroadcastTransactionsCancelOpt = None
|
|
||||||
}
|
}
|
||||||
|
rebroadcastTransactionsCancelOpt = None
|
||||||
()
|
()
|
||||||
case None => ()
|
case None => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def stopFeeRateScheduler(): Unit = synchronized {
|
||||||
|
feeRateCancelOpt match {
|
||||||
|
case Some(cancel) =>
|
||||||
|
if (!cancel.isCancelled) {
|
||||||
|
cancel.cancel(true)
|
||||||
|
}
|
||||||
|
feeRateCancelOpt = None
|
||||||
|
case None =>
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** The creation time of the mnemonic seed If we cannot decrypt the seed
|
/** The creation time of the mnemonic seed If we cannot decrypt the seed
|
||||||
* because of invalid passwords, we return None
|
* because of invalid passwords, we return None
|
||||||
*/
|
*/
|
||||||
def creationTime: Instant = {
|
def creationTime: Instant = {
|
||||||
kmConf.creationTime
|
kmConf.creationTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def startFeeRateCallbackScheduler(): Unit = {
|
||||||
|
val feeRateChangedRunnable = new Runnable {
|
||||||
|
override def run(): Unit = {
|
||||||
|
feeRateApi
|
||||||
|
.getFeeRate()
|
||||||
|
.map(feeRate => Some(feeRate))
|
||||||
|
.recover { case NonFatal(_) =>
|
||||||
|
// logger.error("Cannot get fee rate ", ex)
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.foreach { feeRateOpt =>
|
||||||
|
callBacks.executeOnFeeRateChanged(
|
||||||
|
feeRateOpt.getOrElse(SatoshisPerVirtualByte.negativeOne)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cancel: ScheduledFuture[?] = scheduler.scheduleAtFixedRate(
|
||||||
|
feeRateChangedRunnable,
|
||||||
|
feeRatePollDelay.toSeconds,
|
||||||
|
feeRatePollInterval.toSeconds,
|
||||||
|
TimeUnit.SECONDS
|
||||||
|
)
|
||||||
|
feeRateCancelOpt = Some(cancel)
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object WalletAppConfig
|
object WalletAppConfig
|
||||||
@ -404,25 +463,24 @@ object WalletAppConfig
|
|||||||
/** Creates a wallet based on the given [[WalletAppConfig]] */
|
/** Creates a wallet based on the given [[WalletAppConfig]] */
|
||||||
def createHDWallet(
|
def createHDWallet(
|
||||||
nodeApi: NodeApi,
|
nodeApi: NodeApi,
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi
|
||||||
feeRateApi: FeeRateApi
|
|
||||||
)(implicit
|
)(implicit
|
||||||
walletConf: WalletAppConfig,
|
walletConf: WalletAppConfig,
|
||||||
system: ActorSystem
|
system: ActorSystem
|
||||||
): Future[Wallet] = {
|
): Future[Wallet] = {
|
||||||
import system.dispatcher
|
import system.dispatcher
|
||||||
walletConf.hasWallet().flatMap { walletExists =>
|
val walletF = walletConf.hasWallet().flatMap { walletExists =>
|
||||||
val bip39PasswordOpt = walletConf.bip39PasswordOpt
|
val bip39PasswordOpt = walletConf.bip39PasswordOpt
|
||||||
|
|
||||||
if (walletExists) {
|
if (walletExists) {
|
||||||
logger.info(s"Using pre-existing wallet")
|
logger.info(s"Using pre-existing wallet")
|
||||||
val wallet =
|
val wallet =
|
||||||
Wallet(nodeApi, chainQueryApi, feeRateApi)
|
Wallet(nodeApi, chainQueryApi)
|
||||||
Future.successful(wallet)
|
Future.successful(wallet)
|
||||||
} else {
|
} else {
|
||||||
logger.info(s"Creating new wallet")
|
logger.info(s"Creating new wallet")
|
||||||
val unInitializedWallet =
|
val unInitializedWallet =
|
||||||
Wallet(nodeApi, chainQueryApi, feeRateApi)
|
Wallet(nodeApi, chainQueryApi)
|
||||||
|
|
||||||
Wallet.initialize(
|
Wallet.initialize(
|
||||||
wallet = unInitializedWallet,
|
wallet = unInitializedWallet,
|
||||||
@ -431,6 +489,11 @@ object WalletAppConfig
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
walletF.map { wallet =>
|
||||||
|
walletConf.startRebroadcastTxsScheduler(wallet)
|
||||||
|
wallet
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RebroadcastTransactionsRunnable(wallet: Wallet)(implicit
|
case class RebroadcastTransactionsRunnable(wallet: Wallet)(implicit
|
||||||
|
@ -18,6 +18,7 @@ import org.bitcoins.core.protocol.blockchain.{
|
|||||||
TestNetChainParams
|
TestNetChainParams
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||||
|
import org.bitcoins.core.util.HDUtil
|
||||||
import org.bitcoins.db.SafeDatabase
|
import org.bitcoins.db.SafeDatabase
|
||||||
import org.bitcoins.wallet.callback.WalletCallbacks
|
import org.bitcoins.wallet.callback.WalletCallbacks
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
@ -304,7 +305,7 @@ case class AccountHandling(
|
|||||||
|
|
||||||
override def getNewAddress(account: AccountDb): Future[BitcoinAddress] = {
|
override def getNewAddress(account: AccountDb): Future[BitcoinAddress] = {
|
||||||
val action = getNewAddressAction(account)
|
val action = getNewAddressAction(account)
|
||||||
safeDatabase.run(action)
|
checkRootAccount.flatMap(_ => safeDatabase.run(action))
|
||||||
}
|
}
|
||||||
|
|
||||||
def getNewAddressAction(account: AccountDb): DBIOAction[
|
def getNewAddressAction(account: AccountDb): DBIOAction[
|
||||||
@ -477,7 +478,7 @@ case class AccountHandling(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def getLastAccountOpt(
|
private def getLastAccountOpt(
|
||||||
purpose: HDPurpose
|
purpose: HDPurpose
|
||||||
): Future[Option[AccountDb]] = {
|
): Future[Option[AccountDb]] = {
|
||||||
accountDAO
|
accountDAO
|
||||||
@ -490,6 +491,30 @@ case class AccountHandling(
|
|||||||
.map(_.lastOption)
|
.map(_.lastOption)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def checkRootAccount: Future[Unit] = {
|
||||||
|
val coinType = HDUtil.getCoinType(keyManager.kmParams.network)
|
||||||
|
val coin =
|
||||||
|
HDCoin(purpose = keyManager.kmParams.purpose, coinType = coinType)
|
||||||
|
val account = HDAccount(coin = coin, index = 0)
|
||||||
|
// safe since we're deriving from a priv
|
||||||
|
val xpub = keyManager.deriveXPub(account).get
|
||||||
|
|
||||||
|
accountDAO.read((account.coin, account.index)).flatMap {
|
||||||
|
case Some(account) =>
|
||||||
|
if (account.xpub != xpub) {
|
||||||
|
val errorMsg =
|
||||||
|
s"Divergent xpubs for account=$account. Existing database xpub=${account.xpub}, key manager's xpub=$xpub. " +
|
||||||
|
s"It is possible we have a different key manager being used than expected, key manager=${keyManager.kmParams.seedPath.toAbsolutePath.toString}"
|
||||||
|
Future.failed(new RuntimeException(errorMsg))
|
||||||
|
} else {
|
||||||
|
Future.unit
|
||||||
|
}
|
||||||
|
case None =>
|
||||||
|
val errorMsg = s"Missing root xpub for account $account in database"
|
||||||
|
Future.failed(new RuntimeException(errorMsg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** The default HD coin for this wallet, read from config */
|
/** The default HD coin for this wallet, read from config */
|
||||||
protected[wallet] lazy val DEFAULT_HD_COIN: HDCoin = {
|
protected[wallet] lazy val DEFAULT_HD_COIN: HDCoin = {
|
||||||
val coinType = DEFAULT_HD_COIN_TYPE
|
val coinType = DEFAULT_HD_COIN_TYPE
|
||||||
|
Loading…
Reference in New Issue
Block a user