Create HDWalletApi (#1693)

* Create HDWalletApi

* Rename createWallet to createHDWallet, add listDefaultAccountUtxos function

* Change return types to HDWalletApi

* Fix warning
This commit is contained in:
Ben Carman 2020-07-27 14:06:48 -05:00 committed by GitHub
parent b2b9ca7eec
commit 4abcf3f321
14 changed files with 622 additions and 360 deletions

View file

@ -2,7 +2,7 @@ package org.bitcoins.wallet
import org.bitcoins.core.hd.AddressType import org.bitcoins.core.hd.AddressType
import org.bitcoins.core.protocol.BitcoinAddress import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.wallet.api.WalletApi import org.bitcoins.wallet.api.HDWalletApi
import org.bitcoins.wallet.models.AccountDb import org.bitcoins.wallet.models.AccountDb
import scala.concurrent.Future import scala.concurrent.Future
@ -11,7 +11,7 @@ import scala.concurrent.Future
* ScalaMock cannot stub traits with protected methods, * ScalaMock cannot stub traits with protected methods,
* so we need to stub them manually. * so we need to stub them manually.
*/ */
abstract class MockWalletApi extends WalletApi { abstract class MockWalletApi extends HDWalletApi {
override protected[wallet] def getNewChangeAddress( override protected[wallet] def getNewChangeAddress(
account: AccountDb): Future[BitcoinAddress] = stub account: AccountDb): Future[BitcoinAddress] = stub

View file

@ -89,7 +89,7 @@ object Main extends App with BitcoinSLogger {
uninitializedNode <- uninitializedNodeF uninitializedNode <- uninitializedNodeF
chainApi <- chainApiF chainApi <- chainApiF
_ = logger.info("Initialized chain api") _ = logger.info("Initialized chain api")
wallet <- walletConf.createWallet(uninitializedNode, wallet <- walletConf.createHDWallet(uninitializedNode,
chainApi, chainApi,
BitcoinerLiveFeeRateProvider(60), BitcoinerLiveFeeRateProvider(60),
bip39PasswordOpt) bip39PasswordOpt)
@ -233,7 +233,7 @@ object Main extends App with BitcoinSLogger {
private def startHttpServer( private def startHttpServer(
node: Node, node: Node,
wallet: WalletApi, wallet: HDWalletApi,
rpcPortOpt: Option[Int])(implicit rpcPortOpt: Option[Int])(implicit
system: ActorSystem, system: ActorSystem,
conf: BitcoinSAppConfig): Future[Http.ServerBinding] = { conf: BitcoinSAppConfig): Future[Http.ServerBinding] = {

View file

@ -6,12 +6,12 @@ import akka.http.scaladsl.server._
import org.bitcoins.commons.serializers.Picklers._ import org.bitcoins.commons.serializers.Picklers._
import org.bitcoins.core.currency._ import org.bitcoins.core.currency._
import org.bitcoins.node.Node import org.bitcoins.node.Node
import org.bitcoins.wallet.api.WalletApi import org.bitcoins.wallet.api.HDWalletApi
import scala.concurrent.Future import scala.concurrent.Future
import scala.util.{Failure, Success} import scala.util.{Failure, Success}
case class WalletRoutes(wallet: WalletApi, node: Node)(implicit case class WalletRoutes(wallet: HDWalletApi, node: Node)(implicit
system: ActorSystem) system: ActorSystem)
extends ServerRoute { extends ServerRoute {
import system.dispatcher import system.dispatcher

View file

@ -92,7 +92,7 @@ class NeutrinoNodeWithWalletTest extends NodeUnitTest {
confirmedBalance <- wallet.getConfirmedBalance() confirmedBalance <- wallet.getConfirmedBalance()
unconfirmedBalance <- wallet.getUnconfirmedBalance() unconfirmedBalance <- wallet.getUnconfirmedBalance()
addresses <- wallet.listAddresses() addresses <- wallet.listAddresses()
utxos <- wallet.listUtxos() utxos <- wallet.listDefaultAccountUtxos()
} yield { } yield {
(expectedConfirmedAmount == confirmedBalance) && (expectedConfirmedAmount == confirmedBalance) &&
(expectedUnconfirmedAmount == unconfirmedBalance) && (expectedUnconfirmedAmount == unconfirmedBalance) &&
@ -175,7 +175,7 @@ class NeutrinoNodeWithWalletTest extends NodeUnitTest {
for { for {
addresses <- wallet.listAddresses() addresses <- wallet.listAddresses()
utxos <- wallet.listUtxos() utxos <- wallet.listDefaultAccountUtxos()
_ = assert(addresses.size == 6) _ = assert(addresses.size == 6)
_ = assert(utxos.size == 3) _ = assert(utxos.size == 3)
@ -189,14 +189,14 @@ class NeutrinoNodeWithWalletTest extends NodeUnitTest {
.sendToAddress(address, TestAmount) .sendToAddress(address, TestAmount)
addresses <- wallet.listAddresses() addresses <- wallet.listAddresses()
utxos <- wallet.listUtxos() utxos <- wallet.listDefaultAccountUtxos()
_ = assert(addresses.size == 7) _ = assert(addresses.size == 7)
_ = assert(utxos.size == 3) _ = assert(utxos.size == 3)
_ <- wallet.clearAllUtxosAndAddresses() _ <- wallet.clearAllUtxosAndAddresses()
addresses <- wallet.listAddresses() addresses <- wallet.listAddresses()
utxos <- wallet.listUtxos() utxos <- wallet.listDefaultAccountUtxos()
_ = assert(addresses.isEmpty) _ = assert(addresses.isEmpty)
_ = assert(utxos.isEmpty) _ = assert(utxos.isEmpty)

View file

@ -3,17 +3,16 @@ package org.bitcoins.wallet
import org.bitcoins.core.hd.{AddressType, HDPurposes} import org.bitcoins.core.hd.{AddressType, HDPurposes}
import org.bitcoins.core.protocol.{Bech32Address, P2PKHAddress, P2SHAddress} import org.bitcoins.core.protocol.{Bech32Address, P2PKHAddress, P2SHAddress}
import org.bitcoins.testkit.wallet.BitcoinSWalletTest import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.wallet.api.WalletApi
import org.scalatest.FutureOutcome import org.scalatest.FutureOutcome
class LegacyWalletTest extends BitcoinSWalletTest { class LegacyWalletTest extends BitcoinSWalletTest {
override type FixtureParam = WalletApi override type FixtureParam = Wallet
override def withFixture(test: OneArgAsyncTest): FutureOutcome = override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withLegacyWallet(test) withLegacyWallet(test)
it should "generate legacy addresses" in { wallet: WalletApi => it should "generate legacy addresses" in { wallet: Wallet =>
for { for {
addr <- wallet.getNewAddress() addr <- wallet.getNewAddress()
account <- wallet.getDefaultAccount() account <- wallet.getDefaultAccount()

View file

@ -3,18 +3,17 @@ package org.bitcoins.wallet
import org.bitcoins.core.hd.{AddressType, HDPurposes} import org.bitcoins.core.hd.{AddressType, HDPurposes}
import org.bitcoins.core.protocol.{Bech32Address, P2PKHAddress, P2SHAddress} import org.bitcoins.core.protocol.{Bech32Address, P2PKHAddress, P2SHAddress}
import org.bitcoins.testkit.wallet.BitcoinSWalletTest import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.wallet.api.WalletApi
import org.scalatest.FutureOutcome import org.scalatest.FutureOutcome
class SegwitWalletTest extends BitcoinSWalletTest { class SegwitWalletTest extends BitcoinSWalletTest {
override type FixtureParam = WalletApi override type FixtureParam = Wallet
override def withFixture(test: OneArgAsyncTest): FutureOutcome = { override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
withSegwitWallet(test) withSegwitWallet(test)
} }
it should "generate segwit addresses" in { wallet: WalletApi => it should "generate segwit addresses" in { wallet =>
for { for {
addr <- wallet.getNewAddress() addr <- wallet.getNewAddress()
account <- wallet.getDefaultAccount() account <- wallet.getDefaultAccount()

View file

@ -140,9 +140,9 @@ class UTXOLifeCycleTest extends BitcoinSWalletTest {
for { for {
tx <- wallet.sendToOutputs(Vector(dummyOutput), tx <- wallet.sendToOutputs(Vector(dummyOutput),
Some(SatoshisPerVirtualByte.one), Some(SatoshisPerVirtualByte.one))
reserveUtxos = true)
_ <- wallet.processTransaction(tx, None) _ <- wallet.processTransaction(tx, None)
_ <- wallet.markUTXOsAsReserved(tx)
allReserved <- wallet.listUtxos(TxoState.Reserved) allReserved <- wallet.listUtxos(TxoState.Reserved)
_ = assert( _ = assert(

View file

@ -6,7 +6,7 @@ import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.transaction.TransactionOutput import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant} import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant}
import org.bitcoins.core.script.control.OP_RETURN import org.bitcoins.core.script.control.OP_RETURN
import org.bitcoins.core.wallet.fee.{SatoshisPerByte, SatoshisPerVirtualByte} import org.bitcoins.core.wallet.fee.SatoshisPerByte
import org.bitcoins.crypto.CryptoUtil import org.bitcoins.crypto.CryptoUtil
import org.bitcoins.testkit.wallet.BitcoinSWalletTest import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet
@ -62,10 +62,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
val wallet = fundedWallet.wallet val wallet = fundedWallet.wallet
for { for {
tx <- wallet.sendToAddresses(addresses, tx <- wallet.sendToAddresses(addresses, amounts, feeRateOpt)
amounts,
feeRateOpt,
reserveUtxos = false)
} yield { } yield {
val expectedOutputs = addresses.zip(amounts).map { val expectedOutputs = addresses.zip(amounts).map {
case (addr, amount) => TransactionOutput(amount, addr.scriptPubKey) case (addr, amount) => TransactionOutput(amount, addr.scriptPubKey)
@ -79,10 +76,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
val wallet = fundedWallet.wallet val wallet = fundedWallet.wallet
val sendToAddressesF = val sendToAddressesF =
wallet.sendToAddresses(addresses, wallet.sendToAddresses(addresses, amounts.tail, feeRateOpt)
amounts.tail,
feeRateOpt,
reserveUtxos = false)
recoverToSucceededIf[IllegalArgumentException] { recoverToSucceededIf[IllegalArgumentException] {
sendToAddressesF sendToAddressesF
@ -94,10 +88,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
val wallet = fundedWallet.wallet val wallet = fundedWallet.wallet
val sendToAddressesF = val sendToAddressesF =
wallet.sendToAddresses(addresses.tail, wallet.sendToAddresses(addresses.tail, amounts, feeRateOpt)
amounts,
feeRateOpt,
reserveUtxos = false)
recoverToSucceededIf[IllegalArgumentException] { recoverToSucceededIf[IllegalArgumentException] {
sendToAddressesF sendToAddressesF
@ -112,8 +103,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
} }
for { for {
tx <- tx <- wallet.sendToOutputs(expectedOutputs, feeRateOpt)
wallet.sendToOutputs(expectedOutputs, feeRateOpt, reserveUtxos = false)
} yield { } yield {
assert(expectedOutputs.diff(tx.outputs).isEmpty) assert(expectedOutputs.diff(tx.outputs).isEmpty)
} }
@ -203,7 +193,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
) )
val sendToAddressesF = val sendToAddressesF =
wallet.sendToAddresses(addrs, amounts, feeRateOpt, reserveUtxos = false) wallet.sendToAddresses(addrs, amounts, feeRateOpt)
recoverToSucceededIf[IllegalArgumentException] { recoverToSucceededIf[IllegalArgumentException] {
sendToAddressesF sendToAddressesF
@ -237,7 +227,8 @@ class WalletSendingTest extends BitcoinSWalletTest {
wallet: Wallet, wallet: Wallet,
algo: CoinSelectionAlgo): Future[Assertion] = { algo: CoinSelectionAlgo): Future[Assertion] = {
for { for {
allUtxos <- wallet.listUtxos() account <- wallet.getDefaultAccount()
allUtxos <- wallet.listUtxos(account.hdAccount)
output = TransactionOutput(amountToSend, testAddress.scriptPubKey) output = TransactionOutput(amountToSend, testAddress.scriptPubKey)
expectedUtxos = CoinSelector.selectByAlgo(algo, expectedUtxos = CoinSelector.selectByAlgo(algo,
allUtxos, allUtxos,

View file

@ -3,16 +3,14 @@ package org.bitcoins.wallet
import java.nio.file.Files import java.nio.file.Files
import org.bitcoins.core.hd.HDChainType.{Change, External} import org.bitcoins.core.hd.HDChainType.{Change, External}
import org.bitcoins.core.hd.{HDAccount, HDChainType, HDPurpose} import org.bitcoins.core.hd.{HDAccount, HDChainType}
import org.bitcoins.core.protocol.BitcoinAddress import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.util.FutureUtil import org.bitcoins.core.util.FutureUtil
import org.bitcoins.crypto.AesPassword import org.bitcoins.crypto.AesPassword
import org.bitcoins.keymanager.KeyManagerUnlockError.MnemonicNotFound import org.bitcoins.keymanager.KeyManagerUnlockError.MnemonicNotFound
import org.bitcoins.keymanager.{KeyManagerUnlockError, WalletStorage} import org.bitcoins.keymanager.{KeyManagerUnlockError, WalletStorage}
import org.bitcoins.testkit.EmbeddedPg
import org.bitcoins.testkit.wallet.BitcoinSWalletTest import org.bitcoins.testkit.wallet.BitcoinSWalletTest
import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse
import org.bitcoins.wallet.api.WalletApi
import org.bitcoins.wallet.models.AddressDb import org.bitcoins.wallet.models.AddressDb
import org.scalatest.FutureOutcome import org.scalatest.FutureOutcome
import org.scalatest.compatible.Assertion import org.scalatest.compatible.Assertion
@ -30,7 +28,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
behavior of "Wallet - unit test" behavior of "Wallet - unit test"
it must "write the mnemonic seed to the root datadir -- NOT A NETWORK sub directory" in { it must "write the mnemonic seed to the root datadir -- NOT A NETWORK sub directory" in {
wallet: WalletApi => wallet: Wallet =>
//since datadir has the path that relates it to a network ('mainnet'/'testnet'/'regtest') //since datadir has the path that relates it to a network ('mainnet'/'testnet'/'regtest')
//we need to get the parent of that to find where the encrypted seed should be //we need to get the parent of that to find where the encrypted seed should be
//this is where the bitcoin-s.conf should live too. //this is where the bitcoin-s.conf should live too.
@ -41,7 +39,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
} }
it should "create a new wallet" in { wallet: WalletApi => it should "create a new wallet" in { wallet: Wallet =>
for { for {
accounts <- wallet.listAccounts() accounts <- wallet.listAccounts()
addresses <- wallet.listAddresses() addresses <- wallet.listAddresses()
@ -51,7 +49,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
} }
} }
it should "generate addresses" in { wallet: WalletApi => it should "generate addresses" in { wallet: Wallet =>
for { for {
addr <- wallet.getNewAddress() addr <- wallet.getNewAddress()
otherAddr <- wallet.getNewAddress() otherAddr <- wallet.getNewAddress()
@ -63,9 +61,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
} }
} }
it should "know what the last address index is" in { walletApi => it should "know what the last address index is" in { wallet =>
val wallet = walletApi.asInstanceOf[Wallet]
def getMostRecent( def getMostRecent(
hdAccount: HDAccount, hdAccount: HDAccount,
chain: HDChainType): Future[AddressDb] = { chain: HDChainType): Future[AddressDb] = {
@ -138,7 +134,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
} }
it should "fail to unlock the wallet with a bad password" in { it should "fail to unlock the wallet with a bad password" in {
wallet: WalletApi => wallet: Wallet =>
val badpassphrase = AesPassword.fromNonEmptyString("bad") val badpassphrase = AesPassword.fromNonEmptyString("bad")
val errorType = wallet.unlock(badpassphrase, None) match { val errorType = wallet.unlock(badpassphrase, None) match {
@ -152,7 +148,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
} }
} }
it should "match block filters" in { wallet: WalletApi => it should "match block filters" in { wallet: Wallet =>
for { for {
matched <- wallet.getMatchingBlocks( matched <- wallet.getMatchingBlocks(
scripts = Vector( scripts = Vector(

View file

@ -50,7 +50,7 @@ import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success} import scala.util.{Failure, Success}
abstract class Wallet abstract class Wallet
extends WalletApi extends HDWalletApi
with UtxoHandling with UtxoHandling
with AddressHandling with AddressHandling
with AccountHandling with AccountHandling
@ -367,7 +367,8 @@ abstract class Wallet
address: BitcoinAddress, address: BitcoinAddress,
amount: CurrencyUnit, amount: CurrencyUnit,
feeRate: FeeUnit, feeRate: FeeUnit,
fromAccount: AccountDb): Future[Transaction] = { fromAccount: AccountDb,
newTags: Vector[AddressTag]): Future[Transaction] = {
require( require(
address.networkParameters.isSameNetworkBytes(networkParameters), address.networkParameters.isSameNetworkBytes(networkParameters),
s"Cannot send to address on other network, got ${address.networkParameters}" s"Cannot send to address on other network, got ${address.networkParameters}"
@ -396,7 +397,7 @@ abstract class Wallet
feeRate, feeRate,
changeAddr.scriptPubKey) changeAddr.scriptPubKey)
tx <- finishSend(txBuilder, utxos, amount, feeRate, Vector.empty) tx <- finishSend(txBuilder, utxos, amount, feeRate, newTags)
} yield tx } yield tx
} }
@ -455,7 +456,7 @@ abstract class Wallet
amounts: Vector[CurrencyUnit], amounts: Vector[CurrencyUnit],
feeRate: FeeUnit, feeRate: FeeUnit,
fromAccount: AccountDb, fromAccount: AccountDb,
reserveUtxos: Boolean): Future[Transaction] = { newTags: Vector[AddressTag]): Future[Transaction] = {
require(amounts.size == addresses.size, require(amounts.size == addresses.size,
"Must have an amount for every address") "Must have an amount for every address")
require( require(
@ -468,7 +469,7 @@ abstract class Wallet
logger.info(s"Sending $amount to $address at feerate $feeRate") logger.info(s"Sending $amount to $address at feerate $feeRate")
TransactionOutput(amount, address.scriptPubKey) TransactionOutput(amount, address.scriptPubKey)
} }
sendToOutputs(destinations, feeRate, fromAccount, reserveUtxos) sendToOutputs(destinations, feeRate, fromAccount, newTags)
} }
override def makeOpReturnCommitment( override def makeOpReturnCommitment(
@ -492,24 +493,23 @@ abstract class Wallet
val output = TransactionOutput(0.satoshis, scriptPubKey) val output = TransactionOutput(0.satoshis, scriptPubKey)
sendToOutputs(Vector(output), feeRate, fromAccount, reserveUtxos = false) sendToOutputs(Vector(output), feeRate, fromAccount)
} }
def sendToOutputs( override def sendToOutputs(
outputs: Vector[TransactionOutput], outputs: Vector[TransactionOutput],
feeRate: FeeUnit, feeRate: FeeUnit,
fromAccount: AccountDb, fromAccount: AccountDb,
reserveUtxos: Boolean): Future[Transaction] = { newTags: Vector[AddressTag]): Future[Transaction] = {
for { for {
(txBuilder, utxoInfos) <- fundRawTransactionInternal( (txBuilder, utxoInfos) <- fundRawTransactionInternal(
destinations = outputs, destinations = outputs,
feeRate = feeRate, feeRate = feeRate,
fromAccount = fromAccount, fromAccount = fromAccount,
keyManagerOpt = Some(keyManager), keyManagerOpt = Some(keyManager),
fromTagOpt = None, fromTagOpt = None)
markAsReserved = reserveUtxos)
sentAmount = outputs.foldLeft(CurrencyUnits.zero)(_ + _.value) sentAmount = outputs.foldLeft(CurrencyUnits.zero)(_ + _.value)
tx <- finishSend(txBuilder, utxoInfos, sentAmount, feeRate, Vector.empty) tx <- finishSend(txBuilder, utxoInfos, sentAmount, feeRate, newTags)
} yield tx } yield tx
} }

View file

@ -0,0 +1,520 @@
package org.bitcoins.wallet.api
import org.bitcoins.commons.jsonmodels.wallet.CoinSelectionAlgo
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType, HDPurpose}
import org.bitcoins.core.protocol.transaction.{
Transaction,
TransactionOutPoint,
TransactionOutput
}
import org.bitcoins.core.protocol.{BitcoinAddress, BlockStamp}
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
import org.bitcoins.keymanager.KeyManagerParams
import org.bitcoins.wallet.models.{AccountDb, AddressDb, SpendingInfoDb}
import scala.concurrent.Future
/**
* API for the wallet project.
*
* This wallet API is BIP44 compliant.
*
* @see [[https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki BIP44]]
*/
trait HDWalletApi extends WalletApi {
/** Gets the balance of the given account */
def getBalance(account: HDAccount): Future[CurrencyUnit] = {
val confirmedF = getConfirmedBalance(account)
val unconfirmedF = getUnconfirmedBalance(account)
for {
confirmed <- confirmedF
unconfirmed <- unconfirmedF
} yield {
confirmed + unconfirmed
}
}
def getConfirmedBalance(account: HDAccount): Future[CurrencyUnit]
def getUnconfirmedBalance(account: HDAccount): Future[CurrencyUnit]
/** Generates a new change address */
protected[wallet] def getNewChangeAddress(
account: AccountDb): Future[BitcoinAddress]
override def getNewChangeAddress(): Future[BitcoinAddress] = {
for {
account <- getDefaultAccount()
addr <- getNewChangeAddress(account)
} yield addr
}
/**
* Fetches the default account from the DB
* @return Future[AccountDb]
*/
protected[wallet] def getDefaultAccount(): Future[AccountDb]
/** Fetches the default account for the given address/account kind
* @param addressType
*/
protected[wallet] def getDefaultAccountForType(
addressType: AddressType): Future[AccountDb]
def sendWithAlgo(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
algo: CoinSelectionAlgo,
fromAccount: AccountDb,
newTags: Vector[AddressTag]): Future[Transaction]
def sendWithAlgo(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
algo: CoinSelectionAlgo,
fromAccount: AccountDb): Future[Transaction] =
sendWithAlgo(address, amount, feeRate, algo, fromAccount, Vector.empty)
def sendWithAlgo(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit],
algo: CoinSelectionAlgo,
fromAccount: AccountDb): Future[Transaction] = {
for {
feeRate <- determineFeeRate(feeRateOpt)
tx <- sendWithAlgo(address, amount, feeRate, algo, fromAccount)
} yield tx
}
override def sendWithAlgo(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit],
algo: CoinSelectionAlgo): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendWithAlgo(address, amount, feeRateOpt, algo, account)
} yield tx
}
override def sendWithAlgo(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
algo: CoinSelectionAlgo): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendWithAlgo(address, amount, feeRate, algo, account)
} yield tx
}
def sendWithAlgo(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
algo: CoinSelectionAlgo,
newTags: Vector[AddressTag]): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendWithAlgo(address, amount, feeRate, algo, account, newTags)
} yield tx
}
/**
* Sends money from the specified account
*
* todo: add error handling to signature
*/
def sendFromOutPoints(
outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
fromAccount: AccountDb,
newTags: Vector[AddressTag]): Future[Transaction]
def sendFromOutPoints(
outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
fromAccount: AccountDb): Future[Transaction] =
sendFromOutPoints(outPoints,
address,
amount,
feeRate,
fromAccount,
Vector.empty)
def sendFromOutPoints(
outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb): Future[Transaction] = {
for {
feeRate <- determineFeeRate(feeRateOpt)
tx <- sendFromOutPoints(outPoints, address, amount, feeRate, fromAccount)
} yield tx
}
override def sendFromOutPoints(
outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit]
): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendFromOutPoints(outPoints, address, amount, feeRateOpt, account)
} yield tx
}
override def sendFromOutPoints(
outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendFromOutPoints(outPoints, address, amount, feeRate, account)
} yield tx
}
def sendFromOutPoints(
outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
newTags: Vector[AddressTag]): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <-
sendFromOutPoints(outPoints, address, amount, feeRate, account, newTags)
} yield tx
}
/**
* Sends money from the specified account
*
* todo: add error handling to signature
*/
def sendToAddress(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
fromAccount: AccountDb,
newTags: Vector[AddressTag]): Future[Transaction]
def sendToAddress(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
fromAccount: AccountDb): Future[Transaction] =
sendToAddress(address, amount, feeRate, fromAccount, Vector.empty)
def sendToAddress(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb): Future[Transaction] = {
for {
feeRate <- determineFeeRate(feeRateOpt)
tx <- sendToAddress(address, amount, feeRate, fromAccount)
} yield tx
}
override def sendToAddress(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit]
): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToAddress(address, amount, feeRateOpt, account)
} yield tx
}
override def sendToAddress(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToAddress(address, amount, feeRate, account)
} yield tx
}
def sendToAddress(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
newTags: Vector[AddressTag]): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToAddress(address, amount, feeRate, account, newTags)
} yield tx
}
/**
* Sends money from the specified account
*
* todo: add error handling to signature
*/
def sendToAddresses(
addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit],
feeRate: FeeUnit,
fromAccount: AccountDb,
newTags: Vector[AddressTag]): Future[Transaction]
def sendToAddresses(
addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit],
feeRate: FeeUnit,
fromAccount: AccountDb): Future[Transaction] =
sendToAddresses(addresses, amounts, feeRate, fromAccount, Vector.empty)
def sendToAddresses(
addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit],
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb): Future[Transaction] = {
for {
feeRate <- determineFeeRate(feeRateOpt)
tx <- sendToAddresses(addresses, amounts, feeRate, fromAccount)
} yield tx
}
override def sendToAddresses(
addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit],
feeRateOpt: Option[FeeUnit]
): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToAddresses(addresses, amounts, feeRateOpt, account)
} yield tx
}
override def sendToAddresses(
addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit],
feeRate: FeeUnit): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToAddresses(addresses, amounts, feeRate, account)
} yield tx
}
def sendToAddresses(
addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit],
feeRate: FeeUnit,
newTags: Vector[AddressTag]): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToAddresses(addresses, amounts, feeRate, account, newTags)
} yield tx
}
/**
* Sends money from the specified account
*
* todo: add error handling to signature
*/
def sendToOutputs(
outputs: Vector[TransactionOutput],
feeRate: FeeUnit,
fromAccount: AccountDb,
newTags: Vector[AddressTag]): Future[Transaction]
def sendToOutputs(
outputs: Vector[TransactionOutput],
feeRate: FeeUnit,
fromAccount: AccountDb): Future[Transaction] =
sendToOutputs(outputs, feeRate, fromAccount, Vector.empty)
def sendToOutputs(
outputs: Vector[TransactionOutput],
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb): Future[Transaction] = {
for {
feeRate <- determineFeeRate(feeRateOpt)
tx <- sendToOutputs(outputs, feeRate, fromAccount)
} yield tx
}
def sendToOutputs(
outputs: Vector[TransactionOutput],
feeRate: FeeUnit,
newTags: Vector[AddressTag]): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToOutputs(outputs, feeRate, account, newTags)
} yield tx
}
override def sendToOutputs(
outputs: Vector[TransactionOutput],
feeRateOpt: Option[FeeUnit]
): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToOutputs(outputs, feeRateOpt, account)
} yield tx
}
override def sendToOutputs(
outputs: Vector[TransactionOutput],
feeRate: FeeUnit): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToOutputs(outputs, feeRate, account)
} yield tx
}
def makeOpReturnCommitment(
message: String,
hashMessage: Boolean,
feeRate: FeeUnit,
fromAccount: AccountDb): Future[Transaction]
def makeOpReturnCommitment(
message: String,
hashMessage: Boolean,
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb): Future[Transaction] = {
for {
feeRate <- determineFeeRate(feeRateOpt)
tx <- makeOpReturnCommitment(message, hashMessage, feeRate, fromAccount)
} yield tx
}
override def makeOpReturnCommitment(
message: String,
hashMessage: Boolean,
feeRate: FeeUnit): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- makeOpReturnCommitment(message, hashMessage, feeRate, account)
} yield tx
}
def listDefaultAccountUtxos(): Future[Vector[SpendingInfoDb]] =
listUtxos(walletConfig.defaultAccount)
def listUtxos(account: HDAccount): Future[Vector[SpendingInfoDb]]
def listUtxos(
hdAccount: HDAccount,
tag: AddressTag): Future[Vector[SpendingInfoDb]]
def listUtxos(
hdAccount: HDAccount,
state: TxoState): Future[Vector[SpendingInfoDb]]
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]]
override def clearAllUtxosAndAddresses(): Future[HDWalletApi]
def clearUtxosAndAddresses(account: HDAccount): Future[HDWalletApi]
/** Gets the address associated with the pubkey at
* the resulting `BIP32Path` determined by the
* default account and the given chainType and addressIndex
*/
def getAddress(
chainType: HDChainType,
addressIndex: Int): 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]]
/** Lists all wallet accounts with the given type
* @param purpose
* @return [[Future[Vector[AccountDb]]
*/
def listAccounts(purpose: HDPurpose): Future[Vector[AccountDb]] =
listAccounts().map(_.filter(_.hdAccount.purpose == purpose))
def rescanNeutrinoWallet(
account: HDAccount,
startOpt: Option[BlockStamp],
endOpt: Option[BlockStamp],
addressBatchSize: Int,
useCreationTime: Boolean): Future[Unit]
override def rescanNeutrinoWallet(
startOpt: Option[BlockStamp],
endOpt: Option[BlockStamp],
addressBatchSize: Int,
useCreationTime: Boolean): Future[Unit] = {
for {
account <- getDefaultAccount()
_ <- rescanNeutrinoWallet(account.hdAccount,
startOpt,
endOpt,
addressBatchSize,
useCreationTime)
} yield ()
}
def fullRescanNeutrinoWallet(
account: HDAccount,
addressBatchSize: Int): Future[Unit] = {
rescanNeutrinoWallet(account = account,
startOpt = None,
endOpt = None,
addressBatchSize = addressBatchSize,
useCreationTime = false)
}
/** Helper method to rescan the ENTIRE blockchain. */
override def fullRescanNeutrinoWallet(addressBatchSize: Int): Future[Unit] = {
for {
account <- getDefaultAccount()
_ <- fullRescanNeutrinoWallet(account.hdAccount, addressBatchSize)
} yield ()
}
def createNewAccount(keyManagerParams: KeyManagerParams): 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,
keyManagerParams: KeyManagerParams): Future[HDWalletApi]
}

View file

@ -8,7 +8,7 @@ import org.bitcoins.core.bloom.BloomFilter
import org.bitcoins.core.config.NetworkParameters import org.bitcoins.core.config.NetworkParameters
import org.bitcoins.core.currency.CurrencyUnit import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.gcs.GolombFilter import org.bitcoins.core.gcs.GolombFilter
import org.bitcoins.core.hd.{AddressType, HDAccount, HDChainType, HDPurpose} import org.bitcoins.core.hd.AddressType
import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader, ChainParams} import org.bitcoins.core.protocol.blockchain.{Block, BlockHeader, ChainParams}
import org.bitcoins.core.protocol.script.ScriptPubKey import org.bitcoins.core.protocol.script.ScriptPubKey
import org.bitcoins.core.protocol.transaction.{ import org.bitcoins.core.protocol.transaction.{
@ -27,10 +27,10 @@ import org.bitcoins.crypto.{
} }
import org.bitcoins.keymanager._ import org.bitcoins.keymanager._
import org.bitcoins.keymanager.bip39.BIP39KeyManager import org.bitcoins.keymanager.bip39.BIP39KeyManager
import org.bitcoins.wallet.WalletLogger
import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse
import org.bitcoins.wallet.config.WalletAppConfig import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.models.{AccountDb, AddressDb, SpendingInfoDb} import org.bitcoins.wallet.models.{AddressDb, SpendingInfoDb}
import org.bitcoins.wallet.{Wallet, WalletLogger}
import scala.concurrent.{ExecutionContext, Future} import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success} import scala.util.{Failure, Success}
@ -120,18 +120,6 @@ trait WalletApi extends WalletLogger {
} yield confirmed + unconfirmed } yield confirmed + unconfirmed
} }
/** Gets the balance of the given account */
def getBalance(account: HDAccount): Future[CurrencyUnit] = {
val confirmedF = getConfirmedBalance(account)
val unconfirmedF = getUnconfirmedBalance(account)
for {
confirmed <- confirmedF
unconfirmed <- unconfirmedF
} yield {
confirmed + unconfirmed
}
}
/** Gets the sum of all UTXOs in this wallet with the address tag */ /** Gets the sum of all UTXOs in this wallet with the address tag */
def getBalance(tag: AddressTag): Future[CurrencyUnit] = { def getBalance(tag: AddressTag): Future[CurrencyUnit] = {
val confirmedF = getConfirmedBalance(tag) val confirmedF = getConfirmedBalance(tag)
@ -146,15 +134,11 @@ trait WalletApi extends WalletLogger {
/** Gets the sum of all confirmed UTXOs in this wallet */ /** Gets the sum of all confirmed UTXOs in this wallet */
def getConfirmedBalance(): Future[CurrencyUnit] def getConfirmedBalance(): Future[CurrencyUnit]
def getConfirmedBalance(account: HDAccount): Future[CurrencyUnit]
def getConfirmedBalance(tag: AddressTag): Future[CurrencyUnit] def getConfirmedBalance(tag: AddressTag): Future[CurrencyUnit]
/** Gets the sum of all unconfirmed UTXOs in this wallet */ /** Gets the sum of all unconfirmed UTXOs in this wallet */
def getUnconfirmedBalance(): Future[CurrencyUnit] def getUnconfirmedBalance(): Future[CurrencyUnit]
def getUnconfirmedBalance(account: HDAccount): Future[CurrencyUnit]
def getUnconfirmedBalance(tag: AddressTag): Future[CurrencyUnit] def getUnconfirmedBalance(tag: AddressTag): Future[CurrencyUnit]
/** /**
@ -169,40 +153,24 @@ trait WalletApi extends WalletLogger {
*/ */
def listUtxos(): Future[Vector[SpendingInfoDb]] def listUtxos(): Future[Vector[SpendingInfoDb]]
def listUtxos(account: HDAccount): Future[Vector[SpendingInfoDb]]
def listUtxos(tag: AddressTag): Future[Vector[SpendingInfoDb]] def listUtxos(tag: AddressTag): Future[Vector[SpendingInfoDb]]
def listUtxos(
hdAccount: HDAccount,
tag: AddressTag): Future[Vector[SpendingInfoDb]]
def listUtxos(state: TxoState): Future[Vector[SpendingInfoDb]] def listUtxos(state: TxoState): Future[Vector[SpendingInfoDb]]
def listUtxos(
hdAccount: HDAccount,
state: TxoState): Future[Vector[SpendingInfoDb]]
def listAddresses(): Future[Vector[AddressDb]] def listAddresses(): Future[Vector[AddressDb]]
def listAddresses(account: HDAccount): Future[Vector[AddressDb]]
def listSpentAddresses(): Future[Vector[AddressDb]] def listSpentAddresses(): Future[Vector[AddressDb]]
def listSpentAddresses(account: HDAccount): Future[Vector[AddressDb]]
def listFundedAddresses(): Future[Vector[(AddressDb, CurrencyUnit)]] def listFundedAddresses(): Future[Vector[(AddressDb, CurrencyUnit)]]
def listFundedAddresses(
account: HDAccount): Future[Vector[(AddressDb, CurrencyUnit)]]
def listUnusedAddresses(): Future[Vector[AddressDb]] def listUnusedAddresses(): Future[Vector[AddressDb]]
def listUnusedAddresses(account: HDAccount): Future[Vector[AddressDb]]
def markUTXOsAsReserved( def markUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]]
/** Marks all utxos that are ours in this transactions as reserved */
def markUTXOsAsReserved(tx: Transaction): Future[Vector[SpendingInfoDb]]
def unmarkUTXOsAsReserved( def unmarkUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]]
@ -212,15 +180,6 @@ trait WalletApi extends WalletLogger {
/** Checks if the wallet contains any data */ /** Checks if the wallet contains any data */
def isEmpty(): Future[Boolean] def isEmpty(): Future[Boolean]
/** Removes all utxos and addresses from the wallet account.
* Don't call this unless you are sure you can recover
* your wallet
*/
def clearUtxosAndAddresses(account: HDAccount): Future[WalletApi]
def clearUtxosAndAddresses(): Future[WalletApi] =
clearUtxosAndAddresses(walletConfig.defaultAccount)
/** Removes all utxos and addresses from the wallet. /** Removes all utxos and addresses from the wallet.
* Don't call this unless you are sure you can recover * Don't call this unless you are sure you can recover
* your wallet * your wallet
@ -235,7 +194,7 @@ trait WalletApi extends WalletLogger {
def getNewAddress(addressType: AddressType): Future[BitcoinAddress] def getNewAddress(addressType: AddressType): Future[BitcoinAddress]
/** /**
* Gets a new external address from the default account. * Gets a new external address
* Calling this method multiple * Calling this method multiple
* times will return the same address, until it has * times will return the same address, until it has
* received funds. * received funds.
@ -257,43 +216,19 @@ trait WalletApi extends WalletLogger {
} }
/** /**
* Gets a external address from the account associated with * Gets a external address the given AddressType. Calling this
* the given AddressType. Calling this method multiple * method multiple times will return the same address, until it has
* times will return the same address, until it has
* received funds. * received funds.
*/ */
def getUnusedAddress(addressType: AddressType): Future[BitcoinAddress] def getUnusedAddress(addressType: AddressType): Future[BitcoinAddress]
/** /**
* Gets a external address from the default account. * Gets a external address. Calling this method multiple
* Calling this method multiple
* times will return the same address, until it has * times will return the same address, until it has
* received funds. * received funds.
*/ */
def getUnusedAddress: Future[BitcoinAddress] def getUnusedAddress: Future[BitcoinAddress]
/** 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): 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]
/** /**
* Mimics the `getaddressinfo` RPC call in Bitcoin Core * Mimics the `getaddressinfo` RPC call in Bitcoin Core
* *
@ -317,29 +252,7 @@ trait WalletApi extends WalletLogger {
} }
/** Generates a new change address */ /** Generates a new change address */
protected[wallet] def getNewChangeAddress( protected[wallet] def getNewChangeAddress(): Future[BitcoinAddress]
account: AccountDb): Future[BitcoinAddress]
/** Generates a new change address for the default account */
final protected[wallet] def getNewChangeAddress(): Future[BitcoinAddress] = {
for {
account <- getDefaultAccount
address <- getNewChangeAddress(account)
} yield address
}
/**
* Fetches the default account from the DB
* @return Future[AccountDb]
*/
protected[wallet] def getDefaultAccount(): Future[AccountDb]
/** Fetches the default account for the given address/account kind
* @param addressType
*/
protected[wallet] def getDefaultAccountForType(
addressType: AddressType): Future[AccountDb]
/** /**
* Unlocks the wallet with the provided passphrase, * Unlocks the wallet with the provided passphrase,
@ -349,15 +262,6 @@ trait WalletApi extends WalletLogger {
KeyManagerUnlockError, KeyManagerUnlockError,
WalletApi] WalletApi]
def listAccounts(): Future[Vector[AccountDb]]
/** Lists all wallet accounts with the given type
* @param purpose
* @return [[Future[Vector[AccountDb]]
*/
def listAccounts(purpose: HDPurpose): Future[Vector[AccountDb]] =
listAccounts().map(_.filter(_.hdAccount.purpose == purpose))
/** /**
* Iterates over the block filters in order to find filters that match to the given addresses * Iterates over the block filters in order to find filters that match to the given addresses
* *
@ -406,37 +310,15 @@ trait WalletApi extends WalletLogger {
* @param endOpt end block (if None it ends at the current tip) * @param endOpt end block (if None it ends at the current tip)
* @param addressBatchSize how many addresses to match in a single pass * @param addressBatchSize how many addresses to match in a single pass
*/ */
def rescanNeutrinoWallet( def rescanNeutrinoWallet(
account: HDAccount,
startOpt: Option[BlockStamp], startOpt: Option[BlockStamp],
endOpt: Option[BlockStamp], endOpt: Option[BlockStamp],
addressBatchSize: Int, addressBatchSize: Int,
useCreationTime: Boolean): Future[Unit] useCreationTime: Boolean): Future[Unit]
def rescanNeutrinoWallet(
startOpt: Option[BlockStamp],
endOpt: Option[BlockStamp],
addressBatchSize: Int,
useCreationTime: Boolean): Future[Unit] =
rescanNeutrinoWallet(account = walletConfig.defaultAccount,
startOpt = startOpt,
endOpt = endOpt,
addressBatchSize = addressBatchSize,
useCreationTime = useCreationTime)
/** Helper method to rescan the ENTIRE blockchain. */ /** Helper method to rescan the ENTIRE blockchain. */
def fullRescanNeutrinoWallet(addressBatchSize: Int): Future[Unit] = def fullRescanNeutrinoWallet(addressBatchSize: Int): Future[Unit]
fullRescanNeutrinoWallet(account = walletConfig.defaultAccount,
addressBatchSize = addressBatchSize)
def fullRescanNeutrinoWallet(
account: HDAccount,
addressBatchSize: Int): Future[Unit] =
rescanNeutrinoWallet(account = account,
startOpt = None,
endOpt = None,
addressBatchSize = addressBatchSize,
useCreationTime = false)
/** /**
* Recreates the account using BIP-44 approach * Recreates the account using BIP-44 approach
@ -447,7 +329,7 @@ trait WalletApi extends WalletLogger {
def keyManager: BIP39KeyManager def keyManager: BIP39KeyManager
private def determineFeeRate(feeRateOpt: Option[FeeUnit]): Future[FeeUnit] = protected def determineFeeRate(feeRateOpt: Option[FeeUnit]): Future[FeeUnit] =
feeRateOpt match { feeRateOpt match {
case None => case None =>
feeRateApi.getFeeRate feeRateApi.getFeeRate
@ -459,29 +341,17 @@ trait WalletApi extends WalletLogger {
outPoints: Vector[TransactionOutPoint], outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress, address: BitcoinAddress,
amount: CurrencyUnit, amount: CurrencyUnit,
feeRate: FeeUnit, feeRate: FeeUnit): Future[Transaction]
fromAccount: AccountDb): Future[Transaction]
def sendFromOutPoints( def sendFromOutPoints(
outPoints: Vector[TransactionOutPoint], outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress, address: BitcoinAddress,
amount: CurrencyUnit, amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit], feeRateOpt: Option[FeeUnit]
fromAccount: AccountDb): Future[Transaction] = { ): Future[Transaction] = {
for { for {
feeRate <- determineFeeRate(feeRateOpt) feeRate <- determineFeeRate(feeRateOpt)
tx <- sendFromOutPoints(outPoints, address, amount, feeRate, fromAccount) tx <- sendFromOutPoints(outPoints, address, amount, feeRate)
} yield tx
}
def sendFromOutPoints(
outPoints: Vector[TransactionOutPoint],
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit]): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendFromOutPoints(outPoints, address, amount, feeRateOpt, account)
} yield tx } yield tx
} }
@ -489,215 +359,92 @@ trait WalletApi extends WalletLogger {
address: BitcoinAddress, address: BitcoinAddress,
amount: CurrencyUnit, amount: CurrencyUnit,
feeRate: FeeUnit, feeRate: FeeUnit,
algo: CoinSelectionAlgo, algo: CoinSelectionAlgo): Future[Transaction]
fromAccount: AccountDb,
newTags: Vector[AddressTag]): Future[Transaction]
def sendWithAlgo(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
algo: CoinSelectionAlgo,
fromAccount: AccountDb): Future[Transaction] =
sendWithAlgo(address, amount, feeRate, algo, fromAccount, Vector.empty)
def sendWithAlgo( def sendWithAlgo(
address: BitcoinAddress, address: BitcoinAddress,
amount: CurrencyUnit, amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit], feeRateOpt: Option[FeeUnit],
algo: CoinSelectionAlgo, algo: CoinSelectionAlgo
fromAccount: AccountDb): Future[Transaction] = { ): Future[Transaction] = {
for { for {
feeRate <- determineFeeRate(feeRateOpt) feeRate <- determineFeeRate(feeRateOpt)
tx <- sendWithAlgo(address, amount, feeRate, algo, fromAccount) tx <- sendWithAlgo(address, amount, feeRate, algo)
} yield tx
}
def sendWithAlgo(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit],
algo: CoinSelectionAlgo): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendWithAlgo(address, amount, feeRateOpt, algo, account)
} yield tx } yield tx
} }
/** /**
* Sends money from the specified account * Sends money to the address
* *
* todo: add error handling to signature * todo: add error handling to signature
*/ */
def sendToAddress( def sendToAddress(
address: BitcoinAddress, address: BitcoinAddress,
amount: CurrencyUnit, amount: CurrencyUnit,
feeRate: FeeUnit, feeRate: FeeUnit): Future[Transaction]
fromAccount: AccountDb): Future[Transaction]
/**
* Sends money from the specified account
*
* todo: add error handling to signature
*/
def sendToAddress(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb): Future[Transaction] = {
for {
feeRate <- determineFeeRate(feeRateOpt)
tx <- sendToAddress(address, amount, feeRate, fromAccount)
} yield tx
}
/**
* Sends money from the default account
*
* todo: add error handling to signature
*/
def sendToAddress( def sendToAddress(
address: BitcoinAddress, address: BitcoinAddress,
amount: CurrencyUnit, amount: CurrencyUnit,
feeRateOpt: Option[FeeUnit] feeRateOpt: Option[FeeUnit]
): Future[Transaction] = { ): Future[Transaction] = {
for { for {
account <- getDefaultAccount() feeRate <- determineFeeRate(feeRateOpt)
tx <- sendToAddress(address, amount, feeRateOpt, account) tx <- sendToAddress(address, amount, feeRate)
} yield tx } yield tx
} }
def sendToAddress(
address: BitcoinAddress,
amount: CurrencyUnit,
feeRate: FeeUnit,
fromAccount: AccountDb,
newTags: Vector[AddressTag]): Future[Transaction]
/** /**
* Sends money from the specified account * Sends funds using the specified outputs
* *
* todo: add error handling to signature * todo: add error handling to signature
*/ */
def sendToOutputs( def sendToOutputs(
outputs: Vector[TransactionOutput], outputs: Vector[TransactionOutput],
feeRate: FeeUnit, feeRateOpt: Option[FeeUnit]): Future[Transaction] = {
fromAccount: AccountDb,
reserveUtxos: Boolean): Future[Transaction]
def sendToOutputs(
outputs: Vector[TransactionOutput],
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb,
reserveUtxos: Boolean): Future[Transaction] = {
for { for {
feeRate <- determineFeeRate(feeRateOpt) feeRate <- determineFeeRate(feeRateOpt)
tx <- sendToOutputs(outputs, feeRate, fromAccount, reserveUtxos) tx <- sendToOutputs(outputs, feeRate)
} yield tx } yield tx
} }
/**
* Sends money from the default account
*
* todo: add error handling to signature
*/
def sendToOutputs( def sendToOutputs(
outputs: Vector[TransactionOutput], outputs: Vector[TransactionOutput],
feeRateOpt: Option[FeeUnit], feeRate: FeeUnit): Future[Transaction]
reserveUtxos: Boolean): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <- sendToOutputs(outputs, feeRateOpt, account, reserveUtxos)
} yield tx
}
/** /**
* Sends money from the specified account * Sends funds to each address
*
* todo: add error handling to signature
*/ */
def sendToAddresses( def sendToAddresses(
addresses: Vector[BitcoinAddress], addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit], amounts: Vector[CurrencyUnit],
feeRate: FeeUnit, feeRateOpt: Option[FeeUnit]): Future[Transaction] = {
fromAccount: AccountDb,
reserveUtxos: Boolean): Future[Transaction]
/**
* Sends money from the specified account
*
* todo: add error handling to signature
*/
def sendToAddresses(
addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit],
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb,
reserveUtxos: Boolean): Future[Transaction] = {
for { for {
feeRate <- determineFeeRate(feeRateOpt) feeRate <- determineFeeRate(feeRateOpt)
tx <- tx <- sendToAddresses(addresses, amounts, feeRate)
sendToAddresses(addresses, amounts, feeRate, fromAccount, reserveUtxos)
} yield tx } yield tx
} }
/**
* Sends money from the default account
*
* todo: add error handling to signature
*/
def sendToAddresses( def sendToAddresses(
addresses: Vector[BitcoinAddress], addresses: Vector[BitcoinAddress],
amounts: Vector[CurrencyUnit], amounts: Vector[CurrencyUnit],
feeRateOpt: Option[FeeUnit], feeRate: FeeUnit): Future[Transaction]
reserveUtxos: Boolean): Future[Transaction] = {
for {
account <- getDefaultAccount()
tx <-
sendToAddresses(addresses, amounts, feeRateOpt, account, reserveUtxos)
} yield tx
}
def makeOpReturnCommitment( def makeOpReturnCommitment(
message: String, message: String,
hashMessage: Boolean, hashMessage: Boolean,
feeRate: FeeUnit, feeRate: FeeUnit): Future[Transaction]
fromAccount: AccountDb): Future[Transaction]
def makeOpReturnCommitment(
message: String,
hashMessage: Boolean,
feeRateOpt: Option[FeeUnit],
fromAccount: AccountDb): Future[Transaction] = {
for {
feeRate <- determineFeeRate(feeRateOpt)
tx <- makeOpReturnCommitment(message, hashMessage, feeRate, fromAccount)
} yield tx
}
def makeOpReturnCommitment( def makeOpReturnCommitment(
message: String, message: String,
hashMessage: Boolean, hashMessage: Boolean,
feeRateOpt: Option[FeeUnit]): Future[Transaction] = { feeRateOpt: Option[FeeUnit]): Future[Transaction] = {
for { for {
account <- getDefaultAccount() feeRate <- determineFeeRate(feeRateOpt)
tx <- makeOpReturnCommitment(message, hashMessage, feeRateOpt, account) tx <- makeOpReturnCommitment(message, hashMessage, feeRate)
} yield tx } yield tx
} }
def createNewAccount(keyManagerParams: KeyManagerParams): Future[Wallet]
/**
* 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,
keyManagerParams: KeyManagerParams): Future[Wallet]
} }
object WalletApi { object WalletApi {

View file

@ -14,7 +14,7 @@ import org.bitcoins.keymanager.{
KeyManagerParams, KeyManagerParams,
WalletStorage WalletStorage
} }
import org.bitcoins.wallet.api.WalletApi import org.bitcoins.wallet.api.HDWalletApi
import org.bitcoins.wallet.db.WalletDbManagement import org.bitcoins.wallet.db.WalletDbManagement
import org.bitcoins.wallet.models.AccountDAO import org.bitcoins.wallet.models.AccountDAO
import org.bitcoins.wallet.{Wallet, WalletLogger} import org.bitcoins.wallet.{Wallet, WalletLogger}
@ -157,13 +157,14 @@ case class WalletAppConfig(
} }
/** Creates a wallet based on this [[WalletAppConfig]] */ /** Creates a wallet based on this [[WalletAppConfig]] */
def createWallet( def createHDWallet(
nodeApi: NodeApi, nodeApi: NodeApi,
chainQueryApi: ChainQueryApi, chainQueryApi: ChainQueryApi,
feeRateApi: FeeRateApi, feeRateApi: FeeRateApi,
bip39PasswordOpt: Option[String])(implicit bip39PasswordOpt: Option[String])(implicit
ec: ExecutionContext): Future[WalletApi] = { ec: ExecutionContext): Future[HDWalletApi] = {
WalletAppConfig.createWallet(nodeApi = nodeApi, WalletAppConfig.createHDWallet(
nodeApi = nodeApi,
chainQueryApi = chainQueryApi, chainQueryApi = chainQueryApi,
feeRateApi = feeRateApi, feeRateApi = feeRateApi,
bip39PasswordOpt = bip39PasswordOpt)(this, ec) bip39PasswordOpt = bip39PasswordOpt)(this, ec)
@ -187,13 +188,13 @@ object WalletAppConfig
WalletAppConfig(datadir, useLogbackConf, confs: _*) WalletAppConfig(datadir, useLogbackConf, confs: _*)
/** Creates a wallet based on the given [[WalletAppConfig]] */ /** Creates a wallet based on the given [[WalletAppConfig]] */
def createWallet( def createHDWallet(
nodeApi: NodeApi, nodeApi: NodeApi,
chainQueryApi: ChainQueryApi, chainQueryApi: ChainQueryApi,
feeRateApi: FeeRateApi, feeRateApi: FeeRateApi,
bip39PasswordOpt: Option[String])(implicit bip39PasswordOpt: Option[String])(implicit
walletConf: WalletAppConfig, walletConf: WalletAppConfig,
ec: ExecutionContext): Future[WalletApi] = { ec: ExecutionContext): Future[HDWalletApi] = {
walletConf.hasWallet().flatMap { walletExists => walletConf.hasWallet().flatMap { walletExists =>
if (walletExists) { if (walletExists) {
logger.info(s"Using pre-existing wallet") logger.info(s"Using pre-existing wallet")

View file

@ -36,7 +36,7 @@ private[wallet] trait UtxoHandling extends WalletLogger {
/** @inheritdoc */ /** @inheritdoc */
override def listUtxos(): Future[Vector[SpendingInfoDb]] = { override def listUtxos(): Future[Vector[SpendingInfoDb]] = {
listUtxos(walletConfig.defaultAccount) spendingInfoDAO.findAllUnspent()
} }
override def listUtxos( override def listUtxos(
@ -255,6 +255,15 @@ private[wallet] trait UtxoHandling extends WalletLogger {
} yield utxos } yield utxos
} }
/** @inheritdoc */
override def markUTXOsAsReserved(
tx: Transaction): Future[Vector[SpendingInfoDb]] = {
for {
utxos <- spendingInfoDAO.findOutputsBeingSpent(tx)
reserved <- markUTXOsAsReserved(utxos.toVector)
} yield reserved
}
override def unmarkUTXOsAsReserved( override def unmarkUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] = { utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] = {
val unreserved = utxos.filterNot(_.state == TxoState.Reserved) val unreserved = utxos.filterNot(_.state == TxoState.Reserved)