mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-15 20:30:17 +01:00
Add listFundedAddresses call (#1407)
This commit is contained in:
parent
721b28aefd
commit
711f5cb99c
8 changed files with 95 additions and 2 deletions
|
@ -169,6 +169,9 @@ object ConsoleCli {
|
||||||
.action((_, conf) => conf.copy(command = GetSpentAddresses))
|
.action((_, conf) => conf.copy(command = GetSpentAddresses))
|
||||||
.text(
|
.text(
|
||||||
"Returns list of all wallet addresses that have received funds and been spent"),
|
"Returns list of all wallet addresses that have received funds and been spent"),
|
||||||
|
cmd("getfundedaddresses")
|
||||||
|
.action((_, conf) => conf.copy(command = GetFundedAddresses))
|
||||||
|
.text("Returns list of all wallet addresses that are holding funds"),
|
||||||
cmd("getaccounts")
|
cmd("getaccounts")
|
||||||
.action((_, conf) => conf.copy(command = GetAccounts))
|
.action((_, conf) => conf.copy(command = GetAccounts))
|
||||||
.text("Returns list of all wallet accounts"),
|
.text("Returns list of all wallet accounts"),
|
||||||
|
@ -402,6 +405,8 @@ object ConsoleCli {
|
||||||
RequestParam("getaddresses")
|
RequestParam("getaddresses")
|
||||||
case GetSpentAddresses =>
|
case GetSpentAddresses =>
|
||||||
RequestParam("getspentaddresses")
|
RequestParam("getspentaddresses")
|
||||||
|
case GetFundedAddresses =>
|
||||||
|
RequestParam("getfundedaddresses")
|
||||||
case GetAccounts =>
|
case GetAccounts =>
|
||||||
RequestParam("getaccounts")
|
RequestParam("getaccounts")
|
||||||
case CreateNewAccount =>
|
case CreateNewAccount =>
|
||||||
|
@ -577,6 +582,7 @@ object CliCommand {
|
||||||
case object GetUtxos extends CliCommand
|
case object GetUtxos extends CliCommand
|
||||||
case object GetAddresses extends CliCommand
|
case object GetAddresses extends CliCommand
|
||||||
case object GetSpentAddresses extends CliCommand
|
case object GetSpentAddresses extends CliCommand
|
||||||
|
case object GetFundedAddresses extends CliCommand
|
||||||
case object GetAccounts extends CliCommand
|
case object GetAccounts extends CliCommand
|
||||||
case object CreateNewAccount extends CliCommand
|
case object CreateNewAccount extends CliCommand
|
||||||
case object IsEmpty extends CliCommand
|
case object IsEmpty extends CliCommand
|
||||||
|
|
|
@ -8,7 +8,7 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
import org.bitcoins.chain.api.ChainApi
|
import org.bitcoins.chain.api.ChainApi
|
||||||
import org.bitcoins.core.Core
|
import org.bitcoins.core.Core
|
||||||
import org.bitcoins.core.crypto.ExtPublicKey
|
import org.bitcoins.core.crypto.ExtPublicKey
|
||||||
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit}
|
import org.bitcoins.core.currency.{Bitcoins, CurrencyUnit, Satoshis}
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
import org.bitcoins.core.protocol.BlockStamp.{
|
import org.bitcoins.core.protocol.BlockStamp.{
|
||||||
BlockHash,
|
BlockHash,
|
||||||
|
@ -373,6 +373,31 @@ class RoutesSpec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"return the wallet's funded addresses" in {
|
||||||
|
val addressDb = LegacyAddressDb(
|
||||||
|
LegacyHDPath(HDCoinType.Testnet, 0, HDChainType.External, 0),
|
||||||
|
ECPublicKey.freshPublicKey,
|
||||||
|
Sha256Hash160Digest.fromBytes(ByteVector.low(20)),
|
||||||
|
testAddress.asInstanceOf[P2PKHAddress],
|
||||||
|
testAddress.scriptPubKey
|
||||||
|
)
|
||||||
|
|
||||||
|
(mockWalletApi.listFundedAddresses: () => Future[Vector[(
|
||||||
|
AddressDb,
|
||||||
|
CurrencyUnit)]])
|
||||||
|
.expects()
|
||||||
|
.returning(Future.successful(Vector((addressDb, Satoshis.zero))))
|
||||||
|
|
||||||
|
val route =
|
||||||
|
walletRoutes.handleCommand(ServerCommand("getfundedaddresses", Arr()))
|
||||||
|
|
||||||
|
Get() ~> route ~> check {
|
||||||
|
contentType shouldEqual `application/json`
|
||||||
|
responseAs[String] shouldEqual
|
||||||
|
s"""{"result":["$testAddressStr ${Satoshis.zero}"],"error":null}""".stripMargin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"return the wallet accounts" in {
|
"return the wallet accounts" in {
|
||||||
val xpub = ExtPublicKey
|
val xpub = ExtPublicKey
|
||||||
.fromString(
|
.fromString(
|
||||||
|
|
|
@ -266,7 +266,7 @@ object SendFromOutpoints extends ServerJsonModels {
|
||||||
case other =>
|
case other =>
|
||||||
Failure(
|
Failure(
|
||||||
new IllegalArgumentException(
|
new IllegalArgumentException(
|
||||||
s"Bad number of arguments: ${other.length}. Expected: 3"))
|
s"Bad number of arguments: ${other.length}. Expected: 4"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,17 @@ case class WalletRoutes(wallet: WalletApi, node: Node)(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ServerCommand("getfundedaddresses", _) =>
|
||||||
|
complete {
|
||||||
|
wallet.listFundedAddresses().map { addressDbs =>
|
||||||
|
val addressAndValues = addressDbs.map {
|
||||||
|
case (addressDb, value) => s"${addressDb.address} $value"
|
||||||
|
}
|
||||||
|
|
||||||
|
Server.httpSuccess(addressAndValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case ServerCommand("getaccounts", _) =>
|
case ServerCommand("getaccounts", _) =>
|
||||||
complete {
|
complete {
|
||||||
wallet.listAccounts().map { accounts =>
|
wallet.listAccounts().map { accounts =>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.bitcoins.wallet
|
package org.bitcoins.wallet
|
||||||
|
|
||||||
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
|
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
|
||||||
|
import org.bitcoins.core.protocol.transaction.TransactionOutput
|
||||||
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
|
||||||
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet
|
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet
|
||||||
import org.bitcoins.rpc.util.AsyncUtil
|
import org.bitcoins.rpc.util.AsyncUtil
|
||||||
|
@ -136,4 +137,19 @@ class AddressHandlingTest extends BitcoinSWalletTest {
|
||||||
assert(diff.isEmpty, s"Extra spent addresses $diff")
|
assert(diff.isEmpty, s"Extra spent addresses $diff")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it must "get the correct funded addresses" in { fundedWallet: FundedWallet =>
|
||||||
|
val wallet = fundedWallet.wallet
|
||||||
|
|
||||||
|
for {
|
||||||
|
unspentDbs <- wallet.spendingInfoDAO.findAllUnspent()
|
||||||
|
fundedAddresses <- wallet.listFundedAddresses()
|
||||||
|
} yield {
|
||||||
|
val diff = unspentDbs
|
||||||
|
.map(_.output)
|
||||||
|
.diff(fundedAddresses.map(tuple =>
|
||||||
|
TransactionOutput(tuple._2, tuple._1.scriptPubKey)))
|
||||||
|
assert(diff.isEmpty, s"Extra funded addresses $diff")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,6 +176,11 @@ trait WalletApi extends WalletLogger {
|
||||||
|
|
||||||
def listSpentAddresses(account: HDAccount): 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 markUTXOsAsReserved(
|
def markUTXOsAsReserved(
|
||||||
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]]
|
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.bitcoins.wallet.internal
|
package org.bitcoins.wallet.internal
|
||||||
|
|
||||||
|
import org.bitcoins.core.currency.CurrencyUnit
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
import org.bitcoins.core.number.UInt32
|
import org.bitcoins.core.number.UInt32
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
|
@ -70,6 +71,21 @@ private[wallet] trait AddressHandling extends WalletLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def listFundedAddresses(): Future[
|
||||||
|
Vector[(AddressDb, CurrencyUnit)]] = {
|
||||||
|
addressDAO.getFundedAddresses
|
||||||
|
}
|
||||||
|
|
||||||
|
override def listFundedAddresses(
|
||||||
|
account: HDAccount): Future[Vector[(AddressDb, CurrencyUnit)]] = {
|
||||||
|
val spentAddressesF = addressDAO.getFundedAddresses
|
||||||
|
|
||||||
|
spentAddressesF.map { spentAddresses =>
|
||||||
|
spentAddresses.filter(addr =>
|
||||||
|
HDAccount.isSameAccount(addr._1.path, account))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Enumerates the public keys in this wallet */
|
/** Enumerates the public keys in this wallet */
|
||||||
protected[wallet] def listPubkeys(): Future[Vector[ECPublicKey]] =
|
protected[wallet] def listPubkeys(): Future[Vector[ECPublicKey]] =
|
||||||
addressDAO.findAllPubkeys()
|
addressDAO.findAllPubkeys()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.bitcoins.wallet.models
|
package org.bitcoins.wallet.models
|
||||||
|
|
||||||
|
import org.bitcoins.core.currency.CurrencyUnit
|
||||||
import org.bitcoins.core.hd.{
|
import org.bitcoins.core.hd.{
|
||||||
HDAccount,
|
HDAccount,
|
||||||
HDChainType,
|
HDChainType,
|
||||||
|
@ -126,6 +127,19 @@ case class AddressDAO()(
|
||||||
safeDatabase.runVec(query.result)
|
safeDatabase.runVec(query.result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def getFundedAddresses: Future[Vector[(AddressDb, CurrencyUnit)]] = {
|
||||||
|
val query = table
|
||||||
|
.join(spendingInfoTable)
|
||||||
|
.on(_.scriptPubKey === _.scriptPubKey)
|
||||||
|
.filter(_._2.state.inSet(TxoState.receivedStates))
|
||||||
|
|
||||||
|
safeDatabase
|
||||||
|
.runVec(query.result)
|
||||||
|
.map(_.map {
|
||||||
|
case (addrDb, utxoDb) => (addrDb, utxoDb.output.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private def findMostRecentForChain(
|
private def findMostRecentForChain(
|
||||||
account: HDAccount,
|
account: HDAccount,
|
||||||
chain: HDChainType): slick.sql.SqlAction[
|
chain: HDChainType): slick.sql.SqlAction[
|
||||||
|
|
Loading…
Add table
Reference in a new issue