github.com/xxRanger/go-ethereum@v1.8.23/swarm/storage/localstore_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package storage 18 19 import ( 20 "context" 21 "io/ioutil" 22 "os" 23 "testing" 24 "time" 25 26 ch "github.com/ethereum/go-ethereum/swarm/chunk" 27 ) 28 29 var ( 30 hashfunc = MakeHashFunc(DefaultHash) 31 ) 32 33 // tests that the content address validator correctly checks the data 34 // tests that feed update chunks are passed through content address validator 35 // the test checking the resouce update validator internal correctness is found in storage/feeds/handler_test.go 36 func TestValidator(t *testing.T) { 37 // set up localstore 38 datadir, err := ioutil.TempDir("", "storage-testvalidator") 39 if err != nil { 40 t.Fatal(err) 41 } 42 defer os.RemoveAll(datadir) 43 44 params := NewDefaultLocalStoreParams() 45 params.Init(datadir) 46 store, err := NewLocalStore(params, nil) 47 if err != nil { 48 t.Fatal(err) 49 } 50 51 // check puts with no validators, both succeed 52 chunks := GenerateRandomChunks(259, 2) 53 goodChunk := chunks[0] 54 badChunk := chunks[1] 55 copy(badChunk.Data(), goodChunk.Data()) 56 57 errs := putChunks(store, goodChunk, badChunk) 58 if errs[0] != nil { 59 t.Fatalf("expected no error on good content address chunk in spite of no validation, but got: %s", err) 60 } 61 if errs[1] != nil { 62 t.Fatalf("expected no error on bad content address chunk in spite of no validation, but got: %s", err) 63 } 64 65 // add content address validator and check puts 66 // bad should fail, good should pass 67 store.Validators = append(store.Validators, NewContentAddressValidator(hashfunc)) 68 chunks = GenerateRandomChunks(ch.DefaultSize, 2) 69 goodChunk = chunks[0] 70 badChunk = chunks[1] 71 copy(badChunk.Data(), goodChunk.Data()) 72 73 errs = putChunks(store, goodChunk, badChunk) 74 if errs[0] != nil { 75 t.Fatalf("expected no error on good content address chunk with content address validator only, but got: %s", err) 76 } 77 if errs[1] == nil { 78 t.Fatal("expected error on bad content address chunk with content address validator only, but got nil") 79 } 80 81 // append a validator that always denies 82 // bad should fail, good should pass, 83 var negV boolTestValidator 84 store.Validators = append(store.Validators, negV) 85 86 chunks = GenerateRandomChunks(ch.DefaultSize, 2) 87 goodChunk = chunks[0] 88 badChunk = chunks[1] 89 copy(badChunk.Data(), goodChunk.Data()) 90 91 errs = putChunks(store, goodChunk, badChunk) 92 if errs[0] != nil { 93 t.Fatalf("expected no error on good content address chunk with content address validator only, but got: %s", err) 94 } 95 if errs[1] == nil { 96 t.Fatal("expected error on bad content address chunk with content address validator only, but got nil") 97 } 98 99 // append a validator that always approves 100 // all shall pass 101 var posV boolTestValidator = true 102 store.Validators = append(store.Validators, posV) 103 104 chunks = GenerateRandomChunks(ch.DefaultSize, 2) 105 goodChunk = chunks[0] 106 badChunk = chunks[1] 107 copy(badChunk.Data(), goodChunk.Data()) 108 109 errs = putChunks(store, goodChunk, badChunk) 110 if errs[0] != nil { 111 t.Fatalf("expected no error on good content address chunk with content address validator only, but got: %s", err) 112 } 113 if errs[1] != nil { 114 t.Fatalf("expected no error on bad content address chunk in spite of no validation, but got: %s", err) 115 } 116 117 } 118 119 type boolTestValidator bool 120 121 func (self boolTestValidator) Validate(chunk Chunk) bool { 122 return bool(self) 123 } 124 125 // putChunks adds chunks to localstore 126 // It waits for receive on the stored channel 127 // It logs but does not fail on delivery error 128 func putChunks(store *LocalStore, chunks ...Chunk) []error { 129 i := 0 130 f := func(n int64) Chunk { 131 chunk := chunks[i] 132 i++ 133 return chunk 134 } 135 _, errs := put(store, len(chunks), f) 136 return errs 137 } 138 139 func put(store *LocalStore, n int, f func(i int64) Chunk) (hs []Address, errs []error) { 140 for i := int64(0); i < int64(n); i++ { 141 chunk := f(ch.DefaultSize) 142 err := store.Put(context.TODO(), chunk) 143 errs = append(errs, err) 144 hs = append(hs, chunk.Address()) 145 } 146 return hs, errs 147 } 148 149 // TestGetFrequentlyAccessedChunkWontGetGarbageCollected tests that the most 150 // frequently accessed chunk is not garbage collected from LDBStore, i.e., 151 // from disk when we are at the capacity and garbage collector runs. For that 152 // we start putting random chunks into the DB while continuously accessing the 153 // chunk we care about then check if we can still retrieve it from disk. 154 func TestGetFrequentlyAccessedChunkWontGetGarbageCollected(t *testing.T) { 155 ldbCap := defaultGCRatio 156 store, cleanup := setupLocalStore(t, ldbCap) 157 defer cleanup() 158 159 var chunks []Chunk 160 for i := 0; i < ldbCap; i++ { 161 chunks = append(chunks, GenerateRandomChunk(ch.DefaultSize)) 162 } 163 164 mostAccessed := chunks[0].Address() 165 for _, chunk := range chunks { 166 if err := store.Put(context.Background(), chunk); err != nil { 167 t.Fatal(err) 168 } 169 170 if _, err := store.Get(context.Background(), mostAccessed); err != nil { 171 t.Fatal(err) 172 } 173 // Add time for MarkAccessed() to be able to finish in a separate Goroutine 174 time.Sleep(1 * time.Millisecond) 175 } 176 177 store.DbStore.collectGarbage() 178 if _, err := store.DbStore.Get(context.Background(), mostAccessed); err != nil { 179 t.Logf("most frequntly accessed chunk not found on disk (key: %v)", mostAccessed) 180 t.Fatal(err) 181 } 182 183 } 184 185 func setupLocalStore(t *testing.T, ldbCap int) (ls *LocalStore, cleanup func()) { 186 t.Helper() 187 188 var err error 189 datadir, err := ioutil.TempDir("", "storage") 190 if err != nil { 191 t.Fatal(err) 192 } 193 194 params := &LocalStoreParams{ 195 StoreParams: NewStoreParams(uint64(ldbCap), uint(ldbCap), nil, nil), 196 } 197 params.Init(datadir) 198 199 store, err := NewLocalStore(params, nil) 200 if err != nil { 201 _ = os.RemoveAll(datadir) 202 t.Fatal(err) 203 } 204 205 cleanup = func() { 206 store.Close() 207 _ = os.RemoveAll(datadir) 208 } 209 210 return store, cleanup 211 } 212 213 func TestHas(t *testing.T) { 214 ldbCap := defaultGCRatio 215 store, cleanup := setupLocalStore(t, ldbCap) 216 defer cleanup() 217 218 nonStoredAddr := GenerateRandomChunk(128).Address() 219 220 has := store.Has(context.Background(), nonStoredAddr) 221 if has { 222 t.Fatal("Expected Has() to return false, but returned true!") 223 } 224 225 storeChunks := GenerateRandomChunks(128, 3) 226 for _, ch := range storeChunks { 227 err := store.Put(context.Background(), ch) 228 if err != nil { 229 t.Fatalf("Expected store to store chunk, but it failed: %v", err) 230 } 231 232 has := store.Has(context.Background(), ch.Address()) 233 if !has { 234 t.Fatal("Expected Has() to return true, but returned false!") 235 } 236 } 237 238 //let's be paranoic and test again that the non-existent chunk returns false 239 has = store.Has(context.Background(), nonStoredAddr) 240 if has { 241 t.Fatal("Expected Has() to return false, but returned true!") 242 } 243 244 }