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