mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-26 21:42:48 +01:00
2021 04 18 wallet received txo state (#2914)
* Add invariant to spendingInfoDb to that requires if the spendinginfodb is in a TxoState.spentStates, the SpendingInfoDb.spendingTxIdOpt is defined * Remove unused SpendingInfoDAO.updateTxoState() * Tighten up TxoState -> ReceivedState types we are using in the wallet. The ensures we are talking specific class of states (receiving a txo) rather than accounting for the case of spending and receiving since there is different information required for the spending states * Add TxoState.ImmatureCoinbase to ReceivedStates
This commit is contained in:
parent
0d546f3b65
commit
238c083aad
3 changed files with 31 additions and 49 deletions
|
@ -17,7 +17,7 @@ object TxoState extends StringFactory[TxoState] {
|
|||
/** A coinbase output that has not reached maturity and cannot be spent yet.
|
||||
* https://bitcoin.stackexchange.com/questions/1991/what-is-the-block-maturation-time
|
||||
*/
|
||||
final case object ImmatureCoinbase extends TxoState
|
||||
final case object ImmatureCoinbase extends ReceivedState
|
||||
|
||||
/** Means we have received funds to this utxo, and they have not been confirmed in a block */
|
||||
final case object BroadcastReceived extends ReceivedState
|
||||
|
@ -55,7 +55,10 @@ object TxoState extends StringFactory[TxoState] {
|
|||
Set(TxoState.ConfirmedReceived, TxoState.ConfirmedSpent)
|
||||
|
||||
val receivedStates: Set[TxoState] =
|
||||
Set(PendingConfirmationsReceived, ConfirmedReceived, BroadcastReceived)
|
||||
Set(PendingConfirmationsReceived,
|
||||
ConfirmedReceived,
|
||||
BroadcastReceived,
|
||||
TxoState.ImmatureCoinbase)
|
||||
|
||||
val spentStates: Set[TxoState] =
|
||||
Set(PendingConfirmationsSpent, TxoState.ConfirmedSpent, BroadcastSpent)
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.bitcoins.core.protocol.transaction.{Transaction, TransactionOutput}
|
|||
import org.bitcoins.core.util.TimeUtil
|
||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||
import org.bitcoins.core.wallet.utxo.TxoState._
|
||||
import org.bitcoins.core.wallet.utxo.{AddressTag, TxoState}
|
||||
import org.bitcoins.core.wallet.utxo.{AddressTag, ReceivedState, TxoState}
|
||||
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
|
||||
import org.bitcoins.wallet._
|
||||
|
||||
|
@ -357,17 +357,18 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
|
|||
* error if any (this is because we're operating on data we've
|
||||
* already verified).
|
||||
*/
|
||||
private def processUtxo(
|
||||
private def processReceivedUtxo(
|
||||
transaction: Transaction,
|
||||
index: Int,
|
||||
state: TxoState): Future[SpendingInfoDb] = {
|
||||
addUtxo(transaction = transaction, vout = UInt32(index), state = state)
|
||||
.flatMap {
|
||||
case AddUtxoSuccess(utxo) => Future.successful(utxo)
|
||||
case err: AddUtxoError =>
|
||||
logger.error(s"Could not add UTXO", err)
|
||||
Future.failed(err)
|
||||
}
|
||||
state: ReceivedState): Future[SpendingInfoDb] = {
|
||||
val addIncomingUtxoF =
|
||||
addUtxo(transaction = transaction, vout = UInt32(index), state = state)
|
||||
addIncomingUtxoF.flatMap {
|
||||
case AddUtxoSuccess(utxo) => Future.successful(utxo)
|
||||
case err: AddUtxoError =>
|
||||
logger.error(s"Could not add UTXO", err)
|
||||
Future.failed(err)
|
||||
}
|
||||
}
|
||||
|
||||
private case class OutputWithIndex(output: TransactionOutput, index: Int)
|
||||
|
@ -428,12 +429,13 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
|
|||
}
|
||||
}
|
||||
|
||||
private def addUTXOsFut(
|
||||
/** Adds utxos to the database that we are receiving */
|
||||
private def addReceivedUTXOs(
|
||||
outputsWithIndex: Seq[OutputWithIndex],
|
||||
transaction: Transaction,
|
||||
blockHashOpt: Option[DoubleSha256DigestBE]): Future[
|
||||
Seq[SpendingInfoDb]] = {
|
||||
val stateF: Future[TxoState] = blockHashOpt match {
|
||||
val stateF: Future[ReceivedState] = blockHashOpt match {
|
||||
case None =>
|
||||
Future.successful(TxoState.BroadcastReceived)
|
||||
case Some(blockHash) =>
|
||||
|
@ -453,7 +455,7 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
|
|||
|
||||
stateF.flatMap { state =>
|
||||
val outputsVec = outputsWithIndex.map { out =>
|
||||
processUtxo(
|
||||
processReceivedUtxo(
|
||||
transaction,
|
||||
out.index,
|
||||
state = state
|
||||
|
@ -504,17 +506,6 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
|
|||
Future.successful(Vector.empty)
|
||||
|
||||
case outputsWithIndex =>
|
||||
val count = outputsWithIndex.length
|
||||
val outputStr = {
|
||||
outputsWithIndex
|
||||
.map { elem =>
|
||||
s"${transaction.txIdBE.hex}:${elem.index}"
|
||||
}
|
||||
.mkString(", ")
|
||||
}
|
||||
logger.trace(
|
||||
s"Found $count relevant output(s) in transaction=${transaction.txIdBE.hex}: $outputStr")
|
||||
|
||||
val totalIncoming = outputsWithIndex.map(_.output.value).sum
|
||||
|
||||
val spks = outputsWithIndex.map(_.output.scriptPubKey)
|
||||
|
@ -556,7 +547,7 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
|
|||
for {
|
||||
(txDb, _) <- txDbF
|
||||
ourOutputs <- ourOutputsF
|
||||
utxos <- addUTXOsFut(ourOutputs, txDb.transaction, blockHashOpt)
|
||||
utxos <- addReceivedUTXOs(ourOutputs, txDb.transaction, blockHashOpt)
|
||||
_ <- newTagsF
|
||||
} yield utxos
|
||||
}
|
||||
|
|
|
@ -215,7 +215,7 @@ private[wallet] trait UtxoHandling extends WalletLogger {
|
|||
/** Constructs a DB level representation of the given UTXO, and persist it to disk */
|
||||
private def writeUtxo(
|
||||
tx: Transaction,
|
||||
state: TxoState,
|
||||
state: ReceivedState,
|
||||
output: TransactionOutput,
|
||||
outPoint: TransactionOutPoint,
|
||||
addressDb: AddressDb): Future[SpendingInfoDb] = {
|
||||
|
@ -263,39 +263,27 @@ private[wallet] trait UtxoHandling extends WalletLogger {
|
|||
}
|
||||
}
|
||||
|
||||
private def getAddressDbEitherF(
|
||||
output: TransactionOutput): Future[Either[AddUtxoError, AddressDb]] = {
|
||||
findAddress(output.scriptPubKey)
|
||||
}
|
||||
|
||||
/** Adds the provided UTXO to the wallet
|
||||
*/
|
||||
protected def addUtxo(
|
||||
transaction: Transaction,
|
||||
vout: UInt32,
|
||||
state: TxoState): Future[AddUtxoResult] = {
|
||||
state: ReceivedState): Future[AddUtxoResult] = {
|
||||
|
||||
logger.info(s"Adding UTXO to wallet: ${transaction.txId.hex}:${vout.toInt}")
|
||||
|
||||
// first check: does the provided vout exist in the tx?
|
||||
val voutIndexOutOfBounds: Boolean = {
|
||||
val voutLength = transaction.outputs.length
|
||||
val outOfBunds = voutLength <= vout.toInt
|
||||
|
||||
if (outOfBunds)
|
||||
logger.error(
|
||||
s"TX with TXID ${transaction.txId.hex} only has $voutLength, got request to add vout ${vout.toInt}!")
|
||||
outOfBunds
|
||||
}
|
||||
|
||||
if (voutIndexOutOfBounds) {
|
||||
if (vout.toInt >= transaction.outputs.length) {
|
||||
//out of bounds output
|
||||
Future.successful(VoutIndexOutOfBounds)
|
||||
} else {
|
||||
|
||||
val output = transaction.outputs(vout.toInt)
|
||||
val outPoint = TransactionOutPoint(transaction.txId, vout)
|
||||
|
||||
// second check: do we have an address associated with the provided
|
||||
// output in our DB?
|
||||
def addressDbEitherF: Future[Either[AddUtxoError, AddressDb]] = {
|
||||
findAddress(output.scriptPubKey)
|
||||
}
|
||||
|
||||
val addressDbEitherF = getAddressDbEitherF(output)
|
||||
// insert the UTXO into the DB
|
||||
addressDbEitherF
|
||||
.map { addressDbE =>
|
||||
|
|
Loading…
Add table
Reference in a new issue