Skip to content

Commit 6e60ac7

Browse files
committed
backend: update coin control outputs sorting
Currently, the list of all UTXOs in the coin control selection is ordered by amount. Users sometimes re-use addresses and are not aware that there might be other UTXOs they didn't want or want to use on the same address as their selected output. Grouping the UTXOs by address helps the user to detect if there are other UTXOs. This commit updates the sorting of outputs in the coin control dialog and is now grouped by address from the biggest to the lowest amount.
1 parent ba9a721 commit 6e60ac7

File tree

1 file changed

+46
-12
lines changed

1 file changed

+46
-12
lines changed

backend/coins/btc/account.go

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import (
4444
"github.com/btcsuite/btcd/btcutil"
4545
"github.com/btcsuite/btcd/btcutil/hdkeychain"
4646
"github.com/btcsuite/btcd/chaincfg"
47-
"github.com/btcsuite/btcd/chaincfg/chainhash"
4847
"github.com/btcsuite/btcd/wire"
4948
"github.com/sirupsen/logrus"
5049
)
@@ -772,19 +771,55 @@ func (account *Account) CanVerifyAddresses() (bool, bool, error) {
772771
return keystore.CanVerifyAddress(account.Coin())
773772
}
774773

775-
type byValue struct {
776-
outputs []*SpendableOutput
774+
// addressOutputsSum holds the address and sum of outputs for that address.
775+
type addressOutputsSum struct {
776+
address string
777+
sum int64
777778
}
778779

779-
func (p *byValue) Len() int { return len(p.outputs) }
780-
func (p *byValue) Less(i, j int) bool {
781-
if p.outputs[i].TxOut.Value == p.outputs[j].TxOut.Value {
782-
// Secondary sort to make coin selection deterministic.
783-
return chainhash.HashH(p.outputs[i].TxOut.PkScript).String() < chainhash.HashH(p.outputs[j].TxOut.PkScript).String()
780+
// sortByAddresses sorts the outputs by grouping them based on their addresses and then sorting each group
781+
// from outputs with the biggest amounts to those with the lowest amounts.
782+
func sortByAddresses(result []*SpendableOutput) []*SpendableOutput {
783+
// Create a map to store outputs grouped by address
784+
grouped := make(map[string][]*SpendableOutput)
785+
786+
// Group outputs by address
787+
for _, output := range result {
788+
grouped[output.Address.String()] = append(grouped[output.Address.String()], output)
784789
}
785-
return p.outputs[i].TxOut.Value < p.outputs[j].TxOut.Value
790+
791+
// Create a slice to store the sums of outputs and addresses
792+
sums := make([]addressOutputsSum, 0, len(grouped))
793+
794+
// Calculate sums of values for each group and store in the sums slice
795+
for address, outputs := range grouped {
796+
var sum int64
797+
for _, output := range outputs {
798+
sum += output.TxOut.Value
799+
}
800+
sums = append(sums, addressOutputsSum{
801+
address: address,
802+
sum: sum,
803+
})
804+
}
805+
806+
// Sort the sums slice by the sum of values in descending order
807+
sort.Slice(sums, func(i, j int) bool {
808+
return sums[i].sum > sums[j].sum
809+
})
810+
811+
// Create a new result grouped by addresses, sort them by value
812+
newResult := make([]*SpendableOutput, 0, len(result))
813+
for _, s := range sums {
814+
outputs := grouped[s.address]
815+
sort.Slice(outputs, func(i, j int) bool {
816+
return outputs[i].Value > outputs[j].Value
817+
})
818+
newResult = append(newResult, outputs...)
819+
}
820+
821+
return newResult
786822
}
787-
func (p *byValue) Swap(i, j int) { p.outputs[i], p.outputs[j] = p.outputs[j], p.outputs[i] }
788823

789824
// SpendableOutput is an unspent coin.
790825
type SpendableOutput struct {
@@ -811,8 +846,7 @@ func (account *Account) SpendableOutputs() []*SpendableOutput {
811846
Address: account.getAddress(blockchain.NewScriptHashHex(txOut.TxOut.PkScript)),
812847
})
813848
}
814-
sort.Sort(sort.Reverse(&byValue{result}))
815-
return result
849+
return sortByAddresses(result)
816850
}
817851

818852
// VerifyExtendedPublicKey verifies an account's public key. Returns false, nil if no secure output

0 commit comments

Comments
 (0)