mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-02-23 22:56:52 +01:00
Move Node type of out Wallet API (#1708)
* Move Node type of out wallet api * Remove extensions, add scaladocs
This commit is contained in:
parent
c3dfb369d7
commit
26d5a09532
12 changed files with 174 additions and 167 deletions
|
@ -2,7 +2,7 @@ package org.bitcoins.wallet
|
||||||
|
|
||||||
import org.bitcoins.core.hd.AddressType
|
import org.bitcoins.core.hd.AddressType
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
import org.bitcoins.wallet.api.HDWalletApi
|
import org.bitcoins.wallet.api.AnyHDWalletApi
|
||||||
import org.bitcoins.wallet.models.AccountDb
|
import org.bitcoins.wallet.models.AccountDb
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
@ -11,7 +11,7 @@ import scala.concurrent.Future
|
||||||
* ScalaMock cannot stub traits with protected methods,
|
* ScalaMock cannot stub traits with protected methods,
|
||||||
* so we need to stub them manually.
|
* so we need to stub them manually.
|
||||||
*/
|
*/
|
||||||
abstract class MockWalletApi extends HDWalletApi {
|
abstract class MockWalletApi extends AnyHDWalletApi {
|
||||||
|
|
||||||
override protected[wallet] def getNewChangeAddress(
|
override protected[wallet] def getNewChangeAddress(
|
||||||
account: AccountDb): Future[BitcoinAddress] = stub
|
account: AccountDb): Future[BitcoinAddress] = stub
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.bitcoins.feeprovider.BitcoinerLiveFeeRateProvider
|
||||||
import org.bitcoins.node._
|
import org.bitcoins.node._
|
||||||
import org.bitcoins.node.config.NodeAppConfig
|
import org.bitcoins.node.config.NodeAppConfig
|
||||||
import org.bitcoins.node.models.Peer
|
import org.bitcoins.node.models.Peer
|
||||||
import org.bitcoins.wallet.api._
|
import org.bitcoins.wallet.Wallet
|
||||||
import org.bitcoins.wallet.config.WalletAppConfig
|
import org.bitcoins.wallet.config.WalletAppConfig
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future, Promise}
|
import scala.concurrent.{ExecutionContext, Future, Promise}
|
||||||
|
@ -157,11 +157,11 @@ object Main extends App with BitcoinSLogger {
|
||||||
//start everything!
|
//start everything!
|
||||||
runMain()
|
runMain()
|
||||||
|
|
||||||
private def createCallbacks(wallet: WalletApi)(implicit
|
private def createCallbacks(wallet: Wallet)(implicit
|
||||||
nodeConf: NodeAppConfig,
|
nodeConf: NodeAppConfig,
|
||||||
ec: ExecutionContext): Future[NodeCallbacks] = {
|
ec: ExecutionContext): Future[NodeCallbacks] = {
|
||||||
lazy val onTx: OnTxReceived = { tx =>
|
lazy val onTx: OnTxReceived = { tx =>
|
||||||
wallet.processTransaction(tx, blockHash = None).map(_ => ())
|
wallet.processTransaction(tx, blockHashOpt = None).map(_ => ())
|
||||||
}
|
}
|
||||||
lazy val onCompactFilters: OnCompactFiltersReceived = { blockFilters =>
|
lazy val onCompactFilters: OnCompactFiltersReceived = { blockFilters =>
|
||||||
wallet
|
wallet
|
||||||
|
@ -192,7 +192,7 @@ object Main extends App with BitcoinSLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def addCallbacksAndBloomFilterToNode(node: Node, wallet: WalletApi)(
|
private def addCallbacksAndBloomFilterToNode(node: Node, wallet: Wallet)(
|
||||||
implicit
|
implicit
|
||||||
nodeAppConfig: NodeAppConfig,
|
nodeAppConfig: NodeAppConfig,
|
||||||
ec: ExecutionContext): Future[Node] = {
|
ec: ExecutionContext): Future[Node] = {
|
||||||
|
@ -233,7 +233,7 @@ object Main extends App with BitcoinSLogger {
|
||||||
|
|
||||||
private def startHttpServer(
|
private def startHttpServer(
|
||||||
node: Node,
|
node: Node,
|
||||||
wallet: HDWalletApi,
|
wallet: Wallet,
|
||||||
rpcPortOpt: Option[Int])(implicit
|
rpcPortOpt: Option[Int])(implicit
|
||||||
system: ActorSystem,
|
system: ActorSystem,
|
||||||
conf: BitcoinSAppConfig): Future[Http.ServerBinding] = {
|
conf: BitcoinSAppConfig): Future[Http.ServerBinding] = {
|
||||||
|
|
|
@ -6,12 +6,12 @@ import akka.http.scaladsl.server._
|
||||||
import org.bitcoins.commons.serializers.Picklers._
|
import org.bitcoins.commons.serializers.Picklers._
|
||||||
import org.bitcoins.core.currency._
|
import org.bitcoins.core.currency._
|
||||||
import org.bitcoins.node.Node
|
import org.bitcoins.node.Node
|
||||||
import org.bitcoins.wallet.api.HDWalletApi
|
import org.bitcoins.wallet.api.AnyHDWalletApi
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
case class WalletRoutes(wallet: HDWalletApi, node: Node)(implicit
|
case class WalletRoutes(wallet: AnyHDWalletApi, node: Node)(implicit
|
||||||
system: ActorSystem)
|
system: ActorSystem)
|
||||||
extends ServerRoute {
|
extends ServerRoute {
|
||||||
import system.dispatcher
|
import system.dispatcher
|
||||||
|
|
|
@ -14,7 +14,7 @@ import org.bitcoins.testkit.node.{
|
||||||
NodeUnitTest
|
NodeUnitTest
|
||||||
}
|
}
|
||||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||||
import org.bitcoins.wallet.api.WalletApi
|
import org.bitcoins.wallet.Wallet
|
||||||
import org.scalatest.FutureOutcome
|
import org.scalatest.FutureOutcome
|
||||||
|
|
||||||
import scala.concurrent.{Future, Promise}
|
import scala.concurrent.{Future, Promise}
|
||||||
|
@ -42,8 +42,8 @@ class NeutrinoNodeWithWalletTest extends NodeUnitTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var walletP: Promise[WalletApi] = Promise()
|
private var walletP: Promise[Wallet] = Promise()
|
||||||
private var walletF: Future[WalletApi] = walletP.future
|
private var walletF: Future[Wallet] = walletP.future
|
||||||
after {
|
after {
|
||||||
//reset assertion after a test runs, because we
|
//reset assertion after a test runs, because we
|
||||||
//are doing mutation to work around our callback
|
//are doing mutation to work around our callback
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.bitcoins.crypto.AesPassword
|
||||||
import org.bitcoins.keymanager.KeyManagerUnlockError.MnemonicNotFound
|
import org.bitcoins.keymanager.KeyManagerUnlockError.MnemonicNotFound
|
||||||
import org.bitcoins.keymanager.{KeyManagerUnlockError, WalletStorage}
|
import org.bitcoins.keymanager.{KeyManagerUnlockError, WalletStorage}
|
||||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||||
import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse
|
import org.bitcoins.wallet.api.NeutrinoWalletApi.BlockMatchingResponse
|
||||||
import org.bitcoins.wallet.models.AddressDb
|
import org.bitcoins.wallet.models.AddressDb
|
||||||
import org.scalatest.FutureOutcome
|
import org.scalatest.FutureOutcome
|
||||||
import org.scalatest.compatible.Assertion
|
import org.scalatest.compatible.Assertion
|
||||||
|
|
|
@ -52,7 +52,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
||||||
import scala.util.{Failure, Success}
|
import scala.util.{Failure, Success}
|
||||||
|
|
||||||
abstract class Wallet
|
abstract class Wallet
|
||||||
extends HDWalletApi
|
extends AnyHDWalletApi
|
||||||
with UtxoHandling
|
with UtxoHandling
|
||||||
with AddressHandling
|
with AddressHandling
|
||||||
with AccountHandling
|
with AccountHandling
|
||||||
|
|
|
@ -3,12 +3,12 @@ package org.bitcoins.wallet.api
|
||||||
import org.bitcoins.commons.jsonmodels.wallet.CoinSelectionAlgo
|
import org.bitcoins.commons.jsonmodels.wallet.CoinSelectionAlgo
|
||||||
import org.bitcoins.core.currency.CurrencyUnit
|
import org.bitcoins.core.currency.CurrencyUnit
|
||||||
import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType, HDPurpose}
|
import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType, HDPurpose}
|
||||||
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
import org.bitcoins.core.protocol.transaction.{
|
import org.bitcoins.core.protocol.transaction.{
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionOutPoint,
|
TransactionOutPoint,
|
||||||
TransactionOutput
|
TransactionOutput
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
|
|
||||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||||
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
|
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
|
||||||
import org.bitcoins.keymanager.KeyManagerParams
|
import org.bitcoins.keymanager.KeyManagerParams
|
||||||
|
@ -492,47 +492,6 @@ trait HDWalletApi extends WalletApi {
|
||||||
ec: ExecutionContext): Future[Vector[AccountDb]] =
|
ec: ExecutionContext): Future[Vector[AccountDb]] =
|
||||||
listAccounts().map(_.filter(_.hdAccount.purpose == purpose))
|
listAccounts().map(_.filter(_.hdAccount.purpose == purpose))
|
||||||
|
|
||||||
def rescanNeutrinoWallet(
|
|
||||||
account: HDAccount,
|
|
||||||
startOpt: Option[BlockStamp],
|
|
||||||
endOpt: Option[BlockStamp],
|
|
||||||
addressBatchSize: Int,
|
|
||||||
useCreationTime: Boolean): Future[Unit]
|
|
||||||
|
|
||||||
override def rescanNeutrinoWallet(
|
|
||||||
startOpt: Option[BlockStamp],
|
|
||||||
endOpt: Option[BlockStamp],
|
|
||||||
addressBatchSize: Int,
|
|
||||||
useCreationTime: Boolean)(implicit ec: ExecutionContext): Future[Unit] = {
|
|
||||||
for {
|
|
||||||
account <- getDefaultAccount()
|
|
||||||
_ <- rescanNeutrinoWallet(account.hdAccount,
|
|
||||||
startOpt,
|
|
||||||
endOpt,
|
|
||||||
addressBatchSize,
|
|
||||||
useCreationTime)
|
|
||||||
} yield ()
|
|
||||||
}
|
|
||||||
|
|
||||||
def fullRescanNeutrinoWallet(
|
|
||||||
account: HDAccount,
|
|
||||||
addressBatchSize: Int): Future[Unit] = {
|
|
||||||
rescanNeutrinoWallet(account = account,
|
|
||||||
startOpt = None,
|
|
||||||
endOpt = None,
|
|
||||||
addressBatchSize = addressBatchSize,
|
|
||||||
useCreationTime = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Helper method to rescan the ENTIRE blockchain. */
|
|
||||||
override def fullRescanNeutrinoWallet(addressBatchSize: Int)(implicit
|
|
||||||
ec: ExecutionContext): Future[Unit] = {
|
|
||||||
for {
|
|
||||||
account <- getDefaultAccount()
|
|
||||||
_ <- fullRescanNeutrinoWallet(account.hdAccount, addressBatchSize)
|
|
||||||
} yield ()
|
|
||||||
}
|
|
||||||
|
|
||||||
def createNewAccount(keyManagerParams: KeyManagerParams): Future[HDWalletApi]
|
def createNewAccount(keyManagerParams: KeyManagerParams): Future[HDWalletApi]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package org.bitcoins.wallet.api
|
||||||
|
|
||||||
|
import org.bitcoins.core.gcs.GolombFilter
|
||||||
|
import org.bitcoins.core.protocol.BlockStamp
|
||||||
|
import org.bitcoins.core.protocol.blockchain.Block
|
||||||
|
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||||
|
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||||
|
import org.bitcoins.wallet.api.NeutrinoWalletApi.BlockMatchingResponse
|
||||||
|
|
||||||
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
|
trait NeutrinoWalletApi { self: WalletApi =>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the give block, updating our DB state if it's relevant to us.
|
||||||
|
* @param block The block we're processing
|
||||||
|
*/
|
||||||
|
def processBlock(block: Block): Future[WalletApi]
|
||||||
|
|
||||||
|
def processCompactFilter(
|
||||||
|
blockHash: DoubleSha256Digest,
|
||||||
|
blockFilter: GolombFilter): Future[WalletApi] =
|
||||||
|
processCompactFilters(Vector((blockHash, blockFilter)))
|
||||||
|
|
||||||
|
def processCompactFilters(
|
||||||
|
blockFilters: Vector[(DoubleSha256Digest, GolombFilter)]): Future[
|
||||||
|
WalletApi]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over the block filters in order to find filters that match to the given addresses
|
||||||
|
*
|
||||||
|
* I queries the filter database for [[batchSize]] filters a time
|
||||||
|
* and tries to run [[GolombFilter.matchesAny]] for each filter.
|
||||||
|
*
|
||||||
|
* It tries to match the filters in parallel using [[parallelismLevel]] threads.
|
||||||
|
* For best results use it with a separate execution context.
|
||||||
|
*
|
||||||
|
* @param scripts list of [[ScriptPubKey]]'s to watch
|
||||||
|
* @param startOpt start point (if empty it starts with the genesis block)
|
||||||
|
* @param endOpt end point (if empty it ends with the best tip)
|
||||||
|
* @param batchSize number of filters that can be matched in one batch
|
||||||
|
* @param parallelismLevel max number of threads required to perform matching
|
||||||
|
* (default [[Runtime.getRuntime.availableProcessors()]])
|
||||||
|
* @return a list of matching block hashes
|
||||||
|
*/
|
||||||
|
def getMatchingBlocks(
|
||||||
|
scripts: Vector[ScriptPubKey],
|
||||||
|
startOpt: Option[BlockStamp] = None,
|
||||||
|
endOpt: Option[BlockStamp] = None,
|
||||||
|
batchSize: Int = 100,
|
||||||
|
parallelismLevel: Int = Runtime.getRuntime.availableProcessors())(implicit
|
||||||
|
ec: ExecutionContext): Future[Vector[BlockMatchingResponse]]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)(implicit ec: ExecutionContext): Future[Unit]
|
||||||
|
|
||||||
|
/** Helper method to rescan the ENTIRE blockchain. */
|
||||||
|
def fullRescanNeutrinoWallet(addressBatchSize: Int)(implicit
|
||||||
|
ec: ExecutionContext): Future[Unit] =
|
||||||
|
rescanNeutrinoWallet(startOpt = None,
|
||||||
|
endOpt = None,
|
||||||
|
addressBatchSize = addressBatchSize,
|
||||||
|
useCreationTime = false)
|
||||||
|
|
||||||
|
def discoveryBatchSize(): Int = 25
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object NeutrinoWalletApi {
|
||||||
|
|
||||||
|
case class BlockMatchingResponse(
|
||||||
|
blockHash: DoubleSha256DigestBE,
|
||||||
|
blockHeight: Int)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.bitcoins.wallet.api
|
||||||
|
|
||||||
|
import org.bitcoins.core.bloom.BloomFilter
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API for the wallet project.
|
||||||
|
*
|
||||||
|
* This wallet API is BIP44 compliant.
|
||||||
|
*
|
||||||
|
* @see [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44]]
|
||||||
|
*/
|
||||||
|
trait SpvWalletApi { self: WalletApi =>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreates the account using BIP-44 approach
|
||||||
|
*/
|
||||||
|
def rescanSPVWallet(): Future[Unit]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a bloom filter that that can be sent to a P2P network node
|
||||||
|
* to get information about our transactions, pubkeys and scripts.
|
||||||
|
*/
|
||||||
|
def getBloomFilter(): Future[BloomFilter]
|
||||||
|
}
|
|
@ -4,26 +4,22 @@ import java.time.Instant
|
||||||
|
|
||||||
import org.bitcoins.commons.jsonmodels.wallet.CoinSelectionAlgo
|
import org.bitcoins.commons.jsonmodels.wallet.CoinSelectionAlgo
|
||||||
import org.bitcoins.core.api.{ChainQueryApi, FeeRateApi, NodeApi}
|
import org.bitcoins.core.api.{ChainQueryApi, FeeRateApi, NodeApi}
|
||||||
import org.bitcoins.core.bloom.BloomFilter
|
|
||||||
import org.bitcoins.core.config.NetworkParameters
|
import org.bitcoins.core.config.NetworkParameters
|
||||||
import org.bitcoins.core.currency.CurrencyUnit
|
import org.bitcoins.core.currency.CurrencyUnit
|
||||||
import org.bitcoins.core.gcs.GolombFilter
|
|
||||||
import org.bitcoins.core.hd.AddressType
|
import org.bitcoins.core.hd.AddressType
|
||||||
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader}
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
import org.bitcoins.core.protocol.blockchain.BlockHeader
|
||||||
import org.bitcoins.core.protocol.transaction.{
|
import org.bitcoins.core.protocol.transaction.{
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionOutPoint,
|
TransactionOutPoint,
|
||||||
TransactionOutput
|
TransactionOutput
|
||||||
}
|
}
|
||||||
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
|
|
||||||
import org.bitcoins.core.util.FutureUtil
|
import org.bitcoins.core.util.FutureUtil
|
||||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||||
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
|
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
|
||||||
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
import org.bitcoins.crypto.DoubleSha256DigestBE
|
||||||
import org.bitcoins.keymanager._
|
import org.bitcoins.keymanager._
|
||||||
import org.bitcoins.wallet.WalletLogger
|
import org.bitcoins.wallet.WalletLogger
|
||||||
import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse
|
|
||||||
import org.bitcoins.wallet.models.{AddressDb, SpendingInfoDb}
|
import org.bitcoins.wallet.models.{AddressDb, SpendingInfoDb}
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
@ -50,12 +46,6 @@ trait WalletApi extends WalletLogger {
|
||||||
|
|
||||||
def stop(): Unit
|
def stop(): Unit
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a bloom filter that that can be sent to a P2P network node
|
|
||||||
* to get information about our transactions, pubkeys and scripts.
|
|
||||||
*/
|
|
||||||
def getBloomFilter(): Future[BloomFilter]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the given transaction, updating our DB state if it's relevant to us.
|
* Processes the given transaction, updating our DB state if it's relevant to us.
|
||||||
* @param transaction The transaction we're processing
|
* @param transaction The transaction we're processing
|
||||||
|
@ -82,21 +72,6 @@ trait WalletApi extends WalletLogger {
|
||||||
def updateUtxoPendingStates(
|
def updateUtxoPendingStates(
|
||||||
blockHeader: BlockHeader): Future[Vector[SpendingInfoDb]]
|
blockHeader: BlockHeader): Future[Vector[SpendingInfoDb]]
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes the give block, updating our DB state if it's relevant to us.
|
|
||||||
* @param block The block we're processing
|
|
||||||
*/
|
|
||||||
def processBlock(block: Block): Future[WalletApi]
|
|
||||||
|
|
||||||
def processCompactFilter(
|
|
||||||
blockHash: DoubleSha256Digest,
|
|
||||||
blockFilter: GolombFilter): Future[WalletApi] =
|
|
||||||
processCompactFilters(Vector((blockHash, blockFilter)))
|
|
||||||
|
|
||||||
def processCompactFilters(
|
|
||||||
blockFilters: Vector[(DoubleSha256Digest, GolombFilter)]): 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()
|
||||||
|
@ -130,13 +105,6 @@ trait WalletApi extends WalletLogger {
|
||||||
|
|
||||||
def getUnconfirmedBalance(tag: AddressTag): Future[CurrencyUnit]
|
def getUnconfirmedBalance(tag: AddressTag): Future[CurrencyUnit]
|
||||||
|
|
||||||
/**
|
|
||||||
* If a UTXO is spent outside of the wallet, we
|
|
||||||
* need to remove it from the database so it won't be
|
|
||||||
* attempted spent again by us.
|
|
||||||
*/
|
|
||||||
// def updateUtxo: Future[WalletApi]
|
|
||||||
|
|
||||||
/** Lists unspent transaction outputs in the wallet
|
/** Lists unspent transaction outputs in the wallet
|
||||||
* @return Vector[SpendingInfoDb]
|
* @return Vector[SpendingInfoDb]
|
||||||
*/
|
*/
|
||||||
|
@ -237,72 +205,6 @@ trait WalletApi extends WalletLogger {
|
||||||
protected[wallet] def getNewChangeAddress()(implicit
|
protected[wallet] def getNewChangeAddress()(implicit
|
||||||
ec: ExecutionContext): Future[BitcoinAddress]
|
ec: ExecutionContext): Future[BitcoinAddress]
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterates over the block filters in order to find filters that match to the given addresses
|
|
||||||
*
|
|
||||||
* I queries the filter database for [[batchSize]] filters a time
|
|
||||||
* and tries to run [[GolombFilter.matchesAny]] for each filter.
|
|
||||||
*
|
|
||||||
* It tries to match the filters in parallel using [[parallelismLevel]] threads.
|
|
||||||
* For best results use it with a separate execution context.
|
|
||||||
*
|
|
||||||
* @param scripts list of [[ScriptPubKey]]'s to watch
|
|
||||||
* @param startOpt start point (if empty it starts with the genesis block)
|
|
||||||
* @param endOpt end point (if empty it ends with the best tip)
|
|
||||||
* @param batchSize number of filters that can be matched in one batch
|
|
||||||
* @param parallelismLevel max number of threads required to perform matching
|
|
||||||
* (default [[Runtime.getRuntime.availableProcessors()]])
|
|
||||||
* @return a list of matching block hashes
|
|
||||||
*/
|
|
||||||
def getMatchingBlocks(
|
|
||||||
scripts: Vector[ScriptPubKey],
|
|
||||||
startOpt: Option[BlockStamp] = None,
|
|
||||||
endOpt: Option[BlockStamp] = None,
|
|
||||||
batchSize: Int = 100,
|
|
||||||
parallelismLevel: Int = Runtime.getRuntime.availableProcessors())(implicit
|
|
||||||
ec: ExecutionContext): Future[Vector[BlockMatchingResponse]]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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)(implicit ec: ExecutionContext): Future[Unit]
|
|
||||||
|
|
||||||
/** Helper method to rescan the ENTIRE blockchain. */
|
|
||||||
def fullRescanNeutrinoWallet(addressBatchSize: Int)(implicit
|
|
||||||
ec: ExecutionContext): Future[Unit]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Recreates the account using BIP-44 approach
|
|
||||||
*/
|
|
||||||
def rescanSPVWallet(): Future[Unit]
|
|
||||||
|
|
||||||
def discoveryBatchSize(): Int = 25
|
|
||||||
|
|
||||||
def keyManager: KeyManager
|
def keyManager: KeyManager
|
||||||
|
|
||||||
protected def determineFeeRate(feeRateOpt: Option[FeeUnit]): Future[FeeUnit] =
|
protected def determineFeeRate(feeRateOpt: Option[FeeUnit]): Future[FeeUnit] =
|
||||||
|
@ -426,10 +328,14 @@ trait WalletApi extends WalletLogger {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object WalletApi {
|
/** An HDWallet that uses Neutrino to sync */
|
||||||
|
trait NeutrinoHDWalletApi extends HDWalletApi with NeutrinoWalletApi
|
||||||
|
|
||||||
case class BlockMatchingResponse(
|
/** An HDWallet that uses SPV to sync */
|
||||||
blockHash: DoubleSha256DigestBE,
|
trait SpvHDWalletApi extends HDWalletApi with SpvWalletApi
|
||||||
blockHeight: Int)
|
|
||||||
|
|
||||||
}
|
/** An HDWallet that supports both Neutrino and SPV methods of syncing */
|
||||||
|
trait AnyHDWalletApi
|
||||||
|
extends HDWalletApi
|
||||||
|
with NeutrinoWalletApi
|
||||||
|
with SpvWalletApi
|
||||||
|
|
|
@ -14,7 +14,6 @@ import org.bitcoins.keymanager.{
|
||||||
KeyManagerParams,
|
KeyManagerParams,
|
||||||
WalletStorage
|
WalletStorage
|
||||||
}
|
}
|
||||||
import org.bitcoins.wallet.api.HDWalletApi
|
|
||||||
import org.bitcoins.wallet.db.WalletDbManagement
|
import org.bitcoins.wallet.db.WalletDbManagement
|
||||||
import org.bitcoins.wallet.models.AccountDAO
|
import org.bitcoins.wallet.models.AccountDAO
|
||||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||||
|
@ -162,7 +161,7 @@ case class WalletAppConfig(
|
||||||
chainQueryApi: ChainQueryApi,
|
chainQueryApi: ChainQueryApi,
|
||||||
feeRateApi: FeeRateApi,
|
feeRateApi: FeeRateApi,
|
||||||
bip39PasswordOpt: Option[String])(implicit
|
bip39PasswordOpt: Option[String])(implicit
|
||||||
ec: ExecutionContext): Future[HDWalletApi] = {
|
ec: ExecutionContext): Future[Wallet] = {
|
||||||
WalletAppConfig.createHDWallet(
|
WalletAppConfig.createHDWallet(
|
||||||
nodeApi = nodeApi,
|
nodeApi = nodeApi,
|
||||||
chainQueryApi = chainQueryApi,
|
chainQueryApi = chainQueryApi,
|
||||||
|
@ -194,7 +193,7 @@ object WalletAppConfig
|
||||||
feeRateApi: FeeRateApi,
|
feeRateApi: FeeRateApi,
|
||||||
bip39PasswordOpt: Option[String])(implicit
|
bip39PasswordOpt: Option[String])(implicit
|
||||||
walletConf: WalletAppConfig,
|
walletConf: WalletAppConfig,
|
||||||
ec: ExecutionContext): Future[HDWalletApi] = {
|
ec: ExecutionContext): Future[Wallet] = {
|
||||||
walletConf.hasWallet().flatMap { walletExists =>
|
walletConf.hasWallet().flatMap { walletExists =>
|
||||||
if (walletExists) {
|
if (walletExists) {
|
||||||
logger.info(s"Using pre-existing wallet")
|
logger.info(s"Using pre-existing wallet")
|
||||||
|
|
|
@ -9,9 +9,9 @@ import org.bitcoins.core.protocol.BlockStamp.BlockHeight
|
||||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||||
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
|
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
|
||||||
import org.bitcoins.core.util.FutureUtil
|
import org.bitcoins.core.util.FutureUtil
|
||||||
import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse
|
|
||||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
|
||||||
import org.bitcoins.crypto.DoubleSha256Digest
|
import org.bitcoins.crypto.DoubleSha256Digest
|
||||||
|
import org.bitcoins.wallet.api.NeutrinoWalletApi.BlockMatchingResponse
|
||||||
|
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||||
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
|
@ -23,6 +23,22 @@ private[wallet] trait RescanHandling extends WalletLogger {
|
||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def rescanNeutrinoWallet(
|
override def rescanNeutrinoWallet(
|
||||||
|
startOpt: Option[BlockStamp],
|
||||||
|
endOpt: Option[BlockStamp],
|
||||||
|
addressBatchSize: Int,
|
||||||
|
useCreationTime: Boolean)(implicit ec: ExecutionContext): Future[Unit] = {
|
||||||
|
for {
|
||||||
|
account <- getDefaultAccount()
|
||||||
|
_ <- rescanNeutrinoWallet(account.hdAccount,
|
||||||
|
startOpt,
|
||||||
|
endOpt,
|
||||||
|
addressBatchSize,
|
||||||
|
useCreationTime)
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
def rescanNeutrinoWallet(
|
||||||
account: HDAccount,
|
account: HDAccount,
|
||||||
startOpt: Option[BlockStamp],
|
startOpt: Option[BlockStamp],
|
||||||
endOpt: Option[BlockStamp],
|
endOpt: Option[BlockStamp],
|
||||||
|
|
Loading…
Add table
Reference in a new issue