Create a simplified version of the WalletApi.unmarkUTXOsAsReserved() that just takes in a tx and scans outpoints if they are in our wallet, also move the mark/unmark methods out of Wallet.scala and into UtxoHandling.scala (#1463)

This commit is contained in:
Chris Stewart 2020-05-25 14:45:38 -05:00 committed by GitHub
parent d8170f319a
commit c6aae0dbf9
4 changed files with 84 additions and 58 deletions

View File

@ -99,4 +99,21 @@ class UTXOLifeCycleTest extends BitcoinSWalletTest {
assert(unreservedUtxos.forall(_.state != TxoState.Reserved))
}
}
it should "track a utxo state change to reserved and then to unreserved using the transaction the utxo was included in" in {
param =>
val WalletWithBitcoindRpc(wallet, _) = param
val dummyOutput = TransactionOutput(Satoshis(3000), EmptyScriptPubKey)
for {
tx <- wallet.fundRawTransaction(Vector(dummyOutput),
SatoshisPerVirtualByte.one,
markAsReserved = true)
unreservedUtxos <- wallet.unmarkUTXOsAsReserved(tx)
} yield {
assert(unreservedUtxos.forall(_.state != TxoState.Reserved))
}
}
}

View File

@ -201,52 +201,6 @@ abstract class Wallet
}
}
override def markUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] = {
val updated = utxos.map(_.copyWithState(TxoState.Reserved))
spendingInfoDAO.updateAll(updated)
}
override def unmarkUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] = {
val unreserved = utxos.filterNot(_.state == TxoState.Reserved)
require(unreserved.isEmpty, s"Some utxos are not reserved, got $unreserved")
// unmark all utxos are reserved
val groupedUtxos = utxos
.map(_.copyWithState(TxoState.PendingConfirmationsReceived))
.groupBy(_.blockHash)
val mempoolUtxos = Try(groupedUtxos(None)).getOrElse(Vector.empty)
// get the ones in blocks
val utxosInBlocks = groupedUtxos.map {
case (Some(hash), utxos) =>
Some(hash, utxos)
case (None, _) =>
None
}.flatten
for {
updatedMempoolUtxos <- spendingInfoDAO.updateAll(mempoolUtxos)
// update the confirmed ones
updatedBlockUtxos <- FutureUtil
.sequentially(utxosInBlocks.toVector) {
case (hash, utxos) =>
updateUtxoConfirmedStates(utxos, hash)
}
} yield updatedMempoolUtxos ++ updatedBlockUtxos.flatten
}
/** @inheritdoc */
def updateUtxoPendingStates(
blockHeader: BlockHeader): Future[Vector[SpendingInfoDb]] = {
for {
infos <- spendingInfoDAO.findAllPendingConfirmation
updatedInfos <- updateUtxoConfirmedStates(infos, blockHeader.hashBE)
} yield updatedInfos
}
/** Takes a [[RawTxBuilderWithFinalizer]] for a transaction to be sent, and completes it by:
* finalizing and signing the transaction, then correctly processing and logging it
*/

View File

@ -192,6 +192,9 @@ trait WalletApi extends WalletLogger {
def unmarkUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]]
/** Unmarks all utxos that are ours in this transactions indicating they are no longer reserved */
def unmarkUTXOsAsReserved(tx: Transaction): Future[Vector[SpendingInfoDb]]
/** Checks if the wallet contains any data */
def isEmpty(): Future[Boolean]

View File

@ -4,17 +4,10 @@ import org.bitcoins.core.compat._
import org.bitcoins.core.hd.HDAccount
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script.{
P2WPKHWitnessSPKV0,
P2WPKHWitnessV0,
ScriptPubKey
}
import org.bitcoins.core.protocol.transaction.{
Transaction,
TransactionOutPoint,
TransactionOutput
}
import org.bitcoins.core.util.EitherUtil
import org.bitcoins.core.protocol.blockchain.BlockHeader
import org.bitcoins.core.protocol.script.{P2WPKHWitnessSPKV0, P2WPKHWitnessV0, ScriptPubKey}
import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutPoint, TransactionOutput}
import org.bitcoins.core.util.{EitherUtil, FutureUtil}
import org.bitcoins.core.wallet.utxo.TxoState
import org.bitcoins.crypto.DoubleSha256DigestBE
import org.bitcoins.wallet.api.{AddUtxoError, AddUtxoResult, AddUtxoSuccess}
@ -22,7 +15,7 @@ import org.bitcoins.wallet.models._
import org.bitcoins.wallet.{Wallet, WalletLogger}
import scala.concurrent.Future
import scala.util.{Failure, Success}
import scala.util.{Failure, Success, Try}
/**
* Provides functionality related to handling UTXOs in our wallet.
@ -208,4 +201,63 @@ private[wallet] trait UtxoHandling extends WalletLogger {
}
}
}
override def markUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] = {
val updated = utxos.map(_.copyWithState(TxoState.Reserved))
spendingInfoDAO.updateAll(updated)
}
override def unmarkUTXOsAsReserved(
utxos: Vector[SpendingInfoDb]): Future[Vector[SpendingInfoDb]] = {
val unreserved = utxos.filterNot(_.state == TxoState.Reserved)
require(unreserved.isEmpty, s"Some utxos are not reserved, got $unreserved")
// unmark all utxos are reserved
val groupedUtxos = utxos
.map(_.copyWithState(TxoState.PendingConfirmationsReceived))
.groupBy(_.blockHash)
val mempoolUtxos = Try(groupedUtxos(None)).getOrElse(Vector.empty)
// get the ones in blocks
val utxosInBlocks = groupedUtxos.map {
case (Some(hash), utxos) =>
Some(hash, utxos)
case (None, _) =>
None
}.flatten
for {
updatedMempoolUtxos <- spendingInfoDAO.updateAll(mempoolUtxos)
// update the confirmed ones
updatedBlockUtxos <- FutureUtil
.sequentially(utxosInBlocks.toVector) {
case (hash, utxos) =>
updateUtxoConfirmedStates(utxos, hash)
}
} yield updatedMempoolUtxos ++ updatedBlockUtxos.flatten
}
/** @inheritdoc */
override def unmarkUTXOsAsReserved(tx: Transaction): Future[Vector[SpendingInfoDb]] = {
val utxosF = listUtxos()
val utxosInTxF = for {
utxos <- utxosF
} yield {
val txOutPoints = tx.inputs.map(_.previousOutput)
utxos.filter(si => txOutPoints.contains(si.outPoint))
}
utxosInTxF.flatMap(unmarkUTXOsAsReserved)
}
/** @inheritdoc */
override def updateUtxoPendingStates(
blockHeader: BlockHeader): Future[Vector[SpendingInfoDb]] = {
for {
infos <- spendingInfoDAO.findAllPendingConfirmation
updatedInfos <- updateUtxoConfirmedStates(infos, blockHeader.hashBE)
} yield updatedInfos
}
}