mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-01-18 21:34:39 +01:00
Refactor codebase to have has-a relationship with RescanHandling
rather than is-a (#5675)
* Refactor codebase to have has-a relationship with RescanHandling rather than is-a get everything compiling Get all tests passing * Revert and clean up files * Fix docs
This commit is contained in:
parent
a8e9dcd443
commit
8c5d685953
@ -1,28 +1,33 @@
|
||||
package org.bitcoins.server
|
||||
|
||||
import org.apache.pekko.http.scaladsl.model.ContentTypes._
|
||||
import org.apache.pekko.http.scaladsl.model.ContentTypes.*
|
||||
import org.apache.pekko.http.scaladsl.model.StatusCodes
|
||||
import org.apache.pekko.http.scaladsl.testkit.{
|
||||
RouteTestTimeout,
|
||||
ScalatestRouteTest
|
||||
}
|
||||
import org.bitcoins.core.api.chain.ChainApi
|
||||
import org.bitcoins.core.api.chain.db._
|
||||
import org.bitcoins.core.api.wallet.db._
|
||||
import org.bitcoins.core.api.wallet.{AddressInfo, CoinSelectionAlgo}
|
||||
import org.bitcoins.core.api.chain.db.*
|
||||
import org.bitcoins.core.api.wallet.db.*
|
||||
import org.bitcoins.core.api.wallet.{
|
||||
AccountHandlingApi,
|
||||
AddressInfo,
|
||||
CoinSelectionAlgo,
|
||||
RescanHandlingApi
|
||||
}
|
||||
import org.bitcoins.core.config.RegTest
|
||||
import org.bitcoins.core.crypto.ExtPublicKey
|
||||
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.hd.*
|
||||
import org.bitcoins.core.number.{UInt32, UInt64}
|
||||
import org.bitcoins.core.protocol.BlockStamp.{BlockHash, BlockHeight, BlockTime}
|
||||
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage._
|
||||
import org.bitcoins.core.protocol.dlc.models._
|
||||
import org.bitcoins.core.protocol.dlc.models.DLCMessage.*
|
||||
import org.bitcoins.core.protocol.dlc.models.*
|
||||
import org.bitcoins.core.protocol.script.P2WPKHWitnessV0
|
||||
import org.bitcoins.core.protocol.tlv._
|
||||
import org.bitcoins.core.protocol.transaction._
|
||||
import org.bitcoins.core.protocol.tlv.*
|
||||
import org.bitcoins.core.protocol.transaction.*
|
||||
import org.bitcoins.core.protocol.{
|
||||
Bech32Address,
|
||||
BitcoinAddress,
|
||||
@ -35,8 +40,8 @@ import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.core.util.sorted.OrderedSchnorrSignatures
|
||||
import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerVirtualByte}
|
||||
import org.bitcoins.core.wallet.rescan.RescanState
|
||||
import org.bitcoins.core.wallet.utxo._
|
||||
import org.bitcoins.crypto._
|
||||
import org.bitcoins.core.wallet.utxo.*
|
||||
import org.bitcoins.crypto.*
|
||||
import org.bitcoins.feeprovider.ConstantFeeRateProvider
|
||||
import org.bitcoins.node.Node
|
||||
import org.bitcoins.server.routes.{CommonRoutes, ServerCommand}
|
||||
@ -47,7 +52,7 @@ import org.bitcoins.wallet.{MockWalletApi, WalletHolder}
|
||||
import org.scalamock.scalatest.MockFactory
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
import scodec.bits.ByteVector
|
||||
import ujson._
|
||||
import ujson.*
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.time.{ZoneId, ZonedDateTime}
|
||||
@ -77,6 +82,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
|
||||
val mockWalletApi: MockWalletApi = mock[MockWalletApi]
|
||||
|
||||
val mockRescanHandlingApi: RescanHandlingApi = mock[RescanHandlingApi]
|
||||
|
||||
val mockAccountHandlingApi: AccountHandlingApi = mock[AccountHandlingApi]
|
||||
|
||||
val walletHolder = new WalletHolder(Some(mockWalletApi))
|
||||
|
||||
val feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one)
|
||||
@ -652,9 +661,10 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
|
||||
(() => mockWalletApi.accountHandling)
|
||||
.expects()
|
||||
.returning(mockWalletApi)
|
||||
.returning(mockAccountHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(() => mockWalletApi.listAccounts())
|
||||
(() => mockWalletApi.accountHandling.listAccounts())
|
||||
.expects()
|
||||
.returning(Future.successful(Vector(accountDb)))
|
||||
|
||||
@ -2021,19 +2031,25 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
"run wallet rescan" in {
|
||||
// positive cases
|
||||
|
||||
(() => mockWalletApi.discoveryBatchSize())
|
||||
(() => mockWalletApi.rescanHandling)
|
||||
.expects()
|
||||
.returning(mockRescanHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(() => mockWalletApi.rescanHandling.discoveryBatchSize())
|
||||
.expects()
|
||||
.returning(100)
|
||||
.atLeastOnce()
|
||||
(mockWalletApi
|
||||
|
||||
(mockWalletApi.rescanHandling
|
||||
.rescanNeutrinoWallet(
|
||||
_: Option[BlockStamp],
|
||||
_: Option[BlockStamp],
|
||||
_: Int,
|
||||
_: Boolean,
|
||||
_: Boolean
|
||||
)(_: ExecutionContext))
|
||||
.expects(None, None, 100, false, false, executor)
|
||||
))
|
||||
.expects(None, None, 100, false, false)
|
||||
.returning(
|
||||
Future.successful(
|
||||
RescanState
|
||||
@ -2058,14 +2074,14 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
)
|
||||
}
|
||||
|
||||
(mockWalletApi
|
||||
(mockWalletApi.rescanHandling
|
||||
.rescanNeutrinoWallet(
|
||||
_: Option[BlockStamp],
|
||||
_: Option[BlockStamp],
|
||||
_: Int,
|
||||
_: Boolean,
|
||||
_: Boolean
|
||||
)(_: ExecutionContext))
|
||||
))
|
||||
.expects(
|
||||
Some(
|
||||
BlockTime(
|
||||
@ -2075,8 +2091,7 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
None,
|
||||
100,
|
||||
false,
|
||||
false,
|
||||
executor
|
||||
false
|
||||
)
|
||||
.returning(
|
||||
Future.successful(
|
||||
@ -2106,21 +2121,20 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
|
||||
}
|
||||
|
||||
(mockWalletApi
|
||||
(mockWalletApi.rescanHandling
|
||||
.rescanNeutrinoWallet(
|
||||
_: Option[BlockStamp],
|
||||
_: Option[BlockStamp],
|
||||
_: Int,
|
||||
_: Boolean,
|
||||
_: Boolean
|
||||
)(_: ExecutionContext))
|
||||
))
|
||||
.expects(
|
||||
None,
|
||||
Some(BlockHash(DoubleSha256DigestBE.empty)),
|
||||
100,
|
||||
false,
|
||||
false,
|
||||
executor
|
||||
false
|
||||
)
|
||||
.returning(
|
||||
Future.successful(
|
||||
@ -2149,21 +2163,20 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
)
|
||||
}
|
||||
|
||||
(mockWalletApi
|
||||
(mockWalletApi.rescanHandling
|
||||
.rescanNeutrinoWallet(
|
||||
_: Option[BlockStamp],
|
||||
_: Option[BlockStamp],
|
||||
_: Int,
|
||||
_: Boolean,
|
||||
_: Boolean
|
||||
)(_: ExecutionContext))
|
||||
))
|
||||
.expects(
|
||||
Some(BlockHeight(12345)),
|
||||
Some(BlockHeight(67890)),
|
||||
100,
|
||||
false,
|
||||
false,
|
||||
executor
|
||||
false
|
||||
)
|
||||
.returning(Future.successful(RescanState.RescanDone))
|
||||
|
||||
@ -2234,15 +2247,15 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
)
|
||||
}
|
||||
|
||||
(mockWalletApi
|
||||
(mockWalletApi.rescanHandling
|
||||
.rescanNeutrinoWallet(
|
||||
_: Option[BlockStamp],
|
||||
_: Option[BlockStamp],
|
||||
_: Int,
|
||||
_: Boolean,
|
||||
_: Boolean
|
||||
)(_: ExecutionContext))
|
||||
.expects(None, None, 55, false, false, executor)
|
||||
))
|
||||
.expects(None, None, 55, false, false)
|
||||
.returning(Future.successful(RescanState.RescanDone))
|
||||
|
||||
walletLoader.clearRescanState()
|
||||
|
@ -7,6 +7,7 @@ import org.bitcoins.core.api.commons.ArgumentSource
|
||||
import org.bitcoins.core.api.dlc.wallet.DLCNeutrinoHDWalletApi
|
||||
import org.bitcoins.core.api.feeprovider.FeeRateApi
|
||||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.api.wallet.RescanHandlingApi
|
||||
import org.bitcoins.core.util.StartStopAsync
|
||||
import org.bitcoins.core.wallet.rescan.RescanState
|
||||
import org.bitcoins.crypto.AesPassword
|
||||
@ -114,7 +115,7 @@ sealed trait DLCWalletLoaderApi
|
||||
}
|
||||
|
||||
protected def restartRescanIfNeeded(
|
||||
wallet: DLCNeutrinoHDWalletApi
|
||||
wallet: RescanHandlingApi
|
||||
)(implicit ec: ExecutionContext): Future[RescanState] = {
|
||||
for {
|
||||
isRescanning <- wallet.isRescanning()
|
||||
@ -311,7 +312,7 @@ case class DLCWalletNeutrinoBackendLoader(
|
||||
CallbackUtil.createNeutrinoNodeCallbacksForWallet(walletHolder)
|
||||
_ = nodeConf.replaceCallbacks(nodeCallbacks)
|
||||
_ <- updateWalletName(walletNameOpt)
|
||||
rescanState <- restartRescanIfNeeded(walletHolder)
|
||||
rescanState <- restartRescanIfNeeded(walletHolder.rescanHandling)
|
||||
_ = setRescanState(rescanState)
|
||||
} yield {
|
||||
logger.info(s"Done loading wallet=$walletNameOpt")
|
||||
@ -363,7 +364,7 @@ case class DLCWalletBitcoindBackendLoader(
|
||||
_ = nodeConf.replaceCallbacks(nodeCallbacks)
|
||||
_ <- walletHolder.replaceWallet(dlcWallet)
|
||||
// do something with possible rescan?
|
||||
rescanState <- restartRescanIfNeeded(walletHolder)
|
||||
rescanState <- restartRescanIfNeeded(walletHolder.rescanHandling)
|
||||
_ = setRescanState(rescanState)
|
||||
} yield {
|
||||
logger.info(s"Done loading wallet=$walletNameOpt")
|
||||
|
@ -1186,12 +1186,13 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
|
||||
/** Only call this if we know we are in a state */
|
||||
private def startRescan(rescan: Rescan): Future[RescanState] = {
|
||||
val stateF = wallet
|
||||
val rescanHandling = wallet.rescanHandling
|
||||
val stateF = rescanHandling
|
||||
.rescanNeutrinoWallet(
|
||||
startOpt = rescan.startBlock,
|
||||
endOpt = rescan.endBlock,
|
||||
addressBatchSize =
|
||||
rescan.batchSize.getOrElse(wallet.discoveryBatchSize()),
|
||||
rescan.batchSize.getOrElse(rescanHandling.discoveryBatchSize()),
|
||||
useCreationTime = !rescan.ignoreCreationTime,
|
||||
force = false
|
||||
)
|
||||
|
@ -1,7 +1,8 @@
|
||||
package org.bitcoins.core.api.wallet
|
||||
|
||||
import org.bitcoins.core.api.wallet.db.AccountDb
|
||||
import org.bitcoins.core.hd.AddressType
|
||||
import org.bitcoins.core.hd.{AddressType, HDAccount}
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
@ -9,4 +10,10 @@ trait AccountHandlingApi {
|
||||
def getDefaultAccount(): Future[AccountDb]
|
||||
def listAccounts(): Future[Vector[AccountDb]]
|
||||
def getDefaultAccountForType(addressType: AddressType): Future[AccountDb]
|
||||
def clearUtxos(account: HDAccount): Future[Unit]
|
||||
def generateScriptPubKeys(
|
||||
account: HDAccount,
|
||||
addressBatchSize: Int,
|
||||
forceGenerateSpks: Boolean
|
||||
): Future[Vector[ScriptPubKey]]
|
||||
}
|
||||
|
@ -27,11 +27,12 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||
* @see
|
||||
* [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44]]
|
||||
*/
|
||||
trait HDWalletApi extends WalletApi with AccountHandlingApi {
|
||||
trait HDWalletApi extends WalletApi {
|
||||
|
||||
override def keyManager: BIP39KeyManagerApi
|
||||
def accountHandling: AccountHandlingApi
|
||||
def fundTxHandling: FundTransactionHandlingApi
|
||||
def rescanHandling: RescanHandlingApi
|
||||
|
||||
/** Gets the balance of the given account */
|
||||
def getBalance(account: HDAccount)(implicit
|
||||
@ -457,8 +458,6 @@ trait HDWalletApi extends WalletApi with AccountHandlingApi {
|
||||
|
||||
override def clearAllUtxos(): Future[HDWalletApi]
|
||||
|
||||
def clearUtxos(account: HDAccount): Future[HDWalletApi]
|
||||
|
||||
/** Gets the address associated with the pubkey at the resulting `BIP32Path`
|
||||
* determined by the default account and the given chainType and addressIndex
|
||||
*/
|
||||
|
@ -1,11 +1,9 @@
|
||||
package org.bitcoins.core.api.wallet
|
||||
|
||||
import org.bitcoins.core.gcs.GolombFilter
|
||||
import org.bitcoins.core.protocol.BlockStamp
|
||||
import org.bitcoins.core.wallet.rescan.RescanState
|
||||
import org.bitcoins.crypto.DoubleSha256DigestBE
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.concurrent.Future
|
||||
|
||||
trait NeutrinoWalletApi { self: WalletApi =>
|
||||
|
||||
@ -18,56 +16,6 @@ trait NeutrinoWalletApi { self: WalletApi =>
|
||||
blockFilters: Vector[(DoubleSha256DigestBE, GolombFilter)])
|
||||
: Future[NeutrinoHDWalletApi]
|
||||
|
||||
/** Recreates the account using BIP-157 approach
|
||||
*
|
||||
* DANGER! This method removes all records from the wallet database and
|
||||
* creates new ones while the account discovery process.
|
||||
*
|
||||
* The Wallet UI should check if the database is empty before calling this
|
||||
* method and let the end users to decide whether they want to proceed or
|
||||
* not.
|
||||
*
|
||||
* This method generates [[addressBatchSize]] of addresses, then matches them
|
||||
* against the BIP-158 compact filters, and downloads and processes the
|
||||
* matched blocks. This method keeps doing the steps until there are
|
||||
* [[WalletConfig.addressGapLimit]] or more unused addresses in a row. In
|
||||
* this case it considers the discovery process completed.
|
||||
*
|
||||
* [[addressBatchSize]] - the number of addresses we should generate from a
|
||||
* keychain to attempt to match in in a rescan
|
||||
* [[WalletConfig.addressGapLimit]] - the number of addresses required to go
|
||||
* without a match before we determine that our wallet is "discovered". For
|
||||
* instance, if addressBatchSize=100, and AddressGapLimit=20 we do a rescan
|
||||
* and the last address we find containing funds is at index 75, we would not
|
||||
* generate more addresses to try and rescan. However if the last index
|
||||
* containing funds was 81, we would generate another 100 addresses from the
|
||||
* keychain and attempt to rescan those.
|
||||
*
|
||||
* @param startOpt
|
||||
* start block (if None it starts from the genesis block)
|
||||
* @param endOpt
|
||||
* end block (if None it ends at the current tip)
|
||||
* @param addressBatchSize
|
||||
* how many addresses to match in a single pass
|
||||
*/
|
||||
def rescanNeutrinoWallet(
|
||||
startOpt: Option[BlockStamp],
|
||||
endOpt: Option[BlockStamp],
|
||||
addressBatchSize: Int,
|
||||
useCreationTime: Boolean,
|
||||
force: Boolean)(implicit ec: ExecutionContext): Future[RescanState]
|
||||
|
||||
/** Helper method to rescan the ENTIRE blockchain. */
|
||||
def fullRescanNeutrinoWallet(addressBatchSize: Int, force: Boolean = false)(
|
||||
implicit ec: ExecutionContext): Future[RescanState] =
|
||||
rescanNeutrinoWallet(startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = addressBatchSize,
|
||||
useCreationTime = false,
|
||||
force = force)
|
||||
|
||||
def discoveryBatchSize(): Int
|
||||
|
||||
}
|
||||
|
||||
object NeutrinoWalletApi {
|
||||
|
@ -1,7 +1,70 @@
|
||||
package org.bitcoins.core.api.wallet
|
||||
|
||||
import org.bitcoins.core.api.chain.ChainQueryApi.FilterResponse
|
||||
import org.bitcoins.core.api.wallet.NeutrinoWalletApi.BlockMatchingResponse
|
||||
import org.bitcoins.core.protocol.BlockStamp
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.wallet.rescan.RescanState
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
trait RescanHandlingApi {
|
||||
def isRescanning(): Future[Boolean]
|
||||
|
||||
/** Recreates the account using BIP-157 approach
|
||||
*
|
||||
* DANGER! This method removes all records from the wallet database and
|
||||
* creates new ones while the account discovery process.
|
||||
*
|
||||
* The Wallet UI should check if the database is empty before calling this
|
||||
* method and let the end users to decide whether they want to proceed or
|
||||
* not.
|
||||
*
|
||||
* This method generates [[addressBatchSize]] of addresses, then matches them
|
||||
* against the BIP-158 compact filters, and downloads and processes the
|
||||
* matched blocks. This method keeps doing the steps until there are
|
||||
* [[WalletConfig.addressGapLimit]] or more unused addresses in a row. In
|
||||
* this case it considers the discovery process completed.
|
||||
*
|
||||
* [[addressBatchSize]] - the number of addresses we should generate from a
|
||||
* keychain to attempt to match in in a rescan
|
||||
* [[WalletConfig.addressGapLimit]] - the number of addresses required to go
|
||||
* without a match before we determine that our wallet is "discovered". For
|
||||
* instance, if addressBatchSize=100, and AddressGapLimit=20 we do a rescan
|
||||
* and the last address we find containing funds is at index 75, we would not
|
||||
* generate more addresses to try and rescan. However if the last index
|
||||
* containing funds was 81, we would generate another 100 addresses from the
|
||||
* keychain and attempt to rescan those.
|
||||
*
|
||||
* @param startOpt
|
||||
* start block (if None it starts from the genesis block)
|
||||
* @param endOpt
|
||||
* end block (if None it ends at the current tip)
|
||||
* @param addressBatchSize
|
||||
* how many addresses to match in a single pass
|
||||
*/
|
||||
def rescanNeutrinoWallet(
|
||||
startOpt: Option[BlockStamp],
|
||||
endOpt: Option[BlockStamp],
|
||||
addressBatchSize: Int,
|
||||
useCreationTime: Boolean,
|
||||
force: Boolean): Future[RescanState]
|
||||
|
||||
/** Helper method to rescan the ENTIRE blockchain. */
|
||||
def fullRescanNeutrinoWallet(
|
||||
addressBatchSize: Int,
|
||||
force: Boolean = false): Future[RescanState] =
|
||||
rescanNeutrinoWallet(startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = addressBatchSize,
|
||||
useCreationTime = false,
|
||||
force = force)
|
||||
|
||||
def findMatches(
|
||||
filters: Vector[FilterResponse],
|
||||
scripts: Vector[ScriptPubKey],
|
||||
parallelismLevel: Int
|
||||
): Future[Vector[BlockMatchingResponse]]
|
||||
|
||||
def discoveryBatchSize(): Int
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class RescanDLCTest extends DualWalletTestCachedBitcoind {
|
||||
|
||||
Vector(hash) <- bitcoind.generate(1)
|
||||
|
||||
_ <- wallet.rescanNeutrinoWallet(
|
||||
_ <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = Some(BlockHash(hash)),
|
||||
addressBatchSize = 20,
|
||||
@ -100,7 +100,7 @@ class RescanDLCTest extends DualWalletTestCachedBitcoind {
|
||||
|
||||
Vector(hash) <- bitcoind.generate(1)
|
||||
|
||||
_ <- wallet.rescanNeutrinoWallet(
|
||||
_ <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = Some(BlockHash(hash)),
|
||||
addressBatchSize = 20,
|
||||
|
@ -349,6 +349,7 @@ object DLCAppConfig
|
||||
Wallet
|
||||
.initialize(
|
||||
wallet = unInitializedWallet,
|
||||
accountHandling = unInitializedWallet.accountHandling,
|
||||
bip39PasswordOpt = bip39PasswordOpt
|
||||
)
|
||||
.map(_.asInstanceOf[DLCWallet])
|
||||
|
@ -113,7 +113,8 @@ abstract class DLCWallet
|
||||
DLCActionBuilder(dlcWalletDAOs)
|
||||
}
|
||||
|
||||
override lazy val transactionProcessing: DLCTransactionProcessing = {
|
||||
override protected lazy val transactionProcessing
|
||||
: DLCTransactionProcessing = {
|
||||
val txProcessing = TransactionProcessing(
|
||||
walletApi = this,
|
||||
chainQueryApi = chainQueryApi,
|
||||
@ -123,10 +124,10 @@ abstract class DLCWallet
|
||||
DLCTransactionProcessing(
|
||||
txProcessing = txProcessing,
|
||||
dlcWalletDAOs = dlcWalletDAOs,
|
||||
walletDAOs = walletDAOs,
|
||||
dlcDataManagement = dlcDataManagement,
|
||||
keyManager = keyManager,
|
||||
transactionDAO = transactionDAO,
|
||||
rescanHandling = this,
|
||||
utxoHandling = utxoHandling,
|
||||
dlcWalletApi = this
|
||||
)
|
||||
|
@ -5,7 +5,6 @@ import org.bitcoins.core.api.dlc.wallet.DLCWalletApi
|
||||
import org.bitcoins.core.api.dlc.wallet.db.*
|
||||
import org.bitcoins.core.api.wallet.{
|
||||
ProcessTxResult,
|
||||
RescanHandlingApi,
|
||||
TransactionProcessingApi,
|
||||
UtxoHandlingApi
|
||||
}
|
||||
@ -39,7 +38,7 @@ import org.bitcoins.db.SafeDatabase
|
||||
import org.bitcoins.dlc.wallet.DLCAppConfig
|
||||
import org.bitcoins.dlc.wallet.models.*
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.wallet.models.TransactionDAO
|
||||
import org.bitcoins.wallet.models.{TransactionDAO, WalletDAOs}
|
||||
|
||||
import scala.concurrent.*
|
||||
|
||||
@ -49,10 +48,10 @@ import scala.concurrent.*
|
||||
case class DLCTransactionProcessing(
|
||||
txProcessing: TransactionProcessingApi,
|
||||
dlcWalletDAOs: DLCWalletDAOs,
|
||||
walletDAOs: WalletDAOs,
|
||||
dlcDataManagement: DLCDataManagement,
|
||||
keyManager: BIP39KeyManager,
|
||||
transactionDAO: TransactionDAO,
|
||||
rescanHandling: RescanHandlingApi,
|
||||
utxoHandling: UtxoHandlingApi,
|
||||
dlcWalletApi: DLCWalletApi)(implicit
|
||||
dlcConfig: DLCAppConfig,
|
||||
@ -318,7 +317,7 @@ case class DLCTransactionProcessing(
|
||||
|
||||
_ <- dlcDAO.updateAll(updated)
|
||||
dlcIds = updated.map(_.dlcId).distinct
|
||||
isRescanning <- rescanHandling.isRescanning()
|
||||
isRescanning <- walletDAOs.stateDescriptorDAO.isRescanning
|
||||
_ <- sendWsDLCStateChange(dlcIds, isRescanning)
|
||||
} yield {
|
||||
updated
|
||||
|
@ -107,7 +107,7 @@ val addrBatchSize = 100
|
||||
val rescannedBalanceF = for {
|
||||
_ <- clearedWalletF
|
||||
w <- walletF
|
||||
_ <- w.fullRescanNeutrinoWallet(addrBatchSize)
|
||||
_ <- w.rescanHandling.fullRescanNeutrinoWallet(addrBatchSize)
|
||||
balanceAfterRescan <- w.getBalance()
|
||||
} yield {
|
||||
println(s"Wallet balance after rescan: ${balanceAfterRescan}")
|
||||
|
@ -152,7 +152,7 @@ val wallet = Wallet(new NodeApi {
|
||||
override def getConnectionCount: Future[Int] = Future.successful(0)
|
||||
}, chainApi, ConstantFeeRateProvider(SatoshisPerVirtualByte.one))
|
||||
val walletF: Future[WalletApi] = configF.flatMap { _ =>
|
||||
Wallet.initialize(wallet, None)
|
||||
Wallet.initialize(wallet, wallet.accountHandling, None)
|
||||
}
|
||||
|
||||
// when this future completes, ww have sent a transaction
|
||||
|
@ -214,7 +214,8 @@ class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
|
||||
|
||||
rescan <- wallet.isRescanning()
|
||||
_ = assert(!rescan)
|
||||
rescanState <- wallet.fullRescanNeutrinoWallet(addressBatchSize = 7)
|
||||
rescanState <- wallet.rescanHandling
|
||||
.fullRescanNeutrinoWallet(addressBatchSize = 7)
|
||||
|
||||
_ <- AsyncUtil.awaitConditionF(
|
||||
() => condition(),
|
||||
|
@ -299,7 +299,9 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||
walletConfig.start().flatMap { _ =>
|
||||
val wallet =
|
||||
Wallet(nodeApi, chainQueryApi, new RandomFeeProvider)(walletConfig)
|
||||
Wallet.initialize(wallet, walletConfig.bip39PasswordOpt)
|
||||
Wallet.initialize(wallet,
|
||||
wallet.accountHandling,
|
||||
walletConfig.bip39PasswordOpt)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -327,7 +329,9 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||
)
|
||||
|
||||
Wallet
|
||||
.initialize(wallet, config.walletConf.bip39PasswordOpt)
|
||||
.initialize(wallet,
|
||||
wallet.accountHandling,
|
||||
config.walletConf.bip39PasswordOpt)
|
||||
.map(_.asInstanceOf[DLCWallet])
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
addresses <- wallet.listAddresses(account)
|
||||
_ = assert(addresses.nonEmpty)
|
||||
|
||||
_ <- wallet.clearUtxos(account)
|
||||
_ <- wallet.accountHandling.clearUtxos(account)
|
||||
|
||||
clearedUtxos <- wallet.listUtxos(account)
|
||||
clearedAddresses <- wallet.listAddresses(account)
|
||||
@ -79,7 +79,8 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
initBalance > CurrencyUnits.zero,
|
||||
s"Cannot run rescan test if our init wallet balance is zero!"
|
||||
)
|
||||
rescanState <- wallet.fullRescanNeutrinoWallet(DEFAULT_ADDR_BATCH_SIZE)
|
||||
rescanState <- wallet.rescanHandling.fullRescanNeutrinoWallet(
|
||||
DEFAULT_ADDR_BATCH_SIZE)
|
||||
_ = assert(rescanState.isInstanceOf[RescanState.RescanStarted])
|
||||
_ <- RescanState.awaitRescanDone(rescanState)
|
||||
balanceAfterRescan <- wallet.getBalance()
|
||||
@ -133,7 +134,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
_ <- wallet.clearAllUtxos()
|
||||
zeroBalance <- wallet.getBalance()
|
||||
_ = assert(zeroBalance == Satoshis.zero)
|
||||
rescanState <- wallet.rescanNeutrinoWallet(
|
||||
rescanState <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = txInBlockHeightOpt,
|
||||
endOpt = None,
|
||||
addressBatchSize = DEFAULT_ADDR_BATCH_SIZE,
|
||||
@ -200,8 +201,9 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
_ <- wallet.clearAllUtxos()
|
||||
_ <- wallet.clearAllAddresses()
|
||||
balanceAfterClear <- wallet.getBalance()
|
||||
rescanState <- wallet.fullRescanNeutrinoWallet(addressBatchSize = 1,
|
||||
force = true)
|
||||
rescanState <- wallet.rescanHandling.fullRescanNeutrinoWallet(
|
||||
addressBatchSize = 1,
|
||||
force = true)
|
||||
|
||||
_ <- RescanState.awaitRescanDone(rescanState)
|
||||
_ <- AsyncUtil.awaitConditionF(
|
||||
@ -249,7 +251,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
|
||||
for {
|
||||
_ <- newTxWalletF
|
||||
rescanState <- wallet.rescanNeutrinoWallet(
|
||||
rescanState <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = DEFAULT_ADDR_BATCH_SIZE,
|
||||
@ -293,7 +295,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
)
|
||||
oldestUtxoHeight <- oldestHeightF
|
||||
end = Some(BlockStamp.BlockHeight(oldestUtxoHeight - 1))
|
||||
rescanState <- wallet.rescanNeutrinoWallet(
|
||||
rescanState <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = BlockStamp.height0Opt,
|
||||
endOpt = end,
|
||||
addressBatchSize = DEFAULT_ADDR_BATCH_SIZE,
|
||||
@ -313,7 +315,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
(fixture: WalletWithBitcoindRpc) =>
|
||||
val wallet = fixture.wallet
|
||||
// do these in parallel on purpose to simulate multiple threads calling rescan
|
||||
val startF = wallet.rescanNeutrinoWallet(
|
||||
val startF = wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = DEFAULT_ADDR_BATCH_SIZE,
|
||||
@ -324,7 +326,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
// slight delay to make sure other rescan is started
|
||||
val alreadyStartedF =
|
||||
AsyncUtil.nonBlockingSleep(10.millis).flatMap { _ =>
|
||||
wallet.rescanNeutrinoWallet(
|
||||
wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = DEFAULT_ADDR_BATCH_SIZE,
|
||||
@ -352,7 +354,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
// start a rescan without sending payment to that address
|
||||
for {
|
||||
address <- addressNoFundsF
|
||||
_ <- wallet.rescanNeutrinoWallet(
|
||||
_ <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = 10,
|
||||
@ -392,7 +394,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
val bitcoind = fixture.bitcoind
|
||||
val amt = Bitcoins.one
|
||||
for {
|
||||
_ <- wallet.rescanNeutrinoWallet(
|
||||
_ <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = 10,
|
||||
@ -401,7 +403,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
)
|
||||
addressNoFunds <- wallet.getNewChangeAddress()
|
||||
// rescan again
|
||||
_ <- wallet.rescanNeutrinoWallet(
|
||||
_ <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = 10,
|
||||
@ -428,7 +430,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
"Cannot be rescanning before we started the test"
|
||||
)
|
||||
// start the rescan
|
||||
state <- wallet.rescanNeutrinoWallet(
|
||||
state <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = 10,
|
||||
@ -461,7 +463,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
"Cannot be rescanning before we started the test"
|
||||
)
|
||||
// start the rescan
|
||||
state <- wallet.rescanNeutrinoWallet(
|
||||
state <- wallet.rescanHandling.rescanNeutrinoWallet(
|
||||
startOpt = None,
|
||||
endOpt = None,
|
||||
addressBatchSize = 10,
|
||||
@ -551,7 +553,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
_ <- wallet.clearAllUtxos()
|
||||
_ <- wallet.clearAllAddresses()
|
||||
balanceAfterClear <- wallet.getBalance()
|
||||
rescanState <- wallet.fullRescanNeutrinoWallet(1, true)
|
||||
rescanState <- wallet.rescanHandling.fullRescanNeutrinoWallet(1, true)
|
||||
_ <- RescanState.awaitRescanDone(rescanState)
|
||||
_ <- AsyncUtil.awaitConditionF(
|
||||
() => wallet.getBalance().map(_ == balanceAfterPayment1),
|
||||
@ -574,7 +576,8 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
// a fresh pool of addresses
|
||||
_ <- wallet.clearAllUtxos()
|
||||
_ <- wallet.clearAllAddresses()
|
||||
rescanState <- wallet.fullRescanNeutrinoWallet(DEFAULT_ADDR_BATCH_SIZE)
|
||||
rescanState <- wallet.rescanHandling.fullRescanNeutrinoWallet(
|
||||
DEFAULT_ADDR_BATCH_SIZE)
|
||||
_ = assert(rescanState.isInstanceOf[RescanState.RescanStarted])
|
||||
_ <- RescanState.awaitRescanDone(rescanState)
|
||||
addresses <- wallet.listAddresses()
|
||||
|
@ -158,6 +158,7 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
||||
)(config)
|
||||
init <- Wallet.initialize(
|
||||
wallet = wallet,
|
||||
accountHandling = wallet.accountHandling,
|
||||
bip39PasswordOpt = bip39PasswordOpt
|
||||
)
|
||||
} yield init
|
||||
|
@ -138,7 +138,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
startHeight = 0,
|
||||
endHeight = height
|
||||
)
|
||||
matched <- wallet.findMatches(
|
||||
matched <- wallet.rescanHandling.findMatches(
|
||||
filters = filtersResponse,
|
||||
scripts = Vector(
|
||||
// this is a random address which is included into the test block
|
||||
@ -162,9 +162,13 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
(wallet: Wallet) =>
|
||||
val bip39PasswordOpt = wallet.walletConfig.bip39PasswordOpt
|
||||
val twiceF = Wallet
|
||||
.initialize(wallet, bip39PasswordOpt)
|
||||
.initialize(wallet = wallet,
|
||||
accountHandling = wallet.accountHandling,
|
||||
bip39PasswordOpt = bip39PasswordOpt)
|
||||
.flatMap { _ =>
|
||||
Wallet.initialize(wallet, bip39PasswordOpt)
|
||||
Wallet.initialize(wallet = wallet,
|
||||
accountHandling = wallet.accountHandling,
|
||||
bip39PasswordOpt = bip39PasswordOpt)
|
||||
}
|
||||
|
||||
twiceF.map(_ => succeed)
|
||||
@ -176,11 +180,12 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
val bip39PasswordOpt = wallet.walletConfig.bip39PasswordOpt
|
||||
recoverToSucceededIf[RuntimeException] {
|
||||
Wallet
|
||||
.initialize(wallet, bip39PasswordOpt)
|
||||
.initialize(wallet, wallet.accountHandling, bip39PasswordOpt)
|
||||
.flatMap { _ =>
|
||||
// use a BIP39 password to make the key-managers different
|
||||
Wallet.initialize(
|
||||
wallet,
|
||||
wallet.accountHandling,
|
||||
Some("random-password-to-make-key-managers-different")
|
||||
)
|
||||
}
|
||||
@ -206,7 +211,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
|
||||
recoverToSucceededIf[IllegalArgumentException] {
|
||||
walletDiffKeyManagerF.flatMap { walletDiffKeyManager =>
|
||||
Wallet.initialize(walletDiffKeyManager, None)
|
||||
Wallet.initialize(walletDiffKeyManager, wallet.accountHandling, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -373,6 +378,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
// initialize it
|
||||
initOldWallet <- Wallet.initialize(
|
||||
wallet = wallet,
|
||||
accountHandling = wallet.accountHandling,
|
||||
bip39PasswordOpt = wallet.walletConfig.bip39PasswordOpt
|
||||
)
|
||||
isOldWalletEmpty <- initOldWallet.isEmpty()
|
||||
|
@ -49,7 +49,6 @@ import scala.util.{Failure, Random, Success}
|
||||
abstract class Wallet
|
||||
extends NeutrinoHDWalletApi
|
||||
with AddressHandling
|
||||
with RescanHandling
|
||||
with WalletLogger {
|
||||
|
||||
override def keyManager: BIP39KeyManager = {
|
||||
@ -67,8 +66,6 @@ abstract class Wallet
|
||||
|
||||
val networkParameters: BitcoinNetwork = walletConfig.network
|
||||
|
||||
override val discoveryBatchSize: Int = walletConfig.discoveryBatchSize
|
||||
|
||||
private[bitcoins] val addressDAO: AddressDAO = AddressDAO()
|
||||
private[bitcoins] val accountDAO: AccountDAO = AccountDAO()
|
||||
private[bitcoins] val spendingInfoDAO: SpendingInfoDAO = SpendingInfoDAO()
|
||||
@ -105,16 +102,17 @@ abstract class Wallet
|
||||
UtxoHandling(spendingInfoDAO, transactionDAO, chainQueryApi)
|
||||
|
||||
def fundTxHandling: FundTransactionHandling = FundTransactionHandling(
|
||||
accountHandling = this,
|
||||
accountHandling = accountHandling,
|
||||
utxoHandling = utxoHandling,
|
||||
addressHandling = this,
|
||||
spendingInfoDAO = spendingInfoDAO,
|
||||
transactionDAO = transactionDAO,
|
||||
keyManager = keyManager
|
||||
)
|
||||
def accountHandling: AccountHandlingApi = AccountHandling(accountDAO)
|
||||
def accountHandling: AccountHandlingApi =
|
||||
AccountHandling(this, walletDAOs)
|
||||
|
||||
override lazy val transactionProcessing: TransactionProcessingApi = {
|
||||
protected lazy val transactionProcessing: TransactionProcessingApi = {
|
||||
TransactionProcessing(
|
||||
walletApi = this,
|
||||
chainQueryApi = chainQueryApi,
|
||||
@ -122,6 +120,18 @@ abstract class Wallet
|
||||
walletDAOs = walletDAOs
|
||||
)
|
||||
}
|
||||
override lazy val rescanHandling: RescanHandlingApi = {
|
||||
RescanHandling(
|
||||
transactionProcessing = transactionProcessing,
|
||||
accountHandling = accountHandling,
|
||||
addressHandling = this,
|
||||
chainQueryApi = chainQueryApi,
|
||||
nodeApi = nodeApi,
|
||||
walletDAOs = walletDAOs
|
||||
)
|
||||
}
|
||||
|
||||
override def isRescanning(): Future[Boolean] = rescanHandling.isRescanning()
|
||||
|
||||
def walletCallbacks: WalletCallbacks = walletConfig.callBacks
|
||||
|
||||
@ -303,18 +313,6 @@ abstract class Wallet
|
||||
spendingInfoCount <- spendingInfoDAO.count()
|
||||
} yield addressCount == 0 && spendingInfoCount == 0
|
||||
|
||||
override def clearUtxos(account: HDAccount): Future[Wallet] = {
|
||||
val aggregatedActions
|
||||
: DBIOAction[Wallet, NoStream, Effect.Read with Effect.Write] = {
|
||||
for {
|
||||
accountUtxos <- spendingInfoDAO.findAllForAccountAction(account)
|
||||
_ <- spendingInfoDAO.deleteSpendingInfoDbAllAction(accountUtxos)
|
||||
} yield this
|
||||
}
|
||||
|
||||
safeDatabase.run(aggregatedActions)
|
||||
}
|
||||
|
||||
override def clearAllUtxos(): Future[Wallet] = {
|
||||
val aggregatedActions
|
||||
: DBIOAction[Unit, NoStream, Effect.Write with Effect.Transactional] =
|
||||
@ -1008,7 +1006,7 @@ abstract class Wallet
|
||||
for {
|
||||
accountDb <- getDefaultAccount()
|
||||
walletState <- getSyncState()
|
||||
rescan <- isRescanning()
|
||||
rescan <- rescanHandling.isRescanning()
|
||||
} yield {
|
||||
WalletInfo(
|
||||
walletName = walletConfig.walletName,
|
||||
@ -1204,9 +1202,10 @@ object Wallet extends WalletLogger {
|
||||
|
||||
def initialize(
|
||||
wallet: Wallet,
|
||||
accountHandling: AccountHandlingApi,
|
||||
bip39PasswordOpt: Option[String]
|
||||
): Future[Wallet] = {
|
||||
implicit val walletAppConfig = wallet.walletConfig
|
||||
implicit val walletAppConfig: WalletAppConfig = wallet.walletConfig
|
||||
import walletAppConfig.ec
|
||||
val passwordOpt = walletAppConfig.aesPasswordOpt
|
||||
|
||||
@ -1260,7 +1259,7 @@ object Wallet extends WalletLogger {
|
||||
val threshold = Instant.now().minus(1, ChronoUnit.HOURS)
|
||||
val isOldCreationTime = creationTime.compareTo(threshold) <= 0
|
||||
if (isOldCreationTime) {
|
||||
wallet
|
||||
accountHandling
|
||||
.generateScriptPubKeys(
|
||||
account = walletAppConfig.defaultAccount,
|
||||
addressBatchSize = walletAppConfig.discoveryBatchSize,
|
||||
|
@ -28,14 +28,13 @@ import org.bitcoins.core.protocol.transaction.{
|
||||
TransactionOutPoint,
|
||||
TransactionOutput
|
||||
}
|
||||
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.psbt.PSBT
|
||||
import org.bitcoins.core.wallet.builder.{
|
||||
FundRawTxHelper,
|
||||
ShufflingNonInteractiveFinalizer
|
||||
}
|
||||
import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerVirtualByte}
|
||||
import org.bitcoins.core.wallet.rescan.RescanState
|
||||
import org.bitcoins.core.wallet.utxo.{
|
||||
AddressTag,
|
||||
AddressTagName,
|
||||
@ -69,6 +68,8 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
|
||||
override def accountHandling: AccountHandlingApi = wallet.accountHandling
|
||||
|
||||
override def rescanHandling: RescanHandlingApi = wallet.rescanHandling
|
||||
|
||||
override def fundTxHandling: FundTransactionHandlingApi =
|
||||
wallet.fundTxHandling
|
||||
def isInitialized: Boolean = synchronized {
|
||||
@ -141,24 +142,7 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
delegate(_.processCompactFilters(blockFilters))
|
||||
}
|
||||
|
||||
override def rescanNeutrinoWallet(
|
||||
startOpt: Option[BlockStamp],
|
||||
endOpt: Option[BlockStamp],
|
||||
addressBatchSize: Int,
|
||||
useCreationTime: Boolean,
|
||||
force: Boolean
|
||||
)(implicit ec: ExecutionContext): Future[RescanState] =
|
||||
delegate(
|
||||
_.rescanNeutrinoWallet(
|
||||
startOpt,
|
||||
endOpt,
|
||||
addressBatchSize,
|
||||
useCreationTime,
|
||||
force
|
||||
)
|
||||
)
|
||||
|
||||
override def discoveryBatchSize(): Int = wallet.discoveryBatchSize()
|
||||
override def isRescanning(): Future[Boolean] = delegate(_.isRescanning())
|
||||
|
||||
override lazy val nodeApi: NodeApi = wallet.nodeApi
|
||||
override lazy val chainQueryApi: ChainQueryApi = wallet.chainQueryApi
|
||||
@ -371,8 +355,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
_.getSyncState()
|
||||
)
|
||||
|
||||
override def isRescanning(): Future[Boolean] = delegate(_.isRescanning())
|
||||
|
||||
override def createDLCOffer(
|
||||
contractInfo: ContractInfo,
|
||||
collateral: Satoshis,
|
||||
@ -649,10 +631,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
_.clearAllUtxos()
|
||||
)
|
||||
|
||||
override def clearUtxos(account: HDAccount): Future[HDWalletApi] = delegate(
|
||||
_.clearUtxos(account)
|
||||
)
|
||||
|
||||
override def clearAllAddresses(): Future[WalletApi] = {
|
||||
delegate(_.clearAllAddresses())
|
||||
}
|
||||
@ -782,12 +760,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
): Future[NeutrinoHDWalletApi] =
|
||||
delegate(_.processCompactFilter(blockHash, blockFilter))
|
||||
|
||||
override def fullRescanNeutrinoWallet(addressBatchSize: Int, force: Boolean)(
|
||||
implicit ec: ExecutionContext
|
||||
): Future[RescanState] = delegate(
|
||||
_.fullRescanNeutrinoWallet(addressBatchSize, force)
|
||||
)
|
||||
|
||||
override def getNewChangeAddress()(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[BitcoinAddress] =
|
||||
|
@ -426,6 +426,7 @@ object WalletAppConfig
|
||||
|
||||
Wallet.initialize(
|
||||
wallet = unInitializedWallet,
|
||||
accountHandling = unInitializedWallet.accountHandling,
|
||||
bip39PasswordOpt = bip39PasswordOpt
|
||||
)
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package org.bitcoins.wallet.internal
|
||||
|
||||
import org.bitcoins.commons.util.BitcoinSLogger
|
||||
import org.bitcoins.core.api.wallet.AccountHandlingApi
|
||||
import org.bitcoins.core.api.wallet.db.AccountDb
|
||||
import org.bitcoins.core.hd.AddressType.*
|
||||
import org.bitcoins.core.hd.*
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.blockchain.{
|
||||
ChainParams,
|
||||
MainNetChainParams,
|
||||
@ -11,19 +13,35 @@ import org.bitcoins.core.protocol.blockchain.{
|
||||
SigNetChainParams,
|
||||
TestNetChainParams
|
||||
}
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.db.SafeDatabase
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.wallet.models.AccountDAO
|
||||
import org.bitcoins.wallet.models.{
|
||||
AccountDAO,
|
||||
AddressDAO,
|
||||
ScriptPubKeyDAO,
|
||||
SpendingInfoDAO,
|
||||
WalletDAOs
|
||||
}
|
||||
import slick.dbio.{DBIOAction, Effect, NoStream}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
/** Provides functionality related enumerating accounts. Account creation does
|
||||
* not happen here, as that requires an unlocked wallet.
|
||||
*/
|
||||
case class AccountHandling(accountDAO: AccountDAO)(implicit
|
||||
case class AccountHandling(
|
||||
addressHandling: AddressHandling,
|
||||
walletDAOs: WalletDAOs)(implicit
|
||||
walletConfig: WalletAppConfig,
|
||||
ec: ExecutionContext)
|
||||
extends AccountHandlingApi {
|
||||
|
||||
extends AccountHandlingApi
|
||||
with BitcoinSLogger {
|
||||
private val accountDAO: AccountDAO = walletDAOs.accountDAO
|
||||
private val spendingInfoDAO: SpendingInfoDAO = walletDAOs.utxoDAO
|
||||
private val addressDAO: AddressDAO = walletDAOs.addressDAO
|
||||
private val scriptPubKeyDAO: ScriptPubKeyDAO = walletDAOs.scriptPubKeyDAO
|
||||
private val safeDatabase: SafeDatabase = spendingInfoDAO.safeDatabase
|
||||
private val chainParams: ChainParams = walletConfig.chain
|
||||
|
||||
/** @inheritdoc */
|
||||
@ -71,6 +89,107 @@ case class AccountHandling(accountDAO: AccountDAO)(implicit
|
||||
} yield getOrThrowAccount(account)
|
||||
}
|
||||
|
||||
override def clearUtxos(account: HDAccount): Future[Unit] = {
|
||||
val aggregatedActions
|
||||
: DBIOAction[Unit, NoStream, Effect.Read with Effect.Write] = {
|
||||
for {
|
||||
accountUtxos <- spendingInfoDAO.findAllForAccountAction(account)
|
||||
_ <- spendingInfoDAO.deleteSpendingInfoDbAllAction(accountUtxos)
|
||||
} yield ()
|
||||
}
|
||||
|
||||
safeDatabase.run(aggregatedActions)
|
||||
}
|
||||
|
||||
override def generateScriptPubKeys(
|
||||
account: HDAccount,
|
||||
addressBatchSize: Int,
|
||||
forceGenerateSpks: Boolean
|
||||
): Future[Vector[ScriptPubKey]] = {
|
||||
val action = generateScriptPubKeysAction(
|
||||
account = account,
|
||||
addressBatchSize = addressBatchSize,
|
||||
forceGenerateSpks = forceGenerateSpks
|
||||
)
|
||||
safeDatabase.run(action)
|
||||
}
|
||||
|
||||
/** If forceGeneratSpks is true or addressCount == 0 we generate a new pool of
|
||||
* scriptpubkeys
|
||||
*/
|
||||
private def generateScriptPubKeysAction(
|
||||
account: HDAccount,
|
||||
addressBatchSize: Int,
|
||||
forceGenerateSpks: Boolean
|
||||
): DBIOAction[Vector[
|
||||
ScriptPubKey
|
||||
],
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
val addressCountA = addressDAO.countAction
|
||||
for {
|
||||
addressCount <- addressCountA
|
||||
addresses <- {
|
||||
if (forceGenerateSpks || addressCount == 0) {
|
||||
logger.info(
|
||||
s"Generating $addressBatchSize fresh addresses for the rescan"
|
||||
)
|
||||
generateAddressesForRescanAction(account, addressBatchSize)
|
||||
} else {
|
||||
// we don't want to continously generate addresses
|
||||
// if our wallet already has them, so just use what is in the
|
||||
// database already
|
||||
addressDAO.findAllAddressesAction().map(_.map(_.address))
|
||||
}
|
||||
}
|
||||
spksDb <- scriptPubKeyDAO.findAllAction()
|
||||
} yield {
|
||||
val addrSpks =
|
||||
addresses.map(_.scriptPubKey)
|
||||
val otherSpks = spksDb.map(_.scriptPubKey)
|
||||
|
||||
(addrSpks ++ otherSpks).distinct
|
||||
}
|
||||
}
|
||||
|
||||
private def generateAddressesForRescanAction(
|
||||
account: HDAccount,
|
||||
addressBatchSize: Int
|
||||
): DBIOAction[Vector[
|
||||
BitcoinAddress
|
||||
],
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
val receiveAddressesA: DBIOAction[
|
||||
Vector[
|
||||
BitcoinAddress
|
||||
],
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
DBIOAction.sequence {
|
||||
1.to(addressBatchSize)
|
||||
.map(_ => addressHandling.getNewAddressAction(account))
|
||||
}
|
||||
}.map(_.toVector)
|
||||
|
||||
val changeAddressesA: DBIOAction[
|
||||
Vector[
|
||||
BitcoinAddress
|
||||
],
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
DBIOAction.sequence {
|
||||
1.to(addressBatchSize)
|
||||
.map(_ => addressHandling.getNewChangeAddressAction(account))
|
||||
}
|
||||
}.map(_.toVector)
|
||||
|
||||
for {
|
||||
receiveAddresses <- receiveAddressesA
|
||||
changeAddresses <- changeAddressesA
|
||||
} yield receiveAddresses ++ changeAddresses
|
||||
}
|
||||
|
||||
/** The default HD coin for this wallet, read from config */
|
||||
protected[wallet] lazy val DEFAULT_HD_COIN: HDCoin = {
|
||||
val coinType = DEFAULT_HD_COIN_TYPE
|
||||
|
@ -1,14 +1,17 @@
|
||||
package org.bitcoins.wallet.internal
|
||||
|
||||
import org.apache.pekko.NotUsed
|
||||
import org.apache.pekko.actor.ActorSystem
|
||||
import org.apache.pekko.stream.scaladsl.{Flow, Keep, Merge, Sink, Source}
|
||||
import org.bitcoins.core.api.chain.ChainQueryApi
|
||||
import org.bitcoins.core.api.chain.ChainQueryApi.{
|
||||
FilterResponse,
|
||||
InvalidBlockRange
|
||||
}
|
||||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.api.wallet.NeutrinoWalletApi.BlockMatchingResponse
|
||||
import org.bitcoins.core.api.wallet.{
|
||||
AccountHandlingApi,
|
||||
RescanHandlingApi,
|
||||
TransactionProcessingApi
|
||||
}
|
||||
@ -16,26 +19,44 @@ import org.bitcoins.core.gcs.SimpleFilterMatcher
|
||||
import org.bitcoins.core.hd.{HDAccount, HDChainType}
|
||||
import org.bitcoins.core.protocol.BlockStamp.BlockHeight
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
|
||||
import org.bitcoins.core.protocol.BlockStamp
|
||||
import org.bitcoins.core.util.FutureUtil
|
||||
import org.bitcoins.core.wallet.rescan.RescanState
|
||||
import org.bitcoins.core.wallet.rescan.RescanState.RescanTerminatedEarly
|
||||
import org.bitcoins.crypto.DoubleSha256DigestBE
|
||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||
import slick.dbio.{DBIOAction, Effect, NoStream}
|
||||
import org.bitcoins.wallet.callback.WalletCallbacks
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.wallet.models.{
|
||||
AddressDAO,
|
||||
SpendingInfoDAO,
|
||||
WalletDAOs,
|
||||
WalletStateDescriptorDAO
|
||||
}
|
||||
import org.bitcoins.wallet.WalletLogger
|
||||
|
||||
import java.time.Instant
|
||||
import scala.concurrent.{ExecutionContext, Future, Promise}
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
private[wallet] trait RescanHandling
|
||||
case class RescanHandling(
|
||||
transactionProcessing: TransactionProcessingApi,
|
||||
accountHandling: AccountHandlingApi,
|
||||
addressHandling: AddressHandling,
|
||||
chainQueryApi: ChainQueryApi,
|
||||
nodeApi: NodeApi,
|
||||
walletDAOs: WalletDAOs)(implicit
|
||||
walletConfig: WalletAppConfig,
|
||||
system: ActorSystem)
|
||||
extends RescanHandlingApi
|
||||
with WalletLogger {
|
||||
self: Wallet =>
|
||||
|
||||
/////////////////////
|
||||
// Public facing API
|
||||
|
||||
def transactionProcessing: TransactionProcessingApi
|
||||
private implicit val rescanEC: ExecutionContext =
|
||||
ExecutionContext.fromExecutor(walletConfig.rescanThreadPool)
|
||||
private def walletCallbacks: WalletCallbacks = walletConfig.callBacks
|
||||
private val stateDescriptorDAO: WalletStateDescriptorDAO =
|
||||
walletDAOs.stateDescriptorDAO
|
||||
private val addressDAO: AddressDAO = walletDAOs.addressDAO
|
||||
private val spendingInfoDAO: SpendingInfoDAO = walletDAOs.utxoDAO
|
||||
private val creationTime: Instant = walletConfig.creationTime
|
||||
|
||||
override def isRescanning(): Future[Boolean] = stateDescriptorDAO.isRescanning
|
||||
|
||||
@ -46,9 +67,9 @@ private[wallet] trait RescanHandling
|
||||
addressBatchSize: Int,
|
||||
useCreationTime: Boolean,
|
||||
force: Boolean
|
||||
)(implicit ec: ExecutionContext): Future[RescanState] = {
|
||||
): Future[RescanState] = {
|
||||
for {
|
||||
account <- getDefaultAccount()
|
||||
account <- accountHandling.getDefaultAccount()
|
||||
state <- rescanNeutrinoWallet(
|
||||
account.hdAccount,
|
||||
startOpt,
|
||||
@ -98,7 +119,7 @@ private[wallet] trait RescanHandling
|
||||
case (None, false) =>
|
||||
Future.successful(None)
|
||||
}
|
||||
_ <- clearUtxos(account)
|
||||
_ <- accountHandling.clearUtxos(account)
|
||||
state <- doNeutrinoRescan(
|
||||
account = account,
|
||||
startOpt = start,
|
||||
@ -150,6 +171,8 @@ private[wallet] trait RescanHandling
|
||||
}
|
||||
}
|
||||
|
||||
override def discoveryBatchSize(): Int = walletConfig.discoveryBatchSize
|
||||
|
||||
/** Register callbacks to reset rescan flag in the database if there is a
|
||||
* rescan failure
|
||||
*/
|
||||
@ -197,7 +220,7 @@ private[wallet] trait RescanHandling
|
||||
filterBatchSize: Int,
|
||||
forceGenerateSpks: Boolean
|
||||
): RescanState.RescanStarted = {
|
||||
val scriptsF = generateScriptPubKeys(
|
||||
val scriptsF = accountHandling.generateScriptPubKeys(
|
||||
account = account,
|
||||
addressBatchSize = addressBatchSize,
|
||||
forceGenerateSpks = forceGenerateSpks
|
||||
@ -236,9 +259,7 @@ private[wallet] trait RescanHandling
|
||||
val heightRange = filterResponse.map(_.blockHeight)
|
||||
val f =
|
||||
scriptsF.flatMap { scripts =>
|
||||
searchFiltersForMatches(scripts, filterResponse, parallelism)(
|
||||
ExecutionContext.fromExecutor(walletConfig.rescanThreadPool)
|
||||
)
|
||||
searchFiltersForMatches(scripts, filterResponse, parallelism)
|
||||
}
|
||||
|
||||
f.onComplete {
|
||||
@ -486,82 +507,6 @@ private[wallet] trait RescanHandling
|
||||
rescanStateF
|
||||
}
|
||||
|
||||
private def generateAddressesForRescanAction(
|
||||
account: HDAccount,
|
||||
addressBatchSize: Int
|
||||
): DBIOAction[Vector[
|
||||
BitcoinAddress
|
||||
],
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
val receiveAddressesA: DBIOAction[
|
||||
Vector[
|
||||
BitcoinAddress
|
||||
],
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
DBIOAction.sequence {
|
||||
1.to(addressBatchSize)
|
||||
.map(_ => getNewAddressAction(account))
|
||||
}
|
||||
}.map(_.toVector)
|
||||
|
||||
val changeAddressesA: DBIOAction[
|
||||
Vector[
|
||||
BitcoinAddress
|
||||
],
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
DBIOAction.sequence {
|
||||
1.to(addressBatchSize)
|
||||
.map(_ => getNewChangeAddressAction(account))
|
||||
}
|
||||
}.map(_.toVector)
|
||||
|
||||
for {
|
||||
receiveAddresses <- receiveAddressesA
|
||||
changeAddresses <- changeAddressesA
|
||||
} yield receiveAddresses ++ changeAddresses
|
||||
}
|
||||
|
||||
/** If forceGeneratSpks is true or addressCount == 0 we generate a new pool of
|
||||
* scriptpubkeys
|
||||
*/
|
||||
private def generateScriptPubKeysAction(
|
||||
account: HDAccount,
|
||||
addressBatchSize: Int,
|
||||
forceGenerateSpks: Boolean
|
||||
): DBIOAction[Vector[
|
||||
ScriptPubKey
|
||||
],
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
val addressCountA = addressDAO.countAction
|
||||
for {
|
||||
addressCount <- addressCountA
|
||||
addresses <- {
|
||||
if (forceGenerateSpks || addressCount == 0) {
|
||||
logger.info(
|
||||
s"Generating $addressBatchSize fresh addresses for the rescan"
|
||||
)
|
||||
generateAddressesForRescanAction(account, addressBatchSize)
|
||||
} else {
|
||||
// we don't want to continously generate addresses
|
||||
// if our wallet already has them, so just use what is in the
|
||||
// database already
|
||||
addressDAO.findAllAddressesAction().map(_.map(_.address))
|
||||
}
|
||||
}
|
||||
spksDb <- scriptPubKeyDAO.findAllAction()
|
||||
} yield {
|
||||
val addrSpks =
|
||||
addresses.map(_.scriptPubKey)
|
||||
val otherSpks = spksDb.map(_.scriptPubKey)
|
||||
|
||||
(addrSpks ++ otherSpks).distinct
|
||||
}
|
||||
}
|
||||
|
||||
/** Given a range of filter heights, we fetch the filters associated with
|
||||
* those heights and emit them downstream
|
||||
*/
|
||||
@ -583,19 +528,6 @@ private[wallet] trait RescanHandling
|
||||
}
|
||||
}
|
||||
|
||||
def generateScriptPubKeys(
|
||||
account: HDAccount,
|
||||
addressBatchSize: Int,
|
||||
forceGenerateSpks: Boolean
|
||||
): Future[Vector[ScriptPubKey]] = {
|
||||
val action = generateScriptPubKeysAction(
|
||||
account = account,
|
||||
addressBatchSize = addressBatchSize,
|
||||
forceGenerateSpks = forceGenerateSpks
|
||||
)
|
||||
safeDatabase.run(action)
|
||||
}
|
||||
|
||||
/** Searches the given block filters against the given scriptPubKeys for
|
||||
* matches. If there is a match, request the full block to search
|
||||
*/
|
||||
@ -603,11 +535,11 @@ private[wallet] trait RescanHandling
|
||||
scripts: Vector[ScriptPubKey],
|
||||
filtersResponse: Vector[ChainQueryApi.FilterResponse],
|
||||
parallelismLevel: Int
|
||||
)(implicit ec: ExecutionContext): Future[Vector[BlockMatchingResponse]] = {
|
||||
): Future[Vector[BlockMatchingResponse]] = {
|
||||
val startHeightOpt = filtersResponse.headOption.map(_.blockHeight)
|
||||
val endHeightOpt = filtersResponse.lastOption.map(_.blockHeight)
|
||||
for {
|
||||
filtered <- findMatches(filtersResponse, scripts, parallelismLevel)(ec)
|
||||
filtered <- findMatches(filtersResponse, scripts, parallelismLevel)
|
||||
_ <- downloadAndProcessBlocks(filtered.map(_.blockHash))
|
||||
} yield {
|
||||
logger.debug(
|
||||
@ -617,11 +549,11 @@ private[wallet] trait RescanHandling
|
||||
}
|
||||
}
|
||||
|
||||
private[wallet] def findMatches(
|
||||
override def findMatches(
|
||||
filters: Vector[FilterResponse],
|
||||
scripts: Vector[ScriptPubKey],
|
||||
parallelismLevel: Int
|
||||
)(implicit ec: ExecutionContext): Future[Vector[BlockMatchingResponse]] = {
|
||||
): Future[Vector[BlockMatchingResponse]] = {
|
||||
if (filters.isEmpty) {
|
||||
logger.info("No Filters to check against")
|
||||
Future.successful(Vector.empty)
|
||||
|
Loading…
Reference in New Issue
Block a user