Add listFundedAddresses call (#1407)

This commit is contained in:
Ben Carman 2020-05-12 07:33:22 -05:00 committed by GitHub
parent 721b28aefd
commit 711f5cb99c
8 changed files with 95 additions and 2 deletions

View file

@ -169,6 +169,9 @@ object ConsoleCli {
.action((_, conf) => conf.copy(command = GetSpentAddresses))
.text(
"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")
.action((_, conf) => conf.copy(command = GetAccounts))
.text("Returns list of all wallet accounts"),
@ -402,6 +405,8 @@ object ConsoleCli {
RequestParam("getaddresses")
case GetSpentAddresses =>
RequestParam("getspentaddresses")
case GetFundedAddresses =>
RequestParam("getfundedaddresses")
case GetAccounts =>
RequestParam("getaccounts")
case CreateNewAccount =>
@ -577,6 +582,7 @@ object CliCommand {
case object GetUtxos extends CliCommand
case object GetAddresses extends CliCommand
case object GetSpentAddresses extends CliCommand
case object GetFundedAddresses extends CliCommand
case object GetAccounts extends CliCommand
case object CreateNewAccount extends CliCommand
case object IsEmpty extends CliCommand

View file

@ -8,7 +8,7 @@ import akka.http.scaladsl.testkit.ScalatestRouteTest
import org.bitcoins.chain.api.ChainApi
import org.bitcoins.core.Core
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.protocol.BlockStamp.{
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 {
val xpub = ExtPublicKey
.fromString(

View file

@ -266,7 +266,7 @@ object SendFromOutpoints extends ServerJsonModels {
case other =>
Failure(
new IllegalArgumentException(
s"Bad number of arguments: ${other.length}. Expected: 3"))
s"Bad number of arguments: ${other.length}. Expected: 4"))
}
}

View file

@ -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", _) =>
complete {
wallet.listAccounts().map { accounts =>

View file

@ -1,6 +1,7 @@
package org.bitcoins.wallet
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
import org.bitcoins.core.protocol.transaction.TransactionOutput
import org.bitcoins.core.wallet.fee.SatoshisPerVirtualByte
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedWallet
import org.bitcoins.rpc.util.AsyncUtil
@ -136,4 +137,19 @@ class AddressHandlingTest extends BitcoinSWalletTest {
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")
}
}
}

View file

@ -176,6 +176,11 @@ trait WalletApi extends WalletLogger {
def listSpentAddresses(account: HDAccount): Future[Vector[AddressDb]]
def listFundedAddresses(): Future[Vector[(AddressDb, CurrencyUnit)]]
def listFundedAddresses(
account: HDAccount): Future[Vector[(AddressDb, CurrencyUnit)]]
def markUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]]

View file

@ -1,5 +1,6 @@
package org.bitcoins.wallet.internal
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.hd._
import org.bitcoins.core.number.UInt32
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 */
protected[wallet] def listPubkeys(): Future[Vector[ECPublicKey]] =
addressDAO.findAllPubkeys()

View file

@ -1,5 +1,6 @@
package org.bitcoins.wallet.models
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.hd.{
HDAccount,
HDChainType,
@ -126,6 +127,19 @@ case class AddressDAO()(
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(
account: HDAccount,
chain: HDChainType): slick.sql.SqlAction[