github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/btcutil/txsort/txsort.go (about) 1 // Copyright (c) 2015-2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 // Provides functions for sorting tx inputs and outputs according to BIP 69 6 // (https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki) 7 8 package txsort 9 10 import ( 11 "bytes" 12 "sort" 13 14 "github.com/mit-dci/lit/btcutil/chaincfg/chainhash" 15 "github.com/mit-dci/lit/wire" 16 ) 17 18 // InPlaceSort modifies the passed transaction inputs and outputs to be sorted 19 // based on BIP 69. 20 // 21 // WARNING: This function must NOT be called with published transactions since 22 // it will mutate the transaction if it's not already sorted. This can cause 23 // issues if you mutate a tx in a block, for example, which would invalidate the 24 // block. It could also cause cached hashes, such as in a btcutil.Tx to become 25 // invalidated. 26 // 27 // The function should only be used if the caller is creating the transaction or 28 // is otherwise 100% positive mutating will not cause adverse affects due to 29 // other dependencies. 30 func InPlaceSort(tx *wire.MsgTx) { 31 sort.Sort(sortableInputSlice(tx.TxIn)) 32 sort.Sort(sortableOutputSlice(tx.TxOut)) 33 } 34 35 // Sort returns a new transaction with the inputs and outputs sorted based on 36 // BIP 69. The passed transaction is not modified and the new transaction 37 // might have a different hash if any sorting was done. 38 func Sort(tx *wire.MsgTx) *wire.MsgTx { 39 txCopy := tx.Copy() 40 sort.Sort(sortableInputSlice(txCopy.TxIn)) 41 sort.Sort(sortableOutputSlice(txCopy.TxOut)) 42 return txCopy 43 } 44 45 // IsSorted checks whether tx has inputs and outputs sorted according to BIP 46 // 69. 47 func IsSorted(tx *wire.MsgTx) bool { 48 if !sort.IsSorted(sortableInputSlice(tx.TxIn)) { 49 return false 50 } 51 if !sort.IsSorted(sortableOutputSlice(tx.TxOut)) { 52 return false 53 } 54 return true 55 } 56 57 type sortableInputSlice []*wire.TxIn 58 type sortableOutputSlice []*wire.TxOut 59 60 // For SortableInputSlice and SortableOutputSlice, three functions are needed 61 // to make it sortable with sort.Sort() -- Len, Less, and Swap 62 // Len and Swap are trivial. Less is BIP 69 specific. 63 func (s sortableInputSlice) Len() int { return len(s) } 64 func (s sortableOutputSlice) Len() int { return len(s) } 65 func (s sortableOutputSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 66 func (s sortableInputSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 67 68 // Input comparison function. 69 // First sort based on input hash (reversed / rpc-style), then index. 70 func (s sortableInputSlice) Less(i, j int) bool { 71 // Input hashes are the same, so compare the index. 72 ihash := s[i].PreviousOutPoint.Hash 73 jhash := s[j].PreviousOutPoint.Hash 74 if ihash == jhash { 75 return s[i].PreviousOutPoint.Index < s[j].PreviousOutPoint.Index 76 } 77 78 // At this point, the hashes are not equal, so reverse them to 79 // big-endian and return the result of the comparison. 80 const hashSize = chainhash.HashSize 81 for b := 0; b < hashSize/2; b++ { 82 ihash[b], ihash[hashSize-1-b] = ihash[hashSize-1-b], ihash[b] 83 jhash[b], jhash[hashSize-1-b] = jhash[hashSize-1-b], jhash[b] 84 } 85 return bytes.Compare(ihash[:], jhash[:]) == -1 86 } 87 88 // Output comparison function. 89 // First sort based on amount (smallest first), then PkScript. 90 func (s sortableOutputSlice) Less(i, j int) bool { 91 if s[i].Value == s[j].Value { 92 return bytes.Compare(s[i].PkScript, s[j].PkScript) < 0 93 } 94 return s[i].Value < s[j].Value 95 }