@@ -2,7 +2,8 @@ package tapchannel
22
33import (
44 "bytes"
5- "sort"
5+ "cmp"
6+ "slices"
67)
78
89// InPlaceAllocationSort performs an in-place sort of output allocations.
@@ -14,51 +15,21 @@ import (
1415// transactions, the script does not directly commit to them. Instead, the CLTVs
1516// must be supplied separately to act as a tie-breaker, otherwise we may produce
1617// invalid HTLC signatures if the receiver produces an alternative ordering
17- // during verification.
18+ // during verification. Because multiple shards of the same MPP payment can be
19+ // identical in all other fields, we also use the HtlcIndex as a final
20+ // tie-breaker.
1821//
19- // NOTE: Commitment and commitment anchor outputs should have a 0 CLTV value.
22+ // NOTE: Commitment and commitment anchor outputs should have a 0 CLTV and
23+ // HtlcIndex value.
2024func InPlaceAllocationSort (allocations []* Allocation ) {
21- sort .Sort (sortableAllocationSlice {allocations })
22- }
23-
24- // sortableAllocationSlice is a slice of allocations and the corresponding CLTV
25- // values of any HTLCs. Commitment and commitment anchor outputs should have a
26- // CLTV of 0.
27- type sortableAllocationSlice struct {
28- allocations []* Allocation
29- }
30-
31- // Len returns the length of the sortableAllocationSlice.
32- //
33- // NOTE: Part of the sort.Interface interface.
34- func (s sortableAllocationSlice ) Len () int {
35- return len (s .allocations )
36- }
37-
38- // Swap exchanges the position of outputs i and j.
39- //
40- // NOTE: Part of the sort.Interface interface.
41- func (s sortableAllocationSlice ) Swap (i , j int ) {
42- s .allocations [i ], s .allocations [j ] = s .allocations [j ], s .allocations [i ]
43- }
44-
45- // Less is a modified BIP69 output comparison, that sorts based on value, then
46- // pkScript, then CLTV value.
47- //
48- // NOTE: Part of the sort.Interface interface.
49- func (s sortableAllocationSlice ) Less (i , j int ) bool {
50- allocI , allocJ := s .allocations [i ], s .allocations [j ]
51-
52- if allocI .BtcAmount != allocJ .BtcAmount {
53- return allocI .BtcAmount < allocJ .BtcAmount
54- }
55-
56- pkScriptCmp := bytes .Compare (
57- allocI .SortTaprootKeyBytes , allocJ .SortTaprootKeyBytes ,
58- )
59- if pkScriptCmp != 0 {
60- return pkScriptCmp < 0
61- }
62-
63- return allocI .CLTV < allocJ .CLTV
25+ slices .SortFunc (allocations , func (i , j * Allocation ) int {
26+ return cmp .Or (
27+ cmp .Compare (i .BtcAmount , j .BtcAmount ),
28+ bytes .Compare (
29+ i .SortTaprootKeyBytes , j .SortTaprootKeyBytes ,
30+ ),
31+ cmp .Compare (i .CLTV , j .CLTV ),
32+ cmp .Compare (i .HtlcIndex , j .HtlcIndex ),
33+ )
34+ })
6435}
0 commit comments