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  }