Browse Source

chose readability over performance on Validation method

pull/8094/head
William Banfield 3 years ago
parent
commit
5f23a43930
No known key found for this signature in database GPG Key ID: EFAD3442BF29E3AC
1 changed files with 29 additions and 60 deletions
  1. +29
    -60
      types/tx.go

+ 29
- 60
types/tx.go View File

@ -182,13 +182,11 @@ func (t TxRecordSet) Validate(maxSizeBytes int64, otxs Txs) error {
// and once by sorting the set of the added, removed, and unmodified transactions indexes, // and once by sorting the set of the added, removed, and unmodified transactions indexes,
// which, when combined, comprise the complete list of modified transactions. // which, when combined, comprise the complete list of modified transactions.
// //
// The original list is iterated once and the modified list is iterated multiple times,
// one time alongside each of the 3 indexes for a total of 4 iterations of the modified list.
// Asymptotically, this yields a total runtime of O(N*log(N) + N + 2*M*log(M) + 4*M),
// in the input size of the original list and the input size of the new list respectively.
// A 2 * M performance gain is possible by iterating all of the indexes simultaneously
// alongside the full list, but the multiple iterations were preferred to be more
// readable and maintainable.
// Each of the added, removed, and unmodified indices is then iterated and once
// and each value index is checked against the sorted original list for containment.
// Asymptotically, this yields a total runtime of O(N*log(N) + 2*M*log(M) + M*log(N)).
// in the input size of the original list, N, and the input size of the new list, M, respectively.
// Performance gains are likely possible, but this was prefered for readability and maintainability.
// Sort a copy of the complete transaction slice so we can check for // Sort a copy of the complete transaction slice so we can check for
// duplication. The copy is so we do not change the original ordering. // duplication. The copy is so we do not change the original ordering.
@ -218,14 +216,14 @@ func (t TxRecordSet) Validate(maxSizeBytes int64, otxs Txs) error {
// the caller's data is not altered. // the caller's data is not altered.
otxsCopy := sortedCopy(otxs) otxsCopy := sortedCopy(otxs)
if ix, ok := containsAllTxs(otxsCopy, unmodifiedCopy); !ok {
if ix, ok := containsAll(otxsCopy, unmodifiedCopy); !ok {
return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", unmodifiedCopy[ix].Hash()) return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", unmodifiedCopy[ix].Hash())
} }
if ix, ok := containsAllTxs(otxsCopy, removedCopy); !ok {
if ix, ok := containsAll(otxsCopy, removedCopy); !ok {
return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", removedCopy[ix].Hash()) return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", removedCopy[ix].Hash())
} }
if ix, ok := containsAnyTxs(otxsCopy, addedCopy); ok {
if ix, ok := containsAny(otxsCopy, addedCopy); ok {
return fmt.Errorf("existing transaction incorrectly marked as added, transaction hash: %x", addedCopy[ix].Hash()) return fmt.Errorf("existing transaction incorrectly marked as added, transaction hash: %x", addedCopy[ix].Hash())
} }
return nil return nil
@ -238,69 +236,40 @@ func sortedCopy(txs Txs) Txs {
return cp return cp
} }
// containsAnyTxs checks that list a contains one of the transactions in list
// containsAny checks that list a contains one of the transactions in list
// b. If a match is found, the index in b of the matching transaction is returned. // b. If a match is found, the index in b of the matching transaction is returned.
// Both lists must be sorted. // Both lists must be sorted.
func containsAnyTxs(a, b []Tx) (int, bool) {
aix, bix := 0, 0
nextA:
for ; aix < len(a); aix++ {
for bix < len(b) {
switch bytes.Compare(b[bix], a[aix]) {
case 0:
return bix, true
case -1:
bix++
// we've reached the end of b, and the last value in b was
// smaller than the value under the iterator of a.
// a's values never get smaller, so we know there are no more matches
// in the list. We can terminate the iteration here.
if bix == len(b) {
return -1, false
}
case 1:
continue nextA
}
func containsAny(a, b []Tx) (int, bool) {
for i, cur := range b {
if _, ok := contains(a, cur); ok {
return i, true
} }
} }
return -1, false return -1, false
} }
// containsAllTxs checks that super contains all of the transactions in the sub
// containsAll checks that super contains all of the transactions in the sub
// list. If not all values in sub are present in super, the index in sub of the // list. If not all values in sub are present in super, the index in sub of the
// first Tx absent from super is returned. // first Tx absent from super is returned.
func containsAllTxs(super, sub []Tx) (int, bool) {
// The following iteration assumes sorted lists.
// The checks ensure that all the values in the sorted sub list are present in the sorted super list.
//
// We compare the value under the sub iterator to the value
// under the super iterator. If they match, we advance the
// sub iterator one position. If they don't match, then the value under
// the sub iterator should be greater.
// If it is not, then there is a value in the the sub list that is not present in the
// super list.
subIx := 0
for _, cur := range super {
if subIx == len(sub) {
return -1, true
}
switch bytes.Compare(sub[subIx], cur) {
case 0:
subIx++
case -1:
return subIx, false
func containsAll(super, sub Txs) (int, bool) {
for i, cur := range sub {
if _, ok := contains(super, cur); !ok {
return i, false
} }
} }
return -1, true
}
// Check that the loop visited all values of the transactions from sub.
// If it did not, then there are values present in these indexes that were not
// present in the super list of transactions.
if subIx != len(sub) {
return subIx, false
// contains checks that the sorted list, set contains elem. If set does contain elem, then the
// index in set of elem is returned.
func contains(set []Tx, elem Tx) (int, bool) {
n := sort.Search(len(set), func(i int) bool {
return bytes.Compare(elem, set[i]) <= 0
})
if n == len(set) || !bytes.Equal(elem, set[n]) {
return -1, false
} }
return -1, true
return n, true
} }
// TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree. // TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree.


Loading…
Cancel
Save