mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-13 11:35:40 +01:00
Create HDWalletApi (#1693)
* Create HDWalletApi * Rename createWallet to createHDWallet, add listDefaultAccountUtxos function * Change return types to HDWalletApi * Fix warning
This commit is contained in:
parent
b2b9ca7eec
commit
4abcf3f321
14 changed files with 622 additions and 360 deletions
|
@ -2,7 +2,7 @@ package org.bitcoins.wallet
|
|||
|
||||
import org.bitcoins.core.hd.AddressType
|
||||
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 scala.concurrent.Future
|
||||
|
@ -11,7 +11,7 @@ import scala.concurrent.Future
|
|||
* ScalaMock cannot stub traits with protected methods,
|
||||
* so we need to stub them manually.
|
||||
*/
|
||||
abstract class MockWalletApi extends WalletApi {
|
||||
abstract class MockWalletApi extends HDWalletApi {
|
||||
|
||||
override protected[wallet] def getNewChangeAddress(
|
||||
account: AccountDb): Future[BitcoinAddress] = stub
|
||||
|
|
|
@ -89,10 +89,10 @@ object Main extends App with BitcoinSLogger {
|
|||
uninitializedNode <- uninitializedNodeF
|
||||
chainApi <- chainApiF
|
||||
_ = logger.info("Initialized chain api")
|
||||
wallet <- walletConf.createWallet(uninitializedNode,
|
||||
chainApi,
|
||||
BitcoinerLiveFeeRateProvider(60),
|
||||
bip39PasswordOpt)
|
||||
wallet <- walletConf.createHDWallet(uninitializedNode,
|
||||
chainApi,
|
||||
BitcoinerLiveFeeRateProvider(60),
|
||||
bip39PasswordOpt)
|
||||
} yield {
|
||||
logger.info(s"Done configuring wallet")
|
||||
wallet
|
||||
|
@ -233,7 +233,7 @@ object Main extends App with BitcoinSLogger {
|
|||
|
||||
private def startHttpServer(
|
||||
node: Node,
|
||||
wallet: WalletApi,
|
||||
wallet: HDWalletApi,
|
||||
rpcPortOpt: Option[Int])(implicit
|
||||
system: ActorSystem,
|
||||
conf: BitcoinSAppConfig): Future[Http.ServerBinding] = {
|
||||
|
|
|
@ -6,12 +6,12 @@ import akka.http.scaladsl.server._
|
|||
import org.bitcoins.commons.serializers.Picklers._
|
||||
import org.bitcoins.core.currency._
|
||||
import org.bitcoins.node.Node
|
||||
import org.bitcoins.wallet.api.WalletApi
|
||||
import org.bitcoins.wallet.api.HDWalletApi
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.util.{Failure, Success}
|
||||
|
||||
case class WalletRoutes(wallet: WalletApi, node: Node)(implicit
|
||||
case class WalletRoutes(wallet: HDWalletApi, node: Node)(implicit
|
||||
system: ActorSystem)
|
||||
extends ServerRoute {
|
||||
import system.dispatcher
|
||||
|
|
|
@ -92,7 +92,7 @@ class NeutrinoNodeWithWalletTest extends NodeUnitTest {
|
|||
confirmedBalance <- wallet.getConfirmedBalance()
|
||||
unconfirmedBalance <- wallet.getUnconfirmedBalance()
|
||||
addresses <- wallet.listAddresses()
|
||||
utxos <- wallet.listUtxos()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
} yield {
|
||||
(expectedConfirmedAmount == confirmedBalance) &&
|
||||
(expectedUnconfirmedAmount == unconfirmedBalance) &&
|
||||
|
@ -175,7 +175,7 @@ class NeutrinoNodeWithWalletTest extends NodeUnitTest {
|
|||
|
||||
for {
|
||||
addresses <- wallet.listAddresses()
|
||||
utxos <- wallet.listUtxos()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
_ = assert(addresses.size == 6)
|
||||
_ = assert(utxos.size == 3)
|
||||
|
||||
|
@ -189,14 +189,14 @@ class NeutrinoNodeWithWalletTest extends NodeUnitTest {
|
|||
.sendToAddress(address, TestAmount)
|
||||
|
||||
addresses <- wallet.listAddresses()
|
||||
utxos <- wallet.listUtxos()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
_ = assert(addresses.size == 7)
|
||||
_ = assert(utxos.size == 3)
|
||||
|
||||
_ <- wallet.clearAllUtxosAndAddresses()
|
||||
|
||||
addresses <- wallet.listAddresses()
|
||||
utxos <- wallet.listUtxos()
|
||||
utxos <- wallet.listDefaultAccountUtxos()
|
||||
_ = assert(addresses.isEmpty)
|
||||
_ = assert(utxos.isEmpty)
|
||||
|
||||
|
|
|
@ -3,17 +3,16 @@ package org.bitcoins.wallet
|
|||
import org.bitcoins.core.hd.{AddressType, HDPurposes}
|
||||
import org.bitcoins.core.protocol.{Bech32Address, P2PKHAddress, P2SHAddress}
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.wallet.api.WalletApi
|
||||
import org.scalatest.FutureOutcome
|
||||
|
||||
class LegacyWalletTest extends BitcoinSWalletTest {
|
||||
|
||||
override type FixtureParam = WalletApi
|
||||
override type FixtureParam = Wallet
|
||||
|
||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
|
||||
withLegacyWallet(test)
|
||||
|
||||
it should "generate legacy addresses" in { wallet: WalletApi =>
|
||||
it should "generate legacy addresses" in { wallet: Wallet =>
|
||||
for {
|
||||
addr <- wallet.getNewAddress()
|
||||
account <- wallet.getDefaultAccount()
|
||||
|
|
|
@ -3,18 +3,17 @@ package org.bitcoins.wallet
|
|||
import org.bitcoins.core.hd.{AddressType, HDPurposes}
|
||||
import org.bitcoins.core.protocol.{Bech32Address, P2PKHAddress, P2SHAddress}
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.wallet.api.WalletApi
|
||||
import org.scalatest.FutureOutcome
|
||||
|
||||
class SegwitWalletTest extends BitcoinSWalletTest {
|
||||
|
||||
override type FixtureParam = WalletApi
|
||||
override type FixtureParam = Wallet
|
||||
|
||||
override def withFixture(test: OneArgAsyncTest): FutureOutcome = {
|
||||
withSegwitWallet(test)
|
||||
}
|
||||
|
||||
it should "generate segwit addresses" in { wallet: WalletApi =>
|
||||
it should "generate segwit addresses" in { wallet =>
|
||||
for {
|
||||
addr <- wallet.getNewAddress()
|
||||
account <- wallet.getDefaultAccount()
|
||||
|
|
|
@ -140,9 +140,9 @@ class UTXOLifeCycleTest extends BitcoinSWalletTest {
|
|||
|
||||
for {
|
||||
tx <- wallet.sendToOutputs(Vector(dummyOutput),
|
||||
Some(SatoshisPerVirtualByte.one),
|
||||
reserveUtxos = true)
|
||||
Some(SatoshisPerVirtualByte.one))
|
||||
_ <- wallet.processTransaction(tx, None)
|
||||
_ <- wallet.markUTXOsAsReserved(tx)
|
||||
|
||||
allReserved <- wallet.listUtxos(TxoState.Reserved)
|
||||
_ = assert(
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.bitcoins.core.protocol.BitcoinAddress
|
|||
import org.bitcoins.core.protocol.transaction.TransactionOutput
|
||||
import org.bitcoins.core.script.constant.{BytesToPushOntoStack, ScriptConstant}
|
||||
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.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet
|
||||
|
@ -62,10 +62,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
|
|||
val wallet = fundedWallet.wallet
|
||||
|
||||
for {
|
||||
tx <- wallet.sendToAddresses(addresses,
|
||||
amounts,
|
||||
feeRateOpt,
|
||||
reserveUtxos = false)
|
||||
tx <- wallet.sendToAddresses(addresses, amounts, feeRateOpt)
|
||||
} yield {
|
||||
val expectedOutputs = addresses.zip(amounts).map {
|
||||
case (addr, amount) => TransactionOutput(amount, addr.scriptPubKey)
|
||||
|
@ -79,10 +76,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
|
|||
val wallet = fundedWallet.wallet
|
||||
|
||||
val sendToAddressesF =
|
||||
wallet.sendToAddresses(addresses,
|
||||
amounts.tail,
|
||||
feeRateOpt,
|
||||
reserveUtxos = false)
|
||||
wallet.sendToAddresses(addresses, amounts.tail, feeRateOpt)
|
||||
|
||||
recoverToSucceededIf[IllegalArgumentException] {
|
||||
sendToAddressesF
|
||||
|
@ -94,10 +88,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
|
|||
val wallet = fundedWallet.wallet
|
||||
|
||||
val sendToAddressesF =
|
||||
wallet.sendToAddresses(addresses.tail,
|
||||
amounts,
|
||||
feeRateOpt,
|
||||
reserveUtxos = false)
|
||||
wallet.sendToAddresses(addresses.tail, amounts, feeRateOpt)
|
||||
|
||||
recoverToSucceededIf[IllegalArgumentException] {
|
||||
sendToAddressesF
|
||||
|
@ -112,8 +103,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
|
|||
}
|
||||
|
||||
for {
|
||||
tx <-
|
||||
wallet.sendToOutputs(expectedOutputs, feeRateOpt, reserveUtxos = false)
|
||||
tx <- wallet.sendToOutputs(expectedOutputs, feeRateOpt)
|
||||
} yield {
|
||||
assert(expectedOutputs.diff(tx.outputs).isEmpty)
|
||||
}
|
||||
|
@ -203,7 +193,7 @@ class WalletSendingTest extends BitcoinSWalletTest {
|
|||
)
|
||||
|
||||
val sendToAddressesF =
|
||||
wallet.sendToAddresses(addrs, amounts, feeRateOpt, reserveUtxos = false)
|
||||
wallet.sendToAddresses(addrs, amounts, feeRateOpt)
|
||||
|
||||
recoverToSucceededIf[IllegalArgumentException] {
|
||||
sendToAddressesF
|
||||
|
@ -237,7 +227,8 @@ class WalletSendingTest extends BitcoinSWalletTest {
|
|||
wallet: Wallet,
|
||||
algo: CoinSelectionAlgo): Future[Assertion] = {
|
||||
for {
|
||||
allUtxos <- wallet.listUtxos()
|
||||
account <- wallet.getDefaultAccount()
|
||||
allUtxos <- wallet.listUtxos(account.hdAccount)
|
||||
output = TransactionOutput(amountToSend, testAddress.scriptPubKey)
|
||||
expectedUtxos = CoinSelector.selectByAlgo(algo,
|
||||
allUtxos,
|
||||
|
|
|
@ -3,16 +3,14 @@ package org.bitcoins.wallet
|
|||
import java.nio.file.Files
|
||||
|
||||
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.util.FutureUtil
|
||||
import org.bitcoins.crypto.AesPassword
|
||||
import org.bitcoins.keymanager.KeyManagerUnlockError.MnemonicNotFound
|
||||
import org.bitcoins.keymanager.{KeyManagerUnlockError, WalletStorage}
|
||||
import org.bitcoins.testkit.EmbeddedPg
|
||||
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||
import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse
|
||||
import org.bitcoins.wallet.api.WalletApi
|
||||
import org.bitcoins.wallet.models.AddressDb
|
||||
import org.scalatest.FutureOutcome
|
||||
import org.scalatest.compatible.Assertion
|
||||
|
@ -30,7 +28,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
behavior of "Wallet - unit test"
|
||||
|
||||
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')
|
||||
//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.
|
||||
|
@ -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 {
|
||||
accounts <- wallet.listAccounts()
|
||||
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 {
|
||||
addr <- wallet.getNewAddress()
|
||||
otherAddr <- wallet.getNewAddress()
|
||||
|
@ -63,9 +61,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
}
|
||||
}
|
||||
|
||||
it should "know what the last address index is" in { walletApi =>
|
||||
val wallet = walletApi.asInstanceOf[Wallet]
|
||||
|
||||
it should "know what the last address index is" in { wallet =>
|
||||
def getMostRecent(
|
||||
hdAccount: HDAccount,
|
||||
chain: HDChainType): Future[AddressDb] = {
|
||||
|
@ -138,7 +134,7 @@ class WalletUnitTest extends BitcoinSWalletTest {
|
|||
}
|
||||
|
||||
it should "fail to unlock the wallet with a bad password" in {
|
||||
wallet: WalletApi =>
|
||||
wallet: Wallet =>
|
||||
val badpassphrase = AesPassword.fromNonEmptyString("bad")
|
||||
|
||||
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 {
|
||||
matched <- wallet.getMatchingBlocks(
|
||||
scripts = Vector(
|
||||
|
|
|
@ -50,7 +50,7 @@ import scala.concurrent.{ExecutionContext, Future}
|
|||
import scala.util.{Failure, Success}
|
||||
|
||||
abstract class Wallet
|
||||
extends WalletApi
|
||||
extends HDWalletApi
|
||||
with UtxoHandling
|
||||
with AddressHandling
|
||||
with AccountHandling
|
||||
|
@ -367,7 +367,8 @@ abstract class Wallet
|
|||
address: BitcoinAddress,
|
||||
amount: CurrencyUnit,
|
||||
feeRate: FeeUnit,
|
||||
fromAccount: AccountDb): Future[Transaction] = {
|
||||
fromAccount: AccountDb,
|
||||
newTags: Vector[AddressTag]): Future[Transaction] = {
|
||||
require(
|
||||
address.networkParameters.isSameNetworkBytes(networkParameters),
|
||||
s"Cannot send to address on other network, got ${address.networkParameters}"
|
||||
|
@ -396,7 +397,7 @@ abstract class Wallet
|
|||
feeRate,
|
||||
changeAddr.scriptPubKey)
|
||||
|
||||
tx <- finishSend(txBuilder, utxos, amount, feeRate, Vector.empty)
|
||||
tx <- finishSend(txBuilder, utxos, amount, feeRate, newTags)
|
||||
} yield tx
|
||||
}
|
||||
|
||||
|
@ -455,7 +456,7 @@ abstract class Wallet
|
|||
amounts: Vector[CurrencyUnit],
|
||||
feeRate: FeeUnit,
|
||||
fromAccount: AccountDb,
|
||||
reserveUtxos: Boolean): Future[Transaction] = {
|
||||
newTags: Vector[AddressTag]): Future[Transaction] = {
|
||||
require(amounts.size == addresses.size,
|
||||
"Must have an amount for every address")
|
||||
require(
|
||||
|
@ -468,7 +469,7 @@ abstract class Wallet
|
|||
logger.info(s"Sending $amount to $address at feerate $feeRate")
|
||||
TransactionOutput(amount, address.scriptPubKey)
|
||||
}
|
||||
sendToOutputs(destinations, feeRate, fromAccount, reserveUtxos)
|
||||
sendToOutputs(destinations, feeRate, fromAccount, newTags)
|
||||
}
|
||||
|
||||
override def makeOpReturnCommitment(
|
||||
|
@ -492,24 +493,23 @@ abstract class Wallet
|
|||
|
||||
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],
|
||||
feeRate: FeeUnit,
|
||||
fromAccount: AccountDb,
|
||||
reserveUtxos: Boolean): Future[Transaction] = {
|
||||
newTags: Vector[AddressTag]): Future[Transaction] = {
|
||||
for {
|
||||
(txBuilder, utxoInfos) <- fundRawTransactionInternal(
|
||||
destinations = outputs,
|
||||
feeRate = feeRate,
|
||||
fromAccount = fromAccount,
|
||||
keyManagerOpt = Some(keyManager),
|
||||
fromTagOpt = None,
|
||||
markAsReserved = reserveUtxos)
|
||||
fromTagOpt = None)
|
||||
sentAmount = outputs.foldLeft(CurrencyUnits.zero)(_ + _.value)
|
||||
tx <- finishSend(txBuilder, utxoInfos, sentAmount, feeRate, Vector.empty)
|
||||
tx <- finishSend(txBuilder, utxoInfos, sentAmount, feeRate, newTags)
|
||||
} yield tx
|
||||
}
|
||||
|
||||
|
|
520
wallet/src/main/scala/org/bitcoins/wallet/api/HDWalletApi.scala
Normal file
520
wallet/src/main/scala/org/bitcoins/wallet/api/HDWalletApi.scala
Normal 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]
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@ import org.bitcoins.core.bloom.BloomFilter
|
|||
import org.bitcoins.core.config.NetworkParameters
|
||||
import org.bitcoins.core.currency.CurrencyUnit
|
||||
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.script.ScriptPubKey
|
||||
import org.bitcoins.core.protocol.transaction.{
|
||||
|
@ -27,10 +27,10 @@ import org.bitcoins.crypto.{
|
|||
}
|
||||
import org.bitcoins.keymanager._
|
||||
import org.bitcoins.keymanager.bip39.BIP39KeyManager
|
||||
import org.bitcoins.wallet.WalletLogger
|
||||
import org.bitcoins.wallet.api.WalletApi.BlockMatchingResponse
|
||||
import org.bitcoins.wallet.config.WalletAppConfig
|
||||
import org.bitcoins.wallet.models.{AccountDb, AddressDb, SpendingInfoDb}
|
||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||
import org.bitcoins.wallet.models.{AddressDb, SpendingInfoDb}
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.{Failure, Success}
|
||||
|
@ -120,18 +120,6 @@ trait WalletApi extends WalletLogger {
|
|||
} 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 */
|
||||
def getBalance(tag: AddressTag): Future[CurrencyUnit] = {
|
||||
val confirmedF = getConfirmedBalance(tag)
|
||||
|
@ -146,15 +134,11 @@ trait WalletApi extends WalletLogger {
|
|||
/** Gets the sum of all confirmed UTXOs in this wallet */
|
||||
def getConfirmedBalance(): Future[CurrencyUnit]
|
||||
|
||||
def getConfirmedBalance(account: HDAccount): Future[CurrencyUnit]
|
||||
|
||||
def getConfirmedBalance(tag: AddressTag): Future[CurrencyUnit]
|
||||
|
||||
/** Gets the sum of all unconfirmed UTXOs in this wallet */
|
||||
def getUnconfirmedBalance(): Future[CurrencyUnit]
|
||||
|
||||
def getUnconfirmedBalance(account: HDAccount): Future[CurrencyUnit]
|
||||
|
||||
def getUnconfirmedBalance(tag: AddressTag): Future[CurrencyUnit]
|
||||
|
||||
/**
|
||||
|
@ -169,40 +153,24 @@ trait WalletApi extends WalletLogger {
|
|||
*/
|
||||
def listUtxos(): Future[Vector[SpendingInfoDb]]
|
||||
|
||||
def listUtxos(account: HDAccount): 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(
|
||||
hdAccount: HDAccount,
|
||||
state: TxoState): Future[Vector[SpendingInfoDb]]
|
||||
|
||||
def listAddresses(): Future[Vector[AddressDb]]
|
||||
|
||||
def listAddresses(account: HDAccount): Future[Vector[AddressDb]]
|
||||
|
||||
def listSpentAddresses(): Future[Vector[AddressDb]]
|
||||
|
||||
def listSpentAddresses(account: HDAccount): Future[Vector[AddressDb]]
|
||||
|
||||
def listFundedAddresses(): Future[Vector[(AddressDb, CurrencyUnit)]]
|
||||
|
||||
def listFundedAddresses(
|
||||
account: HDAccount): Future[Vector[(AddressDb, CurrencyUnit)]]
|
||||
|
||||
def listUnusedAddresses(): Future[Vector[AddressDb]]
|
||||
|
||||
def listUnusedAddresses(account: HDAccount): Future[Vector[AddressDb]]
|
||||
|
||||
def markUTXOsAsReserved(
|
||||
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(
|
||||
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]]
|
||||
|
||||
|
@ -212,15 +180,6 @@ trait WalletApi extends WalletLogger {
|
|||
/** Checks if the wallet contains any data */
|
||||
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.
|
||||
* Don't call this unless you are sure you can recover
|
||||
* your wallet
|
||||
|
@ -235,7 +194,7 @@ trait WalletApi extends WalletLogger {
|
|||
def getNewAddress(addressType: AddressType): Future[BitcoinAddress]
|
||||
|
||||
/**
|
||||
* Gets a new external address from the default account.
|
||||
* Gets a new external address
|
||||
* Calling this method multiple
|
||||
* times will return the same address, until it has
|
||||
* received funds.
|
||||
|
@ -257,43 +216,19 @@ trait WalletApi extends WalletLogger {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets a external address from the account associated with
|
||||
* the given AddressType. Calling this method multiple
|
||||
* times will return the same address, until it has
|
||||
* 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 from the default account.
|
||||
* Calling this method multiple
|
||||
* Gets a external address. Calling this method multiple
|
||||
* times will return the same address, until it has
|
||||
* received funds.
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
@ -317,29 +252,7 @@ trait WalletApi extends WalletLogger {
|
|||
}
|
||||
|
||||
/** Generates a new change address */
|
||||
protected[wallet] def getNewChangeAddress(
|
||||
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]
|
||||
protected[wallet] def getNewChangeAddress(): Future[BitcoinAddress]
|
||||
|
||||
/**
|
||||
* Unlocks the wallet with the provided passphrase,
|
||||
|
@ -349,15 +262,6 @@ trait WalletApi extends WalletLogger {
|
|||
KeyManagerUnlockError,
|
||||
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
|
||||
*
|
||||
|
@ -406,37 +310,15 @@ trait WalletApi extends WalletLogger {
|
|||
* @param endOpt end block (if None it ends at the current tip)
|
||||
* @param addressBatchSize how many addresses to match in a single pass
|
||||
*/
|
||||
|
||||
def rescanNeutrinoWallet(
|
||||
account: HDAccount,
|
||||
startOpt: Option[BlockStamp],
|
||||
endOpt: Option[BlockStamp],
|
||||
addressBatchSize: Int,
|
||||
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. */
|
||||
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)
|
||||
def fullRescanNeutrinoWallet(addressBatchSize: Int): Future[Unit]
|
||||
|
||||
/**
|
||||
* Recreates the account using BIP-44 approach
|
||||
|
@ -447,7 +329,7 @@ trait WalletApi extends WalletLogger {
|
|||
|
||||
def keyManager: BIP39KeyManager
|
||||
|
||||
private def determineFeeRate(feeRateOpt: Option[FeeUnit]): Future[FeeUnit] =
|
||||
protected def determineFeeRate(feeRateOpt: Option[FeeUnit]): Future[FeeUnit] =
|
||||
feeRateOpt match {
|
||||
case None =>
|
||||
feeRateApi.getFeeRate
|
||||
|
@ -459,29 +341,17 @@ trait WalletApi extends WalletLogger {
|
|||
outPoints: Vector[TransactionOutPoint],
|
||||
address: BitcoinAddress,
|
||||
amount: CurrencyUnit,
|
||||
feeRate: FeeUnit,
|
||||
fromAccount: AccountDb): Future[Transaction]
|
||||
feeRate: FeeUnit): Future[Transaction]
|
||||
|
||||
def sendFromOutPoints(
|
||||
outPoints: Vector[TransactionOutPoint],
|
||||
address: BitcoinAddress,
|
||||
amount: CurrencyUnit,
|
||||
feeRateOpt: Option[FeeUnit],
|
||||
fromAccount: AccountDb): Future[Transaction] = {
|
||||
feeRateOpt: Option[FeeUnit]
|
||||
): Future[Transaction] = {
|
||||
for {
|
||||
feeRate <- determineFeeRate(feeRateOpt)
|
||||
tx <- sendFromOutPoints(outPoints, address, amount, feeRate, fromAccount)
|
||||
} 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)
|
||||
tx <- sendFromOutPoints(outPoints, address, amount, feeRate)
|
||||
} yield tx
|
||||
}
|
||||
|
||||
|
@ -489,215 +359,92 @@ trait WalletApi extends WalletLogger {
|
|||
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)
|
||||
algo: CoinSelectionAlgo): Future[Transaction]
|
||||
|
||||
def sendWithAlgo(
|
||||
address: BitcoinAddress,
|
||||
amount: CurrencyUnit,
|
||||
feeRateOpt: Option[FeeUnit],
|
||||
algo: CoinSelectionAlgo,
|
||||
fromAccount: AccountDb): Future[Transaction] = {
|
||||
algo: CoinSelectionAlgo
|
||||
): Future[Transaction] = {
|
||||
for {
|
||||
feeRate <- determineFeeRate(feeRateOpt)
|
||||
tx <- sendWithAlgo(address, amount, feeRate, algo, fromAccount)
|
||||
} 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)
|
||||
tx <- sendWithAlgo(address, amount, feeRate, algo)
|
||||
} yield tx
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends money from the specified account
|
||||
* Sends money to the address
|
||||
*
|
||||
* todo: add error handling to signature
|
||||
*/
|
||||
def sendToAddress(
|
||||
address: BitcoinAddress,
|
||||
amount: CurrencyUnit,
|
||||
feeRate: FeeUnit,
|
||||
fromAccount: AccountDb): Future[Transaction]
|
||||
feeRate: FeeUnit): 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(
|
||||
address: BitcoinAddress,
|
||||
amount: CurrencyUnit,
|
||||
feeRateOpt: Option[FeeUnit]
|
||||
): Future[Transaction] = {
|
||||
for {
|
||||
account <- getDefaultAccount()
|
||||
tx <- sendToAddress(address, amount, feeRateOpt, account)
|
||||
feeRate <- determineFeeRate(feeRateOpt)
|
||||
tx <- sendToAddress(address, amount, feeRate)
|
||||
} 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
|
||||
*/
|
||||
def sendToOutputs(
|
||||
outputs: Vector[TransactionOutput],
|
||||
feeRate: FeeUnit,
|
||||
fromAccount: AccountDb,
|
||||
reserveUtxos: Boolean): Future[Transaction]
|
||||
|
||||
def sendToOutputs(
|
||||
outputs: Vector[TransactionOutput],
|
||||
feeRateOpt: Option[FeeUnit],
|
||||
fromAccount: AccountDb,
|
||||
reserveUtxos: Boolean): Future[Transaction] = {
|
||||
feeRateOpt: Option[FeeUnit]): Future[Transaction] = {
|
||||
for {
|
||||
feeRate <- determineFeeRate(feeRateOpt)
|
||||
tx <- sendToOutputs(outputs, feeRate, fromAccount, reserveUtxos)
|
||||
tx <- sendToOutputs(outputs, feeRate)
|
||||
} yield tx
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends money from the default account
|
||||
*
|
||||
* todo: add error handling to signature
|
||||
*/
|
||||
def sendToOutputs(
|
||||
outputs: Vector[TransactionOutput],
|
||||
feeRateOpt: Option[FeeUnit],
|
||||
reserveUtxos: Boolean): Future[Transaction] = {
|
||||
for {
|
||||
account <- getDefaultAccount()
|
||||
tx <- sendToOutputs(outputs, feeRateOpt, account, reserveUtxos)
|
||||
} yield tx
|
||||
}
|
||||
feeRate: FeeUnit): Future[Transaction]
|
||||
|
||||
/**
|
||||
* Sends money from the specified account
|
||||
*
|
||||
* todo: add error handling to signature
|
||||
* Sends funds to each address
|
||||
*/
|
||||
def sendToAddresses(
|
||||
addresses: Vector[BitcoinAddress],
|
||||
amounts: Vector[CurrencyUnit],
|
||||
feeRate: FeeUnit,
|
||||
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] = {
|
||||
feeRateOpt: Option[FeeUnit]): Future[Transaction] = {
|
||||
for {
|
||||
feeRate <- determineFeeRate(feeRateOpt)
|
||||
tx <-
|
||||
sendToAddresses(addresses, amounts, feeRate, fromAccount, reserveUtxos)
|
||||
tx <- sendToAddresses(addresses, amounts, feeRate)
|
||||
} yield tx
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends money from the default account
|
||||
*
|
||||
* todo: add error handling to signature
|
||||
*/
|
||||
def sendToAddresses(
|
||||
addresses: Vector[BitcoinAddress],
|
||||
amounts: Vector[CurrencyUnit],
|
||||
feeRateOpt: Option[FeeUnit],
|
||||
reserveUtxos: Boolean): Future[Transaction] = {
|
||||
for {
|
||||
account <- getDefaultAccount()
|
||||
tx <-
|
||||
sendToAddresses(addresses, amounts, feeRateOpt, account, reserveUtxos)
|
||||
} yield tx
|
||||
}
|
||||
feeRate: FeeUnit): Future[Transaction]
|
||||
|
||||
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
|
||||
}
|
||||
feeRate: FeeUnit): Future[Transaction]
|
||||
|
||||
def makeOpReturnCommitment(
|
||||
message: String,
|
||||
hashMessage: Boolean,
|
||||
feeRateOpt: Option[FeeUnit]): Future[Transaction] = {
|
||||
for {
|
||||
account <- getDefaultAccount()
|
||||
tx <- makeOpReturnCommitment(message, hashMessage, feeRateOpt, account)
|
||||
feeRate <- determineFeeRate(feeRateOpt)
|
||||
tx <- makeOpReturnCommitment(message, hashMessage, feeRate)
|
||||
} 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 {
|
||||
|
|
|
@ -14,7 +14,7 @@ import org.bitcoins.keymanager.{
|
|||
KeyManagerParams,
|
||||
WalletStorage
|
||||
}
|
||||
import org.bitcoins.wallet.api.WalletApi
|
||||
import org.bitcoins.wallet.api.HDWalletApi
|
||||
import org.bitcoins.wallet.db.WalletDbManagement
|
||||
import org.bitcoins.wallet.models.AccountDAO
|
||||
import org.bitcoins.wallet.{Wallet, WalletLogger}
|
||||
|
@ -157,16 +157,17 @@ case class WalletAppConfig(
|
|||
}
|
||||
|
||||
/** Creates a wallet based on this [[WalletAppConfig]] */
|
||||
def createWallet(
|
||||
def createHDWallet(
|
||||
nodeApi: NodeApi,
|
||||
chainQueryApi: ChainQueryApi,
|
||||
feeRateApi: FeeRateApi,
|
||||
bip39PasswordOpt: Option[String])(implicit
|
||||
ec: ExecutionContext): Future[WalletApi] = {
|
||||
WalletAppConfig.createWallet(nodeApi = nodeApi,
|
||||
chainQueryApi = chainQueryApi,
|
||||
feeRateApi = feeRateApi,
|
||||
bip39PasswordOpt = bip39PasswordOpt)(this, ec)
|
||||
ec: ExecutionContext): Future[HDWalletApi] = {
|
||||
WalletAppConfig.createHDWallet(
|
||||
nodeApi = nodeApi,
|
||||
chainQueryApi = chainQueryApi,
|
||||
feeRateApi = feeRateApi,
|
||||
bip39PasswordOpt = bip39PasswordOpt)(this, ec)
|
||||
}
|
||||
|
||||
/** Starts the associated application */
|
||||
|
@ -187,13 +188,13 @@ object WalletAppConfig
|
|||
WalletAppConfig(datadir, useLogbackConf, confs: _*)
|
||||
|
||||
/** Creates a wallet based on the given [[WalletAppConfig]] */
|
||||
def createWallet(
|
||||
def createHDWallet(
|
||||
nodeApi: NodeApi,
|
||||
chainQueryApi: ChainQueryApi,
|
||||
feeRateApi: FeeRateApi,
|
||||
bip39PasswordOpt: Option[String])(implicit
|
||||
walletConf: WalletAppConfig,
|
||||
ec: ExecutionContext): Future[WalletApi] = {
|
||||
ec: ExecutionContext): Future[HDWalletApi] = {
|
||||
walletConf.hasWallet().flatMap { walletExists =>
|
||||
if (walletExists) {
|
||||
logger.info(s"Using pre-existing wallet")
|
||||
|
|
|
@ -36,7 +36,7 @@ private[wallet] trait UtxoHandling extends WalletLogger {
|
|||
|
||||
/** @inheritdoc */
|
||||
override def listUtxos(): Future[Vector[SpendingInfoDb]] = {
|
||||
listUtxos(walletConfig.defaultAccount)
|
||||
spendingInfoDAO.findAllUnspent()
|
||||
}
|
||||
|
||||
override def listUtxos(
|
||||
|
@ -255,6 +255,15 @@ private[wallet] trait UtxoHandling extends WalletLogger {
|
|||
} 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(
|
||||
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] = {
|
||||
val unreserved = utxos.filterNot(_.state == TxoState.Reserved)
|
||||
|
|
Loading…
Add table
Reference in a new issue