mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
2024 09 19 address handling refactor (#5679)
* wallet: Create has-a relationship for AddressHandling rather than is-a * Fix infinite loop in AccountHandlingApi.getNewAddress(account) * Fix appServerTest * Revert Server.scala
This commit is contained in:
parent
8c5d685953
commit
8cfd5e8d6b
@ -1,6 +1,6 @@
|
||||
package org.bitcoins.server.routes
|
||||
|
||||
import org.apache.pekko.http.scaladsl.server.Directives.{complete, provide}
|
||||
import org.apache.pekko.http.scaladsl.server.Directives
|
||||
import org.apache.pekko.http.scaladsl.server.{Directive1, Route}
|
||||
|
||||
import scala.util.Try
|
||||
@ -10,7 +10,7 @@ trait ServerRoute {
|
||||
|
||||
def withValidServerCommand[R](validator: Try[R]): Directive1[R] =
|
||||
validator.fold(
|
||||
e => complete(Server.httpBadRequest(e)),
|
||||
provide
|
||||
{ (e: Throwable) => Directives.complete(Server.httpBadRequest(e)) },
|
||||
{ (r: R) => Directives.provide(r) }
|
||||
)
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class CallBackUtilTest extends BitcoinSWalletTest {
|
||||
it must "have the kill switch kill messages to the createBitcoindNodeCallbacksForWallet callback" in {
|
||||
fundedWallet =>
|
||||
val wallet = fundedWallet.wallet
|
||||
val addressF = wallet.getNewAddress()
|
||||
val addressF = wallet.addressHandling.getNewAddress()
|
||||
val initBalanceF = wallet.getBalance()
|
||||
val tx1F = addressF.map { addr =>
|
||||
TransactionGenerators
|
||||
@ -65,7 +65,7 @@ class CallBackUtilTest extends BitcoinSWalletTest {
|
||||
it must "have the kill switch kill messages to the createNeutrinoNodeCallbacksForWallet callback" in {
|
||||
fundedWallet =>
|
||||
val wallet = fundedWallet.wallet
|
||||
val addressF = wallet.getNewAddress()
|
||||
val addressF = wallet.addressHandling.getNewAddress()
|
||||
val initBalanceF = wallet.getBalance()
|
||||
val tx1F = addressF.map { addr =>
|
||||
TransactionGenerators
|
||||
|
@ -11,6 +11,7 @@ import org.bitcoins.core.api.chain.db.*
|
||||
import org.bitcoins.core.api.wallet.db.*
|
||||
import org.bitcoins.core.api.wallet.{
|
||||
AccountHandlingApi,
|
||||
AddressHandlingApi,
|
||||
AddressInfo,
|
||||
CoinSelectionAlgo,
|
||||
RescanHandlingApi
|
||||
@ -86,6 +87,8 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
|
||||
val mockAccountHandlingApi: AccountHandlingApi = mock[AccountHandlingApi]
|
||||
|
||||
val mockAddressHandlingApi: AddressHandlingApi = mock[AddressHandlingApi]
|
||||
|
||||
val walletHolder = new WalletHolder(Some(mockWalletApi))
|
||||
|
||||
val feeRateApi = ConstantFeeRateProvider(SatoshisPerVirtualByte.one)
|
||||
@ -553,7 +556,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
testAddress.scriptPubKey
|
||||
)
|
||||
|
||||
(() => mockWalletApi.listAddresses())
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(() => mockWalletApi.addressHandling.listAddresses())
|
||||
.expects()
|
||||
.returning(Future.successful(Vector(addressDb)))
|
||||
|
||||
@ -579,7 +587,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
testAddress.scriptPubKey
|
||||
)
|
||||
|
||||
(() => mockWalletApi.listSpentAddresses())
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(() => mockWalletApi.addressHandling.listSpentAddresses())
|
||||
.expects()
|
||||
.returning(Future.successful(Vector(addressDb)))
|
||||
|
||||
@ -605,7 +618,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
testAddress.scriptPubKey
|
||||
)
|
||||
|
||||
(() => mockWalletApi.listFundedAddresses())
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(() => mockWalletApi.addressHandling.listFundedAddresses())
|
||||
.expects()
|
||||
.returning(Future.successful(Vector((addressDb, Satoshis.zero))))
|
||||
|
||||
@ -630,7 +648,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
testAddress.scriptPubKey
|
||||
)
|
||||
|
||||
(() => mockWalletApi.listUnusedAddresses())
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(() => mockWalletApi.addressHandling.listUnusedAddresses())
|
||||
.expects()
|
||||
.returning(Future.successful(Vector(addressDb)))
|
||||
|
||||
@ -682,7 +705,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"return a new address" in {
|
||||
(() => mockWalletApi.getNewAddress())
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(() => mockWalletApi.addressHandling.getNewAddress())
|
||||
.expects()
|
||||
.returning(Future.successful(testAddress))
|
||||
|
||||
@ -704,7 +732,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
val key = ECPublicKey.freshPublicKey
|
||||
val hdPath = HDPath.fromString("m/84'/1'/0'/0/0")
|
||||
|
||||
(mockWalletApi
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.getAddressInfo(_: BitcoinAddress))
|
||||
.expects(testAddress)
|
||||
.returning(Future.successful(Some(AddressInfo(key, RegTest, hdPath))))
|
||||
@ -725,7 +758,13 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"return a new address with a label" in {
|
||||
(mockWalletApi
|
||||
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.getNewAddress(_: Vector[AddressTag]))
|
||||
.expects(Vector(testLabel))
|
||||
.returning(Future.successful(testAddress))
|
||||
@ -816,7 +855,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"label an address" in {
|
||||
(mockWalletApi
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.tagAddress(_: BitcoinAddress, _: AddressTag))
|
||||
.expects(testAddress, testLabel)
|
||||
.returning(Future.successful(AddressTagDb(testAddress, testLabel)))
|
||||
@ -840,7 +884,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"get address tags" in {
|
||||
(mockWalletApi
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.getAddressTags(_: BitcoinAddress))
|
||||
.expects(testAddress)
|
||||
.returning(
|
||||
@ -863,7 +912,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"get address label" in {
|
||||
(mockWalletApi
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.getAddressTags(_: BitcoinAddress, _: AddressTagType))
|
||||
.expects(testAddress, AddressLabelTagType)
|
||||
.returning(
|
||||
@ -886,7 +940,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"get address labels" in {
|
||||
(() => mockWalletApi.getAddressTags())
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(() => mockWalletApi.addressHandling.getAddressTags())
|
||||
.expects()
|
||||
.returning(
|
||||
Future.successful(Vector(AddressTagDb(testAddress, testLabel)))
|
||||
@ -905,7 +964,13 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
|
||||
"drop address label" in {
|
||||
val labelName = "label"
|
||||
(mockWalletApi
|
||||
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.dropAddressTagName(_: BitcoinAddress, _: AddressTagName))
|
||||
.expects(testAddress, AddressLabelTagName(labelName))
|
||||
.returning(Future.successful(1))
|
||||
@ -929,7 +994,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"drop address labels with no labels" in {
|
||||
(mockWalletApi
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.dropAddressTagType(_: BitcoinAddress, _: AddressTagType))
|
||||
.expects(testAddress, AddressLabelTagType)
|
||||
.returning(Future.successful(0))
|
||||
@ -948,7 +1018,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"drop address labels with 1 label" in {
|
||||
(mockWalletApi
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.dropAddressTagType(_: BitcoinAddress, _: AddressTagType))
|
||||
.expects(testAddress, AddressLabelTagType)
|
||||
.returning(Future.successful(1))
|
||||
@ -969,7 +1044,12 @@ class RoutesSpec extends AnyWordSpec with ScalatestRouteTest with MockFactory {
|
||||
}
|
||||
|
||||
"drop address labels with 2 labels" in {
|
||||
(mockWalletApi
|
||||
(() => mockWalletApi.addressHandling)
|
||||
.expects()
|
||||
.returning(mockAddressHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.addressHandling
|
||||
.dropAddressTagType(_: BitcoinAddress, _: AddressTagType))
|
||||
.expects(testAddress, AddressLabelTagType)
|
||||
.returning(Future.successful(2))
|
||||
|
@ -4,6 +4,7 @@ import org.apache.pekko.http.scaladsl.model.ContentTypes.*
|
||||
import org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTest
|
||||
import org.bitcoins.commons.serializers.Picklers
|
||||
import org.bitcoins.core.api.chain.ChainApi
|
||||
import org.bitcoins.core.api.wallet.AccountHandlingApi
|
||||
import org.bitcoins.core.api.wallet.db.AccountDb
|
||||
import org.bitcoins.core.crypto.ExtKeyVersion.SegWitMainNetPriv
|
||||
import org.bitcoins.core.crypto.ExtPrivateKey
|
||||
@ -39,6 +40,7 @@ class WalletRoutesSpec
|
||||
|
||||
val mockNode: Node = mock[Node]
|
||||
val mockWalletApi: MockWalletApi = mock[MockWalletApi]
|
||||
val mockAccountHandlingApi: AccountHandlingApi = mock[AccountHandlingApi]
|
||||
|
||||
val walletHolder = new WalletHolder(Some(mockWalletApi))
|
||||
|
||||
@ -167,12 +169,18 @@ class WalletRoutesSpec
|
||||
val extPubKey = extPrivKey.extPublicKey
|
||||
val hdAccount = HDAccount.fromExtKeyVersion(version = keyVersion, idx = 0)
|
||||
val accountDb = AccountDb(extPubKey, hdAccount = hdAccount)
|
||||
(mockWalletApi
|
||||
|
||||
(() => mockWalletApi.accountHandling)
|
||||
.expects()
|
||||
.returning(mockAccountHandlingApi)
|
||||
.anyNumberOfTimes()
|
||||
|
||||
(mockWalletApi.accountHandling
|
||||
.createNewAccount(_: HDPurpose))
|
||||
.expects(HDPurpose.default)
|
||||
.returning(Future.successful(mockWalletApi))
|
||||
.returning(Future.successful(extPubKey))
|
||||
|
||||
(() => mockWalletApi.listAccounts())
|
||||
(() => mockWalletApi.accountHandling.listAccounts())
|
||||
.expects()
|
||||
.returning(Future.successful(Vector(accountDb)))
|
||||
|
||||
|
@ -3,7 +3,6 @@ package org.bitcoins.wallet
|
||||
import org.bitcoins.core.api.dlc.wallet.DLCNeutrinoHDWalletApi
|
||||
import org.bitcoins.core.api.wallet.db.AccountDb
|
||||
import org.bitcoins.core.hd.AddressType
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
@ -12,9 +11,6 @@ import scala.concurrent.Future
|
||||
*/
|
||||
abstract class MockWalletApi extends DLCNeutrinoHDWalletApi {
|
||||
|
||||
override def getNewChangeAddress(account: AccountDb): Future[BitcoinAddress] =
|
||||
stub
|
||||
|
||||
override def getDefaultAccount(): Future[AccountDb] = stub
|
||||
|
||||
override def getDefaultAccountForType(
|
||||
|
@ -177,9 +177,9 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
complete {
|
||||
val addressF = labelOpt match {
|
||||
case Some(label) =>
|
||||
wallet.getNewAddress(Vector(label))
|
||||
wallet.addressHandling.getNewAddress(Vector(label))
|
||||
case None =>
|
||||
wallet.getNewAddress()
|
||||
wallet.addressHandling.getNewAddress()
|
||||
}
|
||||
addressF.map(address => Server.httpSuccess(address))
|
||||
}
|
||||
@ -231,7 +231,7 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
withValidServerCommand(LabelAddress.fromJsArr(arr)) {
|
||||
case LabelAddress(address, label) =>
|
||||
complete {
|
||||
wallet.tagAddress(address, label).map { tagDb =>
|
||||
wallet.addressHandling.tagAddress(address, label).map { tagDb =>
|
||||
Server.httpSuccess(
|
||||
s"Added label \'${tagDb.tagName.name}\' to ${tagDb.address.value}"
|
||||
)
|
||||
@ -243,7 +243,7 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
withValidServerCommand(GetAddressTags.fromJsArr(arr)) {
|
||||
case GetAddressTags(address) =>
|
||||
complete {
|
||||
wallet.getAddressTags(address).map { tagDbs =>
|
||||
wallet.addressHandling.getAddressTags(address).map { tagDbs =>
|
||||
val retStr = tagDbs.map(_.tagName.name)
|
||||
Server.httpSuccess(retStr)
|
||||
}
|
||||
@ -254,16 +254,18 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
withValidServerCommand(GetAddressLabel.fromJsArr(arr)) {
|
||||
case GetAddressLabel(address) =>
|
||||
complete {
|
||||
wallet.getAddressTags(address, AddressLabelTagType).map { tagDbs =>
|
||||
val retStr = tagDbs.map(_.tagName.name)
|
||||
Server.httpSuccess(retStr)
|
||||
}
|
||||
wallet.addressHandling
|
||||
.getAddressTags(address, AddressLabelTagType)
|
||||
.map { tagDbs =>
|
||||
val retStr = tagDbs.map(_.tagName.name)
|
||||
Server.httpSuccess(retStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case ServerCommand("getaddresslabels", _) =>
|
||||
complete {
|
||||
val allTagsF = wallet.getAddressTags()
|
||||
val allTagsF = wallet.addressHandling.getAddressTags()
|
||||
for {
|
||||
allTags <- allTagsF
|
||||
grouped = allTags.groupBy(_.address)
|
||||
@ -284,7 +286,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
case DropAddressLabel(address, label) =>
|
||||
complete {
|
||||
val tagName = AddressLabelTagName(label)
|
||||
val droppedF = wallet.dropAddressTagName(address, tagName)
|
||||
val droppedF =
|
||||
wallet.addressHandling.dropAddressTagName(address, tagName)
|
||||
droppedF.map(handleTagResponse)
|
||||
}
|
||||
}
|
||||
@ -293,7 +296,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
case DropAddressLabels(address) =>
|
||||
complete {
|
||||
val droppedF =
|
||||
wallet.dropAddressTagType(address, AddressLabelTagType)
|
||||
wallet.addressHandling.dropAddressTagType(address,
|
||||
AddressLabelTagType)
|
||||
droppedF.map(handleTagResponse)
|
||||
}
|
||||
}
|
||||
@ -803,7 +807,7 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
|
||||
case ServerCommand("getaddresses", _) =>
|
||||
complete {
|
||||
wallet.listAddresses().map { addressDbs =>
|
||||
wallet.addressHandling.listAddresses().map { addressDbs =>
|
||||
val addresses = addressDbs.map(_.address)
|
||||
Server.httpSuccess(addresses)
|
||||
}
|
||||
@ -811,7 +815,7 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
|
||||
case ServerCommand("getspentaddresses", _) =>
|
||||
complete {
|
||||
wallet.listSpentAddresses().map { addressDbs =>
|
||||
wallet.addressHandling.listSpentAddresses().map { addressDbs =>
|
||||
val addresses = addressDbs.map(_.address)
|
||||
Server.httpSuccess(addresses)
|
||||
}
|
||||
@ -819,7 +823,7 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
|
||||
case ServerCommand("getfundedaddresses", _) =>
|
||||
complete {
|
||||
wallet.listFundedAddresses().map { addressDbs =>
|
||||
wallet.addressHandling.listFundedAddresses().map { addressDbs =>
|
||||
val addressAndValues = addressDbs.map { case (addressDb, value) =>
|
||||
Obj(
|
||||
"address" -> Str(addressDb.address.value),
|
||||
@ -833,7 +837,7 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
|
||||
case ServerCommand("getunusedaddresses", _) =>
|
||||
complete {
|
||||
wallet.listUnusedAddresses().map { addressDbs =>
|
||||
wallet.addressHandling.listUnusedAddresses().map { addressDbs =>
|
||||
val addresses = addressDbs.map(_.address)
|
||||
Server.httpSuccess(addresses)
|
||||
}
|
||||
@ -851,7 +855,7 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
withValidServerCommand(GetAddressInfo.fromJsArr(arr)) {
|
||||
case GetAddressInfo(address) =>
|
||||
complete {
|
||||
wallet.getAddressInfo(address).map {
|
||||
wallet.addressHandling.getAddressInfo(address).map {
|
||||
case Some(addressInfo) =>
|
||||
val json = Obj(
|
||||
"pubkey" -> Str(addressInfo.pubkey.hex),
|
||||
@ -869,8 +873,8 @@ case class WalletRoutes(loadWalletApi: DLCWalletLoaderApi)(implicit
|
||||
case CreateNewAccount(purpose) =>
|
||||
complete {
|
||||
for {
|
||||
newWallet <- wallet.createNewAccount(purpose)
|
||||
accounts <- newWallet.listAccounts()
|
||||
_ <- wallet.accountHandling.createNewAccount(purpose)
|
||||
accounts <- wallet.accountHandling.listAccounts()
|
||||
} yield {
|
||||
val xpubs = accounts.map(_.xpub)
|
||||
val json =
|
||||
|
@ -1,19 +1,80 @@
|
||||
package org.bitcoins.core.api.wallet
|
||||
|
||||
import org.bitcoins.core.api.wallet.db.AccountDb
|
||||
import org.bitcoins.core.hd.{AddressType, HDAccount}
|
||||
import org.bitcoins.core.api.wallet.db.{AccountDb, AddressDb}
|
||||
import org.bitcoins.core.crypto.ExtPublicKey
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType, HDPurpose}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
trait AccountHandlingApi {
|
||||
|
||||
/** Tries to create a new account in this wallet. Fails if the most recent
|
||||
* account has no transaction history, as per BIP44
|
||||
*
|
||||
* @see
|
||||
* [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account BIP44 account section]]
|
||||
*/
|
||||
def createNewAccount(
|
||||
hdAccount: HDAccount
|
||||
): Future[ExtPublicKey]
|
||||
|
||||
def createNewAccount(purpose: HDPurpose): Future[ExtPublicKey]
|
||||
|
||||
def getDefaultAccount(): Future[AccountDb]
|
||||
def listAccounts(): Future[Vector[AccountDb]]
|
||||
def getDefaultAccountForType(addressType: AddressType): Future[AccountDb]
|
||||
def getNewAddress(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType
|
||||
): Future[BitcoinAddress]
|
||||
|
||||
final def getNewAddress(account: HDAccount)(implicit
|
||||
ec: ExecutionContext): Future[BitcoinAddress] = {
|
||||
val accountDbOptF = findAccount(account)
|
||||
accountDbOptF.flatMap {
|
||||
case Some(accountDb) => getNewAddress(accountDb)
|
||||
case None =>
|
||||
Future.failed(
|
||||
new RuntimeException(
|
||||
s"No account found for given hdaccount=${account}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
def getNewAddress(accountDb: AccountDb): Future[BitcoinAddress]
|
||||
|
||||
final def getNewChangeAddress(account: HDAccount)(implicit
|
||||
ec: ExecutionContext): Future[BitcoinAddress] = {
|
||||
val accountDbOptF = findAccount(account)
|
||||
accountDbOptF.flatMap {
|
||||
case Some(accountDb) => getNewChangeAddress(accountDb)
|
||||
case None =>
|
||||
Future.failed(
|
||||
new RuntimeException(
|
||||
s"No account found for given hdaccount=${account}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
def getNewChangeAddress(accountDb: AccountDb): Future[BitcoinAddress]
|
||||
def clearUtxos(account: HDAccount): Future[Unit]
|
||||
def findAccount(account: HDAccount): Future[Option[AccountDb]]
|
||||
def generateScriptPubKeys(
|
||||
account: HDAccount,
|
||||
addressBatchSize: Int,
|
||||
forceGenerateSpks: Boolean
|
||||
): Future[Vector[ScriptPubKey]]
|
||||
def listUnusedAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[AddressDb]]
|
||||
def listSpentAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[AddressDb]]
|
||||
def listAddresses(account: HDAccount): Future[Vector[AddressDb]]
|
||||
def listFundedAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[(AddressDb, CurrencyUnit)]]
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
package org.bitcoins.core.api.wallet
|
||||
|
||||
import org.bitcoins.core.api.wallet.db.{AddressDb, AddressTagDb, ScriptPubKeyDb}
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.hd.AddressType
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
Transaction,
|
||||
TransactionOutPoint,
|
||||
TransactionOutput
|
||||
}
|
||||
import org.bitcoins.core.wallet.utxo.{
|
||||
AddressTag,
|
||||
AddressTagName,
|
||||
AddressTagType
|
||||
}
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
trait AddressHandlingApi {
|
||||
def dropAddressTag(addressTagDb: AddressTagDb): Future[Int]
|
||||
def dropAddressTagType(
|
||||
addressTagType: AddressTagType
|
||||
): Future[Int]
|
||||
def dropAddressTagType(
|
||||
address: BitcoinAddress,
|
||||
addressTagType: AddressTagType
|
||||
): Future[Int]
|
||||
def dropAddressTagName(
|
||||
address: BitcoinAddress,
|
||||
addressTagName: AddressTagName
|
||||
): Future[Int]
|
||||
|
||||
/** Given a transaction, returns the outputs (with their corresponding
|
||||
* outpoints) that pay to this wallet
|
||||
*/
|
||||
def findOurOutputs(
|
||||
transaction: Transaction
|
||||
): Future[Vector[(TransactionOutput, TransactionOutPoint)]]
|
||||
def getNewAddress(): Future[BitcoinAddress]
|
||||
def getNewAddress(
|
||||
tags: Vector[AddressTag]
|
||||
): Future[BitcoinAddress]
|
||||
def getNewAddress(
|
||||
addressType: AddressType
|
||||
): Future[BitcoinAddress]
|
||||
def getNewChangeAddress(): Future[BitcoinAddress]
|
||||
def getNewAddress(
|
||||
addressType: AddressType,
|
||||
tags: Vector[AddressTag]
|
||||
): Future[BitcoinAddress]
|
||||
def getAddressInfo(
|
||||
address: BitcoinAddress
|
||||
): Future[Option[AddressInfo]]
|
||||
def getAddressTags(): Future[Vector[AddressTagDb]]
|
||||
def getAddressTags(address: BitcoinAddress): Future[Vector[AddressTagDb]]
|
||||
def getAddressTags(
|
||||
address: BitcoinAddress,
|
||||
tagType: AddressTagType
|
||||
): Future[Vector[AddressTagDb]]
|
||||
def listAddresses(): Future[Vector[AddressDb]]
|
||||
def listUnusedAddresses(): Future[Vector[AddressDb]]
|
||||
def listScriptPubKeys(): Future[Vector[ScriptPubKeyDb]]
|
||||
def listSpentAddresses(): Future[Vector[AddressDb]]
|
||||
def listFundedAddresses(): Future[Vector[(AddressDb, CurrencyUnit)]]
|
||||
def tagAddress(
|
||||
address: BitcoinAddress,
|
||||
tag: AddressTag
|
||||
): Future[AddressTagDb]
|
||||
def watchScriptPubKey(
|
||||
scriptPubKey: ScriptPubKey
|
||||
): Future[ScriptPubKeyDb]
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package org.bitcoins.core.api.wallet
|
||||
|
||||
import org.bitcoins.core.api.keymanager.BIP39KeyManagerApi
|
||||
import org.bitcoins.core.api.wallet.db.{AccountDb, AddressDb, SpendingInfoDb}
|
||||
import org.bitcoins.core.api.wallet.db.{AccountDb, SpendingInfoDb}
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType, HDPurpose}
|
||||
import org.bitcoins.core.hd.{AddressType, HDAccount, HDPurpose}
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
Transaction,
|
||||
@ -33,6 +33,7 @@ trait HDWalletApi extends WalletApi {
|
||||
def accountHandling: AccountHandlingApi
|
||||
def fundTxHandling: FundTransactionHandlingApi
|
||||
def rescanHandling: RescanHandlingApi
|
||||
def addressHandling: AddressHandlingApi
|
||||
|
||||
/** Gets the balance of the given account */
|
||||
def getBalance(account: HDAccount)(implicit
|
||||
@ -51,21 +52,6 @@ trait HDWalletApi extends WalletApi {
|
||||
|
||||
def getUnconfirmedBalance(account: HDAccount): Future[CurrencyUnit]
|
||||
|
||||
def getNewAddress(account: HDAccount): Future[BitcoinAddress]
|
||||
|
||||
def getNewAddress(account: AccountDb): Future[BitcoinAddress]
|
||||
|
||||
/** Generates a new change address */
|
||||
def getNewChangeAddress(account: AccountDb): Future[BitcoinAddress]
|
||||
|
||||
override def getNewChangeAddress()(implicit
|
||||
ec: ExecutionContext): Future[BitcoinAddress] = {
|
||||
for {
|
||||
account <- getDefaultAccount()
|
||||
addr <- getNewChangeAddress(account)
|
||||
} yield addr
|
||||
}
|
||||
|
||||
/** Fetches the default account from the DB
|
||||
* @return
|
||||
* Future[AccountDb]
|
||||
@ -443,40 +429,12 @@ trait HDWalletApi extends WalletApi {
|
||||
} yield tx
|
||||
}
|
||||
|
||||
def listAddresses(account: HDAccount): Future[Vector[AddressDb]]
|
||||
|
||||
def listSpentAddresses(account: HDAccount): Future[Vector[AddressDb]]
|
||||
|
||||
def listFundedAddresses(
|
||||
account: HDAccount): Future[Vector[(AddressDb, CurrencyUnit)]]
|
||||
|
||||
def listUnusedAddresses(account: HDAccount): Future[Vector[AddressDb]]
|
||||
|
||||
def listDefaultAccountUtxos(): Future[Vector[SpendingInfoDb]]
|
||||
|
||||
def listUtxos(hdAccount: HDAccount): Future[Vector[SpendingInfoDb]]
|
||||
|
||||
override def clearAllUtxos(): Future[HDWalletApi]
|
||||
|
||||
/** Gets the address associated with the pubkey at the resulting `BIP32Path`
|
||||
* determined by the default account and the given chainType and addressIndex
|
||||
*/
|
||||
def getAddress(chainType: HDChainType, addressIndex: Int)(implicit
|
||||
ec: ExecutionContext): Future[AddressDb] = {
|
||||
for {
|
||||
account <- getDefaultAccount()
|
||||
address <- getAddress(account, chainType, addressIndex)
|
||||
} yield address
|
||||
}
|
||||
|
||||
/** Gets the address associated with the pubkey at the resulting `BIP32Path`
|
||||
* determined the given account, chainType, and addressIndex
|
||||
*/
|
||||
def getAddress(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType,
|
||||
addressIndex: Int): Future[AddressDb]
|
||||
|
||||
def listAccounts(): Future[Vector[AccountDb]] = {
|
||||
accountHandling.listAccounts()
|
||||
}
|
||||
@ -491,19 +449,6 @@ trait HDWalletApi extends WalletApi {
|
||||
accountHandling.listAccounts().map(_.filter(_.hdAccount.purpose == purpose))
|
||||
}
|
||||
|
||||
/** Creates a new account with the given purpose */
|
||||
def createNewAccount(purpose: HDPurpose): Future[HDWalletApi]
|
||||
|
||||
/** Tries to create a new account in this wallet. Fails if the most recent
|
||||
* account has no transaction history, as per BIP44
|
||||
*
|
||||
* @see
|
||||
* [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki#account BIP44 account section]]
|
||||
*/
|
||||
def createNewAccount(hdAccount: HDAccount): Future[HDWalletApi]
|
||||
|
||||
def findAccount(account: HDAccount): Future[Option[AccountDb]]
|
||||
|
||||
def fundRawTransaction(
|
||||
destinations: Vector[TransactionOutput],
|
||||
feeRate: FeeUnit,
|
||||
|
@ -5,10 +5,9 @@ import org.bitcoins.core.api.feeprovider.FeeRateApi
|
||||
import org.bitcoins.core.api.keymanager.KeyManagerApi
|
||||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.api.wallet.db.*
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.crypto.ExtPublicKey
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.hd.{AddressType, HDAccount}
|
||||
import org.bitcoins.core.hd.HDAccount
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.blockchain.Block
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
@ -17,19 +16,13 @@ import org.bitcoins.core.protocol.transaction.{
|
||||
TransactionOutPoint,
|
||||
TransactionOutput
|
||||
}
|
||||
import org.bitcoins.core.util.{FutureUtil, StartStopAsync}
|
||||
import org.bitcoins.core.util.StartStopAsync
|
||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||
import org.bitcoins.core.wallet.utxo.{
|
||||
AddressTag,
|
||||
AddressTagName,
|
||||
AddressTagType,
|
||||
TxoState
|
||||
}
|
||||
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
|
||||
import java.time.Instant
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
/** API for the wallet project.
|
||||
*
|
||||
@ -108,21 +101,15 @@ trait WalletApi extends StartStopAsync[WalletApi] {
|
||||
|
||||
def getConfirmedBalance(tag: AddressTag): Future[CurrencyUnit]
|
||||
|
||||
def getNewAddress(): Future[BitcoinAddress]
|
||||
|
||||
def getNewChangeAddress(): Future[BitcoinAddress]
|
||||
|
||||
/** Gets the sum of all unconfirmed UTXOs in this wallet */
|
||||
def getUnconfirmedBalance(): Future[CurrencyUnit]
|
||||
|
||||
def getUnconfirmedBalance(tag: AddressTag): Future[CurrencyUnit]
|
||||
|
||||
def listAddresses(): Future[Vector[AddressDb]]
|
||||
|
||||
def listSpentAddresses(): Future[Vector[AddressDb]]
|
||||
|
||||
def listFundedAddresses(): Future[Vector[(AddressDb, CurrencyUnit)]]
|
||||
|
||||
def listUnusedAddresses(): Future[Vector[AddressDb]]
|
||||
|
||||
def listScriptPubKeys(): Future[Vector[ScriptPubKeyDb]]
|
||||
|
||||
def listTransactions(): Future[Vector[TransactionDb]]
|
||||
|
||||
def listUtxos(): Future[Vector[SpendingInfoDb]]
|
||||
@ -131,8 +118,6 @@ trait WalletApi extends StartStopAsync[WalletApi] {
|
||||
|
||||
def listUtxos(tag: AddressTag): Future[Vector[SpendingInfoDb]]
|
||||
|
||||
def watchScriptPubKey(scriptPubKey: ScriptPubKey): Future[ScriptPubKeyDb]
|
||||
|
||||
/** Checks if the wallet contains any data */
|
||||
def isEmpty(): Future[Boolean]
|
||||
|
||||
@ -143,86 +128,6 @@ trait WalletApi extends StartStopAsync[WalletApi] {
|
||||
|
||||
def clearAllAddresses(): Future[WalletApi]
|
||||
|
||||
/** Gets a new external address with the specified type.
|
||||
* @param addressType
|
||||
*/
|
||||
def getNewAddress(addressType: AddressType): Future[BitcoinAddress]
|
||||
|
||||
/** Gets a new external address Calling this method multiple times will return
|
||||
* the same address, until it has received funds.
|
||||
*/
|
||||
def getNewAddress(): Future[BitcoinAddress]
|
||||
|
||||
def getNewAddress(
|
||||
addressType: AddressType,
|
||||
tags: Vector[AddressTag]): Future[BitcoinAddress]
|
||||
|
||||
def getNewAddress(tags: Vector[AddressTag]): Future[BitcoinAddress]
|
||||
|
||||
/** Gets a external address the given AddressType. Calling this method
|
||||
* multiple times will return the same address, until it has received funds.
|
||||
*/
|
||||
def getUnusedAddress(addressType: AddressType): Future[BitcoinAddress]
|
||||
|
||||
/** Gets a external address. Calling this method multiple times will return
|
||||
* the same address, until it has received funds.
|
||||
*/
|
||||
def getUnusedAddress: Future[BitcoinAddress]
|
||||
|
||||
/** Mimics the `getaddressinfo` RPC call in Bitcoin Core
|
||||
*
|
||||
* @param address
|
||||
* @return
|
||||
* If the address is found in our database `Some(address)` is returned,
|
||||
* otherwise `None`
|
||||
*/
|
||||
def getAddressInfo(address: BitcoinAddress): Future[Option[AddressInfo]]
|
||||
|
||||
def getAddressInfo(
|
||||
spendingInfoDb: SpendingInfoDb,
|
||||
networkParameters: NetworkParameters): Future[Option[AddressInfo]] = {
|
||||
val addressT = BitcoinAddress.fromScriptPubKeyT(
|
||||
spk = spendingInfoDb.output.scriptPubKey,
|
||||
np = networkParameters)
|
||||
addressT match {
|
||||
case Success(addr) =>
|
||||
getAddressInfo(addr)
|
||||
case Failure(_) =>
|
||||
FutureUtil.none
|
||||
}
|
||||
}
|
||||
|
||||
/** Tags the address with the address tag, updates the tag if one of tag's
|
||||
* TagType already exists
|
||||
*/
|
||||
def tagAddress(address: BitcoinAddress, tag: AddressTag): Future[AddressTagDb]
|
||||
|
||||
def getAddressTags(address: BitcoinAddress): Future[Vector[AddressTagDb]]
|
||||
|
||||
def getAddressTags(
|
||||
address: BitcoinAddress,
|
||||
tagType: AddressTagType): Future[Vector[AddressTagDb]]
|
||||
|
||||
def getAddressTags(): Future[Vector[AddressTagDb]]
|
||||
|
||||
def getAddressTags(tagType: AddressTagType): Future[Vector[AddressTagDb]]
|
||||
|
||||
def dropAddressTag(addressTagDb: AddressTagDb): Future[Int]
|
||||
|
||||
def dropAddressTagType(addressTagType: AddressTagType): Future[Int]
|
||||
|
||||
def dropAddressTagType(
|
||||
address: BitcoinAddress,
|
||||
addressTagType: AddressTagType): Future[Int]
|
||||
|
||||
def dropAddressTagName(
|
||||
address: BitcoinAddress,
|
||||
tagName: AddressTagName): Future[Int]
|
||||
|
||||
/** Generates a new change address */
|
||||
protected[wallet] def getNewChangeAddress()(implicit
|
||||
ec: ExecutionContext): Future[BitcoinAddress]
|
||||
|
||||
def keyManager: KeyManagerApi
|
||||
|
||||
protected def determineFeeRate(feeRateOpt: Option[FeeUnit]): Future[FeeUnit] =
|
||||
|
@ -301,8 +301,8 @@ abstract class DLCWallet
|
||||
index: Int
|
||||
): Future[Vector[AddressDb]] = {
|
||||
for {
|
||||
zero <- getAddress(account, chainType, index)
|
||||
one <- getAddress(account, chainType, index + 1)
|
||||
zero <- addressHandling.getAddress(account, chainType, index)
|
||||
one <- addressHandling.getAddress(account, chainType, index + 1)
|
||||
} yield {
|
||||
logger.debug(s"Wrote DLC key addresses to database using index $index")
|
||||
Vector(zero, one)
|
||||
@ -432,7 +432,7 @@ abstract class DLCWallet
|
||||
chainType = HDChainType.External
|
||||
|
||||
account <- getDefaultAccountForType(AddressType.SegWit)
|
||||
nextIndex <- getNextAvailableIndex(account, chainType)
|
||||
nextIndex <- addressHandling.getNextAvailableIndex(account, chainType)
|
||||
_ <- writeDLCKeysToAddressDb(account, chainType, nextIndex)
|
||||
|
||||
fundRawTxHelper <- fundTxHandling.fundRawTransactionInternal(
|
||||
@ -636,7 +636,8 @@ abstract class DLCWallet
|
||||
Future.successful(initAccept)
|
||||
}
|
||||
case None =>
|
||||
val nextIndexF = getNextAvailableIndex(account, chainType)
|
||||
val nextIndexF =
|
||||
addressHandling.getNextAvailableIndex(account, chainType)
|
||||
val acceptWithoutSigsWithKeysF
|
||||
: Future[(DLCAcceptWithoutSigs, DLCPublicKeys)] =
|
||||
nextIndexF.map { nextIndex =>
|
||||
|
@ -59,7 +59,7 @@ class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
|
||||
): Future[Boolean] = {
|
||||
for {
|
||||
balance <- wallet.getBalance()
|
||||
addresses <- wallet.listAddresses()
|
||||
addresses <- wallet.addressHandling.listAddresses()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
} yield {
|
||||
// +- fee rate because signatures could vary in size
|
||||
@ -108,7 +108,7 @@ class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
|
||||
_ <- NodeTestUtil.awaitSync(node, bitcoind)
|
||||
_ <- AsyncUtil.awaitConditionF(condition1, maxTries = 100) // 10 seconds
|
||||
// receive
|
||||
address <- wallet.getNewAddress()
|
||||
address <- wallet.addressHandling.getNewAddress()
|
||||
txId <- bitcoind.sendToAddress(address, TestAmount)
|
||||
expectedTx <- bitcoind.getRawTransactionRaw(txId)
|
||||
|
||||
@ -145,7 +145,7 @@ class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
|
||||
|
||||
for {
|
||||
// start watching
|
||||
_ <- wallet.watchScriptPubKey(spk)
|
||||
_ <- wallet.addressHandling.watchScriptPubKey(spk)
|
||||
|
||||
// send
|
||||
txSent <- wallet.sendToOutputs(Vector(output), FeeRate)
|
||||
@ -168,7 +168,7 @@ class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
|
||||
for {
|
||||
rescan <- wallet.isRescanning()
|
||||
balance <- wallet.getBalance()
|
||||
addresses <- wallet.listAddresses()
|
||||
addresses <- wallet.addressHandling.listAddresses()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
spks = utxos
|
||||
.map(_.output.scriptPubKey)
|
||||
@ -181,7 +181,7 @@ class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
|
||||
}
|
||||
|
||||
for {
|
||||
addresses <- wallet.listAddresses()
|
||||
addresses <- wallet.addressHandling.listAddresses()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
_ = assert(addresses.size == 6)
|
||||
_ = assert(utxos.size == 3)
|
||||
@ -191,7 +191,7 @@ class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
|
||||
bitcoind
|
||||
.sendToAddress(address, TestAmount)
|
||||
|
||||
addresses <- wallet.listAddresses()
|
||||
addresses <- wallet.addressHandling.listAddresses()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
_ = assert(addresses.size == 7)
|
||||
_ = assert(utxos.size == 3)
|
||||
@ -207,7 +207,7 @@ class NeutrinoNodeWithWalletTest extends NodeTestWithCachedBitcoindNewest {
|
||||
.map(_.get.height == bitcoindHeight)
|
||||
})
|
||||
_ <- wallet.clearAllUtxos()
|
||||
addresses <- wallet.listAddresses()
|
||||
addresses <- wallet.addressHandling.listAddresses()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
_ = assert(addresses.nonEmpty)
|
||||
_ = assert(utxos.isEmpty)
|
||||
|
@ -392,10 +392,10 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||
for {
|
||||
wallet <- defaultWalletF
|
||||
account1 = WalletTestUtil.getHdAccount1(wallet.walletConfig)
|
||||
newAccountWallet <- wallet.createNewAccount(
|
||||
_ <- wallet.accountHandling.createNewAccount(
|
||||
hdAccount = account1
|
||||
)
|
||||
} yield newAccountWallet
|
||||
} yield wallet
|
||||
|
||||
}
|
||||
|
||||
@ -411,10 +411,10 @@ object BitcoinSWalletTest extends WalletLogger {
|
||||
chainQueryApi = chainQueryApi
|
||||
)
|
||||
account1 = WalletTestUtil.getHdAccount1(wallet.walletConfig)
|
||||
newAccountWallet <- wallet.createNewAccount(
|
||||
_ <- wallet.accountHandling.createNewAccount(
|
||||
hdAccount = account1
|
||||
)
|
||||
} yield newAccountWallet.asInstanceOf[DLCWallet]
|
||||
} yield wallet
|
||||
}
|
||||
|
||||
/** Pairs the given wallet with a bitcoind instance that has money in the
|
||||
|
@ -68,7 +68,7 @@ trait FundWalletUtil extends BitcoinSLogger {
|
||||
)(implicit ec: ExecutionContext): Future[Wallet] = {
|
||||
|
||||
val addressesF: Future[Vector[BitcoinAddress]] = Future.sequence {
|
||||
Vector.fill(3)(wallet.getNewAddress(account))
|
||||
Vector.fill(3)(wallet.accountHandling.getNewAddress(account))
|
||||
}
|
||||
|
||||
// construct three txs that send money to these addresses
|
||||
@ -100,7 +100,7 @@ trait FundWalletUtil extends BitcoinSLogger {
|
||||
)(implicit ec: ExecutionContext): Future[HDWalletApi] = {
|
||||
|
||||
val addressesF: Future[Vector[BitcoinAddress]] = Future.sequence {
|
||||
Vector.fill(3)(wallet.getNewAddress(account))
|
||||
Vector.fill(3)(wallet.accountHandling.getNewAddress(account))
|
||||
}
|
||||
|
||||
val txAndHashF = for {
|
||||
|
@ -27,11 +27,11 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
it must "generate a new address for the default account and then find it" in {
|
||||
(fundedWallet: FundedWallet) =>
|
||||
val wallet = fundedWallet.wallet
|
||||
val addressF = wallet.getNewAddress()
|
||||
val addressF = wallet.addressHandling.getNewAddress()
|
||||
|
||||
for {
|
||||
address <- addressF
|
||||
exists <- wallet.contains(address, None)
|
||||
exists <- wallet.addressHandling.contains(address, None)
|
||||
} yield {
|
||||
assert(exists, s"Wallet must contain address after generating it")
|
||||
}
|
||||
@ -41,12 +41,14 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
(fundedWallet: FundedWallet) =>
|
||||
val wallet = fundedWallet.wallet
|
||||
val account1 = WalletTestUtil.getHdAccount1(wallet.walletConfig)
|
||||
val addressF = wallet.getNewAddress(account1)
|
||||
val addressF = wallet.accountHandling.getNewAddress(account1)
|
||||
for {
|
||||
address <- addressF
|
||||
listAddressesForAcct <- wallet.listAddresses(account1)
|
||||
exists <- wallet.contains(address, Some(account1))
|
||||
doesNotExist <- wallet.contains(address, None)
|
||||
listAddressesForAcct <- wallet.accountHandling.listAddresses(account1)
|
||||
exists <- wallet.addressHandling.contains(
|
||||
address,
|
||||
Some((wallet.accountHandling, account1)))
|
||||
doesNotExist <- wallet.addressHandling.contains(address, None)
|
||||
} yield {
|
||||
assert(listAddressesForAcct.nonEmpty)
|
||||
assert(listAddressesForAcct.map(_.address).contains(address))
|
||||
@ -66,13 +68,13 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
val wallet = fundedWallet.wallet
|
||||
|
||||
for {
|
||||
address1 <- wallet.getUnusedAddress
|
||||
exists <- wallet.contains(address1, None)
|
||||
address1 <- wallet.addressHandling.getUnusedAddress
|
||||
exists <- wallet.addressHandling.contains(address1, None)
|
||||
_ = assert(exists, s"Wallet must contain address after generating it")
|
||||
address2 <- wallet.getUnusedAddress
|
||||
address2 <- wallet.addressHandling.getUnusedAddress
|
||||
_ = assert(address1 == address2, "Must generate same address")
|
||||
_ <- wallet.sendToAddress(address1, Satoshis(10000), None)
|
||||
address3 <- wallet.getUnusedAddress
|
||||
address3 <- wallet.addressHandling.getUnusedAddress
|
||||
} yield {
|
||||
assert(address1 != address3, "Must generate a new address")
|
||||
assert(address2 != address3, "Must generate a new address")
|
||||
@ -83,7 +85,7 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
(fundedWallet: FundedWallet) =>
|
||||
val wallet = fundedWallet.wallet
|
||||
val addressesF = Future.sequence {
|
||||
Vector.fill(10)(wallet.getNewAddress())
|
||||
Vector.fill(10)(wallet.addressHandling.getNewAddress())
|
||||
}
|
||||
|
||||
for {
|
||||
@ -102,16 +104,16 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
val wallet = fundedWallet.wallet
|
||||
|
||||
for {
|
||||
emptySpentAddresses <- wallet.listSpentAddresses()
|
||||
emptySpentAddresses <- wallet.addressHandling.listSpentAddresses()
|
||||
_ = assert(
|
||||
emptySpentAddresses.isEmpty,
|
||||
s"Wallet did not start with empty spent addresses, got $emptySpentAddresses"
|
||||
)
|
||||
|
||||
tempAddress <- wallet.getNewAddress()
|
||||
tempAddress <- wallet.addressHandling.getNewAddress()
|
||||
tx <- wallet.sendToAddress(tempAddress, Bitcoins(1), None)
|
||||
spentDbs <- wallet.spendingInfoDAO.findOutputsBeingSpent(tx)
|
||||
spentAddresses <- wallet.listSpentAddresses()
|
||||
spentAddresses <- wallet.addressHandling.listSpentAddresses()
|
||||
} yield {
|
||||
val diff = spentDbs
|
||||
.map(_.output.scriptPubKey)
|
||||
@ -126,7 +128,7 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
|
||||
for {
|
||||
unspentDbs <- wallet.spendingInfoDAO.findAllUnspent()
|
||||
fundedAddresses <- wallet.listFundedAddresses()
|
||||
fundedAddresses <- wallet.addressHandling.listFundedAddresses()
|
||||
} yield {
|
||||
val diff = unspentDbs
|
||||
.map(_.output)
|
||||
@ -144,7 +146,7 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
|
||||
for {
|
||||
addrDbs <- wallet.spendingInfoDAO.findAllSpendingInfos()
|
||||
fundedAddresses <- wallet.listUnusedAddresses()
|
||||
fundedAddresses <- wallet.addressHandling.listUnusedAddresses()
|
||||
} yield {
|
||||
val intersect = addrDbs
|
||||
.map(_.output.scriptPubKey)
|
||||
@ -157,13 +159,13 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
val wallet = fundedWallet.wallet
|
||||
|
||||
for {
|
||||
addr <- wallet.getNewAddress()
|
||||
initTags <- wallet.getAddressTags(addr)
|
||||
addr <- wallet.addressHandling.getNewAddress()
|
||||
initTags <- wallet.addressHandling.getAddressTags(addr)
|
||||
_ = assert(initTags.isEmpty)
|
||||
|
||||
tag = AddressLabelTag("for test")
|
||||
_ <- wallet.tagAddress(addr, tag)
|
||||
tags <- wallet.getAddressTags(addr)
|
||||
_ <- wallet.addressHandling.tagAddress(addr, tag)
|
||||
tags <- wallet.addressHandling.getAddressTags(addr)
|
||||
} yield {
|
||||
assert(tags.size == 1)
|
||||
val tagDb = tags.head
|
||||
@ -176,19 +178,19 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
val wallet = fundedWallet.wallet
|
||||
|
||||
for {
|
||||
addr <- wallet.getNewAddress()
|
||||
initTags <- wallet.getAddressTags(addr)
|
||||
addr <- wallet.addressHandling.getNewAddress()
|
||||
initTags <- wallet.addressHandling.getAddressTags(addr)
|
||||
_ = assert(initTags.isEmpty)
|
||||
|
||||
tag = AddressLabelTag("no one knows the supply of eth")
|
||||
_ <- wallet.tagAddress(addr, tag)
|
||||
tags <- wallet.getAddressTags(addr)
|
||||
_ <- wallet.addressHandling.tagAddress(addr, tag)
|
||||
tags <- wallet.addressHandling.getAddressTags(addr)
|
||||
_ = assert(tags.size == 1)
|
||||
tagDb = tags.head
|
||||
_ = assert(tagDb.address == addr)
|
||||
_ = assert(tagDb.addressTag == tag)
|
||||
|
||||
num <- wallet.dropAddressTag(tagDb)
|
||||
num <- wallet.addressHandling.dropAddressTag(tagDb)
|
||||
} yield assert(num == 1)
|
||||
}
|
||||
|
||||
@ -198,16 +200,16 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
for {
|
||||
addr <- wallet.getNewAddress()
|
||||
addr1 <- wallet.getNewAddress()
|
||||
initTags <- wallet.getAddressTags(addr)
|
||||
initTags1 <- wallet.getAddressTags(addr1)
|
||||
initTags <- wallet.addressHandling.getAddressTags(addr)
|
||||
initTags1 <- wallet.addressHandling.getAddressTags(addr1)
|
||||
_ = assert(initTags.isEmpty)
|
||||
_ = assert(initTags1.isEmpty)
|
||||
|
||||
tag = AddressLabelTag("no one knows the supply of eth")
|
||||
_ <- wallet.tagAddress(addr, tag)
|
||||
_ <- wallet.tagAddress(addr, HotStorage)
|
||||
_ <- wallet.tagAddress(addr1, tag)
|
||||
tags <- wallet.getAddressTags(AddressLabelTagType)
|
||||
_ <- wallet.addressHandling.tagAddress(addr, tag)
|
||||
_ <- wallet.addressHandling.tagAddress(addr, HotStorage)
|
||||
_ <- wallet.addressHandling.tagAddress(addr1, tag)
|
||||
tags <- wallet.addressHandling.getAddressTags(AddressLabelTagType)
|
||||
_ = assert(tags.size == 2)
|
||||
tagDb = tags.head
|
||||
_ = assert(tagDb.address == addr)
|
||||
@ -216,8 +218,10 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
_ = assert(tagDb1.address == addr1)
|
||||
_ = assert(tagDb1.addressTag == tag)
|
||||
|
||||
numDropped <- wallet.dropAddressTagType(AddressLabelTagType)
|
||||
hotStorageTags <- wallet.getAddressTags(StorageLocationTagType)
|
||||
numDropped <- wallet.addressHandling.dropAddressTagType(
|
||||
AddressLabelTagType)
|
||||
hotStorageTags <- wallet.addressHandling.getAddressTags(
|
||||
StorageLocationTagType)
|
||||
} yield {
|
||||
assert(numDropped == 2)
|
||||
assert(hotStorageTags.size == 1)
|
||||
@ -229,9 +233,9 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||
val wallet = fundedWallet.wallet
|
||||
val spk = EmptyScriptPubKey
|
||||
for {
|
||||
before <- wallet.listScriptPubKeys()
|
||||
spkDb <- wallet.watchScriptPubKey(spk)
|
||||
after <- wallet.listScriptPubKeys()
|
||||
before <- wallet.addressHandling.listScriptPubKeys()
|
||||
spkDb <- wallet.addressHandling.watchScriptPubKey(spk)
|
||||
after <- wallet.addressHandling.listScriptPubKeys()
|
||||
} yield {
|
||||
assert(before.size + 1 == after.size)
|
||||
assert(spkDb.scriptPubKey == spk)
|
||||
|
@ -21,10 +21,10 @@ class AddressLabelTest extends BitcoinSWalletTest {
|
||||
val tag1 = UnknownAddressTag("test_tag_name_1", "test_tag_type_1")
|
||||
val tag2 = UnknownAddressTag("test_tag_name_2", "test_tag_type_2")
|
||||
val addressF = for {
|
||||
address <- wallet.getNewAddress(Vector(tag1))
|
||||
address <- wallet.addressHandling.getNewAddress(Vector(tag1))
|
||||
// add another tag to address
|
||||
tagDb1 <- wallet.getAddressTags(address)
|
||||
tagDb2 <- wallet.tagAddress(address, tag2)
|
||||
tagDb1 <- wallet.addressHandling.getAddressTags(address)
|
||||
tagDb2 <- wallet.addressHandling.tagAddress(address, tag2)
|
||||
} yield {
|
||||
assert(tagDb1.head.address == address)
|
||||
assert(tagDb1.head.tagName == tag1.tagName)
|
||||
@ -50,8 +50,8 @@ class AddressLabelTest extends BitcoinSWalletTest {
|
||||
val resultF = for {
|
||||
address <- wallet.getNewAddress()
|
||||
// add another tag to address
|
||||
_ <- wallet.tagAddress(address, tag1)
|
||||
_ <- wallet.tagAddress(address, tag2)
|
||||
_ <- wallet.addressHandling.tagAddress(address, tag1)
|
||||
_ <- wallet.addressHandling.tagAddress(address, tag2)
|
||||
} yield ()
|
||||
|
||||
recoverToSucceededIf[SQLException](resultF)
|
||||
|
@ -41,7 +41,7 @@ class AddressTagIntegrationTest extends BitcoinSWalletTest {
|
||||
IncomingTransactionDAO()(system.dispatcher, walletConfig)
|
||||
for {
|
||||
addr <- wallet.getNewAddress()
|
||||
taggedAddr <- wallet.getNewAddress(Vector(exampleTag))
|
||||
taggedAddr <- wallet.addressHandling.getNewAddress(Vector(exampleTag))
|
||||
txId <- bitcoind.sendMany(
|
||||
Map(addr -> valueFromBitcoind, taggedAddr -> valueFromBitcoind)
|
||||
)
|
||||
@ -132,7 +132,7 @@ class AddressTagIntegrationTest extends BitcoinSWalletTest {
|
||||
|
||||
val bitcoindAddrF = bitcoind.getNewAddress
|
||||
val walletAddr1F = wallet.getNewAddress()
|
||||
val taggedAddrF = wallet.getNewAddress(Vector(exampleTag))
|
||||
val taggedAddrF = wallet.addressHandling.getNewAddress(Vector(exampleTag))
|
||||
for {
|
||||
walletAddr1 <- walletAddr1F
|
||||
txid <- bitcoind.sendToAddress(walletAddr1, Bitcoins.two)
|
||||
|
@ -169,7 +169,7 @@ class FundTransactionHandlingTest
|
||||
val wallet = fundedWallet.wallet
|
||||
val walletConfig = fundedWallet.walletConfig
|
||||
val account1 = WalletTestUtil.getHdAccount1(walletConfig)
|
||||
val account1DbF = wallet.findAccount(account1)
|
||||
val account1DbF = wallet.accountHandling.findAccount(account1)
|
||||
for {
|
||||
feeRate <- wallet.getFeeRate()
|
||||
account1DbOpt <- account1DbF
|
||||
@ -195,7 +195,7 @@ class FundTransactionHandlingTest
|
||||
val wallet = fundedWallet.wallet
|
||||
val walletConfig = fundedWallet.walletConfig
|
||||
val account1 = WalletTestUtil.getHdAccount1(walletConfig)
|
||||
val account1DbF = wallet.findAccount(account1)
|
||||
val account1DbF = wallet.accountHandling.findAccount(account1)
|
||||
val fundedTxF = for {
|
||||
feeRate <- wallet.getFeeRate()
|
||||
account1DbOpt <- account1DbF
|
||||
@ -218,11 +218,12 @@ class FundTransactionHandlingTest
|
||||
val bitcoind = fundedWallet.bitcoind
|
||||
val fundedTxF = for {
|
||||
feeRate <- wallet.getFeeRate()
|
||||
_ <- wallet.createNewAccount(wallet.keyManager.kmParams.purpose)
|
||||
_ <- wallet.accountHandling.createNewAccount(
|
||||
wallet.keyManager.kmParams.purpose)
|
||||
accounts <- wallet.listAccounts()
|
||||
account2 = accounts.find(_.hdAccount.index == 2).get
|
||||
|
||||
addr <- wallet.getNewAddress(account2)
|
||||
addr <- wallet.accountHandling.getNewAddress(account2)
|
||||
|
||||
hash <- bitcoind.generateToAddress(1, addr).map(_.head)
|
||||
block <- bitcoind.getBlockRaw(hash)
|
||||
@ -273,7 +274,7 @@ class FundTransactionHandlingTest
|
||||
): Future[Assertion] = {
|
||||
for {
|
||||
feeRate <- wallet.getFeeRate()
|
||||
taggedAddr <- wallet.getNewAddress(Vector(tag))
|
||||
taggedAddr <- wallet.addressHandling.getNewAddress(Vector(tag))
|
||||
_ <-
|
||||
wallet.sendToAddress(taggedAddr, destination.value * 2, Some(feeRate))
|
||||
taggedBalance <- wallet.getBalance(tag)
|
||||
|
@ -17,8 +17,8 @@ class LegacyWalletTest extends BitcoinSWalletTest {
|
||||
addr <- wallet.getNewAddress()
|
||||
account <- wallet.getDefaultAccount()
|
||||
otherAddr <- wallet.getNewAddress()
|
||||
thirdAddr <- wallet.getNewAddress(AddressType.Legacy)
|
||||
allAddrs <- wallet.listAddresses()
|
||||
thirdAddr <- wallet.addressHandling.getNewAddress(AddressType.Legacy)
|
||||
allAddrs <- wallet.addressHandling.listAddresses()
|
||||
} yield {
|
||||
assert(account.hdAccount.purpose == HDPurpose.Legacy)
|
||||
assert(allAddrs.forall(_.address.isInstanceOf[P2PKHAddress]))
|
||||
@ -32,7 +32,7 @@ class LegacyWalletTest extends BitcoinSWalletTest {
|
||||
it should "generate segwit addresses" in { wallet =>
|
||||
for {
|
||||
account <- wallet.getDefaultAccountForType(AddressType.SegWit)
|
||||
addr <- wallet.getNewAddress(AddressType.SegWit)
|
||||
addr <- wallet.addressHandling.getNewAddress(AddressType.SegWit)
|
||||
} yield {
|
||||
assert(account.hdAccount.purpose == HDPurpose.SegWit)
|
||||
assert(addr.isInstanceOf[Bech32Address])
|
||||
@ -41,9 +41,9 @@ class LegacyWalletTest extends BitcoinSWalletTest {
|
||||
|
||||
it should "generate mixed addresses" in { wallet =>
|
||||
for {
|
||||
segwit <- wallet.getNewAddress(AddressType.SegWit)
|
||||
legacy <- wallet.getNewAddress(AddressType.Legacy)
|
||||
nested <- wallet.getNewAddress(AddressType.NestedSegWit)
|
||||
segwit <- wallet.addressHandling.getNewAddress(AddressType.SegWit)
|
||||
legacy <- wallet.addressHandling.getNewAddress(AddressType.Legacy)
|
||||
nested <- wallet.addressHandling.getNewAddress(AddressType.NestedSegWit)
|
||||
} yield {
|
||||
assert(segwit.isInstanceOf[Bech32Address])
|
||||
assert(legacy.isInstanceOf[P2PKHAddress])
|
||||
|
@ -184,7 +184,7 @@ class ProcessBlockTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
UInt32.zero
|
||||
)
|
||||
|
||||
addrDb <- wallet.getAddressInfo(recvAddr).map(_.get)
|
||||
addrDb <- wallet.addressHandling.getAddressInfo(recvAddr).map(_.get)
|
||||
path = addrDb.path
|
||||
coin = path.coin
|
||||
accountDb <- accountDAO
|
||||
|
@ -29,13 +29,13 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
utxos <- wallet.listUtxos(account)
|
||||
_ = assert(utxos.nonEmpty)
|
||||
|
||||
addresses <- wallet.listAddresses(account)
|
||||
addresses <- wallet.accountHandling.listAddresses(account)
|
||||
_ = assert(addresses.nonEmpty)
|
||||
|
||||
_ <- wallet.accountHandling.clearUtxos(account)
|
||||
|
||||
clearedUtxos <- wallet.listUtxos(account)
|
||||
clearedAddresses <- wallet.listAddresses(account)
|
||||
clearedAddresses <- wallet.accountHandling.listAddresses(account)
|
||||
} yield {
|
||||
assert(clearedUtxos.isEmpty)
|
||||
assert(clearedAddresses.nonEmpty)
|
||||
@ -52,13 +52,13 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
utxos <- wallet.listUtxos()
|
||||
_ = assert(utxos.nonEmpty)
|
||||
|
||||
addresses <- wallet.listAddresses()
|
||||
addresses <- wallet.addressHandling.listAddresses()
|
||||
_ = assert(addresses.nonEmpty)
|
||||
|
||||
_ <- wallet.clearAllUtxos()
|
||||
|
||||
clearedUtxos <- wallet.listUtxos()
|
||||
clearedAddresses <- wallet.listAddresses()
|
||||
clearedAddresses <- wallet.addressHandling.listAddresses()
|
||||
} yield {
|
||||
assert(clearedUtxos.isEmpty)
|
||||
assert(clearedAddresses.nonEmpty)
|
||||
@ -362,7 +362,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
force = false
|
||||
)
|
||||
|
||||
usedAddresses <- wallet.listFundedAddresses()
|
||||
usedAddresses <- wallet.addressHandling.listFundedAddresses()
|
||||
|
||||
_ = assert(
|
||||
!usedAddresses.exists(_._1.address == address),
|
||||
@ -372,7 +372,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
hashes <- bitcoind.generateToAddress(1, address)
|
||||
block <- bitcoind.getBlockRaw(hashes.head)
|
||||
_ <- wallet.processBlock(block)
|
||||
fundedAddresses <- wallet.listFundedAddresses()
|
||||
fundedAddresses <- wallet.addressHandling.listFundedAddresses()
|
||||
utxos <- wallet.listUtxos(TxoState.ImmatureCoinbase)
|
||||
} yield {
|
||||
// note 25 bitcoin reward from coinbase tx here
|
||||
@ -580,7 +580,7 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
|
||||
DEFAULT_ADDR_BATCH_SIZE)
|
||||
_ = assert(rescanState.isInstanceOf[RescanState.RescanStarted])
|
||||
_ <- RescanState.awaitRescanDone(rescanState)
|
||||
addresses <- wallet.listAddresses()
|
||||
addresses <- wallet.addressHandling.listAddresses()
|
||||
} yield {
|
||||
assert(addresses.exists(_.isChange))
|
||||
assert(addresses.exists(!_.isChange))
|
||||
|
@ -18,8 +18,8 @@ class SegwitWalletTest extends BitcoinSWalletTest {
|
||||
addr <- wallet.getNewAddress()
|
||||
account <- wallet.getDefaultAccount()
|
||||
otherAddr <- wallet.getNewAddress()
|
||||
thirdAddr <- wallet.getNewAddress(AddressType.SegWit)
|
||||
allAddrs <- wallet.listAddresses()
|
||||
thirdAddr <- wallet.addressHandling.getNewAddress(AddressType.SegWit)
|
||||
allAddrs <- wallet.addressHandling.listAddresses()
|
||||
} yield {
|
||||
assert(account.hdAccount.purpose == HDPurpose.SegWit)
|
||||
assert(allAddrs.forall(_.address.isInstanceOf[Bech32Address]))
|
||||
@ -33,7 +33,7 @@ class SegwitWalletTest extends BitcoinSWalletTest {
|
||||
it should "generate legacy addresses" in { wallet =>
|
||||
for {
|
||||
account <- wallet.getDefaultAccountForType(AddressType.Legacy)
|
||||
addr <- wallet.getNewAddress(AddressType.Legacy)
|
||||
addr <- wallet.addressHandling.getNewAddress(AddressType.Legacy)
|
||||
} yield {
|
||||
assert(account.hdAccount.purpose == HDPurpose.Legacy)
|
||||
assert(addr.isInstanceOf[P2PKHAddress])
|
||||
@ -42,9 +42,9 @@ class SegwitWalletTest extends BitcoinSWalletTest {
|
||||
|
||||
it should "generate mixed addresses" in { wallet =>
|
||||
for {
|
||||
segwit <- wallet.getNewAddress(AddressType.SegWit)
|
||||
legacy <- wallet.getNewAddress(AddressType.Legacy)
|
||||
nested <- wallet.getNewAddress(AddressType.NestedSegWit)
|
||||
segwit <- wallet.addressHandling.getNewAddress(AddressType.SegWit)
|
||||
legacy <- wallet.addressHandling.getNewAddress(AddressType.Legacy)
|
||||
nested <- wallet.addressHandling.getNewAddress(AddressType.NestedSegWit)
|
||||
} yield {
|
||||
assert(segwit.isInstanceOf[Bech32Address])
|
||||
assert(legacy.isInstanceOf[P2PKHAddress])
|
||||
|
@ -196,7 +196,7 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
||||
val accountsToCreate = existing.length until testVectors.length
|
||||
FutureUtil
|
||||
.sequentially(accountsToCreate) { _ =>
|
||||
wallet.createNewAccount(keyManagerParams.purpose)
|
||||
wallet.accountHandling.createNewAccount(keyManagerParams.purpose)
|
||||
}
|
||||
.map(_ => ())
|
||||
}
|
||||
@ -212,9 +212,10 @@ class TrezorAddressTest extends BitcoinSWalletTest with EmptyFixture {
|
||||
val addrFutures: Future[Seq[AddressDb]] =
|
||||
FutureUtil.sequentially(vec.addresses) { vector =>
|
||||
val addrFut = vector.chain match {
|
||||
case HDChainType.Change => wallet.getNewChangeAddress(acc)
|
||||
case HDChainType.Change =>
|
||||
wallet.accountHandling.getNewChangeAddress(acc)
|
||||
case HDChainType.External =>
|
||||
wallet.getNewAddress(acc)
|
||||
wallet.accountHandling.getNewAddress(acc)
|
||||
}
|
||||
addrFut.flatMap(wallet.addressDAO.findAddress).map {
|
||||
case Some(addr) => addr
|
||||
|
@ -49,7 +49,7 @@ class WalletCallbackTest extends BitcoinSWalletTest {
|
||||
|
||||
for {
|
||||
address <- wallet.getNewAddress()
|
||||
exists <- wallet.contains(address, None)
|
||||
exists <- wallet.addressHandling.contains(address, None)
|
||||
_ = assert(exists, "Wallet must contain address after generating it")
|
||||
result <- resultP.future
|
||||
} yield assert(result == address)
|
||||
|
@ -37,7 +37,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
it should "create a new wallet" in { (wallet: Wallet) =>
|
||||
for {
|
||||
accounts <- wallet.listAccounts()
|
||||
addresses <- wallet.listAddresses()
|
||||
addresses <- wallet.addressHandling.listAddresses()
|
||||
} yield {
|
||||
assert(accounts.length == 3) // legacy, segwit and nested segwit
|
||||
assert(addresses.isEmpty)
|
||||
@ -48,7 +48,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
for {
|
||||
addr <- wallet.getNewAddress()
|
||||
otherAddr <- wallet.getNewAddress()
|
||||
allAddrs <- wallet.listAddresses()
|
||||
allAddrs <- wallet.addressHandling.listAddresses()
|
||||
} yield {
|
||||
assert(allAddrs.length == 2)
|
||||
assert(allAddrs.exists(_.address == addr))
|
||||
@ -221,7 +221,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
|
||||
for {
|
||||
accountDb <- wallet.accountDAO.findAll().map(_.head)
|
||||
addr <- wallet.getNewAddress(accountDb)
|
||||
addr <- wallet.accountHandling.getNewAddress(accountDb)
|
||||
addrDb <- wallet.addressDAO.findAddress(addr).map(_.get)
|
||||
walletKey = addrDb.ecPublicKey
|
||||
walletPath = addrDb.path
|
||||
@ -247,7 +247,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
it must "be able to sign a psbt with our own p2pkh utxo" in {
|
||||
(wallet: Wallet) =>
|
||||
for {
|
||||
addr <- wallet.getNewAddress(AddressType.Legacy)
|
||||
addr <- wallet.addressHandling.getNewAddress(AddressType.Legacy)
|
||||
addrDb <- wallet.addressDAO.findAddress(addr).map(_.get)
|
||||
walletKey = addrDb.ecPublicKey
|
||||
|
||||
@ -271,7 +271,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
it must "be able to sign a psbt with our own p2sh segwit utxo" in {
|
||||
(wallet: Wallet) =>
|
||||
for {
|
||||
addr <- wallet.getNewAddress(AddressType.NestedSegWit)
|
||||
addr <- wallet.addressHandling.getNewAddress(AddressType.NestedSegWit)
|
||||
addrDb <- wallet.addressDAO.findAddress(addr).map(_.get)
|
||||
walletKey = addrDb.ecPublicKey
|
||||
|
||||
@ -295,7 +295,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
it must "be able to sign a psbt with our own p2wpkh utxo" in {
|
||||
(wallet: Wallet) =>
|
||||
for {
|
||||
addr <- wallet.getNewAddress(AddressType.SegWit)
|
||||
addr <- wallet.addressHandling.getNewAddress(AddressType.SegWit)
|
||||
addrDb <- wallet.addressDAO.findAddress(addr).map(_.get)
|
||||
walletKey = addrDb.ecPublicKey
|
||||
|
||||
@ -326,7 +326,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
||||
|
||||
it must "get correct txs to broadcast" in { (wallet: Wallet) =>
|
||||
for {
|
||||
addr <- wallet.getNewAddress(AddressType.SegWit)
|
||||
addr <- wallet.addressHandling.getNewAddress(AddressType.SegWit)
|
||||
addrDb <- wallet.addressDAO.findAddress(addr).map(_.get)
|
||||
walletKey = addrDb.ecPublicKey
|
||||
|
||||
|
@ -44,12 +44,9 @@ import java.time.temporal.ChronoUnit
|
||||
import java.util.concurrent.TimeUnit
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.control.NonFatal
|
||||
import scala.util.{Failure, Random, Success}
|
||||
import scala.util.Random
|
||||
|
||||
abstract class Wallet
|
||||
extends NeutrinoHDWalletApi
|
||||
with AddressHandling
|
||||
with WalletLogger {
|
||||
abstract class Wallet extends NeutrinoHDWalletApi with WalletLogger {
|
||||
|
||||
override def keyManager: BIP39KeyManager = {
|
||||
walletConfig.kmConf.toBip39KeyManager
|
||||
@ -104,13 +101,16 @@ abstract class Wallet
|
||||
def fundTxHandling: FundTransactionHandling = FundTransactionHandling(
|
||||
accountHandling = accountHandling,
|
||||
utxoHandling = utxoHandling,
|
||||
addressHandling = this,
|
||||
addressHandling = addressHandling,
|
||||
spendingInfoDAO = spendingInfoDAO,
|
||||
transactionDAO = transactionDAO,
|
||||
keyManager = keyManager
|
||||
)
|
||||
def accountHandling: AccountHandlingApi =
|
||||
AccountHandling(this, walletDAOs)
|
||||
def accountHandling: AccountHandling =
|
||||
AccountHandling(walletDAOs, keyManager)
|
||||
|
||||
def addressHandling: AddressHandling =
|
||||
AddressHandling(accountHandling, walletDAOs)
|
||||
|
||||
protected lazy val transactionProcessing: TransactionProcessingApi = {
|
||||
TransactionProcessing(
|
||||
@ -124,7 +124,7 @@ abstract class Wallet
|
||||
RescanHandling(
|
||||
transactionProcessing = transactionProcessing,
|
||||
accountHandling = accountHandling,
|
||||
addressHandling = this,
|
||||
addressHandling = addressHandling,
|
||||
chainQueryApi = chainQueryApi,
|
||||
nodeApi = nodeApi,
|
||||
walletDAOs = walletDAOs
|
||||
@ -173,6 +173,14 @@ abstract class Wallet
|
||||
Future.successful(this)
|
||||
}
|
||||
|
||||
override def getNewAddress(): Future[BitcoinAddress] = {
|
||||
addressHandling.getNewAddress()
|
||||
}
|
||||
|
||||
override def getNewChangeAddress(): Future[BitcoinAddress] = {
|
||||
addressHandling.getNewChangeAddress()
|
||||
}
|
||||
|
||||
override def getSyncDescriptorOpt(): Future[Option[SyncHeightDescriptor]] = {
|
||||
stateDescriptorDAO.getSyncHeight()
|
||||
}
|
||||
@ -219,7 +227,7 @@ abstract class Wallet
|
||||
blockFilters: Vector[(DoubleSha256DigestBE, GolombFilter)]
|
||||
): Future[Wallet] = {
|
||||
val utxosF = utxoHandling.listUtxos()
|
||||
val spksF = listScriptPubKeys()
|
||||
val spksF = addressHandling.listScriptPubKeys()
|
||||
val blockHashOpt = blockFilters.lastOption.map(_._1)
|
||||
val heightOptF = blockHashOpt match {
|
||||
case Some(blockHash) =>
|
||||
@ -411,7 +419,7 @@ abstract class Wallet
|
||||
val signed = rawTxHelper.signedTx
|
||||
|
||||
val processedTxF = for {
|
||||
ourOuts <- findOurOuts(signed)
|
||||
ourOuts <- addressHandling.findOurOutputs(signed)
|
||||
creditingAmount = rawTxHelper.scriptSigParams.foldLeft(
|
||||
CurrencyUnits.zero
|
||||
)(_ + _.amount)
|
||||
@ -540,7 +548,7 @@ abstract class Wallet
|
||||
.zip(prevTxs)
|
||||
.map(info => info._1.toUTXOInfo(keyManager, info._2))
|
||||
|
||||
changeAddr <- getNewChangeAddress(fromAccount.hdAccount)
|
||||
changeAddr <- accountHandling.getNewChangeAddress(fromAccount.hdAccount)
|
||||
|
||||
output = TransactionOutput(amount, address.scriptPubKey)
|
||||
txBuilder = ShufflingNonInteractiveFinalizer.txBuilderFrom(
|
||||
@ -862,7 +870,7 @@ abstract class Wallet
|
||||
Random.shuffle(spendingInfos).head
|
||||
}
|
||||
|
||||
addr <- getNewChangeAddress()
|
||||
addr <- addressHandling.getNewChangeAddress()
|
||||
childTx <- sendFromOutPoints(Vector(spendingInfo.outPoint), addr, feeRate)
|
||||
} yield childTx
|
||||
}
|
||||
@ -942,62 +950,6 @@ abstract class Wallet
|
||||
}
|
||||
}
|
||||
|
||||
protected def getLastAccountOpt(
|
||||
purpose: HDPurpose
|
||||
): Future[Option[AccountDb]] = {
|
||||
accountDAO
|
||||
.findAll()
|
||||
.map(_.filter(_.hdAccount.purpose == purpose))
|
||||
.map(_.sortBy(_.hdAccount.index))
|
||||
// we want to the most recently created account,
|
||||
// to know what the index of our new account
|
||||
// should be.
|
||||
.map(_.lastOption)
|
||||
}
|
||||
|
||||
/** Creates a new account my reading from our account database, finding the
|
||||
* last account, and then incrementing the account index by one, and then
|
||||
* creating that account
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override def createNewAccount(purpose: HDPurpose): Future[Wallet] = {
|
||||
getLastAccountOpt(purpose).flatMap {
|
||||
case Some(accountDb) =>
|
||||
val hdAccount = accountDb.hdAccount
|
||||
val newAccount = hdAccount.copy(index = hdAccount.index + 1)
|
||||
createNewAccount(newAccount)
|
||||
case None =>
|
||||
createNewAccount(walletConfig.defaultAccount)
|
||||
}
|
||||
}
|
||||
|
||||
// todo: check if there's addresses in the most recent
|
||||
// account before creating new
|
||||
override def createNewAccount(
|
||||
hdAccount: HDAccount
|
||||
): Future[Wallet] = {
|
||||
logger.info(
|
||||
s"Creating new account at index ${hdAccount.index} for purpose ${hdAccount.purpose}"
|
||||
)
|
||||
|
||||
val xpub: ExtPublicKey = {
|
||||
keyManager.deriveXPub(hdAccount) match {
|
||||
case Failure(exception) =>
|
||||
// this won't happen, because we're deriving from a privkey
|
||||
// this should really be changed in the method signature
|
||||
logger.error(s"Unexpected error when deriving xpub: $exception")
|
||||
throw exception
|
||||
case Success(xpub) => xpub
|
||||
}
|
||||
}
|
||||
val newAccountDb = AccountDb(xpub, hdAccount)
|
||||
accountDAO.create(newAccountDb).map { created =>
|
||||
logger.debug(s"Created new account ${created.hdAccount}")
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
override def getWalletName(): Future[String] = {
|
||||
Future.successful(walletConfig.walletName)
|
||||
}
|
||||
|
@ -13,11 +13,10 @@ import org.bitcoins.core.api.keymanager.BIP39KeyManagerApi
|
||||
import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.api.wallet.*
|
||||
import org.bitcoins.core.api.wallet.db.*
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.currency.{CurrencyUnit, Satoshis}
|
||||
import org.bitcoins.core.dlc.accounting.DLCWalletAccounting
|
||||
import org.bitcoins.core.gcs.GolombFilter
|
||||
import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType, HDPurpose}
|
||||
import org.bitcoins.core.hd.{AddressType, HDAccount, HDPurpose}
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.blockchain.Block
|
||||
import org.bitcoins.core.protocol.dlc.models.*
|
||||
@ -35,12 +34,7 @@ import org.bitcoins.core.wallet.builder.{
|
||||
ShufflingNonInteractiveFinalizer
|
||||
}
|
||||
import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerVirtualByte}
|
||||
import org.bitcoins.core.wallet.utxo.{
|
||||
AddressTag,
|
||||
AddressTagName,
|
||||
AddressTagType,
|
||||
TxoState
|
||||
}
|
||||
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
|
||||
import org.bitcoins.crypto.{DoubleSha256DigestBE, Sha256Digest}
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
@ -72,6 +66,8 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
|
||||
override def fundTxHandling: FundTransactionHandlingApi =
|
||||
wallet.fundTxHandling
|
||||
|
||||
override def addressHandling: AddressHandlingApi = wallet.addressHandling
|
||||
def isInitialized: Boolean = synchronized {
|
||||
walletOpt.isDefined
|
||||
}
|
||||
@ -107,6 +103,11 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
Future(wallet).flatMap[T](_)
|
||||
}
|
||||
|
||||
override def getNewAddress(): Future[BitcoinAddress] = delegate(
|
||||
_.getNewAddress())
|
||||
|
||||
override def getNewChangeAddress(): Future[BitcoinAddress] = delegate(
|
||||
_.getNewChangeAddress())
|
||||
override def processBlock(block: Block): Future[Unit] =
|
||||
delegate(_.processBlock(block))
|
||||
|
||||
@ -214,31 +215,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
delegate(_.listUtxos(tag))
|
||||
}
|
||||
|
||||
override def listAddresses(): Future[Vector[AddressDb]] = delegate(
|
||||
_.listAddresses()
|
||||
)
|
||||
|
||||
override def listSpentAddresses(): Future[Vector[AddressDb]] = delegate(
|
||||
_.listSpentAddresses()
|
||||
)
|
||||
|
||||
override def listFundedAddresses()
|
||||
: Future[Vector[(AddressDb, CurrencyUnit)]] = delegate(
|
||||
_.listFundedAddresses()
|
||||
)
|
||||
|
||||
override def listUnusedAddresses(): Future[Vector[AddressDb]] = delegate(
|
||||
_.listUnusedAddresses()
|
||||
)
|
||||
|
||||
override def listScriptPubKeys(): Future[Vector[ScriptPubKeyDb]] = delegate(
|
||||
_.listScriptPubKeys()
|
||||
)
|
||||
|
||||
override def watchScriptPubKey(
|
||||
scriptPubKey: ScriptPubKey
|
||||
): Future[ScriptPubKeyDb] = delegate(_.watchScriptPubKey(scriptPubKey))
|
||||
|
||||
override def markUTXOsAsReserved(
|
||||
utxos: Vector[SpendingInfoDb]
|
||||
): Future[Vector[SpendingInfoDb]] = delegate(_.markUTXOsAsReserved(utxos))
|
||||
@ -257,75 +233,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
|
||||
override def isEmpty(): Future[Boolean] = delegate(_.isEmpty())
|
||||
|
||||
override def getNewAddress(addressType: AddressType): Future[BitcoinAddress] =
|
||||
delegate(_.getNewAddress(addressType))
|
||||
|
||||
override def getNewAddress(account: HDAccount): Future[BitcoinAddress] = {
|
||||
delegate(_.getNewAddress(account))
|
||||
}
|
||||
|
||||
override def getNewAddress(): Future[BitcoinAddress] = delegate(
|
||||
_.getNewAddress()
|
||||
)
|
||||
|
||||
override def getNewAddress(
|
||||
addressType: AddressType,
|
||||
tags: Vector[AddressTag]
|
||||
): Future[BitcoinAddress] = delegate(_.getNewAddress(addressType, tags))
|
||||
|
||||
override def getNewAddress(tags: Vector[AddressTag]): Future[BitcoinAddress] =
|
||||
delegate(_.getNewAddress(tags))
|
||||
|
||||
override def getUnusedAddress(
|
||||
addressType: AddressType
|
||||
): Future[BitcoinAddress] = delegate(_.getUnusedAddress(addressType))
|
||||
|
||||
override def getUnusedAddress: Future[BitcoinAddress] = delegate(
|
||||
_.getUnusedAddress
|
||||
)
|
||||
|
||||
override def getAddressInfo(
|
||||
address: BitcoinAddress
|
||||
): Future[Option[AddressInfo]] = delegate(_.getAddressInfo(address))
|
||||
|
||||
override def tagAddress(
|
||||
address: BitcoinAddress,
|
||||
tag: AddressTag
|
||||
): Future[AddressTagDb] = delegate(_.tagAddress(address, tag))
|
||||
|
||||
override def getAddressTags(
|
||||
address: BitcoinAddress
|
||||
): Future[Vector[AddressTagDb]] = delegate(_.getAddressTags(address))
|
||||
|
||||
override def getAddressTags(
|
||||
address: BitcoinAddress,
|
||||
tagType: AddressTagType
|
||||
): Future[Vector[AddressTagDb]] = delegate(_.getAddressTags(address, tagType))
|
||||
|
||||
override def getAddressTags(): Future[Vector[AddressTagDb]] = delegate(
|
||||
_.getAddressTags()
|
||||
)
|
||||
|
||||
override def getAddressTags(
|
||||
tagType: AddressTagType
|
||||
): Future[Vector[AddressTagDb]] = delegate(_.getAddressTags(tagType))
|
||||
|
||||
override def dropAddressTag(addressTagDb: AddressTagDb): Future[Int] =
|
||||
delegate(_.dropAddressTag(addressTagDb))
|
||||
|
||||
override def dropAddressTagType(addressTagType: AddressTagType): Future[Int] =
|
||||
delegate(_.dropAddressTagType(addressTagType))
|
||||
|
||||
override def dropAddressTagType(
|
||||
address: BitcoinAddress,
|
||||
addressTagType: AddressTagType
|
||||
): Future[Int] = delegate(_.dropAddressTagType(address, addressTagType))
|
||||
|
||||
override def dropAddressTagName(
|
||||
address: BitcoinAddress,
|
||||
tagName: AddressTagName
|
||||
): Future[Int] = delegate(_.dropAddressTagName(address, tagName))
|
||||
|
||||
override def sendFromOutPoints(
|
||||
outPoints: Vector[TransactionOutPoint],
|
||||
address: BitcoinAddress,
|
||||
@ -528,9 +435,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
override def getUnconfirmedBalance(account: HDAccount): Future[CurrencyUnit] =
|
||||
delegate(_.getUnconfirmedBalance(account))
|
||||
|
||||
override def getNewChangeAddress(account: AccountDb): Future[BitcoinAddress] =
|
||||
delegate(_.getNewChangeAddress(account))
|
||||
|
||||
override def getDefaultAccount(): Future[AccountDb] = delegate(
|
||||
_.getDefaultAccount()
|
||||
)
|
||||
@ -610,23 +514,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
_.makeOpReturnCommitment(message, hashMessage, feeRate, fromAccount)
|
||||
)
|
||||
|
||||
override def listAddresses(account: HDAccount): Future[Vector[AddressDb]] =
|
||||
delegate(_.listAddresses(account))
|
||||
|
||||
override def listSpentAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[AddressDb]] = delegate(_.listSpentAddresses(account))
|
||||
|
||||
override def listFundedAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[(AddressDb, CurrencyUnit)]] = delegate(
|
||||
_.listFundedAddresses(account)
|
||||
)
|
||||
|
||||
override def listUnusedAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[AddressDb]] = delegate(_.listUnusedAddresses(account))
|
||||
|
||||
override def clearAllUtxos(): Future[HDWalletApi] = delegate(
|
||||
_.clearAllUtxos()
|
||||
)
|
||||
@ -635,23 +522,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
delegate(_.clearAllAddresses())
|
||||
}
|
||||
|
||||
override def getAddress(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType,
|
||||
addressIndex: Int
|
||||
): Future[AddressDb] = delegate(
|
||||
_.getAddress(account, chainType, addressIndex)
|
||||
)
|
||||
|
||||
override def createNewAccount(
|
||||
purpose: HDPurpose
|
||||
): Future[HDWalletApi] = delegate(_.createNewAccount(purpose))
|
||||
|
||||
override def createNewAccount(hdAccount: HDAccount): Future[HDWalletApi] =
|
||||
delegate(
|
||||
_.createNewAccount(hdAccount)
|
||||
)
|
||||
|
||||
override def getSyncDescriptorOpt(): Future[Option[SyncHeightDescriptor]] =
|
||||
delegate(_.getSyncDescriptorOpt())
|
||||
|
||||
@ -676,12 +546,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[CurrencyUnit] = delegate(_.getBalance(tag))
|
||||
|
||||
override def getAddressInfo(
|
||||
spendingInfoDb: SpendingInfoDb,
|
||||
networkParameters: NetworkParameters
|
||||
): Future[Option[AddressInfo]] =
|
||||
delegate(_.getAddressInfo(spendingInfoDb, networkParameters))
|
||||
|
||||
override def sendFromOutPoints(
|
||||
outPoints: Vector[TransactionOutPoint],
|
||||
address: BitcoinAddress,
|
||||
@ -760,11 +624,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
): Future[NeutrinoHDWalletApi] =
|
||||
delegate(_.processCompactFilter(blockHash, blockFilter))
|
||||
|
||||
override def getNewChangeAddress()(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[BitcoinAddress] =
|
||||
delegate(_.getNewChangeAddress())
|
||||
|
||||
override def sendWithAlgo(
|
||||
address: BitcoinAddress,
|
||||
amount: CurrencyUnit,
|
||||
@ -945,11 +804,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
)(implicit ec: ExecutionContext): Future[Transaction] =
|
||||
delegate(_.makeOpReturnCommitment(message, hashMessage, feeRate))
|
||||
|
||||
override def getAddress(chainType: HDChainType, addressIndex: Int)(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[AddressDb] =
|
||||
delegate(_.getAddress(chainType, addressIndex))
|
||||
|
||||
override def listAccounts(purpose: HDPurpose)(implicit
|
||||
ec: ExecutionContext
|
||||
): Future[Vector[AccountDb]] =
|
||||
@ -1041,14 +895,6 @@ class WalletHolder(initWalletOpt: Option[DLCNeutrinoHDWalletApi])(implicit
|
||||
delegate(_.findOutputsBeingSpent(tx))
|
||||
}
|
||||
|
||||
override def findAccount(account: HDAccount): Future[Option[AccountDb]] = {
|
||||
delegate(_.findAccount(account))
|
||||
}
|
||||
|
||||
override def getNewAddress(account: AccountDb): Future[BitcoinAddress] = {
|
||||
delegate(_.getNewAddress(account))
|
||||
}
|
||||
|
||||
override def findByScriptPubKey(
|
||||
scriptPubKey: ScriptPubKey
|
||||
): Future[Vector[SpendingInfoDb]] = {
|
||||
|
@ -1,8 +1,12 @@
|
||||
package org.bitcoins.wallet.internal
|
||||
|
||||
import org.bitcoins.commons.util.BitcoinSLogger
|
||||
import org.bitcoins.core.api.keymanager.BIP39KeyManagerApi
|
||||
import org.bitcoins.core.api.wallet.AccountHandlingApi
|
||||
import org.bitcoins.core.api.wallet.db.AccountDb
|
||||
import org.bitcoins.core.api.wallet.db.{AccountDb, AddressDb, AddressDbHelper}
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.crypto.ExtPublicKey
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.hd.AddressType.*
|
||||
import org.bitcoins.core.hd.*
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
@ -15,6 +19,7 @@ import org.bitcoins.core.protocol.blockchain.{
|
||||
}
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
import org.bitcoins.db.SafeDatabase
|
||||
import org.bitcoins.wallet.callback.WalletCallbacks
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.wallet.models.{
|
||||
AccountDAO,
|
||||
@ -26,13 +31,14 @@ import org.bitcoins.wallet.models.{
|
||||
import slick.dbio.{DBIOAction, Effect, NoStream}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
/** Provides functionality related enumerating accounts. Account creation does
|
||||
* not happen here, as that requires an unlocked wallet.
|
||||
*/
|
||||
case class AccountHandling(
|
||||
addressHandling: AddressHandling,
|
||||
walletDAOs: WalletDAOs)(implicit
|
||||
walletDAOs: WalletDAOs,
|
||||
keyManager: BIP39KeyManagerApi)(implicit
|
||||
walletConfig: WalletAppConfig,
|
||||
ec: ExecutionContext)
|
||||
extends AccountHandlingApi
|
||||
@ -43,6 +49,49 @@ case class AccountHandling(
|
||||
private val scriptPubKeyDAO: ScriptPubKeyDAO = walletDAOs.scriptPubKeyDAO
|
||||
private val safeDatabase: SafeDatabase = spendingInfoDAO.safeDatabase
|
||||
private val chainParams: ChainParams = walletConfig.chain
|
||||
private val networkParameters: NetworkParameters = walletConfig.network
|
||||
private def walletCallbacks: WalletCallbacks = walletConfig.callBacks
|
||||
|
||||
override def createNewAccount(
|
||||
hdAccount: HDAccount
|
||||
): Future[ExtPublicKey] = {
|
||||
logger.info(
|
||||
s"Creating new account at index ${hdAccount.index} for purpose ${hdAccount.purpose}"
|
||||
)
|
||||
|
||||
val xpub: ExtPublicKey = {
|
||||
keyManager.deriveXPub(hdAccount) match {
|
||||
case Failure(exception) =>
|
||||
// this won't happen, because we're deriving from a privkey
|
||||
// this should really be changed in the method signature
|
||||
logger.error(s"Unexpected error when deriving xpub: $exception")
|
||||
throw exception
|
||||
case Success(xpub) => xpub
|
||||
}
|
||||
}
|
||||
val newAccountDb = AccountDb(xpub, hdAccount)
|
||||
accountDAO.create(newAccountDb).map { created =>
|
||||
logger.debug(s"Created new account ${created.hdAccount}")
|
||||
created.xpub
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates a new account my reading from our account database, finding the
|
||||
* last account, and then incrementing the account index by one, and then
|
||||
* creating that account
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
override def createNewAccount(purpose: HDPurpose): Future[ExtPublicKey] = {
|
||||
getLastAccountOpt(purpose).flatMap {
|
||||
case Some(accountDb) =>
|
||||
val hdAccount = accountDb.hdAccount
|
||||
val newAccount = hdAccount.copy(index = hdAccount.index + 1)
|
||||
createNewAccount(newAccount)
|
||||
case None =>
|
||||
createNewAccount(walletConfig.defaultAccount)
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def listAccounts(): Future[Vector[AccountDb]] =
|
||||
@ -168,7 +217,7 @@ case class AccountHandling(
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
DBIOAction.sequence {
|
||||
1.to(addressBatchSize)
|
||||
.map(_ => addressHandling.getNewAddressAction(account))
|
||||
.map(_ => getNewAddressAction(account))
|
||||
}
|
||||
}.map(_.toVector)
|
||||
|
||||
@ -180,7 +229,7 @@ case class AccountHandling(
|
||||
Effect.Read with Effect.Write with Effect.Transactional] = {
|
||||
DBIOAction.sequence {
|
||||
1.to(addressBatchSize)
|
||||
.map(_ => addressHandling.getNewChangeAddressAction(account))
|
||||
.map(_ => getNewChangeAddressAction(account))
|
||||
}
|
||||
}.map(_.toVector)
|
||||
|
||||
@ -190,6 +239,247 @@ case class AccountHandling(
|
||||
} yield receiveAddresses ++ changeAddresses
|
||||
}
|
||||
|
||||
override def getNewChangeAddress(
|
||||
account: AccountDb): Future[BitcoinAddress] = {
|
||||
val action = getNewChangeAddressAction(account)
|
||||
safeDatabase.run(action)
|
||||
}
|
||||
|
||||
/** Queues a request to generate an address and returns a Future that will be
|
||||
* completed when the request is processed in the queue. If the queue is full
|
||||
* it throws an exception.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
override def getNewAddress(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType
|
||||
): Future[BitcoinAddress] = {
|
||||
val action = getNewAddressHelperAction(account, chainType)
|
||||
safeDatabase.run(action)
|
||||
}
|
||||
|
||||
def getNewAddressAction(account: HDAccount): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
val accountDbOptA = findAccountAction(account)
|
||||
accountDbOptA.flatMap {
|
||||
case Some(accountDb) => getNewAddressAction(accountDb)
|
||||
case None =>
|
||||
DBIOAction.failed(
|
||||
new RuntimeException(
|
||||
s"No account found for given hdaccount=${account}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def getNewChangeAddressAction(account: HDAccount): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
val accountDbOptA = findAccountAction(account)
|
||||
accountDbOptA.flatMap {
|
||||
case Some(accountDb) => getNewChangeAddressAction(accountDb)
|
||||
case None =>
|
||||
DBIOAction.failed(
|
||||
new RuntimeException(
|
||||
s"No account found for given hdaccount=${account}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override def getNewAddress(account: AccountDb): Future[BitcoinAddress] = {
|
||||
val action = getNewAddressAction(account)
|
||||
safeDatabase.run(action)
|
||||
}
|
||||
|
||||
def getNewAddressAction(account: AccountDb): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
getNewAddressHelperAction(account, HDChainType.External)
|
||||
}
|
||||
|
||||
def getNewChangeAddressAction(account: AccountDb): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
getNewAddressHelperAction(account, HDChainType.Change)
|
||||
}
|
||||
|
||||
private def findAccountAction(
|
||||
account: HDAccount
|
||||
): DBIOAction[Option[AccountDb], NoStream, Effect.Read] = {
|
||||
accountDAO.findByAccountAction(account)
|
||||
}
|
||||
|
||||
override def findAccount(account: HDAccount): Future[Option[AccountDb]] = {
|
||||
safeDatabase.run(findAccountAction(account))
|
||||
}
|
||||
|
||||
override def listUnusedAddresses(
|
||||
account: HDAccount): Future[Vector[AddressDb]] = {
|
||||
val unusedAddressesF = addressDAO.getUnusedAddresses
|
||||
unusedAddressesF.map { unusedAddresses =>
|
||||
unusedAddresses.filter(addr =>
|
||||
HDAccount.isSameAccount(addr.path, account))
|
||||
}
|
||||
}
|
||||
|
||||
override def listAddresses(account: HDAccount): Future[Vector[AddressDb]] = {
|
||||
val allAddressesF: Future[Vector[AddressDb]] = addressDAO.findAllAddresses()
|
||||
|
||||
val accountAddressesF = {
|
||||
allAddressesF.map { addresses =>
|
||||
addresses.filter { a =>
|
||||
logger.debug(s"a.path=${a.path} account=${account}")
|
||||
HDAccount.isSameAccount(a.path, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
accountAddressesF
|
||||
}
|
||||
|
||||
override def listSpentAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[AddressDb]] = {
|
||||
val spentAddressesF = addressDAO.getSpentAddresses
|
||||
|
||||
spentAddressesF.map { spentAddresses =>
|
||||
spentAddresses.filter(addr => HDAccount.isSameAccount(addr.path, account))
|
||||
}
|
||||
}
|
||||
|
||||
override def listFundedAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[(AddressDb, CurrencyUnit)]] = {
|
||||
val spentAddressesF = addressDAO.getFundedAddresses
|
||||
|
||||
spentAddressesF.map { spentAddresses =>
|
||||
spentAddresses.filter(addr =>
|
||||
HDAccount.isSameAccount(addr._1.path, account))
|
||||
}
|
||||
}
|
||||
|
||||
private def getNewAddressHelperAction(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType
|
||||
): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
logger.debug(s"Processing $account $chainType in our address request queue")
|
||||
val resultA: DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = for {
|
||||
addressDb <- getNewAddressDbAction(account, chainType)
|
||||
writtenAddressDb <- addressDAO.createAction(addressDb)
|
||||
} yield {
|
||||
logger.info(
|
||||
s"Generated new address=${addressDb.address} path=${addressDb.path} isChange=${addressDb.isChange}"
|
||||
)
|
||||
writtenAddressDb.address
|
||||
}
|
||||
|
||||
val callbackExecuted = resultA.flatMap { address =>
|
||||
val executedF =
|
||||
walletCallbacks.executeOnNewAddressGenerated(address)
|
||||
DBIOAction
|
||||
.from(executedF)
|
||||
.map(_ => address)
|
||||
}
|
||||
|
||||
callbackExecuted
|
||||
}
|
||||
|
||||
private def getNewAddressDbAction(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType
|
||||
): DBIOAction[AddressDb, NoStream, Effect.Read] = {
|
||||
logger.debug(s"Getting new $chainType adddress for ${account.hdAccount}")
|
||||
|
||||
val lastAddrOptA = chainType match {
|
||||
case HDChainType.External =>
|
||||
addressDAO.findMostRecentExternalAction(account.hdAccount)
|
||||
case HDChainType.Change =>
|
||||
addressDAO.findMostRecentChangeAction(account.hdAccount)
|
||||
}
|
||||
|
||||
lastAddrOptA.map { lastAddrOpt =>
|
||||
val addrPath: HDPath = lastAddrOpt match {
|
||||
case Some(addr) =>
|
||||
val next = addr.path.next
|
||||
logger.debug(
|
||||
s"Found previous address at path=${addr.path}, next=$next"
|
||||
)
|
||||
next
|
||||
case None =>
|
||||
val address = account.hdAccount
|
||||
.toChain(chainType)
|
||||
.toHDAddress(0)
|
||||
|
||||
val path = address.toPath
|
||||
logger.debug(s"Did not find previous address, next=$path")
|
||||
path
|
||||
}
|
||||
|
||||
val pathDiff =
|
||||
account.hdAccount.diff(addrPath) match {
|
||||
case Some(value) => value
|
||||
case None =>
|
||||
throw new RuntimeException(
|
||||
s"Could not diff ${account.hdAccount} and $addrPath"
|
||||
)
|
||||
}
|
||||
|
||||
val pubkey = account.xpub.deriveChildPubKey(pathDiff) match {
|
||||
case Failure(exception) => throw exception
|
||||
case Success(value) => value.key
|
||||
}
|
||||
|
||||
addrPath match {
|
||||
case segwitPath: SegWitHDPath =>
|
||||
AddressDbHelper
|
||||
.getSegwitAddress(pubkey, segwitPath, networkParameters)
|
||||
case legacyPath: LegacyHDPath =>
|
||||
AddressDbHelper.getLegacyAddress(
|
||||
pubkey,
|
||||
legacyPath,
|
||||
networkParameters
|
||||
)
|
||||
case nestedPath: NestedSegWitHDPath =>
|
||||
AddressDbHelper.getNestedSegwitAddress(
|
||||
pubkey,
|
||||
nestedPath,
|
||||
networkParameters
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected def getLastAccountOpt(
|
||||
purpose: HDPurpose
|
||||
): Future[Option[AccountDb]] = {
|
||||
accountDAO
|
||||
.findAll()
|
||||
.map(_.filter(_.hdAccount.purpose == purpose))
|
||||
.map(_.sortBy(_.hdAccount.index))
|
||||
// we want to the most recently created account,
|
||||
// to know what the index of our new account
|
||||
// should be.
|
||||
.map(_.lastOption)
|
||||
}
|
||||
|
||||
/** 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,10 +1,15 @@
|
||||
package org.bitcoins.wallet.internal
|
||||
|
||||
import org.bitcoins.core.api.wallet
|
||||
import org.bitcoins.core.api.wallet.AddressInfo
|
||||
import org.bitcoins.core.api.wallet.db._
|
||||
import org.bitcoins.core.api.wallet.{
|
||||
AccountHandlingApi,
|
||||
AddressHandlingApi,
|
||||
AddressInfo
|
||||
}
|
||||
import org.bitcoins.core.api.wallet.db.*
|
||||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
import org.bitcoins.core.hd._
|
||||
import org.bitcoins.core.hd.*
|
||||
import org.bitcoins.core.number.UInt32
|
||||
import org.bitcoins.core.protocol.BitcoinAddress
|
||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||
@ -18,26 +23,40 @@ import org.bitcoins.core.wallet.utxo.{
|
||||
AddressTagName,
|
||||
AddressTagType
|
||||
}
|
||||
import org.bitcoins.crypto.ECPublicKey
|
||||
import org.bitcoins.wallet._
|
||||
import slick.dbio.{DBIOAction, Effect, NoStream}
|
||||
import org.bitcoins.wallet.*
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.wallet.models.{
|
||||
AddressDAO,
|
||||
AddressTagDAO,
|
||||
ScriptPubKeyDAO,
|
||||
WalletDAOs
|
||||
}
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
/** Provides functionality related to addresses. This includes enumeratng and
|
||||
* creating them, primarily.
|
||||
*/
|
||||
private[wallet] trait AddressHandling extends WalletLogger {
|
||||
self: Wallet =>
|
||||
case class AddressHandling(
|
||||
accountHandling: AccountHandlingApi,
|
||||
walletDAOs: WalletDAOs)(implicit
|
||||
walletConfig: WalletAppConfig,
|
||||
ec: ExecutionContext)
|
||||
extends AddressHandlingApi
|
||||
with WalletLogger {
|
||||
private val addressDAO: AddressDAO = walletDAOs.addressDAO
|
||||
private val addressTagDAO: AddressTagDAO = walletDAOs.addressTagDAO
|
||||
private val scriptPubKeyDAO: ScriptPubKeyDAO = walletDAOs.scriptPubKeyDAO
|
||||
private val networkParameters: NetworkParameters = walletConfig.network
|
||||
|
||||
def contains(
|
||||
address: BitcoinAddress,
|
||||
accountOpt: Option[HDAccount]
|
||||
accountOpt: Option[(AccountHandlingApi, HDAccount)]
|
||||
): Future[Boolean] = {
|
||||
val possibleAddressesF = accountOpt match {
|
||||
case Some(account) =>
|
||||
listAddresses(account)
|
||||
case Some((ah, account)) =>
|
||||
ah.listAddresses(account)
|
||||
case None =>
|
||||
listAddresses()
|
||||
}
|
||||
@ -50,66 +69,19 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||
override def listAddresses(): Future[Vector[AddressDb]] =
|
||||
addressDAO.findAllAddresses()
|
||||
|
||||
override def listAddresses(account: HDAccount): Future[Vector[AddressDb]] = {
|
||||
val allAddressesF: Future[Vector[AddressDb]] = listAddresses()
|
||||
|
||||
val accountAddressesF = {
|
||||
allAddressesF.map { addresses =>
|
||||
addresses.filter { a =>
|
||||
logger.info(s"a.path=${a.path} account=${account}")
|
||||
HDAccount.isSameAccount(a.path, account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
accountAddressesF
|
||||
}
|
||||
|
||||
override def listSpentAddresses(): Future[Vector[AddressDb]] = {
|
||||
addressDAO.getSpentAddresses
|
||||
}
|
||||
|
||||
override def listSpentAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[AddressDb]] = {
|
||||
val spentAddressesF = addressDAO.getSpentAddresses
|
||||
|
||||
spentAddressesF.map { spentAddresses =>
|
||||
spentAddresses.filter(addr => HDAccount.isSameAccount(addr.path, account))
|
||||
}
|
||||
}
|
||||
|
||||
override def listFundedAddresses()
|
||||
: Future[Vector[(AddressDb, CurrencyUnit)]] = {
|
||||
addressDAO.getFundedAddresses
|
||||
}
|
||||
|
||||
override def listFundedAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[(AddressDb, CurrencyUnit)]] = {
|
||||
val spentAddressesF = addressDAO.getFundedAddresses
|
||||
|
||||
spentAddressesF.map { spentAddresses =>
|
||||
spentAddresses.filter(addr =>
|
||||
HDAccount.isSameAccount(addr._1.path, account))
|
||||
}
|
||||
}
|
||||
|
||||
override def listUnusedAddresses(): Future[Vector[AddressDb]] = {
|
||||
addressDAO.getUnusedAddresses
|
||||
}
|
||||
|
||||
override def listUnusedAddresses(
|
||||
account: HDAccount
|
||||
): Future[Vector[AddressDb]] = {
|
||||
val unusedAddressesF = addressDAO.getUnusedAddresses
|
||||
|
||||
unusedAddressesF.map { unusedAddresses =>
|
||||
unusedAddresses.filter(addr =>
|
||||
HDAccount.isSameAccount(addr.path, account))
|
||||
}
|
||||
}
|
||||
|
||||
override def listScriptPubKeys(): Future[Vector[ScriptPubKeyDb]] =
|
||||
scriptPubKeyDAO.findAll()
|
||||
|
||||
@ -118,92 +90,20 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||
): Future[ScriptPubKeyDb] =
|
||||
scriptPubKeyDAO.createIfNotExists(ScriptPubKeyDb(scriptPubKey))
|
||||
|
||||
/** Enumerates the public keys in this wallet */
|
||||
protected[wallet] def listPubkeys(): Future[Vector[ECPublicKey]] =
|
||||
addressDAO.findAllPubkeys()
|
||||
|
||||
/** Enumerates the scriptPubKeys in this wallet */
|
||||
protected[wallet] def listSPKs(): Future[Vector[ScriptPubKey]] =
|
||||
addressDAO.findAllSPKs()
|
||||
|
||||
/** Given a transaction, returns the outputs (with their corresponding
|
||||
* outpoints) that pay to this wallet
|
||||
*/
|
||||
def findOurOuts(
|
||||
def findOurOutputs(
|
||||
transaction: Transaction
|
||||
): Future[Vector[(TransactionOutput, TransactionOutPoint)]] =
|
||||
for {
|
||||
spks <- listSPKs()
|
||||
spks <- listScriptPubKeys()
|
||||
} yield transaction.outputs.zipWithIndex.collect {
|
||||
case (out, index) if spks.contains(out.scriptPubKey) =>
|
||||
case (out, index)
|
||||
if spks.map(_.scriptPubKey).contains(out.scriptPubKey) =>
|
||||
(out, TransactionOutPoint(transaction.txId, UInt32(index)))
|
||||
}.toVector
|
||||
|
||||
private def getNewAddressDbAction(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType
|
||||
): DBIOAction[AddressDb, NoStream, Effect.Read] = {
|
||||
logger.debug(s"Getting new $chainType adddress for ${account.hdAccount}")
|
||||
|
||||
val lastAddrOptA = chainType match {
|
||||
case HDChainType.External =>
|
||||
addressDAO.findMostRecentExternalAction(account.hdAccount)
|
||||
case HDChainType.Change =>
|
||||
addressDAO.findMostRecentChangeAction(account.hdAccount)
|
||||
}
|
||||
|
||||
lastAddrOptA.map { lastAddrOpt =>
|
||||
val addrPath: HDPath = lastAddrOpt match {
|
||||
case Some(addr) =>
|
||||
val next = addr.path.next
|
||||
logger.debug(
|
||||
s"Found previous address at path=${addr.path}, next=$next"
|
||||
)
|
||||
next
|
||||
case None =>
|
||||
val address = account.hdAccount
|
||||
.toChain(chainType)
|
||||
.toHDAddress(0)
|
||||
|
||||
val path = address.toPath
|
||||
logger.debug(s"Did not find previous address, next=$path")
|
||||
path
|
||||
}
|
||||
|
||||
val pathDiff =
|
||||
account.hdAccount.diff(addrPath) match {
|
||||
case Some(value) => value
|
||||
case None =>
|
||||
throw new RuntimeException(
|
||||
s"Could not diff ${account.hdAccount} and $addrPath"
|
||||
)
|
||||
}
|
||||
|
||||
val pubkey = account.xpub.deriveChildPubKey(pathDiff) match {
|
||||
case Failure(exception) => throw exception
|
||||
case Success(value) => value.key
|
||||
}
|
||||
|
||||
addrPath match {
|
||||
case segwitPath: SegWitHDPath =>
|
||||
AddressDbHelper
|
||||
.getSegwitAddress(pubkey, segwitPath, networkParameters)
|
||||
case legacyPath: LegacyHDPath =>
|
||||
AddressDbHelper.getLegacyAddress(
|
||||
pubkey,
|
||||
legacyPath,
|
||||
networkParameters
|
||||
)
|
||||
case nestedPath: NestedSegWitHDPath =>
|
||||
AddressDbHelper.getNestedSegwitAddress(
|
||||
pubkey,
|
||||
nestedPath,
|
||||
networkParameters
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Derives a new address in the wallet for the given account and chain type
|
||||
* (change/external). After deriving the address it inserts it into our table
|
||||
* of addresses.
|
||||
@ -220,55 +120,10 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||
account: AccountDb,
|
||||
chainType: HDChainType
|
||||
): Future[AddressDb] = {
|
||||
val action = getNewAddressDbAction(account, chainType)
|
||||
safeDatabase.run(action)
|
||||
}
|
||||
|
||||
private def getNewAddressHelperAction(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType
|
||||
): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
logger.debug(s"Processing $account $chainType in our address request queue")
|
||||
val resultA: DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = for {
|
||||
addressDb <- getNewAddressDbAction(account, chainType)
|
||||
writtenAddressDb <- addressDAO.createAction(addressDb)
|
||||
} yield {
|
||||
logger.info(
|
||||
s"Generated new address=${addressDb.address} path=${addressDb.path} isChange=${addressDb.isChange}"
|
||||
)
|
||||
writtenAddressDb.address
|
||||
}
|
||||
|
||||
val callbackExecuted = resultA.flatMap { address =>
|
||||
val executedF =
|
||||
walletCallbacks.executeOnNewAddressGenerated(address)
|
||||
DBIOAction
|
||||
.from(executedF)
|
||||
.map(_ => address)
|
||||
}
|
||||
|
||||
callbackExecuted
|
||||
}
|
||||
|
||||
/** Queues a request to generate an address and returns a Future that will be
|
||||
* completed when the request is processed in the queue. If the queue is full
|
||||
* it throws an exception.
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
private def getNewAddressHelper(
|
||||
account: AccountDb,
|
||||
chainType: HDChainType
|
||||
): Future[BitcoinAddress] = {
|
||||
val action = getNewAddressHelperAction(account, chainType)
|
||||
safeDatabase.run(action)
|
||||
accountHandling
|
||||
.getNewAddress(account, chainType)
|
||||
.flatMap(addr => addressDAO.findAddress(addr))
|
||||
.map(_.get)
|
||||
}
|
||||
|
||||
def getNextAvailableIndex(
|
||||
@ -278,73 +133,6 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||
getNewAddressDb(accountDb, chainType).map(_.path.path.last.index)
|
||||
}
|
||||
|
||||
def getNewAddressAction(account: HDAccount): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
val accountDbOptA = findAccountAction(account)
|
||||
accountDbOptA.flatMap {
|
||||
case Some(accountDb) => getNewAddressAction(accountDb)
|
||||
case None =>
|
||||
DBIOAction.failed(
|
||||
new RuntimeException(
|
||||
s"No account found for given hdaccount=${account}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def getNewChangeAddressAction(account: HDAccount): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
val accountDbOptA = findAccountAction(account)
|
||||
accountDbOptA.flatMap {
|
||||
case Some(accountDb) => getNewChangeAddressAction(accountDb)
|
||||
case None =>
|
||||
DBIOAction.failed(
|
||||
new RuntimeException(
|
||||
s"No account found for given hdaccount=${account}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def getNewAddress(account: HDAccount): Future[BitcoinAddress] = {
|
||||
val accountDbOptF = findAccount(account)
|
||||
accountDbOptF.flatMap {
|
||||
case Some(accountDb) => getNewAddress(accountDb)
|
||||
case None =>
|
||||
Future.failed(
|
||||
new RuntimeException(
|
||||
s"No account found for given hdaccount=${account}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def getNewAddressAction(account: AccountDb): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
getNewAddressHelperAction(account, HDChainType.External)
|
||||
}
|
||||
|
||||
def getNewChangeAddressAction(account: AccountDb): DBIOAction[
|
||||
BitcoinAddress,
|
||||
NoStream,
|
||||
Effect.Read with Effect.Write with Effect.Transactional
|
||||
] = {
|
||||
getNewAddressHelperAction(account, HDChainType.Change)
|
||||
}
|
||||
|
||||
def getNewAddress(account: AccountDb): Future[BitcoinAddress] = {
|
||||
safeDatabase.run(getNewAddressAction(account))
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getNewAddress(): Future[BitcoinAddress] = {
|
||||
getNewAddress(walletConfig.defaultAddressType)
|
||||
@ -357,6 +145,13 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||
getNewAddress(walletConfig.defaultAddressType, tags)
|
||||
}
|
||||
|
||||
override def getNewChangeAddress(): Future[BitcoinAddress] = {
|
||||
for {
|
||||
account <- accountHandling.getDefaultAccount()
|
||||
addr <- accountHandling.getNewChangeAddress(account)
|
||||
} yield addr
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
def getAddress(
|
||||
account: AccountDb,
|
||||
@ -434,11 +229,11 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||
/** @inheritdoc */
|
||||
def getUnusedAddress(addressType: AddressType): Future[BitcoinAddress] = {
|
||||
for {
|
||||
account <- getDefaultAccountForType(addressType)
|
||||
account <- accountHandling.getDefaultAccountForType(addressType)
|
||||
addresses <- addressDAO.getUnusedAddresses(account.hdAccount)
|
||||
address <-
|
||||
if (addresses.isEmpty) {
|
||||
getNewAddress(account.hdAccount)
|
||||
accountHandling.getNewAddress(account.hdAccount)
|
||||
} else {
|
||||
Future.successful(addresses.head.address)
|
||||
}
|
||||
@ -448,34 +243,24 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||
/** @inheritdoc */
|
||||
def getUnusedAddress: Future[BitcoinAddress] = {
|
||||
for {
|
||||
account <- getDefaultAccount()
|
||||
account <- accountHandling.getDefaultAccount()
|
||||
addresses <- addressDAO.getUnusedAddresses(account.hdAccount)
|
||||
address <-
|
||||
if (addresses.isEmpty) {
|
||||
getNewAddress(account.hdAccount)
|
||||
accountHandling.getNewAddress(account.hdAccount)
|
||||
} else {
|
||||
Future.successful(addresses.head.address)
|
||||
}
|
||||
} yield address
|
||||
}
|
||||
|
||||
def findAccountAction(
|
||||
account: HDAccount
|
||||
): DBIOAction[Option[AccountDb], NoStream, Effect.Read] = {
|
||||
accountDAO.findByAccountAction(account)
|
||||
}
|
||||
|
||||
override def findAccount(account: HDAccount): Future[Option[AccountDb]] = {
|
||||
safeDatabase.run(findAccountAction(account))
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getNewAddress(
|
||||
addressType: AddressType
|
||||
): Future[BitcoinAddress] = {
|
||||
for {
|
||||
account <- getDefaultAccountForType(addressType)
|
||||
address <- getNewAddressHelper(account, HDChainType.External)
|
||||
account <- accountHandling.getDefaultAccountForType(addressType)
|
||||
address <- accountHandling.getNewAddress(account, HDChainType.External)
|
||||
} yield address
|
||||
}
|
||||
|
||||
@ -485,32 +270,14 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||
tags: Vector[AddressTag]
|
||||
): Future[BitcoinAddress] = {
|
||||
for {
|
||||
account <- getDefaultAccountForType(addressType)
|
||||
address <- getNewAddressHelper(account, HDChainType.External)
|
||||
account <- accountHandling.getDefaultAccountForType(addressType)
|
||||
address <- accountHandling.getNewAddress(account, HDChainType.External)
|
||||
|
||||
tagDbs = tags.map(tag => AddressTagDb(address, tag))
|
||||
_ <- addressTagDAO.createAll(tagDbs)
|
||||
} yield address
|
||||
}
|
||||
|
||||
/** Generates a new change address */
|
||||
override def getNewChangeAddress(
|
||||
account: AccountDb
|
||||
): Future[BitcoinAddress] = {
|
||||
getNewAddressHelper(account, HDChainType.Change)
|
||||
}
|
||||
|
||||
def getNewChangeAddress(account: HDAccount): Future[BitcoinAddress] = {
|
||||
val accountDbOptF = findAccount(account)
|
||||
accountDbOptF.flatMap {
|
||||
case Some(accountDb) => getNewChangeAddress(accountDb)
|
||||
case None =>
|
||||
Future.failed(
|
||||
new RuntimeException(s"No account found for given hdaccount=$account")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def getAddressInfo(
|
||||
address: BitcoinAddress
|
||||
|
@ -19,9 +19,9 @@ import slick.dbio.{DBIO, DBIOAction, Effect, NoStream}
|
||||
import scala.concurrent.Future
|
||||
|
||||
case class FundTransactionHandling(
|
||||
accountHandling: AccountHandlingApi,
|
||||
accountHandling: AccountHandling,
|
||||
utxoHandling: UtxoHandling,
|
||||
addressHandling: AddressHandling,
|
||||
addressHandling: AddressHandlingApi,
|
||||
spendingInfoDAO: SpendingInfoDAO,
|
||||
transactionDAO: TransactionDAO,
|
||||
keyManager: BIP39KeyManagerApi)(implicit
|
||||
@ -191,7 +191,7 @@ case class FundTransactionHandling(
|
||||
|
||||
for {
|
||||
(selectedUtxos, callbackF) <- selectedUtxosA
|
||||
change <- addressHandling.getNewChangeAddressAction(fromAccount)
|
||||
change <- accountHandling.getNewChangeAddressAction(fromAccount)
|
||||
utxoSpendingInfos = {
|
||||
selectedUtxos.map { case (utxo, prevTx) =>
|
||||
utxo.toUTXOInfo(keyManager = keyManager, prevTx)
|
||||
|
@ -12,6 +12,7 @@ import org.bitcoins.core.api.node.NodeApi
|
||||
import org.bitcoins.core.api.wallet.NeutrinoWalletApi.BlockMatchingResponse
|
||||
import org.bitcoins.core.api.wallet.{
|
||||
AccountHandlingApi,
|
||||
AddressHandlingApi,
|
||||
RescanHandlingApi,
|
||||
TransactionProcessingApi
|
||||
}
|
||||
@ -41,7 +42,7 @@ import scala.util.{Failure, Success}
|
||||
case class RescanHandling(
|
||||
transactionProcessing: TransactionProcessingApi,
|
||||
accountHandling: AccountHandlingApi,
|
||||
addressHandling: AddressHandling,
|
||||
addressHandling: AddressHandlingApi,
|
||||
chainQueryApi: ChainQueryApi,
|
||||
nodeApi: NodeApi,
|
||||
walletDAOs: WalletDAOs)(implicit
|
||||
|
Loading…
Reference in New Issue
Block a user