mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
139 lines
4.6 KiB
Go
139 lines
4.6 KiB
Go
// Copyright (c) 2015-2016 The btcsuite developers
|
|
// Copyright (c) 2017 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package txsort
|
|
|
|
import (
|
|
"bytes"
|
|
"sort"
|
|
|
|
"github.com/decred/dcrd/chaincfg/chainhash"
|
|
"github.com/decred/dcrd/wire"
|
|
)
|
|
|
|
// sortableInputSlice implements sort.Interface to allow a slice of transaction
|
|
// inputs to be sorted according to the description in the package
|
|
// documentation.
|
|
type sortableInputSlice []*wire.TxIn
|
|
|
|
// Len returns the number of transaction inputs in the slice. It is part of the
|
|
// sort.Interface implementation.
|
|
func (s sortableInputSlice) Len() int {
|
|
return len(s)
|
|
}
|
|
|
|
// Swap swaps the transaction inputs at the passed indices. It is part of the
|
|
// sort.Interface implementation.
|
|
func (s sortableInputSlice) Swap(i, j int) {
|
|
s[i], s[j] = s[j], s[i]
|
|
}
|
|
|
|
// Less returns whether the transaction input with index i should sort before
|
|
// the transaction input with index j. It is part of the sort.Interface
|
|
// implementation.
|
|
//
|
|
// First sort based on input tree, then input hash (reversed / big-endian), then
|
|
// index.
|
|
func (s sortableInputSlice) Less(i, j int) bool {
|
|
// Sort by tree first.
|
|
if s[i].PreviousOutPoint.Tree != s[j].PreviousOutPoint.Tree {
|
|
return s[i].PreviousOutPoint.Tree < s[j].PreviousOutPoint.Tree
|
|
}
|
|
|
|
// Input hashes are the same, so compare the index.
|
|
ihash := s[i].PreviousOutPoint.Hash
|
|
jhash := s[j].PreviousOutPoint.Hash
|
|
if ihash == jhash {
|
|
return s[i].PreviousOutPoint.Index < s[j].PreviousOutPoint.Index
|
|
}
|
|
|
|
// At this point, the hashes are not equal, so reverse them to
|
|
// big endian and return the result of the comparison.
|
|
const hashSize = chainhash.HashSize
|
|
for b := 0; b < hashSize/2; b++ {
|
|
ihash[b], ihash[hashSize-1-b] = ihash[hashSize-1-b], ihash[b]
|
|
jhash[b], jhash[hashSize-1-b] = jhash[hashSize-1-b], jhash[b]
|
|
}
|
|
return bytes.Compare(ihash[:], jhash[:]) == -1
|
|
}
|
|
|
|
// sortableOutputSlice implements sort.Interface to allow a slice of transaction
|
|
// outputs to be sorted according to the description in the package
|
|
// documentation.
|
|
type sortableOutputSlice []*wire.TxOut
|
|
|
|
// Len returns the number of transaction outputs in the slice. It is part of
|
|
// the sort.Interface implementation.
|
|
func (s sortableOutputSlice) Len() int {
|
|
return len(s)
|
|
}
|
|
|
|
// Swap swaps the transaction outputs at the passed indices. It is part of the
|
|
// sort.Interface implementation.
|
|
func (s sortableOutputSlice) Swap(i, j int) {
|
|
s[i], s[j] = s[j], s[i]
|
|
}
|
|
|
|
// Less returns whether the transaction output with index i should sort before
|
|
// the transaction output with index j. It is part of the sort.Interface
|
|
// implementation.
|
|
//
|
|
// First sort based on amount (smallest first), then by script version, then by
|
|
// script.
|
|
func (s sortableOutputSlice) Less(i, j int) bool {
|
|
// Sort by amount first.
|
|
if s[i].Value != s[j].Value {
|
|
return s[i].Value < s[j].Value
|
|
}
|
|
|
|
// At this point, the amounts are the same, so sort by the script version.
|
|
if s[i].Version != s[j].Version {
|
|
return s[i].Version < s[j].Version
|
|
}
|
|
|
|
// At this point, the script versions are the same, so sort by the script.
|
|
return bytes.Compare(s[i].PkScript, s[j].PkScript) < 0
|
|
}
|
|
|
|
// InPlaceSort modifies the passed transaction inputs and outputs to be sorted
|
|
// according to the description in the package documentation.
|
|
//
|
|
// WARNING: This function must NOT be called with published transactions since
|
|
// it will mutate the transaction if it's not already sorted. This can cause
|
|
// issues if you mutate a tx in a block, for example, which would invalidate the
|
|
// block. It could also cause cached hashes, such as in a dcrutil.Tx to become
|
|
// invalidated.
|
|
//
|
|
// The function should only be used if the caller is creating the transaction or
|
|
// is otherwise 100% positive mutating will not cause adverse affects due to
|
|
// other dependencies.
|
|
func InPlaceSort(tx *wire.MsgTx) {
|
|
sort.Sort(sortableInputSlice(tx.TxIn))
|
|
sort.Sort(sortableOutputSlice(tx.TxOut))
|
|
}
|
|
|
|
// Sort returns a new transaction with the inputs and outputs sorted according
|
|
// to the description in the package documentation. The passed transaction is
|
|
// not modified and the new transaction might have a different hash if any
|
|
// sorting was done.
|
|
func Sort(tx *wire.MsgTx) *wire.MsgTx {
|
|
txCopy := tx.Copy()
|
|
sort.Sort(sortableInputSlice(txCopy.TxIn))
|
|
sort.Sort(sortableOutputSlice(txCopy.TxOut))
|
|
return txCopy
|
|
}
|
|
|
|
// IsSorted checks whether tx has inputs and outputs sorted according to the
|
|
// description in the package documentation.
|
|
func IsSorted(tx *wire.MsgTx) bool {
|
|
if !sort.IsSorted(sortableInputSlice(tx.TxIn)) {
|
|
return false
|
|
}
|
|
if !sort.IsSorted(sortableOutputSlice(tx.TxOut)) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|