github.com/decred/dcrlnd@v0.7.6/lnwallet/commit_sort.go (about) 1 package lnwallet 2 3 import ( 4 "bytes" 5 "sort" 6 7 "github.com/decred/dcrd/chaincfg/chainhash" 8 "github.com/decred/dcrd/wire" 9 ) 10 11 // InPlaceCommitSort performs an in-place sort of a commitment transaction, 12 // given an unsorted transaction and a list of CLTV values for the HTLCs. 13 // 14 // The sort applied is a modified BIP69 sort, that uses the CLTV values of HTLCs 15 // as a tie breaker in case two HTLC outputs have an identical amount and 16 // pkscript. The pkscripts can be the same if they share the same payment hash, 17 // but since the CLTV is enforced via the nLockTime of the second-layer 18 // transactions, the script does not directly commit to them. Instead, the CLTVs 19 // must be supplied separately to act as a tie-breaker, otherwise we may produce 20 // invalid HTLC signatures if the receiver produces an alternative ordering 21 // during verification. 22 // 23 // NOTE: Commitment outputs should have a 0 CLTV corresponding to their index on 24 // the unsorted commitment transaction. 25 func InPlaceCommitSort(tx *wire.MsgTx, cltvs []uint32) { 26 if len(tx.TxOut) != len(cltvs) { 27 panic("output and cltv list size mismatch") 28 } 29 30 sort.Sort(sortableInputSlice(tx.TxIn)) 31 sort.Sort(sortableCommitOutputSlice{tx.TxOut, cltvs}) 32 } 33 34 // sortableInputSlice is a slice of transaction inputs that supports sorting via 35 // BIP69. 36 type sortableInputSlice []*wire.TxIn 37 38 // Len returns the length of the sortableInputSlice. 39 // 40 // NOTE: Part of the sort.Interface interface. 41 func (s sortableInputSlice) Len() int { return len(s) } 42 43 // Swap exchanges the position of inputs i and j. 44 // 45 // NOTE: Part of the sort.Interface interface. 46 func (s sortableInputSlice) Swap(i, j int) { 47 s[i], s[j] = s[j], s[i] 48 } 49 50 // Less is the BIP69 input comparison function. The sort is first applied on 51 // input hash (reversed / rpc-style), then index. This logic is copied from 52 // dcrutil/txsort. 53 // 54 // NOTE: Part of the sort.Interface interface. 55 func (s sortableInputSlice) Less(i, j int) bool { 56 // Input hashes are the same, so compare the index. 57 ihash := s[i].PreviousOutPoint.Hash 58 jhash := s[j].PreviousOutPoint.Hash 59 if ihash == jhash { 60 return s[i].PreviousOutPoint.Index < s[j].PreviousOutPoint.Index 61 } 62 63 // At this point, the hashes are not equal, so reverse them to 64 // big-endian and return the result of the comparison. 65 const hashSize = chainhash.HashSize 66 for b := 0; b < hashSize/2; b++ { 67 ihash[b], ihash[hashSize-1-b] = ihash[hashSize-1-b], ihash[b] 68 jhash[b], jhash[hashSize-1-b] = jhash[hashSize-1-b], jhash[b] 69 } 70 return bytes.Compare(ihash[:], jhash[:]) == -1 71 } 72 73 // sortableCommitOutputSlice is a slice of transaction outputs on a commitment 74 // transaction and the corresponding CLTV values of any HTLCs. Commitment 75 // outputs should have a CLTV of 0 and the same index in cltvs. 76 type sortableCommitOutputSlice struct { 77 txouts []*wire.TxOut 78 cltvs []uint32 79 } 80 81 // Len returns the length of the sortableCommitOutputSlice. 82 // 83 // NOTE: Part of the sort.Interface interface. 84 func (s sortableCommitOutputSlice) Len() int { 85 return len(s.txouts) 86 } 87 88 // Swap exchanges the position of outputs i and j, as well as their 89 // corresponding CLTV values. 90 // 91 // NOTE: Part of the sort.Interface interface. 92 func (s sortableCommitOutputSlice) Swap(i, j int) { 93 s.txouts[i], s.txouts[j] = s.txouts[j], s.txouts[i] 94 s.cltvs[i], s.cltvs[j] = s.cltvs[j], s.cltvs[i] 95 } 96 97 // Less is a modified BIP69 output comparison, that sorts based on value, then 98 // pkscript, then CLTV value. 99 // 100 // NOTE: Part of the sort.Interface interface. 101 func (s sortableCommitOutputSlice) Less(i, j int) bool { 102 outi, outj := s.txouts[i], s.txouts[j] 103 104 if outi.Value != outj.Value { 105 return outi.Value < outj.Value 106 } 107 108 pkScriptCmp := bytes.Compare(outi.PkScript, outj.PkScript) 109 if pkScriptCmp != 0 { 110 return pkScriptCmp < 0 111 } 112 113 return s.cltvs[i] < s.cltvs[j] 114 }