wallet: use constant input source for change calculation
To fix a bug where specifying multiple UTXOs that are by themselves large enough to satisfy the output amount would lead to the rest of them being added to fees, we need to provide the transaction author with a constant list of UTXOs. If we didn't, the author would only consider one input and calculate the change based on that alone. But since we'd add all inputs to the PSBT, the rest of the amounts would go to fees.
This commit is contained in:
parent
34bfc5efb9
commit
98e779a102
2 changed files with 50 additions and 8 deletions
|
@ -134,7 +134,14 @@ func (w *Wallet) FundPsbt(packet *psbt.Packet, account uint32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can leverage the fee calculation of the txauthor package
|
// We can leverage the fee calculation of the txauthor package
|
||||||
// if we provide the selected UTXOs as a coin source.
|
// if we provide the selected UTXOs as a coin source. We just
|
||||||
|
// need to make sure we always return the full list of user-
|
||||||
|
// selected UTXOs rather than a subset, otherwise our change
|
||||||
|
// amount will be off (in case the user selected multiple UTXOs
|
||||||
|
// that are large enough on their own). That's why we use our
|
||||||
|
// own static input source creator instead of the more generic
|
||||||
|
// makeInputSource() that selects a subset that is "large
|
||||||
|
// enough".
|
||||||
credits := make([]wtxmgr.Credit, len(txIn))
|
credits := make([]wtxmgr.Credit, len(txIn))
|
||||||
for idx, in := range txIn {
|
for idx, in := range txIn {
|
||||||
utxo := packet.Inputs[idx].WitnessUtxo
|
utxo := packet.Inputs[idx].WitnessUtxo
|
||||||
|
@ -144,7 +151,7 @@ func (w *Wallet) FundPsbt(packet *psbt.Packet, account uint32,
|
||||||
PkScript: utxo.PkScript,
|
PkScript: utxo.PkScript,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inputSource := makeInputSource(credits)
|
inputSource := constantInputSource(credits)
|
||||||
|
|
||||||
// We also need a change source which needs to be able to insert
|
// We also need a change source which needs to be able to insert
|
||||||
// a new change addresse into the database.
|
// a new change addresse into the database.
|
||||||
|
@ -319,3 +326,30 @@ func (w *Wallet) FinalizePsbt(packet *psbt.Packet) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// constantInputSource creates an input source function that always returns the
|
||||||
|
// static set of user-selected UTXOs.
|
||||||
|
func constantInputSource(eligible []wtxmgr.Credit) txauthor.InputSource {
|
||||||
|
// Current inputs and their total value. These won't change over
|
||||||
|
// different invocations as we want our inputs to remain static since
|
||||||
|
// they're selected by the user.
|
||||||
|
currentTotal := btcutil.Amount(0)
|
||||||
|
currentInputs := make([]*wire.TxIn, 0, len(eligible))
|
||||||
|
currentScripts := make([][]byte, 0, len(eligible))
|
||||||
|
currentInputValues := make([]btcutil.Amount, 0, len(eligible))
|
||||||
|
|
||||||
|
for _, credit := range eligible {
|
||||||
|
nextInput := wire.NewTxIn(&credit.OutPoint, nil, nil)
|
||||||
|
currentTotal += credit.Amount
|
||||||
|
currentInputs = append(currentInputs, nextInput)
|
||||||
|
currentScripts = append(currentScripts, credit.PkScript)
|
||||||
|
currentInputValues = append(currentInputValues, credit.Amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn,
|
||||||
|
[]btcutil.Amount, [][]byte, error) {
|
||||||
|
|
||||||
|
return currentTotal, currentInputs, currentInputValues,
|
||||||
|
currentScripts, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -68,6 +68,8 @@ func TestFundPsbt(t *testing.T) {
|
||||||
feeRateSatPerKB btcutil.Amount
|
feeRateSatPerKB btcutil.Amount
|
||||||
expectedErr string
|
expectedErr string
|
||||||
validatePackage bool
|
validatePackage bool
|
||||||
|
expectedFee int64
|
||||||
|
expectedChange int64
|
||||||
numExpectedInputs int
|
numExpectedInputs int
|
||||||
}{{
|
}{{
|
||||||
name: "no outputs provided",
|
name: "no outputs provided",
|
||||||
|
@ -106,6 +108,8 @@ func TestFundPsbt(t *testing.T) {
|
||||||
feeRateSatPerKB: 2000, // 2 sat/byte
|
feeRateSatPerKB: 2000, // 2 sat/byte
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
validatePackage: true,
|
validatePackage: true,
|
||||||
|
expectedChange: 1000000 - 150000 - 368,
|
||||||
|
expectedFee: 368,
|
||||||
numExpectedInputs: 1,
|
numExpectedInputs: 1,
|
||||||
}, {
|
}, {
|
||||||
name: "two outputs, two inputs",
|
name: "two outputs, two inputs",
|
||||||
|
@ -136,6 +140,8 @@ func TestFundPsbt(t *testing.T) {
|
||||||
feeRateSatPerKB: 2000, // 2 sat/byte
|
feeRateSatPerKB: 2000, // 2 sat/byte
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
validatePackage: true,
|
validatePackage: true,
|
||||||
|
expectedFee: 506,
|
||||||
|
expectedChange: 2000000 - 150000 - 506,
|
||||||
numExpectedInputs: 2,
|
numExpectedInputs: 2,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@ -296,15 +302,17 @@ func TestFundPsbt(t *testing.T) {
|
||||||
txOuts[p2wshIndex].PkScript)
|
txOuts[p2wshIndex].PkScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, check the change output size and that it
|
// Finally, check the change output size and fee.
|
||||||
// belongs to the wallet.
|
fee := totalIn - totalOut
|
||||||
expectedFee := int64(368)
|
if fee != tc.expectedFee {
|
||||||
expectedChange := 1000000 - 150000 - expectedFee
|
t.Fatalf("unexpected fee, got %d wanted %d",
|
||||||
if txOuts[changeIndex].Value != expectedChange {
|
fee, tc.expectedFee)
|
||||||
|
}
|
||||||
|
if txOuts[changeIndex].Value != tc.expectedChange {
|
||||||
t.Fatalf("unexpected change output size, got "+
|
t.Fatalf("unexpected change output size, got "+
|
||||||
"%d wanted %d",
|
"%d wanted %d",
|
||||||
txOuts[changeIndex].Value,
|
txOuts[changeIndex].Value,
|
||||||
expectedChange)
|
tc.expectedChange)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue