Merge pull request #2029

* Add more processing blocks tests

* Add immature coinbase txo state

* Test balance, add doc
This commit is contained in:
Ben Carman 2020-09-20 10:39:31 -05:00 committed by GitHub
parent 1e3aee55c3
commit 3ec3b5d699
6 changed files with 76 additions and 9 deletions

View file

@ -128,7 +128,8 @@ case class NestedSegwitV0SpendingInfo(
sealed trait SpendingInfoDb extends DbRowAutoInc[SpendingInfoDb] {
state match {
case TxoState.ConfirmedSpent | TxoState.ConfirmedReceived =>
case TxoState.ConfirmedSpent | TxoState.ConfirmedReceived |
TxoState.ImmatureCoinbase =>
require(blockHash.isDefined,
"Transaction cannot be confirmed without a blockHash")
case TxoState.DoesNotExist | TxoState.PendingConfirmationsSpent |

View file

@ -14,6 +14,11 @@ object TxoState extends StringFactory[TxoState] {
/** Means that no funds have been sent to this utxo EVER */
final case object DoesNotExist extends 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
/** Means we have received funds to this utxo, but they are not confirmed */
final case object PendingConfirmationsReceived extends ReceivedState
@ -43,6 +48,7 @@ object TxoState extends StringFactory[TxoState] {
Set(PendingConfirmationsSpent, TxoState.ConfirmedSpent, Reserved)
val all: Vector[TxoState] = Vector(DoesNotExist,
ImmatureCoinbase,
PendingConfirmationsReceived,
ConfirmedReceived,
Reserved,

View file

@ -1,18 +1,21 @@
package org.bitcoins.wallet
import org.bitcoins.core.currency._
import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, WalletWithBitcoindRpc}
import org.bitcoins.core.gcs.FilterType
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.core.wallet.utxo.TxoState
import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, WalletWithBitcoindV19}
import org.scalatest.FutureOutcome
class ProcessBlockTest extends BitcoinSWalletTest {
override def withFixture(test: OneArgAsyncTest): FutureOutcome =
withNewWalletAndBitcoind(test)
withNewWalletAndBitcoindV19(test, getBIP39PasswordOpt())
override type FixtureParam = WalletWithBitcoindRpc
override type FixtureParam = WalletWithBitcoindV19
it must "process a block" in { param =>
val WalletWithBitcoindRpc(wallet, bitcoind) = param
val WalletWithBitcoindV19(wallet, bitcoind) = param
for {
startingUtxos <- wallet.listUtxos()
@ -36,4 +39,45 @@ class ProcessBlockTest extends BitcoinSWalletTest {
assert(utxos.head.txid == txId)
}
}
it must "process coinbase txs" in { param =>
val WalletWithBitcoindV19(wallet, bitcoind) = param
for {
startingUtxos <- wallet.listUtxos(TxoState.ImmatureCoinbase)
startingBalance <- wallet.getBalance()
_ = assert(startingUtxos.isEmpty)
_ = assert(startingBalance == Satoshis.zero)
addr <- wallet.getNewAddress()
hashes <- bitcoind.generateToAddress(101, addr)
blocks <- FutureUtil.sequentially(hashes)(bitcoind.getBlockRaw)
_ <- FutureUtil.sequentially(blocks)(wallet.processBlock)
utxos <- wallet.listUtxos(TxoState.ImmatureCoinbase)
balance <- wallet.getBalance()
} yield {
assert(utxos.size == 100)
assert(balance == Bitcoins(50))
}
}
it must "process coinbase txs using filters" in { param =>
val WalletWithBitcoindV19(wallet, bitcoind) = param
for {
startingUtxos <- wallet.listUtxos(TxoState.ImmatureCoinbase)
startingBalance <- wallet.getBalance()
_ = assert(startingUtxos.isEmpty)
_ = assert(startingBalance == Satoshis.zero)
addr <- wallet.getNewAddress()
hashes <- bitcoind.generateToAddress(101, addr)
filters <- FutureUtil.sequentially(hashes)(
bitcoind.getBlockFilter(_, FilterType.Basic))
filtersWithBlockHash = hashes.map(_.flip).zip(filters.map(_.filter))
_ <- wallet.processCompactFilters(filtersWithBlockHash)
utxos <- wallet.listUtxos(TxoState.ImmatureCoinbase)
balance <- wallet.getBalance()
} yield {
assert(utxos.size == 100)
assert(balance == Bitcoins(50))
}
}
}

View file

@ -251,7 +251,8 @@ abstract class Wallet
TxoState.ConfirmedReceived =>
txo.output.value
case TxoState.Reserved | TxoState.PendingConfirmationsSpent |
TxoState.ConfirmedSpent | TxoState.DoesNotExist =>
TxoState.ConfirmedSpent | TxoState.DoesNotExist |
TxoState.ImmatureCoinbase =>
CurrencyUnits.zero
}
}

View file

@ -2,6 +2,7 @@ package org.bitcoins.wallet.internal
import org.bitcoins.core.api.wallet.{AddUtxoError, AddUtxoSuccess}
import org.bitcoins.core.api.wallet.db._
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.BitcoinAddress
@ -226,7 +227,8 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
private def markAsPendingSpent(
out: SpendingInfoDb): Future[Option[SpendingInfoDb]] = {
out.state match {
case TxoState.ConfirmedReceived | TxoState.PendingConfirmationsReceived =>
case TxoState.ConfirmedReceived | TxoState.PendingConfirmationsReceived |
TxoState.ImmatureCoinbase =>
val updated =
out.copyWithState(state = TxoState.PendingConfirmationsSpent)
val updatedF =
@ -297,7 +299,7 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
case TxoState.PendingConfirmationsReceived |
TxoState.ConfirmedReceived |
TxoState.PendingConfirmationsSpent | TxoState.ConfirmedSpent |
TxoState.DoesNotExist =>
TxoState.DoesNotExist | TxoState.ImmatureCoinbase =>
txoWithHash
}
@ -355,7 +357,9 @@ private[wallet] trait TransactionProcessing extends WalletLogger {
case None =>
TxoState.PendingConfirmationsReceived
case Some(confs) =>
if (confs >= walletConfig.requiredConfirmations) {
if (transaction.isCoinbase && confs <= Consensus.coinbaseMaturity) {
TxoState.ImmatureCoinbase
} else if (confs >= walletConfig.requiredConfirmations) {
TxoState.ConfirmedReceived
} else {
TxoState.PendingConfirmationsReceived

View file

@ -8,6 +8,7 @@ import org.bitcoins.core.api.wallet.{
AddUtxoSuccess
}
import org.bitcoins.core.compat._
import org.bitcoins.core.consensus.Consensus
import org.bitcoins.core.hd.HDAccount
import org.bitcoins.core.number.UInt32
import org.bitcoins.core.protocol.BitcoinAddress
@ -109,6 +110,16 @@ private[wallet] trait UtxoHandling extends WalletLogger {
case Some(confs) =>
txos.map { txo =>
txo.state match {
case TxoState.ImmatureCoinbase =>
if (confs > Consensus.coinbaseMaturity) {
if (confs >= walletConfig.requiredConfirmations) {
txo.copyWithState(TxoState.ConfirmedReceived)
} else {
txo.copyWithState(TxoState.PendingConfirmationsReceived)
}
} else {
txo
}
case TxoState.PendingConfirmationsReceived =>
if (confs >= walletConfig.requiredConfirmations) {
txo.copyWithState(TxoState.ConfirmedReceived)