mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2025-03-03 18:47:38 +01:00
Refactor coin selection to be not be bitcoin-s specific (#4496)
* Refactor coin selection to be not be bitcoin-s specific * Add to CoinSelectorUtxo
This commit is contained in:
parent
0a127368f0
commit
c210052640
7 changed files with 95 additions and 64 deletions
|
@ -1,6 +1,6 @@
|
||||||
package org.bitcoins.core.wallet
|
package org.bitcoins.core.wallet
|
||||||
|
|
||||||
import org.bitcoins.core.api.wallet.CoinSelector
|
import org.bitcoins.core.api.wallet.{CoinSelector, CoinSelectorUtxo}
|
||||||
import org.bitcoins.core.api.wallet.db._
|
import org.bitcoins.core.api.wallet.db._
|
||||||
import org.bitcoins.core.currency._
|
import org.bitcoins.core.currency._
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
|
@ -16,9 +16,10 @@ class CoinSelectorTest extends BitcoinSUnitTest {
|
||||||
|
|
||||||
behavior of "CoinSelector"
|
behavior of "CoinSelector"
|
||||||
|
|
||||||
val utxos: Vector[SpendingInfoDb] =
|
val utxos: Vector[CoinSelectorUtxo] =
|
||||||
createSpendingInfoDbs(Vector(Bitcoins(1), Bitcoins(2)))
|
createSpendingInfoDbs(Vector(Bitcoins(1), Bitcoins(2)))
|
||||||
val inAmt: CurrencyUnit = utxos.map(_.output.value).sum
|
.map(CoinSelectorUtxo.fromSpendingInfoDb)
|
||||||
|
val inAmt: CurrencyUnit = utxos.map(_.prevOut.value).sum
|
||||||
val target: Bitcoins = Bitcoins(2)
|
val target: Bitcoins = Bitcoins(2)
|
||||||
val changeCost: Satoshis = Satoshis.one
|
val changeCost: Satoshis = Satoshis.one
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package org.bitcoins.core.api.wallet
|
package org.bitcoins.core.api.wallet
|
||||||
|
|
||||||
import org.bitcoins.core.api.wallet.CoinSelectionAlgo._
|
import org.bitcoins.core.api.wallet.CoinSelectionAlgo._
|
||||||
import org.bitcoins.core.api.wallet.db.SpendingInfoDb
|
|
||||||
import org.bitcoins.core.currency._
|
import org.bitcoins.core.currency._
|
||||||
import org.bitcoins.core.protocol.transaction.TransactionOutput
|
import org.bitcoins.core.protocol.script._
|
||||||
|
import org.bitcoins.core.protocol.transaction._
|
||||||
import org.bitcoins.core.wallet.fee.FeeUnit
|
import org.bitcoins.core.wallet.fee.FeeUnit
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
@ -16,9 +16,9 @@ trait CoinSelector {
|
||||||
* should only be used for research purposes
|
* should only be used for research purposes
|
||||||
*/
|
*/
|
||||||
def randomSelection(
|
def randomSelection(
|
||||||
walletUtxos: Vector[SpendingInfoDb],
|
walletUtxos: Vector[CoinSelectorUtxo],
|
||||||
outputs: Vector[TransactionOutput],
|
outputs: Vector[TransactionOutput],
|
||||||
feeRate: FeeUnit): Vector[SpendingInfoDb] = {
|
feeRate: FeeUnit): Vector[CoinSelectorUtxo] = {
|
||||||
val randomUtxos = Random.shuffle(walletUtxos)
|
val randomUtxos = Random.shuffle(walletUtxos)
|
||||||
|
|
||||||
accumulate(randomUtxos, outputs, feeRate)
|
accumulate(randomUtxos, outputs, feeRate)
|
||||||
|
@ -28,11 +28,11 @@ trait CoinSelector {
|
||||||
* below their fees. Better for high fee environments than accumulateSmallestViable.
|
* below their fees. Better for high fee environments than accumulateSmallestViable.
|
||||||
*/
|
*/
|
||||||
def accumulateLargest(
|
def accumulateLargest(
|
||||||
walletUtxos: Vector[SpendingInfoDb],
|
walletUtxos: Vector[CoinSelectorUtxo],
|
||||||
outputs: Vector[TransactionOutput],
|
outputs: Vector[TransactionOutput],
|
||||||
feeRate: FeeUnit): Vector[SpendingInfoDb] = {
|
feeRate: FeeUnit): Vector[CoinSelectorUtxo] = {
|
||||||
val sortedUtxos =
|
val sortedUtxos =
|
||||||
walletUtxos.sortBy(_.output.value).reverse
|
walletUtxos.sortBy(_.prevOut.value).reverse
|
||||||
|
|
||||||
accumulate(sortedUtxos, outputs, feeRate)
|
accumulate(sortedUtxos, outputs, feeRate)
|
||||||
}
|
}
|
||||||
|
@ -43,29 +43,29 @@ trait CoinSelector {
|
||||||
* Has the potential privacy breach of connecting a ton of UTXOs to one address.
|
* Has the potential privacy breach of connecting a ton of UTXOs to one address.
|
||||||
*/
|
*/
|
||||||
def accumulateSmallestViable(
|
def accumulateSmallestViable(
|
||||||
walletUtxos: Vector[SpendingInfoDb],
|
walletUtxos: Vector[CoinSelectorUtxo],
|
||||||
outputs: Vector[TransactionOutput],
|
outputs: Vector[TransactionOutput],
|
||||||
feeRate: FeeUnit): Vector[SpendingInfoDb] = {
|
feeRate: FeeUnit): Vector[CoinSelectorUtxo] = {
|
||||||
val sortedUtxos = walletUtxos.sortBy(_.output.value)
|
val sortedUtxos = walletUtxos.sortBy(_.prevOut.value)
|
||||||
|
|
||||||
accumulate(sortedUtxos, outputs, feeRate)
|
accumulate(sortedUtxos, outputs, feeRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Greedily selects from walletUtxos in order, skipping outputs with values below their fees */
|
/** Greedily selects from walletUtxos in order, skipping outputs with values below their fees */
|
||||||
def accumulate(
|
def accumulate(
|
||||||
walletUtxos: Vector[SpendingInfoDb],
|
walletUtxos: Vector[CoinSelectorUtxo],
|
||||||
outputs: Vector[TransactionOutput],
|
outputs: Vector[TransactionOutput],
|
||||||
feeRate: FeeUnit): Vector[SpendingInfoDb] = {
|
feeRate: FeeUnit): Vector[CoinSelectorUtxo] = {
|
||||||
val totalValue = outputs.foldLeft(CurrencyUnits.zero) {
|
val totalValue = outputs.foldLeft(CurrencyUnits.zero) {
|
||||||
case (totVal, output) => totVal + output.value
|
case (totVal, output) => totVal + output.value
|
||||||
}
|
}
|
||||||
|
|
||||||
@tailrec
|
@tailrec
|
||||||
def addUtxos(
|
def addUtxos(
|
||||||
alreadyAdded: Vector[SpendingInfoDb],
|
alreadyAdded: Vector[CoinSelectorUtxo],
|
||||||
valueSoFar: CurrencyUnit,
|
valueSoFar: CurrencyUnit,
|
||||||
bytesSoFar: Long,
|
bytesSoFar: Long,
|
||||||
utxosLeft: Vector[SpendingInfoDb]): Vector[SpendingInfoDb] = {
|
utxosLeft: Vector[CoinSelectorUtxo]): Vector[CoinSelectorUtxo] = {
|
||||||
val fee = feeRate * bytesSoFar
|
val fee = feeRate * bytesSoFar
|
||||||
if (valueSoFar > totalValue + fee) {
|
if (valueSoFar > totalValue + fee) {
|
||||||
alreadyAdded
|
alreadyAdded
|
||||||
|
@ -79,7 +79,7 @@ trait CoinSelector {
|
||||||
addUtxos(alreadyAdded, valueSoFar, bytesSoFar, utxosLeft.tail)
|
addUtxos(alreadyAdded, valueSoFar, bytesSoFar, utxosLeft.tail)
|
||||||
} else {
|
} else {
|
||||||
val newAdded = alreadyAdded.:+(nextUtxo)
|
val newAdded = alreadyAdded.:+(nextUtxo)
|
||||||
val newValue = valueSoFar + nextUtxo.output.value
|
val newValue = valueSoFar + nextUtxo.prevOut.value
|
||||||
val approxUtxoSize = CoinSelector.approximateUtxoSize(nextUtxo)
|
val approxUtxoSize = CoinSelector.approximateUtxoSize(nextUtxo)
|
||||||
|
|
||||||
addUtxos(newAdded,
|
addUtxos(newAdded,
|
||||||
|
@ -93,30 +93,38 @@ trait CoinSelector {
|
||||||
addUtxos(Vector.empty, CurrencyUnits.zero, bytesSoFar = 0L, walletUtxos)
|
addUtxos(Vector.empty, CurrencyUnits.zero, bytesSoFar = 0L, walletUtxos)
|
||||||
}
|
}
|
||||||
|
|
||||||
def calculateUtxoFee(utxo: SpendingInfoDb, feeRate: FeeUnit): CurrencyUnit = {
|
def calculateUtxoFee(
|
||||||
|
utxo: CoinSelectorUtxo,
|
||||||
|
feeRate: FeeUnit): CurrencyUnit = {
|
||||||
val approxUtxoSize = CoinSelector.approximateUtxoSize(utxo)
|
val approxUtxoSize = CoinSelector.approximateUtxoSize(utxo)
|
||||||
feeRate * approxUtxoSize
|
feeRate * approxUtxoSize
|
||||||
}
|
}
|
||||||
|
|
||||||
def calcEffectiveValue(
|
def calcEffectiveValue(
|
||||||
utxo: SpendingInfoDb,
|
utxo: CoinSelectorUtxo,
|
||||||
feeRate: FeeUnit): CurrencyUnit = {
|
feeRate: FeeUnit): CurrencyUnit = {
|
||||||
val utxoFee = calculateUtxoFee(utxo, feeRate)
|
val utxoFee = calculateUtxoFee(utxo, feeRate)
|
||||||
utxo.output.value - utxoFee
|
utxo.prevOut.value - utxoFee
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object CoinSelector extends CoinSelector {
|
object CoinSelector extends CoinSelector {
|
||||||
|
|
||||||
/** Cribbed from [[https://github.com/bitcoinjs/coinselect/blob/master/utils.js]] */
|
/** Cribbed from [[https://github.com/bitcoinjs/coinselect/blob/master/utils.js]] */
|
||||||
def approximateUtxoSize(utxo: SpendingInfoDb): Long = {
|
def approximateUtxoSize(utxo: CoinSelectorUtxo): Long = {
|
||||||
val inputBase = 32 + 4 + 1 + 4
|
val inputBase = 32 + 4 + 1 + 4
|
||||||
val scriptSize = utxo.redeemScriptOpt match {
|
val scriptSize = utxo.redeemScriptOpt match {
|
||||||
case Some(script) => script.bytes.length
|
case Some(script) => script.bytes.length
|
||||||
case None =>
|
case None =>
|
||||||
utxo.scriptWitnessOpt match {
|
utxo.scriptWitnessOpt match {
|
||||||
case Some(script) => script.bytes.length
|
case Some(script) => script.bytes.length
|
||||||
case None => 107 // PUBKEYHASH
|
case None =>
|
||||||
|
utxo.prevOut.scriptPubKey match {
|
||||||
|
case _: NonWitnessScriptPubKey => 107 // P2PKH
|
||||||
|
case _: WitnessScriptPubKeyV0 => 107 // P2WPKH
|
||||||
|
case _: TaprootScriptPubKey => 64 // Single Schnorr signature
|
||||||
|
case _: UnassignedWitnessScriptPubKey => 0 // unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,10 +133,10 @@ object CoinSelector extends CoinSelector {
|
||||||
|
|
||||||
def selectByAlgo(
|
def selectByAlgo(
|
||||||
coinSelectionAlgo: CoinSelectionAlgo,
|
coinSelectionAlgo: CoinSelectionAlgo,
|
||||||
walletUtxos: Vector[SpendingInfoDb],
|
walletUtxos: Vector[CoinSelectorUtxo],
|
||||||
outputs: Vector[TransactionOutput],
|
outputs: Vector[TransactionOutput],
|
||||||
feeRate: FeeUnit,
|
feeRate: FeeUnit,
|
||||||
longTermFeeRateOpt: Option[FeeUnit] = None): Vector[SpendingInfoDb] =
|
longTermFeeRateOpt: Option[FeeUnit] = None): Vector[CoinSelectorUtxo] =
|
||||||
coinSelectionAlgo match {
|
coinSelectionAlgo match {
|
||||||
case RandomSelection =>
|
case RandomSelection =>
|
||||||
randomSelection(walletUtxos, outputs, feeRate)
|
randomSelection(walletUtxos, outputs, feeRate)
|
||||||
|
@ -147,7 +155,7 @@ object CoinSelector extends CoinSelector {
|
||||||
"longTermFeeRateOpt must be defined for LeastWaste")
|
"longTermFeeRateOpt must be defined for LeastWaste")
|
||||||
}
|
}
|
||||||
case SelectedUtxos(outPoints) =>
|
case SelectedUtxos(outPoints) =>
|
||||||
val result = walletUtxos.foldLeft(Vector.empty[SpendingInfoDb]) {
|
val result = walletUtxos.foldLeft(Vector.empty[CoinSelectorUtxo]) {
|
||||||
(acc, utxo) =>
|
(acc, utxo) =>
|
||||||
val outPoint = (utxo.outPoint.txId, utxo.outPoint.vout.toInt)
|
val outPoint = (utxo.outPoint.txId, utxo.outPoint.vout.toInt)
|
||||||
if (outPoints(outPoint)) acc :+ utxo else acc
|
if (outPoints(outPoint)) acc :+ utxo else acc
|
||||||
|
@ -170,7 +178,7 @@ object CoinSelector extends CoinSelector {
|
||||||
private case class CoinSelectionResults(
|
private case class CoinSelectionResults(
|
||||||
waste: CurrencyUnit,
|
waste: CurrencyUnit,
|
||||||
totalSpent: CurrencyUnit,
|
totalSpent: CurrencyUnit,
|
||||||
selection: Vector[SpendingInfoDb])
|
selection: Vector[CoinSelectorUtxo])
|
||||||
|
|
||||||
implicit
|
implicit
|
||||||
private val coinSelectionResultsOrder: Ordering[CoinSelectionResults] = {
|
private val coinSelectionResultsOrder: Ordering[CoinSelectionResults] = {
|
||||||
|
@ -181,11 +189,11 @@ object CoinSelector extends CoinSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
def selectByLeastWaste(
|
def selectByLeastWaste(
|
||||||
walletUtxos: Vector[SpendingInfoDb],
|
walletUtxos: Vector[CoinSelectorUtxo],
|
||||||
outputs: Vector[TransactionOutput],
|
outputs: Vector[TransactionOutput],
|
||||||
feeRate: FeeUnit,
|
feeRate: FeeUnit,
|
||||||
longTermFeeRate: FeeUnit
|
longTermFeeRate: FeeUnit
|
||||||
): Vector[SpendingInfoDb] = {
|
): Vector[CoinSelectorUtxo] = {
|
||||||
val target = outputs.map(_.value).sum
|
val target = outputs.map(_.value).sum
|
||||||
val results = CoinSelectionAlgo.independentAlgos.flatMap { algo =>
|
val results = CoinSelectionAlgo.independentAlgos.flatMap { algo =>
|
||||||
// Skip failed selection attempts
|
// Skip failed selection attempts
|
||||||
|
@ -207,7 +215,7 @@ object CoinSelector extends CoinSelector {
|
||||||
feeRate,
|
feeRate,
|
||||||
longTermFeeRate)
|
longTermFeeRate)
|
||||||
|
|
||||||
val totalSpent = selection.map(_.output.value).sum
|
val totalSpent = selection.map(_.prevOut.value).sum
|
||||||
CoinSelectionResults(waste, totalSpent, selection)
|
CoinSelectionResults(waste, totalSpent, selection)
|
||||||
}.toOption
|
}.toOption
|
||||||
}
|
}
|
||||||
|
@ -236,7 +244,7 @@ object CoinSelector extends CoinSelector {
|
||||||
* @return The waste
|
* @return The waste
|
||||||
*/
|
*/
|
||||||
def calculateSelectionWaste(
|
def calculateSelectionWaste(
|
||||||
utxos: Vector[SpendingInfoDb],
|
utxos: Vector[CoinSelectorUtxo],
|
||||||
changeCostOpt: Option[CurrencyUnit],
|
changeCostOpt: Option[CurrencyUnit],
|
||||||
target: CurrencyUnit,
|
target: CurrencyUnit,
|
||||||
feeRate: FeeUnit,
|
feeRate: FeeUnit,
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.bitcoins.core.api.wallet
|
||||||
|
|
||||||
|
import org.bitcoins.core.api.wallet.db.SpendingInfoDb
|
||||||
|
import org.bitcoins.core.protocol.script._
|
||||||
|
import org.bitcoins.core.protocol.transaction._
|
||||||
|
|
||||||
|
case class CoinSelectorUtxo(
|
||||||
|
prevOut: TransactionOutput,
|
||||||
|
outPoint: TransactionOutPoint,
|
||||||
|
redeemScriptOpt: Option[ScriptPubKey],
|
||||||
|
scriptWitnessOpt: Option[ScriptWitness])
|
||||||
|
|
||||||
|
object CoinSelectorUtxo {
|
||||||
|
|
||||||
|
def fromSpendingInfoDb(db: SpendingInfoDb): CoinSelectorUtxo = {
|
||||||
|
CoinSelectorUtxo(db.output,
|
||||||
|
db.outPoint,
|
||||||
|
db.redeemScriptOpt,
|
||||||
|
db.scriptWitnessOpt)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package org.bitcoins.core.api.wallet.db
|
||||||
|
|
||||||
import org.bitcoins.core.api.db.DbRowAutoInc
|
import org.bitcoins.core.api.db.DbRowAutoInc
|
||||||
import org.bitcoins.core.api.keymanager.BIP39KeyManagerApi
|
import org.bitcoins.core.api.keymanager.BIP39KeyManagerApi
|
||||||
|
import org.bitcoins.core.api.wallet.CoinSelectorUtxo
|
||||||
import org.bitcoins.core.hd._
|
import org.bitcoins.core.hd._
|
||||||
import org.bitcoins.core.protocol.script.{
|
import org.bitcoins.core.protocol.script.{
|
||||||
P2SHScriptPubKey,
|
P2SHScriptPubKey,
|
||||||
|
@ -195,6 +196,10 @@ sealed trait SpendingInfoDb extends DbRowAutoInc[SpendingInfoDb] {
|
||||||
hashType
|
hashType
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def toCoinSelectorUtxo: CoinSelectorUtxo = {
|
||||||
|
CoinSelectorUtxo.fromSpendingInfoDb(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object SpendingInfoDb {
|
object SpendingInfoDb {
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
package org.bitcoins.wallet
|
package org.bitcoins.wallet
|
||||||
|
|
||||||
import org.bitcoins.core.api.wallet.CoinSelector
|
import org.bitcoins.core.api.wallet.{CoinSelector, CoinSelectorUtxo}
|
||||||
import org.bitcoins.core.api.wallet.db.{SegwitV0SpendingInfo, SpendingInfoDb}
|
|
||||||
import org.bitcoins.core.currency._
|
import org.bitcoins.core.currency._
|
||||||
import org.bitcoins.core.protocol.script.ScriptPubKey
|
import org.bitcoins.core.protocol.script.ScriptPubKey
|
||||||
import org.bitcoins.core.protocol.transaction.TransactionOutput
|
import org.bitcoins.core.protocol.transaction.TransactionOutput
|
||||||
import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerByte}
|
import org.bitcoins.core.wallet.fee.{FeeUnit, SatoshisPerByte}
|
||||||
import org.bitcoins.core.wallet.utxo.TxoState
|
import org.bitcoins.testkit.wallet.BitcoinSWalletTest
|
||||||
import org.bitcoins.testkit.wallet.{BitcoinSWalletTest, WalletTestUtil}
|
|
||||||
import org.bitcoins.testkitcore.Implicits._
|
import org.bitcoins.testkitcore.Implicits._
|
||||||
import org.bitcoins.testkitcore.gen.{TransactionGenerators, WitnessGenerators}
|
import org.bitcoins.testkitcore.gen.{TransactionGenerators, WitnessGenerators}
|
||||||
import org.scalatest.FutureOutcome
|
import org.scalatest.FutureOutcome
|
||||||
|
@ -17,10 +15,12 @@ class CoinSelectorTest extends BitcoinSWalletTest {
|
||||||
case class CoinSelectionFixture(
|
case class CoinSelectionFixture(
|
||||||
output: TransactionOutput,
|
output: TransactionOutput,
|
||||||
feeRate: FeeUnit,
|
feeRate: FeeUnit,
|
||||||
utxo1: SpendingInfoDb,
|
utxo1: CoinSelectorUtxo,
|
||||||
utxo2: SpendingInfoDb,
|
utxo2: CoinSelectorUtxo,
|
||||||
utxo3: SpendingInfoDb) {
|
utxo3: CoinSelectorUtxo) {
|
||||||
val utxoSet: Vector[SpendingInfoDb] = Vector(utxo1, utxo2, utxo3)
|
|
||||||
|
val utxoSet: Vector[CoinSelectorUtxo] = Vector(utxo1, utxo2, utxo3)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override type FixtureParam = CoinSelectionFixture
|
override type FixtureParam = CoinSelectionFixture
|
||||||
|
@ -30,35 +30,26 @@ class CoinSelectorTest extends BitcoinSWalletTest {
|
||||||
val feeRate = SatoshisPerByte(CurrencyUnits.zero)
|
val feeRate = SatoshisPerByte(CurrencyUnits.zero)
|
||||||
|
|
||||||
val outpoint1 = TransactionGenerators.outPoint.sampleSome
|
val outpoint1 = TransactionGenerators.outPoint.sampleSome
|
||||||
val utxo1 = SegwitV0SpendingInfo(
|
val utxo1 = CoinSelectorUtxo(
|
||||||
state = TxoState.PendingConfirmationsReceived,
|
|
||||||
id = Some(1),
|
|
||||||
outPoint = outpoint1,
|
outPoint = outpoint1,
|
||||||
output = TransactionOutput(10.sats, ScriptPubKey.empty),
|
prevOut = TransactionOutput(10.sats, ScriptPubKey.empty),
|
||||||
privKeyPath = WalletTestUtil.sampleSegwitPath,
|
scriptWitnessOpt = Some(WitnessGenerators.scriptWitness.sampleSome),
|
||||||
scriptWitness = WitnessGenerators.scriptWitness.sampleSome,
|
redeemScriptOpt = None
|
||||||
spendingTxIdOpt = None
|
|
||||||
)
|
)
|
||||||
val outPoint2 = TransactionGenerators.outPoint.sampleSome
|
val outPoint2 = TransactionGenerators.outPoint.sampleSome
|
||||||
val utxo2 = SegwitV0SpendingInfo(
|
val utxo2 = CoinSelectorUtxo(
|
||||||
state = TxoState.ConfirmedReceived,
|
|
||||||
id = Some(2),
|
|
||||||
outPoint = outPoint2,
|
outPoint = outPoint2,
|
||||||
output = TransactionOutput(90.sats, ScriptPubKey.empty),
|
prevOut = TransactionOutput(90.sats, ScriptPubKey.empty),
|
||||||
privKeyPath = WalletTestUtil.sampleSegwitPath,
|
scriptWitnessOpt = Some(WitnessGenerators.scriptWitness.sampleSome),
|
||||||
scriptWitness = WitnessGenerators.scriptWitness.sampleSome,
|
redeemScriptOpt = None
|
||||||
spendingTxIdOpt = None
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val outPoint3 = TransactionGenerators.outPoint.sampleSome
|
val outPoint3 = TransactionGenerators.outPoint.sampleSome
|
||||||
val utxo3 = SegwitV0SpendingInfo(
|
val utxo3 = CoinSelectorUtxo(
|
||||||
state = TxoState.ConfirmedReceived,
|
|
||||||
id = Some(3),
|
|
||||||
outPoint = outPoint3,
|
outPoint = outPoint3,
|
||||||
output = TransactionOutput(20.sats, ScriptPubKey.empty),
|
prevOut = TransactionOutput(20.sats, ScriptPubKey.empty),
|
||||||
privKeyPath = WalletTestUtil.sampleSegwitPath,
|
scriptWitnessOpt = Some(WitnessGenerators.scriptWitness.sampleSome),
|
||||||
scriptWitness = WitnessGenerators.scriptWitness.sampleSome,
|
redeemScriptOpt = None
|
||||||
spendingTxIdOpt = None
|
|
||||||
)
|
)
|
||||||
|
|
||||||
test(CoinSelectionFixture(output, feeRate, utxo1, utxo2, utxo3))
|
test(CoinSelectionFixture(output, feeRate, utxo1, utxo2, utxo3))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package org.bitcoins.wallet
|
package org.bitcoins.wallet
|
||||||
|
|
||||||
import org.bitcoins.core.api.wallet.{CoinSelectionAlgo, CoinSelector}
|
import org.bitcoins.core.api.wallet._
|
||||||
import org.bitcoins.core.currency._
|
import org.bitcoins.core.currency._
|
||||||
import org.bitcoins.core.number.{Int32, UInt32}
|
import org.bitcoins.core.number.{Int32, UInt32}
|
||||||
import org.bitcoins.core.protocol.BitcoinAddress
|
import org.bitcoins.core.protocol.BitcoinAddress
|
||||||
|
@ -464,7 +464,10 @@ class WalletSendingTest extends BitcoinSWalletTest {
|
||||||
for {
|
for {
|
||||||
account <- wallet.getDefaultAccount()
|
account <- wallet.getDefaultAccount()
|
||||||
feeRate <- wallet.getFeeRate()
|
feeRate <- wallet.getFeeRate()
|
||||||
allUtxos <- wallet.listUtxos(account.hdAccount)
|
allUtxos <- wallet
|
||||||
|
.listUtxos(account.hdAccount)
|
||||||
|
.map(_.map(CoinSelectorUtxo.fromSpendingInfoDb))
|
||||||
|
|
||||||
output = TransactionOutput(amountToSend, testAddress.scriptPubKey)
|
output = TransactionOutput(amountToSend, testAddress.scriptPubKey)
|
||||||
expectedUtxos =
|
expectedUtxos =
|
||||||
CoinSelector.selectByAlgo(algo, allUtxos, Vector(output), feeRate)
|
CoinSelector.selectByAlgo(algo, allUtxos, Vector(output), feeRate)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.bitcoins.wallet.internal
|
package org.bitcoins.wallet.internal
|
||||||
|
|
||||||
import org.bitcoins.core.api.wallet.db.{AccountDb, SpendingInfoDb}
|
import org.bitcoins.core.api.wallet.db.{AccountDb, SpendingInfoDb}
|
||||||
import org.bitcoins.core.api.wallet.{CoinSelectionAlgo, CoinSelector}
|
import org.bitcoins.core.api.wallet._
|
||||||
import org.bitcoins.core.policy.Policy
|
import org.bitcoins.core.policy.Policy
|
||||||
import org.bitcoins.core.protocol.transaction._
|
import org.bitcoins.core.protocol.transaction._
|
||||||
import org.bitcoins.core.wallet.builder._
|
import org.bitcoins.core.wallet.builder._
|
||||||
|
@ -93,6 +93,7 @@ trait FundTransactionHandling extends WalletLogger { self: Wallet =>
|
||||||
selectableUtxos = walletUtxos
|
selectableUtxos = walletUtxos
|
||||||
.map(_._1)
|
.map(_._1)
|
||||||
.filter(_.output.value > Policy.dustThreshold)
|
.filter(_.output.value > Policy.dustThreshold)
|
||||||
|
.map(CoinSelectorUtxo.fromSpendingInfoDb)
|
||||||
|
|
||||||
utxos = CoinSelector.selectByAlgo(
|
utxos = CoinSelector.selectByAlgo(
|
||||||
coinSelectionAlgo = coinSelectionAlgo,
|
coinSelectionAlgo = coinSelectionAlgo,
|
||||||
|
@ -101,7 +102,8 @@ trait FundTransactionHandling extends WalletLogger { self: Wallet =>
|
||||||
feeRate = feeRate,
|
feeRate = feeRate,
|
||||||
longTermFeeRateOpt = Some(self.walletConfig.longTermFeeRate)
|
longTermFeeRateOpt = Some(self.walletConfig.longTermFeeRate)
|
||||||
)
|
)
|
||||||
filtered = walletUtxos.filter(utxo => utxos.contains(utxo._1))
|
filtered = walletUtxos.filter(utxo =>
|
||||||
|
utxos.exists(_.outPoint == utxo._1.outPoint))
|
||||||
_ <-
|
_ <-
|
||||||
if (markAsReserved) markUTXOsAsReserved(filtered.map(_._1))
|
if (markAsReserved) markUTXOsAsReserved(filtered.map(_._1))
|
||||||
else Future.unit
|
else Future.unit
|
||||||
|
|
Loading…
Add table
Reference in a new issue