Refactor TransactionProcessing.processTransaction() to use BlockHashWithConfs (#5744)

* Refactor TransactionProcessing.processTransaction() to use BlockHashWithConfs

Create WalletUtil.getBlockHashWithConfs(), use it in various places through the codebase

* Fix docs
This commit is contained in:
Chris Stewart 2024-10-28 12:52:53 -05:00 committed by GitHub
parent 2521c5da0e
commit f75a52b521
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 240 additions and 160 deletions

View file

@ -25,7 +25,7 @@ object CallbackUtil extends BitcoinSLogger {
val txSink = Sink.foreachAsync[Transaction](1) { case tx: Transaction =>
logger.debug(s"Receiving transaction txid=${tx.txIdBE.hex} as a callback")
wallet.transactionProcessing
.processTransaction(tx, blockHashOpt = None)
.processTransaction(tx, blockHashWithConfsOpt = None)
.map(_ => ())
}
@ -109,7 +109,7 @@ object CallbackUtil extends BitcoinSLogger {
val txSink = Sink.foreachAsync[Transaction](1) { case tx: Transaction =>
logger.debug(s"Receiving transaction txid=${tx.txIdBE.hex} as a callback")
wallet.transactionProcessing
.processTransaction(tx, blockHashOpt = None)
.processTransaction(tx, blockHashWithConfsOpt = None)
.map(_ => ())
}
val onTx: OnTxReceived = { tx =>

View file

@ -4,6 +4,7 @@ import org.bitcoins.core.api.wallet.db.{SpendingInfoDb, TransactionDb}
import org.bitcoins.core.currency.CurrencyUnit
import org.bitcoins.core.protocol.blockchain.Block
import org.bitcoins.core.protocol.transaction.{OutputWithIndex, Transaction}
import org.bitcoins.core.util.BlockHashWithConfs
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.utxo.AddressTag
import org.bitcoins.crypto.{DoubleSha256Digest, DoubleSha256DigestBE}
@ -26,7 +27,7 @@ trait TransactionProcessingApi {
def processTransaction(
transaction: Transaction,
blockHashOpt: Option[DoubleSha256DigestBE]
blockHashWithConfsOpt: Option[BlockHashWithConfs]
): Future[Unit]
/** Processes TXs originating from our wallet. This is called right after
@ -37,7 +38,7 @@ trait TransactionProcessingApi {
feeRate: FeeUnit,
inputAmount: CurrencyUnit,
sentAmount: CurrencyUnit,
blockHashOpt: Option[DoubleSha256DigestBE],
blockHashWithConfsOpt: Option[BlockHashWithConfs],
newTags: Vector[AddressTag]
): Future[ProcessTxResult]
@ -52,7 +53,7 @@ trait TransactionProcessingApi {
def processReceivedUtxos(
tx: Transaction,
blockHashOpt: Option[DoubleSha256DigestBE],
blockHashWithConfsOpt: Option[BlockHashWithConfs],
spendingInfoDbs: Vector[SpendingInfoDb],
newTags: Vector[AddressTag],
relevantReceivedOutputs: Vector[OutputWithIndex]
@ -61,7 +62,7 @@ trait TransactionProcessingApi {
def processSpentUtxos(
transaction: Transaction,
outputsBeingSpent: Vector[SpendingInfoDb],
blockHashOpt: Option[DoubleSha256DigestBE]
blockHashWithConfsOpt: Option[BlockHashWithConfs]
): Future[Vector[SpendingInfoDb]]
def insertTransaction(

View file

@ -5,6 +5,7 @@ import org.bitcoins.core.protocol.dlc.models.{
DLCStatus,
SingleContractInfo
}
import org.bitcoins.core.util.BlockHashWithConfs
import org.bitcoins.dlc.wallet.callback.{DLCWalletCallbacks, OnDLCStateChange}
import org.bitcoins.testkit.wallet.FundWalletUtil.FundedDLCWallet
import org.bitcoins.testkit.wallet.{BitcoinSDualWalletTest, DLCWalletUtil}
@ -96,9 +97,11 @@ class DLCWalletCallbackTest extends BitcoinSDualWalletTest {
_ <- initF
contractId <- DLCWalletUtil.getContractId(wallets._1.wallet)
fundingTx <- walletA.getDLCFundingTx(contractId)
blockHash = CryptoGenerators.doubleSha256DigestBE.sample.get
blockHashWithConfs = BlockHashWithConfs(blockHash, Some(1))
_ <- walletA.transactionProcessing.processTransaction(
transaction = fundingTx,
blockHashOpt = Some(CryptoGenerators.doubleSha256DigestBE.sample.get)
blockHashWithConfsOpt = Some(blockHashWithConfs)
)
sigs = {
DLCWalletUtil.sampleContractInfo match {
@ -207,9 +210,11 @@ class DLCWalletCallbackTest extends BitcoinSDualWalletTest {
_ <- initF
contractId <- DLCWalletUtil.getContractId(wallets._1.wallet)
fundingTx <- walletA.getDLCFundingTx(contractId)
blockHash = CryptoGenerators.doubleSha256DigestBE.sample.get
blockHashWithConfs = BlockHashWithConfs(blockHash, Some(1))
_ <- walletA.transactionProcessing.processTransaction(
transaction = fundingTx,
blockHashOpt = Some(CryptoGenerators.doubleSha256DigestBE.sample.get)
blockHashWithConfsOpt = Some(blockHashWithConfs)
)
transaction <- walletA.executeDLCRefund(contractId)
_ <- walletB.transactionProcessing.processTransaction(transaction, None)

View file

@ -1935,7 +1935,8 @@ case class DLCWallet(override val walletApi: Wallet)(implicit
_ <- updateClosingTxId(contractId, refundTx.txIdBE)
_ <- transactionProcessing.processTransaction(refundTx,
blockHashOpt = None)
blockHashWithConfsOpt =
None)
status <- findDLC(dlcDb.dlcId)
_ <- dlcConfig.walletCallbacks.executeOnDLCStateChange(status.get)
} yield refundTx

View file

@ -22,7 +22,7 @@ import org.bitcoins.core.protocol.transaction.{
WitnessTransaction
}
import org.bitcoins.core.psbt.InputPSBTRecord.PartialSignature
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.core.util.{BlockHashWithConfs, FutureUtil}
import org.bitcoins.core.wallet.fee.FeeUnit
import org.bitcoins.core.wallet.utxo.{
AddressTag,
@ -519,23 +519,25 @@ case class DLCTransactionProcessing(
override def processTransaction(
transaction: Transaction,
blockHashOpt: Option[DoubleSha256DigestBE]): Future[Unit] = {
blockHashWithConfsOpt: Option[BlockHashWithConfs]): Future[Unit] = {
txProcessing
.processTransaction(transaction, blockHashOpt)
.flatMap(_ => processFundingTx(transaction, blockHashOpt))
.flatMap(_ => processSettledDLCs(transaction, blockHashOpt))
.processTransaction(transaction, blockHashWithConfsOpt)
.flatMap(_ =>
processFundingTx(transaction, blockHashWithConfsOpt.map(_.blockHash)))
.flatMap(_ =>
processSettledDLCs(transaction, blockHashWithConfsOpt.map(_.blockHash)))
.map(_ => ())
}
override def processReceivedUtxos(
tx: Transaction,
blockHashOpt: Option[DoubleSha256DigestBE],
blockHashWithConfsOpt: Option[BlockHashWithConfs],
spendingInfoDbs: Vector[SpendingInfoDb],
newTags: Vector[AddressTag],
relevantReceivedOutputs: Vector[OutputWithIndex])
: Future[Vector[SpendingInfoDb]] = {
txProcessing.processReceivedUtxos(tx,
blockHashOpt,
blockHashWithConfsOpt,
spendingInfoDbs,
newTags,
relevantReceivedOutputs)
@ -544,9 +546,11 @@ case class DLCTransactionProcessing(
override def processSpentUtxos(
transaction: Transaction,
outputsBeingSpent: Vector[SpendingInfoDb],
blockHashOpt: Option[DoubleSha256DigestBE])
blockHashWithConfsOpt: Option[BlockHashWithConfs])
: Future[Vector[SpendingInfoDb]] = {
txProcessing.processSpentUtxos(transaction, outputsBeingSpent, blockHashOpt)
txProcessing.processSpentUtxos(transaction,
outputsBeingSpent,
blockHashWithConfsOpt)
}
/** Processes TXs originating from our wallet. This is called right after
@ -557,13 +561,13 @@ case class DLCTransactionProcessing(
feeRate: FeeUnit,
inputAmount: CurrencyUnit,
sentAmount: CurrencyUnit,
blockHashOpt: Option[DoubleSha256DigestBE],
blockHashWithConfsOpt: Option[BlockHashWithConfs],
newTags: Vector[AddressTag]): Future[ProcessTxResult] = {
txProcessing.processOurTransaction(transaction,
feeRate,
inputAmount,
sentAmount,
blockHashOpt,
blockHashWithConfsOpt,
newTags)
}

View file

@ -64,7 +64,9 @@ import org.bitcoins.rpc.client.common.BitcoindRpcClient
import org.bitcoins.rpc.config._
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.core.api.wallet.WalletApi
import org.bitcoins.core.util.BlockHashWithConfs
import org.bitcoins.wallet.Wallet
import org.bitcoins.wallet.util.WalletUtil
import com.typesafe.config.ConfigFactory
import java.nio.file.Files
@ -157,12 +159,13 @@ val walletF: Future[WalletApi] = configF.flatMap { _ =>
// when this future completes, ww have sent a transaction
// from bitcoind to the Bitcoin-S wallet
val transactionF: Future[(Transaction, Option[DoubleSha256DigestBE])] = for {
val transactionF: Future[(Transaction, Option[BlockHashWithConfs])] = for {
wallet <- walletF
address <- wallet.getNewAddress()
txid <- bitcoind.sendToAddress(address, 3.bitcoin)
transaction <- bitcoind.getRawTransaction(txid)
} yield (transaction.hex, transaction.blockhash)
blockHashWithConfs <- WalletUtil.getBlockHashWithConfs(bitcoind,transaction.blockhash)
} yield (transaction.hex, blockHashWithConfs)
// when this future completes, we have processed
// the transaction from bitcoind, and we have

View file

@ -5,7 +5,7 @@ import org.bitcoins.core.api.chain.ChainQueryApi.FilterResponse
import org.bitcoins.core.gcs.BlockFilter
import org.bitcoins.core.protocol.BlockStamp
import org.bitcoins.core.protocol.blockchain.RegTestNetChainParams
import org.bitcoins.core.util.FutureUtil
import org.bitcoins.core.util.{BlockHashWithConfs, FutureUtil}
import org.bitcoins.crypto.DoubleSha256DigestBE
import scala.concurrent.Future
@ -19,6 +19,9 @@ object MockChainQueryApi extends ChainQueryApi {
"00000000496dcc754fabd97f3e2df0a7337eab417d75537fecf97a7ebb0e7c75"
)
val blockHashWithConfs: BlockHashWithConfs =
BlockHashWithConfs(testBlockHash, Some(6))
/** Gets the height of the given block */
override def getBlockHeight(
blockHash: DoubleSha256DigestBE
@ -41,7 +44,7 @@ object MockChainQueryApi extends ChainQueryApi {
blockHash: DoubleSha256DigestBE
): Future[Option[Int]] = {
if (blockHash == testBlockHash) {
Future.successful(Some(6))
Future.successful(blockHashWithConfs.confirmationsOpt)
} else FutureUtil.none
}

View file

@ -25,6 +25,7 @@ import org.bitcoins.testkit.wallet.FundWalletUtil.{
import org.bitcoins.testkitcore.gen.TransactionGenerators
import org.bitcoins.testkitcore.util.TransactionTestUtil
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.util.WalletUtil
import scala.concurrent.{ExecutionContext, Future}
@ -107,7 +108,10 @@ trait FundWalletUtil extends BitcoinSLogger {
addresses <- addressesF
addressAmountMap = addresses.zip(amts).toMap
(tx, blockHash) <- fundAddressesWithBitcoind(addressAmountMap, bitcoind)
_ <- wallet.transactionProcessing.processTransaction(tx, Some(blockHash))
blockHashWithConfs <- WalletUtil.getBlockHashWithConfs(bitcoind,
blockHash)
_ <- wallet.transactionProcessing.processTransaction(tx,
blockHashWithConfs)
} yield (tx, blockHash)
txAndHashF.map(_ => wallet)

View file

@ -75,17 +75,18 @@ class ProcessTransactionTest extends BitcoinSWalletTest {
_ <- wallet.transactionProcessing.processTransaction(
tx,
Some(MockChainQueryApi.testBlockHash)
Some(MockChainQueryApi.blockHashWithConfs)
)
newConfirmed <- wallet.getConfirmedBalance()
newUnconfirmed <- wallet.getUnconfirmedBalance()
utxosPostAdd <- wallet.utxoHandling.listUtxos()
// repeating the action should not make a difference
_ <- checkUtxosAndBalance(wallet) {
wallet.transactionProcessing.processTransaction(
tx,
Some(MockChainQueryApi.testBlockHash))
Some(MockChainQueryApi.blockHashWithConfs))
}
} yield {
val ourOutputs =
@ -196,7 +197,7 @@ class ProcessTransactionTest extends BitcoinSWalletTest {
)
_ <- wallet.transactionProcessing.processTransaction(
transaction = rawTxHelper.signedTx,
blockHashOpt = None
blockHashWithConfsOpt = None
)
balance <- wallet.getBalance()
} yield assert(balance == amount)

View file

@ -11,6 +11,8 @@ import org.bitcoins.testkit.wallet.{
BitcoinSWalletTestCachedBitcoindNewest,
WalletWithBitcoindRpc
}
import org.bitcoins.wallet.util.WalletUtil
import scala.concurrent.duration.DurationInt
class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
@ -110,9 +112,12 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
bitcoindAddr <- bitcoindAddrF
blockHashes <-
bitcoind.generateToAddress(blocks = numBlocks, address = bitcoindAddr)
blockHashWithConfs <- WalletUtil.getBlockHashWithConfs(
bitcoind,
blockHashes.headOption)
_ <- wallet.transactionProcessing.processTransaction(
transaction = tx,
blockHashOpt = blockHashes.headOption
blockHashWithConfsOpt = blockHashWithConfs
)
balance <- wallet.getBalance()
unconfirmedBalance <- wallet.getUnconfirmedBalance()
@ -171,9 +176,12 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
bitcoindAddr <- bitcoindAddrF
blockHashes <-
bitcoind.generateToAddress(blocks = numBlocks, address = bitcoindAddr)
blockHashWithConfs <- WalletUtil.getBlockHashWithConfs(
bitcoind,
blockHashes.headOption)
_ <- wallet.transactionProcessing.processTransaction(
transaction = tx,
blockHashOpt = blockHashes.headOption
blockHashWithConfsOpt = blockHashWithConfs
)
balance <- wallet.getBalance()
unconfirmedBalance <- wallet.getUnconfirmedBalance()
@ -235,9 +243,12 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
bitcoindAddr <- bitcoindAddrF
blockHashes <-
bitcoind.generateToAddress(blocks = numBlocks, address = bitcoindAddr)
blockHashWithConfs <- WalletUtil.getBlockHashWithConfs(
bitcoind,
blockHashes.headOption)
_ <- wallet.transactionProcessing.processTransaction(
transaction = tx,
blockHashOpt = blockHashes.headOption
blockHashWithConfsOpt = blockHashWithConfs
)
balance <- wallet.getBalance()
unconfirmedBalance <- wallet.getUnconfirmedBalance()
@ -523,9 +534,12 @@ class RescanHandlingTest extends BitcoinSWalletTestCachedBitcoindNewest {
bitcoindAddr <- bitcoindAddrF
blockHashes <-
bitcoind.generateToAddress(blocks = numBlocks, address = bitcoindAddr)
blockHashWithConfs <- WalletUtil.getBlockHashWithConfs(
bitcoind,
blockHashes.headOption)
_ <- wallet.transactionProcessing.processTransaction(
transaction = tx,
blockHashOpt = blockHashes.headOption
blockHashWithConfsOpt = blockHashWithConfs
)
balance <- wallet.getBalance()
unconfirmedBalance <- wallet.getUnconfirmedBalance()

View file

@ -4,21 +4,22 @@ import org.bitcoins.commons.util.BitcoinSLogger
import org.bitcoins.core.api.wallet.CoinSelectionAlgo
import org.bitcoins.core.api.wallet.db.SpendingInfoDb
import org.bitcoins.core.currency.{Bitcoins, Satoshis}
import org.bitcoins.core.number._
import org.bitcoins.core.number.*
import org.bitcoins.core.protocol.BitcoinAddress
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction._
import org.bitcoins.core.protocol.script.*
import org.bitcoins.core.protocol.transaction.*
import org.bitcoins.core.psbt.PSBT
import org.bitcoins.core.wallet.builder.RawTxSigner
import org.bitcoins.core.wallet.fee.{SatoshisPerByte, SatoshisPerVirtualByte}
import org.bitcoins.core.wallet.utxo.TxoState
import org.bitcoins.core.wallet.utxo.TxoState._
import org.bitcoins.core.wallet.utxo.TxoState.*
import org.bitcoins.crypto.{DoubleSha256DigestBE, ECPublicKey}
import org.bitcoins.testkit.wallet.{
BitcoinSWalletTestCachedBitcoindNewest,
WalletWithBitcoindRpc
}
import org.bitcoins.wallet.models.SpendingInfoDAO
import org.bitcoins.wallet.util.WalletUtil
import org.scalatest.{Assertion, FutureOutcome, Outcome}
import scala.concurrent.Future
@ -83,7 +84,9 @@ class UTXOLifeCycleTest
// Give tx a fake hash so it can appear as it's in a block
hash <- bitcoind.getBestBlockHash()
_ <- wallet.transactionProcessing.processTransaction(tx, Some(hash))
blockHashWithConfs <- WalletUtil.getBlockHashWithConfs(bitcoind, hash)
_ <- wallet.transactionProcessing.processTransaction(tx,
blockHashWithConfs)
_ <- wallet.utxoHandling.updateUtxoPendingStates()
pendingCoins <- wallet.utxoHandling.findOutputsBeingSpent(tx)
@ -345,7 +348,9 @@ class UTXOLifeCycleTest
// Give tx a fake hash so it can appear as it's in a block
hash <- bitcoind.getBestBlockHash()
_ <- wallet.transactionProcessing.processTransaction(tx, Some(hash))
blockHashWithConfs <- WalletUtil.getBlockHashWithConfs(bitcoind, hash)
_ <- wallet.transactionProcessing.processTransaction(tx,
blockHashWithConfs)
_ <- wallet.utxoHandling.updateUtxoPendingStates()
pendingCoins <- wallet.utxoHandling.findOutputsBeingSpent(tx)
@ -390,7 +395,7 @@ class UTXOLifeCycleTest
feeRate = SatoshisPerByte(Satoshis(3)),
inputAmount = Satoshis(4000),
sentAmount = Satoshis(3000),
blockHashOpt = None,
blockHashWithConfsOpt = None,
newTags = Vector.empty
)
@ -419,7 +424,7 @@ class UTXOLifeCycleTest
feeRate = SatoshisPerByte(Satoshis(3)),
inputAmount = Satoshis(4000),
sentAmount = Satoshis(3000),
blockHashOpt = None,
blockHashWithConfsOpt = None,
newTags = Vector.empty
)
@ -431,7 +436,10 @@ class UTXOLifeCycleTest
hash <- bitcoind.getNewAddress
.flatMap(bitcoind.generateToAddress(1, _))
.map(_.head)
_ <- wallet.transactionProcessing.processTransaction(tx, Some(hash))
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(bitcoind, hash)
_ <- wallet.transactionProcessing.processTransaction(
tx,
blockHashWithConfsOpt)
pendingCoins <-
wallet.utxoHandling.findByScriptPubKey(addr.scriptPubKey)
@ -456,12 +464,14 @@ class UTXOLifeCycleTest
txId <- bitcoind.sendToAddress(addr, Satoshis(3000))
tx <- bitcoind.getRawTransactionRaw(txId)
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(bitcoind,
Some(blockHash))
_ <- wallet.transactionProcessing.processOurTransaction(
transaction = tx,
feeRate = SatoshisPerByte(Satoshis(3)),
inputAmount = Satoshis(4000),
sentAmount = Satoshis(3000),
blockHashOpt = Some(blockHash), // give fake hash
blockHashWithConfsOpt = blockHashWithConfsOpt, // give fake hash
newTags = Vector.empty
)

View file

@ -19,6 +19,7 @@ import org.bitcoins.wallet.models.{
IncomingTransactionDAO,
OutgoingTransactionDAO
}
import org.bitcoins.wallet.util.WalletUtil
import org.scalatest.{FutureOutcome, Outcome}
import scala.concurrent.Future
@ -92,7 +93,11 @@ class WalletIntegrationTest extends BitcoinSWalletTestCachedBitcoindNewest {
rawTx <- bitcoind.getRawTransaction(txId)
// after this, tx should be confirmed
_ <- wallet.transactionProcessing.processTransaction(tx, rawTx.blockhash)
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(bitcoind,
rawTx.blockhash)
_ <- wallet.transactionProcessing.processTransaction(
tx,
blockHashWithConfsOpt)
_ <-
wallet.utxoHandling
.listUtxos()
@ -176,8 +181,11 @@ class WalletIntegrationTest extends BitcoinSWalletTestCachedBitcoindNewest {
txId <- bitcoind.sendToAddress(addr, valueFromBitcoind)
rawTx <- bitcoind.getRawTransaction(txId)
_ <- bitcoind.generate(6)
_ <- wallet.transactionProcessing.processTransaction(rawTx.hex,
rawTx.blockhash)
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(bitcoind,
rawTx.blockhash)
_ <- wallet.transactionProcessing.processTransaction(
rawTx.hex,
blockHashWithConfsOpt)
// Verify we funded the wallet
balance <- wallet.getBalance()
@ -243,8 +251,11 @@ class WalletIntegrationTest extends BitcoinSWalletTestCachedBitcoindNewest {
txId <- bitcoind.sendToAddress(addr, valueFromBitcoind)
_ <- bitcoind.generate(6)
rawTx <- bitcoind.getRawTransaction(txId)
_ <- wallet.transactionProcessing.processTransaction(rawTx.hex,
rawTx.blockhash)
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(bitcoind,
rawTx.blockhash)
_ <- wallet.transactionProcessing.processTransaction(
rawTx.hex,
blockHashWithConfsOpt)
// Verify we funded the wallet
balance <- wallet.getBalance()
@ -262,8 +273,11 @@ class WalletIntegrationTest extends BitcoinSWalletTestCachedBitcoindNewest {
_ <- bitcoind.generate(1)
rawTx1 <- bitcoind.getRawTransaction(rbf.txIdBE)
_ = require(rawTx1.blockhash.isDefined)
_ <- wallet.transactionProcessing.processTransaction(rbf,
rawTx1.blockhash)
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(bitcoind,
rawTx.blockhash)
_ <- wallet.transactionProcessing.processTransaction(
rbf,
blockHashWithConfsOpt)
// fail to RBF confirmed tx
res <- recoverToSucceededIf[IllegalArgumentException] {
@ -283,8 +297,11 @@ class WalletIntegrationTest extends BitcoinSWalletTestCachedBitcoindNewest {
txId <- bitcoind.sendToAddress(addr, valueFromBitcoind)
rawTx <- bitcoind.getRawTransaction(txId)
_ <- bitcoind.generate(6)
_ <- wallet.transactionProcessing.processTransaction(rawTx.hex,
rawTx.blockhash)
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(bitcoind,
rawTx.blockhash)
_ <- wallet.transactionProcessing.processTransaction(
rawTx.hex,
blockHashWithConfsOpt)
// Verify we funded the wallet
balance <- wallet.getBalance()

View file

@ -19,6 +19,7 @@ import org.bitcoins.testkitcore.Implicits.GeneratorOps
import org.bitcoins.testkitcore.gen.FeeUnitGen
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.models.{OutgoingTransactionDAO, SpendingInfoDAO}
import org.bitcoins.wallet.util.WalletUtil
import org.scalatest.{Assertion, FutureOutcome}
import scodec.bits.ByteVector
@ -379,9 +380,12 @@ class WalletSendingTest extends BitcoinSWalletTest {
tx <- wallet.sendFundsHandling.sendToAddress(testAddress,
amountToSend,
feeRate)
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(
chainQueryApi,
Some(DoubleSha256DigestBE.empty))
_ <- wallet.transactionProcessing.processTransaction(
tx,
Some(DoubleSha256DigestBE.empty))
blockHashWithConfsOpt)
res <- recoverToSucceededIf[IllegalArgumentException] {
wallet.sendFundsHandling.bumpFeeRBF(tx.txIdBE, newFeeRate)
@ -470,9 +474,12 @@ class WalletSendingTest extends BitcoinSWalletTest {
tx <- wallet.sendFundsHandling.sendToAddress(testAddress,
amountToSend,
feeRate)
blockHashWithConfsOpt <- WalletUtil.getBlockHashWithConfs(
chainQueryApi,
Some(DoubleSha256DigestBE.empty))
_ <- wallet.transactionProcessing.processTransaction(
tx,
Some(DoubleSha256DigestBE.empty))
blockHashWithConfsOpt)
res <- recoverToSucceededIf[IllegalArgumentException] {
wallet.sendFundsHandling.bumpFeeCPFP(tx.txIdBE, feeRate)

View file

@ -254,9 +254,9 @@ class WalletUnitTest extends BitcoinSWalletTest {
spk = addr.scriptPubKey
_ = assert(spk == P2PKHScriptPubKey(walletKey))
dummyPrevTx = dummyTx(spk = spk)
_ <- wallet.transactionProcessing.processTransaction(dummyPrevTx,
blockHashOpt =
None)
_ <- wallet.transactionProcessing.processTransaction(
dummyPrevTx,
blockHashWithConfsOpt = None)
psbt = dummyPSBT(prevTxId = dummyPrevTx.txId)
@ -280,9 +280,9 @@ class WalletUnitTest extends BitcoinSWalletTest {
spk = addr.scriptPubKey
_ = assert(spk == P2SHScriptPubKey(P2WPKHWitnessSPKV0(walletKey)))
dummyPrevTx = dummyTx(spk = spk)
_ <- wallet.transactionProcessing.processTransaction(dummyPrevTx,
blockHashOpt =
None)
_ <- wallet.transactionProcessing.processTransaction(
dummyPrevTx,
blockHashWithConfsOpt = None)
psbt = dummyPSBT(prevTxId = dummyPrevTx.txId)
@ -306,9 +306,9 @@ class WalletUnitTest extends BitcoinSWalletTest {
spk = addr.scriptPubKey
_ = assert(spk == P2WPKHWitnessSPKV0(walletKey))
dummyPrevTx = dummyTx(spk = spk)
_ <- wallet.transactionProcessing.processTransaction(dummyPrevTx,
blockHashOpt =
None)
_ <- wallet.transactionProcessing.processTransaction(
dummyPrevTx,
blockHashWithConfsOpt = None)
psbt = dummyPSBT(prevTxId = dummyPrevTx.txId)
.addUTXOToInput(dummyPrevTx, 0)
@ -339,12 +339,14 @@ class WalletUnitTest extends BitcoinSWalletTest {
spk = addr.scriptPubKey
_ = assert(spk == P2WPKHWitnessSPKV0(walletKey))
dummyPrevTx = dummyTx(spk = spk)
_ <- wallet.transactionProcessing.processTransaction(dummyPrevTx,
blockHashOpt = None)
_ <- wallet.transactionProcessing.processTransaction(
dummyPrevTx,
blockHashWithConfsOpt = None)
dummyPrevTx1 = dummyTx(prevTxId = dummyPrevTx.txId, spk = spk)
_ <- wallet.transactionProcessing.processTransaction(dummyPrevTx1,
blockHashOpt = None)
_ <- wallet.transactionProcessing.processTransaction(
dummyPrevTx1,
blockHashWithConfsOpt = None)
toBroadcast <- wallet.sendFundsHandling.getTransactionsToBroadcast
} yield assert(toBroadcast.map(_.txIdBE) == Vector(dummyPrevTx1.txIdBE))

View file

@ -232,7 +232,8 @@ case class Wallet(
for {
_ <- nodeApi.broadcastTransaction(transaction)
_ <- transactionProcessing.processTransaction(transaction,
blockHashOpt = None)
blockHashWithConfsOpt =
None)
_ <- walletCallbacks.executeOnTransactionBroadcast(transaction)
} yield ()

View file

@ -604,7 +604,7 @@ case class SendFundsHandlingHandling(
feeRate = feeRate,
inputAmount = creditingAmount,
sentAmount = sentAmount,
blockHashOpt = None,
blockHashWithConfsOpt = None,
newTags = newTags
)
} yield {

View file

@ -38,6 +38,7 @@ import org.bitcoins.wallet.models.{
WalletDAOs,
WalletStateDescriptorDAO
}
import org.bitcoins.wallet.util.WalletUtil
import slick.dbio.{DBIOAction, Effect, NoStream}
import scala.collection.mutable
@ -82,13 +83,13 @@ case class TransactionProcessing(
/** @inheritdoc */
override def processTransaction(
transaction: Transaction,
blockHashOpt: Option[DoubleSha256DigestBE]
blockHashWithConfsOpt: Option[BlockHashWithConfs]
): Future[Unit] = {
for {
relevantReceivedOutputs <- getRelevantOutputs(transaction)
action <- processTransactionImpl(
action = processTransactionImpl(
transaction = transaction,
blockHashOpt = blockHashOpt,
blockHashWithConfsOpt = blockHashWithConfsOpt,
newTags = Vector.empty,
receivedSpendingInfoDbsOpt = None,
spentSpendingInfoDbsOpt = None,
@ -187,7 +188,9 @@ case class TransactionProcessing(
val spentSpendingInfoDbsF =
spendingInfoDAO.findOutputsBeingSpent(block.transactions.toVector)
val blockHashOpt = Some(block.blockHeader.hash.flip)
val blockHash = block.blockHeader.hashBE
val blockHashWithConfsOptF: Future[Option[BlockHashWithConfs]] =
WalletUtil.getBlockHashWithConfs(chainQueryApi, blockHash)
// fetch all outputs we may have received in this block in advance
// as an optimization
@ -201,6 +204,7 @@ case class TransactionProcessing(
spentSpendingInfoDbs <- spentSpendingInfoDbsF
relevantReceivedOutputsForBlock <-
relevantReceivedOutputsForBlockF
blockHashWithConfsOpt <- blockHashWithConfsOptF
} yield {
// we need to keep a cache of spentSpendingInfoDb
// for the case where we receive & then spend that
@ -215,10 +219,10 @@ case class TransactionProcessing(
_ <- walletF
relevantReceivedOutputsForTx = relevantReceivedOutputsForBlock
.getOrElse(transaction.txIdBE, Vector.empty)
action <-
action =
processTransactionImpl(
transaction = transaction,
blockHashOpt = blockHashOpt,
blockHashWithConfsOpt = blockHashWithConfsOpt,
newTags = Vector.empty,
receivedSpendingInfoDbsOpt = receivedSpendingInfoDbsOpt,
spentSpendingInfoDbsOpt = cachedSpentOpt,
@ -314,12 +318,12 @@ case class TransactionProcessing(
feeRate: FeeUnit,
inputAmount: CurrencyUnit,
sentAmount: CurrencyUnit,
blockHashOpt: Option[DoubleSha256DigestBE],
blockHashWithConfsOpt: Option[BlockHashWithConfs],
newTags: Vector[AddressTag]
): Future[ProcessTxResult] = {
logger.info(
s"Processing TX from our wallet, transaction=${transaction.txIdBE.hex} with blockHash=${blockHashOpt
.map(_.hex)}"
s"Processing TX from our wallet, transaction=${transaction.txIdBE.hex} with blockHash=${blockHashWithConfsOpt
.map(_.blockHash.hex)}"
)
val relevantOutputsF = getRelevantOutputs(transaction)
for {
@ -329,12 +333,12 @@ case class TransactionProcessing(
feeRate,
inputAmount,
sentAmount,
blockHashOpt
blockHashWithConfsOpt.map(_.blockHash)
)
relevantOutputs <- relevantOutputsF
action <- processTransactionImpl(
action = processTransactionImpl(
transaction = txDb.transaction,
blockHashOpt = blockHashOpt,
blockHashWithConfsOpt = blockHashWithConfsOpt,
newTags = newTags,
receivedSpendingInfoDbsOpt = None,
spentSpendingInfoDbsOpt = None,
@ -444,48 +448,30 @@ case class TransactionProcessing(
*/
override def processReceivedUtxos(
transaction: Transaction,
blockHashOpt: Option[DoubleSha256DigestBE],
blockHashWithConfsOpt: Option[BlockHashWithConfs],
spendingInfoDbs: Vector[SpendingInfoDb],
newTags: Vector[AddressTag],
relevantReceivedOutputs: Vector[OutputWithIndex]
): Future[Vector[SpendingInfoDb]] = {
val confsOptF: Future[Option[BlockHashWithConfs]] = blockHashOpt match {
case Some(blockHash) =>
chainQueryApi
.getNumberOfConfirmations(blockHash)
.map(confsOpt => Some(BlockHashWithConfs(blockHash, confsOpt)))
case None => Future.successful(None)
}
val actionF = confsOptF.map { confsOpt =>
processReceivedUtxosAction(transaction,
confsOpt,
spendingInfoDbs,
newTags,
relevantReceivedOutputs)
}
actionF.flatMap(a => safeDatabase.run(a))
val action = processReceivedUtxosAction(transaction,
blockHashWithConfsOpt,
spendingInfoDbs,
newTags,
relevantReceivedOutputs)
safeDatabase.run(action)
}
override def processSpentUtxos(
transaction: Transaction,
outputsBeingSpent: Vector[SpendingInfoDb],
blockHashOpt: Option[DoubleSha256DigestBE]
blockHashWithConfsOpt: Option[BlockHashWithConfs]
): Future[Vector[SpendingInfoDb]] = {
val blockHashWithConfsF: Future[Option[BlockHashWithConfs]] =
blockHashOpt match {
case Some(blockHash) =>
chainQueryApi
.getNumberOfConfirmations(blockHash)
.map(BlockHashWithConfs(blockHash, _))
.map(Some.apply)
case None => Future.successful(None)
}
val actionF = blockHashWithConfsF.map { confsOpt =>
processSpentUtxosAction(transaction, outputsBeingSpent, confsOpt)
}
val action = processSpentUtxosAction(transaction,
outputsBeingSpent,
blockHashWithConfsOpt)
actionF.flatMap(safeDatabase.run)
safeDatabase.run(action)
}
/** Searches for outputs on the given transaction that are being spent from
@ -521,17 +507,16 @@ case class TransactionProcessing(
*/
private[internal] def processTransactionImpl(
transaction: Transaction,
blockHashOpt: Option[DoubleSha256DigestBE],
blockHashWithConfsOpt: Option[BlockHashWithConfs],
newTags: Vector[AddressTag],
receivedSpendingInfoDbsOpt: Option[Vector[SpendingInfoDb]],
spentSpendingInfoDbsOpt: Option[Vector[SpendingInfoDb]],
relevantReceivedOutputs: Vector[OutputWithIndex]
): Future[
DBIOAction[ProcessTxResult, NoStream, Effect.Read & Effect.Write]] = {
): DBIOAction[ProcessTxResult, NoStream, Effect.Read & Effect.Write] = {
logger.debug(
s"Processing transaction=${transaction.txIdBE.hex} with blockHash=${blockHashOpt
.map(_.hex)}"
s"Processing transaction=${transaction.txIdBE.hex} with blockHash=${blockHashWithConfsOpt
.map(_.blockHash.hex)}"
)
val receivedSpendingInfoDbsA
: DBIOAction[Vector[SpendingInfoDb], NoStream, Effect.Read] = {
@ -561,51 +546,43 @@ case class TransactionProcessing(
spendingInfoDAO.findOutputsBeingSpentAction(Vector(transaction))
}
}
val confsOptF: Future[Option[BlockHashWithConfs]] = blockHashOpt match {
case Some(blockHash) =>
chainQueryApi
.getNumberOfConfirmations(blockHash)
.map(confsOpt => Some(BlockHashWithConfs(blockHash, confsOpt)))
case None => Future.successful(None)
}
val actionF: Future[
DBIOAction[ProcessTxResult, NoStream, Effect.Write & Effect.Read]] =
confsOptF.map { confsOpt =>
for {
receivedSpendingInfoDbs <- receivedSpendingInfoDbsA
receivedStart = TimeUtil.currentEpochMs
incoming <- processReceivedUtxosAction(
transaction = transaction,
blockHashWithConfsOpt = confsOpt,
spendingInfoDbs = receivedSpendingInfoDbs,
newTags = newTags,
relevantReceivedOutputs = relevantReceivedOutputs
val action
: DBIOAction[ProcessTxResult, NoStream, Effect.Write & Effect.Read] =
for {
receivedSpendingInfoDbs <- receivedSpendingInfoDbsA
receivedStart = TimeUtil.currentEpochMs
incoming <- processReceivedUtxosAction(
transaction = transaction,
blockHashWithConfsOpt = blockHashWithConfsOpt,
spendingInfoDbs = receivedSpendingInfoDbs,
newTags = newTags,
relevantReceivedOutputs = relevantReceivedOutputs
)
_ = if (incoming.nonEmpty) {
logger.info(
s"Finished processing ${incoming.length} received outputs, balance=${incoming
.map(_.output.value)
.sum} it took=${TimeUtil.currentEpochMs - receivedStart}ms"
)
_ = if (incoming.nonEmpty) {
logger.info(
s"Finished processing ${incoming.length} received outputs, balance=${incoming
.map(_.output.value)
.sum} it took=${TimeUtil.currentEpochMs - receivedStart}ms"
)
}
spentSpendingInfoDbs <- spentSpendingInfoDbsF
spentStart = TimeUtil.currentEpochMs
outgoing <- processSpentUtxosAction(
transaction = transaction,
outputsBeingSpent = spentSpendingInfoDbs,
blockHashWithConfs = confsOpt
)
_ = if (outgoing.nonEmpty) {
logger.info(
s"Finished processing ${outgoing.length} spent outputs, it took=${TimeUtil.currentEpochMs - spentStart}ms"
)
}
} yield {
ProcessTxResult(incoming, outgoing)
}
spentSpendingInfoDbs <- spentSpendingInfoDbsF
spentStart = TimeUtil.currentEpochMs
outgoing <- processSpentUtxosAction(
transaction = transaction,
outputsBeingSpent = spentSpendingInfoDbs,
blockHashWithConfs = blockHashWithConfsOpt
)
_ = if (outgoing.nonEmpty) {
logger.info(
s"Finished processing ${outgoing.length} spent outputs, it took=${TimeUtil.currentEpochMs - spentStart}ms"
)
}
} yield {
ProcessTxResult(incoming, outgoing)
}
actionF
action
}

View file

@ -24,6 +24,7 @@ import org.bitcoins.db.SafeDatabase
import org.bitcoins.wallet.callback.WalletCallbacks
import org.bitcoins.wallet.config.WalletAppConfig
import org.bitcoins.wallet.models.{AddressDAO, SpendingInfoDAO, TransactionDAO}
import org.bitcoins.wallet.util.WalletUtil
import slick.dbio.{DBIOAction, Effect, NoStream}
import scala.concurrent.Future
@ -258,9 +259,8 @@ case class UtxoHandling(
case (blockHashOpt, spendingInfoDbs) =>
blockHashOpt match {
case Some(blockHash) =>
chainQueryApi
.getNumberOfConfirmations(blockHash)
.map(confs => Some(BlockHashWithConfs(blockHash, confs)))
WalletUtil
.getBlockHashWithConfs(chainQueryApi, blockHash)
.map(blockWithConfsOpt => (blockWithConfsOpt, spendingInfoDbs))
case None =>
Future.successful((None, spendingInfoDbs))

View file

@ -0,0 +1,30 @@
package org.bitcoins.wallet.util
import org.bitcoins.core.api.chain.ChainQueryApi
import org.bitcoins.core.util.BlockHashWithConfs
import org.bitcoins.crypto.DoubleSha256DigestBE
import scala.concurrent.{ExecutionContext, Future}
object WalletUtil {
def getBlockHashWithConfs(
chainQueryApi: ChainQueryApi,
blockHashOpt: Option[DoubleSha256DigestBE])(implicit
ec: ExecutionContext): Future[Option[BlockHashWithConfs]] = {
blockHashOpt match {
case Some(blockHash) =>
chainQueryApi
.getNumberOfConfirmations(blockHash)
.map(confsOpt => Some(BlockHashWithConfs(blockHash, confsOpt)))
case None => Future.successful(None)
}
}
def getBlockHashWithConfs(
chainQueryApi: ChainQueryApi,
blockHash: DoubleSha256DigestBE)(implicit
ec: ExecutionContext): Future[Option[BlockHashWithConfs]] = {
getBlockHashWithConfs(chainQueryApi, Some(blockHash))
}
}