github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/blockchain/indexers/addrindex_test.go (about) 1 // Copyright (c) 2016 The btcsuite developers 2 // Copyright (c) 2016 The Dash developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package indexers 7 8 import ( 9 "bytes" 10 "fmt" 11 "testing" 12 13 "github.com/dashpay/godash/wire" 14 ) 15 16 // addrIndexBucket provides a mock address index database bucket by implementing 17 // the internalBucket interface. 18 type addrIndexBucket struct { 19 levels map[[levelKeySize]byte][]byte 20 } 21 22 // Clone returns a deep copy of the mock adress index bucket. 23 func (b *addrIndexBucket) Clone() *addrIndexBucket { 24 levels := make(map[[levelKeySize]byte][]byte) 25 for k, v := range b.levels { 26 vCopy := make([]byte, len(v)) 27 copy(vCopy, v) 28 levels[k] = vCopy 29 } 30 return &addrIndexBucket{levels: levels} 31 } 32 33 // Get returns the value associated with the key from the mock address index 34 // bucket. 35 // 36 // This is part of the internalBucket interface. 37 func (b *addrIndexBucket) Get(key []byte) []byte { 38 var levelKey [levelKeySize]byte 39 copy(levelKey[:], key) 40 return b.levels[levelKey] 41 } 42 43 // Put stores the provided key/value pair to the mock address index bucket. 44 // 45 // This is part of the internalBucket interface. 46 func (b *addrIndexBucket) Put(key []byte, value []byte) error { 47 var levelKey [levelKeySize]byte 48 copy(levelKey[:], key) 49 b.levels[levelKey] = value 50 return nil 51 } 52 53 // Delete removes the provided key from the mock address index bucket. 54 // 55 // This is part of the internalBucket interface. 56 func (b *addrIndexBucket) Delete(key []byte) error { 57 var levelKey [levelKeySize]byte 58 copy(levelKey[:], key) 59 delete(b.levels, levelKey) 60 return nil 61 } 62 63 // printLevels returns a string with a visual representation of the provided 64 // address key taking into account the max size of each level. It is useful 65 // when creating and debugging test cases. 66 func (b *addrIndexBucket) printLevels(addrKey [addrKeySize]byte) string { 67 highestLevel := uint8(0) 68 for k := range b.levels { 69 if !bytes.Equal(k[:levelOffset], addrKey[:]) { 70 continue 71 } 72 level := uint8(k[levelOffset]) 73 if level > highestLevel { 74 highestLevel = level 75 } 76 } 77 78 var levelBuf bytes.Buffer 79 _, _ = levelBuf.WriteString("\n") 80 maxEntries := level0MaxEntries 81 for level := uint8(0); level <= highestLevel; level++ { 82 data := b.levels[keyForLevel(addrKey, level)] 83 numEntries := len(data) / txEntrySize 84 for i := 0; i < numEntries; i++ { 85 start := i * txEntrySize 86 num := byteOrder.Uint32(data[start:]) 87 _, _ = levelBuf.WriteString(fmt.Sprintf("%02d ", num)) 88 } 89 for i := numEntries; i < maxEntries; i++ { 90 _, _ = levelBuf.WriteString("_ ") 91 } 92 _, _ = levelBuf.WriteString("\n") 93 maxEntries *= 2 94 } 95 96 return levelBuf.String() 97 } 98 99 // sanityCheck ensures that all data stored in the bucket for the given address 100 // adheres to the level-based rules described by the address index 101 // documentation. 102 func (b *addrIndexBucket) sanityCheck(addrKey [addrKeySize]byte, expectedTotal int) error { 103 // Find the highest level for the key. 104 highestLevel := uint8(0) 105 for k := range b.levels { 106 if !bytes.Equal(k[:levelOffset], addrKey[:]) { 107 continue 108 } 109 level := uint8(k[levelOffset]) 110 if level > highestLevel { 111 highestLevel = level 112 } 113 } 114 115 // Ensure the expected total number of entries are present and that 116 // all levels adhere to the rules described in the address index 117 // documentation. 118 var totalEntries int 119 maxEntries := level0MaxEntries 120 for level := uint8(0); level <= highestLevel; level++ { 121 // Level 0 can'have more entries than the max allowed if the 122 // levels after it have data and it can't be empty. All other 123 // levels must either be half full or full. 124 data := b.levels[keyForLevel(addrKey, level)] 125 numEntries := len(data) / txEntrySize 126 totalEntries += numEntries 127 if level == 0 { 128 if (highestLevel != 0 && numEntries == 0) || 129 numEntries > maxEntries { 130 131 return fmt.Errorf("level %d has %d entries", 132 level, numEntries) 133 } 134 } else if numEntries != maxEntries && numEntries != maxEntries/2 { 135 return fmt.Errorf("level %d has %d entries", level, 136 numEntries) 137 } 138 maxEntries *= 2 139 } 140 if totalEntries != expectedTotal { 141 return fmt.Errorf("expected %d entries - got %d", expectedTotal, 142 totalEntries) 143 } 144 145 // Ensure all of the numbers are in order starting from the highest 146 // level moving to the lowest level. 147 expectedNum := uint32(0) 148 for level := highestLevel + 1; level > 0; level-- { 149 data := b.levels[keyForLevel(addrKey, level)] 150 numEntries := len(data) / txEntrySize 151 for i := 0; i < numEntries; i++ { 152 start := i * txEntrySize 153 num := byteOrder.Uint32(data[start:]) 154 if num != expectedNum { 155 return fmt.Errorf("level %d offset %d does "+ 156 "not contain the expected number of "+ 157 "%d - got %d", level, i, num, 158 expectedNum) 159 } 160 expectedNum++ 161 } 162 } 163 164 return nil 165 } 166 167 // TestAddrIndexLevels ensures that adding and deleting entries to the address 168 // index creates multiple levels as decribed by the address index documentation. 169 func TestAddrIndexLevels(t *testing.T) { 170 t.Parallel() 171 172 tests := []struct { 173 name string 174 key [addrKeySize]byte 175 numInsert int 176 printLevels bool // Set to help debug a specific test. 177 }{ 178 { 179 name: "level 0 not full", 180 numInsert: level0MaxEntries - 1, 181 }, 182 { 183 name: "level 1 half", 184 numInsert: level0MaxEntries + 1, 185 }, 186 { 187 name: "level 1 full", 188 numInsert: level0MaxEntries*2 + 1, 189 }, 190 { 191 name: "level 2 half, level 1 half", 192 numInsert: level0MaxEntries*3 + 1, 193 }, 194 { 195 name: "level 2 half, level 1 full", 196 numInsert: level0MaxEntries*4 + 1, 197 }, 198 { 199 name: "level 2 full, level 1 half", 200 numInsert: level0MaxEntries*5 + 1, 201 }, 202 { 203 name: "level 2 full, level 1 full", 204 numInsert: level0MaxEntries*6 + 1, 205 }, 206 { 207 name: "level 3 half, level 2 half, level 1 half", 208 numInsert: level0MaxEntries*7 + 1, 209 }, 210 { 211 name: "level 3 full, level 2 half, level 1 full", 212 numInsert: level0MaxEntries*12 + 1, 213 }, 214 } 215 216 nextTest: 217 for testNum, test := range tests { 218 // Insert entries in order. 219 populatedBucket := &addrIndexBucket{ 220 levels: make(map[[levelKeySize]byte][]byte), 221 } 222 for i := 0; i < test.numInsert; i++ { 223 txLoc := wire.TxLoc{TxStart: i * 2} 224 err := dbPutAddrIndexEntry(populatedBucket, test.key, 225 uint32(i), txLoc) 226 if err != nil { 227 t.Errorf("dbPutAddrIndexEntry #%d (%s) - "+ 228 "unexpected error: %v", testNum, 229 test.name, err) 230 continue nextTest 231 } 232 } 233 if test.printLevels { 234 t.Log(populatedBucket.printLevels(test.key)) 235 } 236 237 // Delete entries from the populated bucket until all entries 238 // have been deleted. The bucket is reset to the fully 239 // populated bucket on each iteration so every combination is 240 // tested. Notice the upper limit purposes exceeds the number 241 // of entries to ensure attempting to delete more entries than 242 // there are works correctly. 243 for numDelete := 0; numDelete <= test.numInsert+1; numDelete++ { 244 // Clone populated bucket to run each delete against. 245 bucket := populatedBucket.Clone() 246 247 // Remove the number of entries for this iteration. 248 err := dbRemoveAddrIndexEntries(bucket, test.key, 249 numDelete) 250 if err != nil { 251 if numDelete <= test.numInsert { 252 t.Errorf("dbRemoveAddrIndexEntries (%s) "+ 253 " delete %d - unexpected error: "+ 254 "%v", test.name, numDelete, err) 255 continue nextTest 256 } 257 } 258 if test.printLevels { 259 t.Log(bucket.printLevels(test.key)) 260 } 261 262 // Sanity check the levels to ensure the adhere to all 263 // rules. 264 numExpected := test.numInsert 265 if numDelete <= test.numInsert { 266 numExpected -= numDelete 267 } 268 err = bucket.sanityCheck(test.key, numExpected) 269 if err != nil { 270 t.Errorf("sanity check fail (%s) delete %d: %v", 271 test.name, numDelete, err) 272 continue nextTest 273 } 274 } 275 } 276 }