Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions wallet/createtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -1657,9 +1657,11 @@ func (w *Wallet) purchaseTickets(ctx context.Context, op errors.Op,
return purchaseTicketsResponse, err
}

// ReserveOutputsForAmount returns locked spendable outpoints from the given
// reserveOutputsForAmount returns locked spendable outpoints from the given
// account. It is the responsibility of the caller to unlock the outpoints.
func (w *Wallet) ReserveOutputsForAmount(ctx context.Context, account uint32, amount dcrutil.Amount, minconf int32) ([]Input, error) {
// This unexported func supports specifying a minimum non-zero change amount,
// so that the selected outputs will total at least "amount + minChange".
func (w *Wallet) reserveOutputsForAmount(ctx context.Context, account uint32, amount dcrutil.Amount, minconf int32, minChange dcrutil.Amount) ([]Input, error) {
defer w.lockedOutpointMu.Unlock()
w.lockedOutpointMu.Lock()

Expand All @@ -1668,10 +1670,15 @@ func (w *Wallet) ReserveOutputsForAmount(ctx context.Context, account uint32, am
// Get current block's height
_, tipHeight := w.txStore.MainChainTip(dbtx)

targetAmount := amount
if minChange > 0 {
targetAmount = amount + minChange
}

var err error
const minAmount = 0
const maxResults = 0
outputs, err = w.findEligibleOutputsAmount(dbtx, account, minconf, amount, tipHeight,
outputs, err = w.findEligibleOutputsAmount(dbtx, account, minconf, targetAmount, tipHeight,
minAmount, maxResults)
if err != nil {
return err
Expand All @@ -1690,6 +1697,12 @@ func (w *Wallet) ReserveOutputsForAmount(ctx context.Context, account uint32, am
return outputs, nil
}

// ReserveOutputsForAmount returns locked spendable outpoints from the given
// account. It is the responsibility of the caller to unlock the outpoints.
func (w *Wallet) ReserveOutputsForAmount(ctx context.Context, account uint32, amount dcrutil.Amount, minconf int32) ([]Input, error) {
return w.reserveOutputsForAmount(ctx, account, amount, minconf, 0)
}

func (w *Wallet) reserveOutputs(ctx context.Context, account uint32, minconf int32) ([]Input, error) {
defer w.lockedOutpointMu.Unlock()
w.lockedOutpointMu.Lock()
Expand Down
26 changes: 20 additions & 6 deletions wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -4663,13 +4663,31 @@ func (s sigDataSource) GetScript(a stdaddr.Address) ([]byte, error) { return s.s
func (w *Wallet) CreateVspPayment(ctx context.Context, tx *wire.MsgTx, fee dcrutil.Amount,
feeAddr stdaddr.Address, feeAcct uint32, changeAcct uint32) error {

feeRate := w.RelayFee()

// Reserve new outputs to pay the fee if outputs have not already been
// reserved. This will be the case for fee payments that were begun on
// already purchased tickets, where the caller did not ensure that fee
// outputs would already be reserved.
if len(tx.TxIn) == 0 {
const minconf = 1
inputs, err := w.ReserveOutputsForAmount(ctx, feeAcct, fee, minconf)

minMixableChange := smallestMixChange(feeRate)

// Estimate tx fee for a typical VSP payment (1 input, 2 outputs)
// to ensure enough is reserved for mixable change after all fees.
estScriptSizes := []int{txsizes.RedeemP2PKHSigScriptSize}
estOutputs := []*wire.TxOut{
{PkScript: make([]byte, txsizes.P2PKHPkScriptSize)}, // fee output
{PkScript: make([]byte, txsizes.P2PKHPkScriptSize)}, // change output
}
estSize := txsizes.EstimateSerializeSize(estScriptSizes, estOutputs, 0)
estTxFee := txrules.FeeForSerializeSize(feeRate, estSize)

// Request inputs totaling at least (fee + minMixableChange + estTxFee)
// so change after paying VSP fee and tx fee will be mixable.
minChange := minMixableChange + dcrutil.Amount(estTxFee)
inputs, err := w.reserveOutputsForAmount(ctx, feeAcct, fee, minconf, minChange)
if err != nil {
return fmt.Errorf("unable to reserve outputs: %w", err)
}
Expand Down Expand Up @@ -4716,11 +4734,7 @@ func (w *Wallet) CreateVspPayment(ctx context.Context, tx *wire.MsgTx, fee dcrut
Version: vers,
PkScript: feeScript,
})
feeRate := w.RelayFee()
scriptSizes := make([]int, len(tx.TxIn))
for i := range scriptSizes {
scriptSizes[i] = txsizes.RedeemP2PKHSigScriptSize
}
scriptSizes := slices.Repeat([]int{txsizes.RedeemP2PKHSigScriptSize}, len(tx.TxIn))
est := txsizes.EstimateSerializeSize(scriptSizes, tx.TxOut, txsizes.P2PKHPkScriptSize)
change := input
change -= tx.TxOut[0].Value
Expand Down
Loading