mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-15 20:30:17 +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.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
|
||||||
|
|
|
@ -89,10 +89,10 @@ 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)
|
||||||
} yield {
|
} yield {
|
||||||
logger.info(s"Done configuring wallet")
|
logger.info(s"Done configuring wallet")
|
||||||
wallet
|
wallet
|
||||||
|
@ -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] = {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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.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 {
|
||||||
|
|
|
@ -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,16 +157,17 @@ 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(
|
||||||
chainQueryApi = chainQueryApi,
|
nodeApi = nodeApi,
|
||||||
feeRateApi = feeRateApi,
|
chainQueryApi = chainQueryApi,
|
||||||
bip39PasswordOpt = bip39PasswordOpt)(this, ec)
|
feeRateApi = feeRateApi,
|
||||||
|
bip39PasswordOpt = bip39PasswordOpt)(this, ec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Starts the associated application */
|
/** Starts the associated application */
|
||||||
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Reference in a new issue