txsort: Convert tests, optimize, and cleanup code.

- Move hex for test txns into separate files in the testdata directory
- Convert tests to table-driven tests
- Make comments more consistent with the rest of the codebase
- Optimize the input sorting function to perform the hash equivalence
  check before reversing the bytes so it can be avoided in that case
This commit is contained in:
Dave Collins 2015-10-23 02:55:56 -05:00
parent 98fd0a0661
commit e0e9257790
7 changed files with 129 additions and 203 deletions

1
txsort/testdata/li01-1.hex vendored Normal file

File diff suppressed because one or more lines are too long

1
txsort/testdata/li01-2.hex vendored Normal file
View file

@ -0,0 +1 @@
010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00000000

1
txsort/testdata/li01-3.hex vendored Normal file
View file

@ -0,0 +1 @@
0100000001d992e5a888a86d4c7a6a69167a4728ee69497509740fc5f456a24528c340219a000000008b483045022100f0519bdc9282ff476da1323b8ef7ffe33f495c1a8d52cc522b437022d83f6a230220159b61d197fbae01b4a66622a23bc3f1def65d5fa24efd5c26fa872f3a246b8e014104839f9023296a1fabb133140128ca2709f6818c7d099491690bd8ac0fd55279def6a2ceb6ab7b5e4a71889b6e739f09509565eec789e86886f6f936fa42097adeffffffff02000fe208010000001976a914948c765a6914d43f2a7ac177da2c2f6b52de3d7c88ac00e32321000000001976a9140c34f4e29ab5a615d5ea28d4817f12b137d62ed588ac00000000

1
txsort/testdata/li01-4.hex vendored Normal file
View file

@ -0,0 +1 @@
01000000059daf0abe7a92618546a9dbcfd65869b6178c66ec21ccfda878c1175979cfd9ef000000004a493046022100c2f7f25be5de6ce88ac3c1a519514379e91f39b31ddff279a3db0b1a229b708b022100b29efbdbd9837cc6a6c7318aa4900ed7e4d65662c34d1622a2035a3a5534a99a01ffffffffd516330ebdf075948da56db13d22632a4fb941122df2884397dda45d451acefb0000000048473044022051243debe6d4f2b433bee0cee78c5c4073ead0e3bde54296dbed6176e128659c022044417bfe16f44eb7b6eb0cdf077b9ce972a332e15395c09ca5e4f602958d266101ffffffffe1f5aa33961227b3c344e57179417ce01b7ccd421117fe2336289b70489883f900000000484730440220593252bb992ce3c85baf28d6e3aa32065816271d2c822398fe7ee28a856bc943022066d429dd5025d3c86fd8fd8a58e183a844bd94aa312cefe00388f57c85b0ca3201ffffffffe207e83718129505e6a7484831442f668164ae659fddb82e9e5421a081fb90d50000000049483045022067cf27eb733e5bcae412a586b25a74417c237161a084167c2a0b439abfebdcb2022100efcc6baa6824b4c5205aa967e0b76d31abf89e738d4b6b014e788c9a8cccaf0c01ffffffffe23b8d9d80a9e9d977fab3c94dbe37befee63822443c3ec5ae5a713ede66c3940000000049483045022020f2eb35036666b1debe0d1d2e77a36d5d9c4e96c1dba23f5100f193dbf524790221008ce79bc1321fb4357c6daee818038d41544749127751726e46b2b320c8b565a201ffffffff0200ba1dd2050000001976a914366a27645806e817a6cd40bc869bdad92fe5509188ac40420f00000000001976a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac00000000

1
txsort/testdata/li01-5.hex vendored Normal file
View file

@ -0,0 +1 @@
01000000011f636d0003f673b3aeea4971daef16b8eed784cf6e8019a5ae7da4985fbb06e5000000008a47304402205103941e2b11e746dfa817888d422f6e7f4d16dbbfb8ffa61d15ffb924a84b8802202fe861b0f23f17139d15a3374bfc6c7196d371f3d1a324e31cc0aadbba87e53c0141049e7e1b251a7e26cae9ee7553b278ef58ef3c28b4b20134d51b747d9b18b0a19b94b66cef320e2549dec0ea3d725cb4c742f368928b1fb74b4603e24a1e262c80ffffffff0240420f00000000001976a914bcfa0e27218a7c97257b351b03a9eac95c25a23988ac40420f00000000001976a9140c6a68f20bafc678164d171ee4f077adfa9b091688ac00000000

View file

@ -2,6 +2,9 @@
// Use of this source code is governed by an ISC // Use of this source code is governed by an ISC
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Provides functions for sorting tx inputs and outputs according to BIP LI01
// (https://github.com/kristovatlas/rfc/blob/master/bips/bip-li01.mediawiki)
package txsort package txsort
import ( import (
@ -11,13 +14,9 @@ import (
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
) )
// TxSort // Sort returns a new transaction with the inputs and outputs sorted based on
// Provides functions for sorting tx inputs and outputs according to BIP LI01 // BIP LI01. The passed transaction is not modified and the new transaction
// (https://github.com/kristovatlas/rfc/blob/master/bips/bip-li01.mediawiki) // might have a different hash if any sorting was done.
// Sort sorts the inputs and outputs of a tx based on BIP LI01
// It does not modify the transaction given, but returns a new copy
// which has been sorted and may have a different txid.
func Sort(tx *wire.MsgTx) *wire.MsgTx { func Sort(tx *wire.MsgTx) *wire.MsgTx {
txCopy := tx.Copy() txCopy := tx.Copy()
sort.Sort(sortableInputSlice(txCopy.TxIn)) sort.Sort(sortableInputSlice(txCopy.TxIn))
@ -25,8 +24,8 @@ func Sort(tx *wire.MsgTx) *wire.MsgTx {
return txCopy return txCopy
} }
// IsSorted checks whether tx has inputs and outputs sorted according // IsSorted checks whether tx has inputs and outputs sorted according to BIP
// to BIP LI01. // LI01.
func IsSorted(tx *wire.MsgTx) bool { func IsSorted(tx *wire.MsgTx) bool {
if !sort.IsSorted(sortableInputSlice(tx.TxIn)) { if !sort.IsSorted(sortableInputSlice(tx.TxIn)) {
return false return false
@ -40,45 +39,39 @@ func IsSorted(tx *wire.MsgTx) bool {
type sortableInputSlice []*wire.TxIn type sortableInputSlice []*wire.TxIn
type sortableOutputSlice []*wire.TxOut type sortableOutputSlice []*wire.TxOut
// for SortableInputSlice and SortableOutputSlice, three functions are needed // For SortableInputSlice and SortableOutputSlice, three functions are needed
// to make it sortable with sort.Sort() -- Len, Less, and Swap // to make it sortable with sort.Sort() -- Len, Less, and Swap
// Len and Swap are trivial. Less is BIP LI01 specific. // Len and Swap are trivial. Less is BIP LI01 specific.
func (ins sortableInputSlice) Len() int { func (s sortableInputSlice) Len() int { return len(s) }
return len(ins) func (s sortableOutputSlice) Len() int { return len(s) }
} func (s sortableOutputSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (outs sortableOutputSlice) Len() int { func (s sortableInputSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
return len(outs)
}
func (ins sortableInputSlice) Swap(i, j int) {
ins[i], ins[j] = ins[j], ins[i]
}
func (outs sortableOutputSlice) Swap(i, j int) {
outs[i], outs[j] = outs[j], outs[i]
}
// Input comparison function. // Input comparison function.
// First sort based on input txid (reversed / rpc-style), then index // First sort based on input hash (reversed / rpc-style), then index.
func (ins sortableInputSlice) Less(i, j int) bool { func (s sortableInputSlice) Less(i, j int) bool {
ihash := ins[i].PreviousOutPoint.Hash // Input hashes are the same, so compare the index.
jhash := ins[j].PreviousOutPoint.Hash ihash := s[i].PreviousOutPoint.Hash
for b := 0; b < wire.HashSize/2; b++ { jhash := s[j].PreviousOutPoint.Hash
ihash[b], ihash[wire.HashSize-1-b] = ihash[wire.HashSize-1-b], ihash[b] if ihash == jhash {
jhash[b], jhash[wire.HashSize-1-b] = jhash[wire.HashSize-1-b], jhash[b] return s[i].PreviousOutPoint.Index < s[j].PreviousOutPoint.Index
} }
c := bytes.Compare(ihash[:], jhash[:])
if c == 0 { // At this point, the hashes are not equal, so reverse them to
// input txids are the same, compare index // big-endian and return the result of the comparison.
return ins[i].PreviousOutPoint.Index < ins[j].PreviousOutPoint.Index const hashSize = wire.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 c == -1. return bytes.Compare(ihash[:], jhash[:]) == -1
} }
// Output comparison function. // Output comparison function.
// First sort based on amount (smallest first), then PkScript // First sort based on amount (smallest first), then PkScript.
func (outs sortableOutputSlice) Less(i, j int) bool { func (s sortableOutputSlice) Less(i, j int) bool {
if outs[i].Value == outs[j].Value { if s[i].Value == s[j].Value {
return bytes.Compare(outs[i].PkScript, outs[j].PkScript) < 0 return bytes.Compare(s[i].PkScript, s[j].PkScript) < 0
} }
return outs[i].Value < outs[j].Value return s[i].Value < s[j].Value
} }

File diff suppressed because one or more lines are too long