2021 04 18 Reset txo state when overwriting spendingTxId (#2919)

* Add invariant to spendingInfoDb to that requires if the spendinginfodb is in a TxoState.spentStates, the SpendingInfoDb.spendingTxIdOpt is defined

* Remove unused SpendingInfoDAO.updateTxoState()

* Fix bug that doesn't revert TxoState to TxoState.BroadcastSpent when overwriting the spendingTxId

* Remove unecessary transition from TxoState.Reserved -> TxoState.PendingConfirmationsReceived inside of processSpentUtxos()

* Update TransactionProcessing.markAsSpent() to treat TxoState.PendingConfirmationsSpent and TxoState.ConfirmedSpent the same way

* Remove unecessary comment
This commit is contained in:
Chris Stewart 2021-04-21 07:21:42 -05:00 committed by GitHub
parent 27992ed37d
commit 13fc3c2b4e

View File

@ -260,7 +260,10 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
Future.sequence(processedVec)
}
protected def processOutgoingUtxos(
/** Searches for outputs on the given transaction that are
* being spent from our wallet
*/
protected def processSpentUtxos(
transaction: Transaction,
blockHashOpt: Option[DoubleSha256DigestBE]): Future[
Vector[SpendingInfoDb]] = {
@ -272,21 +275,9 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
insertTransaction(transaction, blockHashOpt)
else Future.unit
// unreserved outputs now they are in a block
outputsToUse = blockHashOpt match {
case Some(_) =>
outputsBeingSpent.map { out =>
if (out.state == TxoState.Reserved)
out.copyWithState(TxoState.PendingConfirmationsReceived)
else out
}
case None =>
outputsBeingSpent
}
processed <- Future
.sequence {
outputsToUse.map(markAsSpent(_, transaction.txIdBE))
outputsBeingSpent.map(markAsSpent(_, transaction.txIdBE))
}
.map(_.toVector.flatten)
@ -309,7 +300,7 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
for {
incoming <- processIncomingUtxos(transaction, blockHashOpt, newTags)
outgoing <- processOutgoingUtxos(transaction, blockHashOpt)
outgoing <- processSpentUtxos(transaction, blockHashOpt)
_ <- walletCallbacks.executeOnTransactionProcessed(logger, transaction)
} yield {
ProcessTxResult(incoming, outgoing)
@ -335,17 +326,25 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
logger.debug(
s"Marked utxo=${updated.toHumanReadableString} as state=${updated.state}"))
updatedF.map(Some(_))
case TxoState.Reserved | TxoState.PendingConfirmationsSpent |
BroadcastSpent =>
case TxoState.Reserved =>
val updated =
out
.copyWithSpendingTxId(spendingTxId)
.copyWithState(state = BroadcastSpent)
val updatedF = spendingInfoDAO.update(updated)
updatedF.map(Some(_))
case TxoState.BroadcastSpent =>
logger.warn(
s"Updating the spendingTxId of a transaction that is already spent, " +
s"old state=${TxoState.BroadcastSpent} old spendingTxId=${out.spendingTxIdOpt} new spendingTxId=${spendingTxId}")
val updated =
out.copyWithSpendingTxId(spendingTxId)
val updatedF =
spendingInfoDAO.update(updated)
val updatedF = spendingInfoDAO.update(updated)
updatedF.map(Some(_))
case TxoState.ImmatureCoinbase =>
Future.failed(new RuntimeException(
s"Attempting to spend an ImmatureCoinbase ${out.outPoint.hex}, this should not be possible until it is confirmed."))
case TxoState.ConfirmedSpent =>
case TxoState.ConfirmedSpent | TxoState.PendingConfirmationsSpent =>
if (!out.spendingTxIdOpt.contains(spendingTxId)) {
Future.failed(new RuntimeException(
s"Attempted to mark an already spent utxo ${out.outPoint.hex} with a new spending tx ${spendingTxId.hex}"))