github.com/xxRanger/go-ethereum@v1.8.23/swarm/storage/common_test.go (about) 1 // Copyright 2016 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 "bytes" 21 "context" 22 "flag" 23 "fmt" 24 "io" 25 "io/ioutil" 26 "os" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/ethereum/go-ethereum/log" 32 ch "github.com/ethereum/go-ethereum/swarm/chunk" 33 "github.com/mattn/go-colorable" 34 ) 35 36 var ( 37 loglevel = flag.Int("loglevel", 3, "verbosity of logs") 38 getTimeout = 30 * time.Second 39 ) 40 41 func init() { 42 flag.Parse() 43 log.PrintOrigins(true) 44 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) 45 } 46 47 type brokenLimitedReader struct { 48 lr io.Reader 49 errAt int 50 off int 51 size int 52 } 53 54 func brokenLimitReader(data io.Reader, size int, errAt int) *brokenLimitedReader { 55 return &brokenLimitedReader{ 56 lr: data, 57 errAt: errAt, 58 size: size, 59 } 60 } 61 62 func newLDBStore(t *testing.T) (*LDBStore, func()) { 63 dir, err := ioutil.TempDir("", "bzz-storage-test") 64 if err != nil { 65 t.Fatal(err) 66 } 67 log.Trace("memstore.tempdir", "dir", dir) 68 69 ldbparams := NewLDBStoreParams(NewDefaultStoreParams(), dir) 70 db, err := NewLDBStore(ldbparams) 71 if err != nil { 72 t.Fatal(err) 73 } 74 75 cleanup := func() { 76 db.Close() 77 err := os.RemoveAll(dir) 78 if err != nil { 79 t.Fatal(err) 80 } 81 } 82 83 return db, cleanup 84 } 85 86 func mputRandomChunks(store ChunkStore, n int) ([]Chunk, error) { 87 return mput(store, n, GenerateRandomChunk) 88 } 89 90 func mput(store ChunkStore, n int, f func(i int64) Chunk) (hs []Chunk, err error) { 91 // put to localstore and wait for stored channel 92 // does not check delivery error state 93 errc := make(chan error) 94 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 95 defer cancel() 96 for i := int64(0); i < int64(n); i++ { 97 chunk := f(ch.DefaultSize) 98 go func() { 99 select { 100 case errc <- store.Put(ctx, chunk): 101 case <-ctx.Done(): 102 } 103 }() 104 hs = append(hs, chunk) 105 } 106 107 // wait for all chunks to be stored 108 for i := 0; i < n; i++ { 109 err := <-errc 110 if err != nil { 111 return nil, err 112 } 113 } 114 return hs, nil 115 } 116 117 func mget(store ChunkStore, hs []Address, f func(h Address, chunk Chunk) error) error { 118 wg := sync.WaitGroup{} 119 wg.Add(len(hs)) 120 errc := make(chan error) 121 122 for _, k := range hs { 123 go func(h Address) { 124 defer wg.Done() 125 // TODO: write timeout with context 126 chunk, err := store.Get(context.TODO(), h) 127 if err != nil { 128 errc <- err 129 return 130 } 131 if f != nil { 132 err = f(h, chunk) 133 if err != nil { 134 errc <- err 135 return 136 } 137 } 138 }(k) 139 } 140 go func() { 141 wg.Wait() 142 close(errc) 143 }() 144 var err error 145 timeout := 10 * time.Second 146 select { 147 case err = <-errc: 148 case <-time.NewTimer(timeout).C: 149 err = fmt.Errorf("timed out after %v", timeout) 150 } 151 return err 152 } 153 154 func (r *brokenLimitedReader) Read(buf []byte) (int, error) { 155 if r.off+len(buf) > r.errAt { 156 return 0, fmt.Errorf("Broken reader") 157 } 158 r.off += len(buf) 159 return r.lr.Read(buf) 160 } 161 162 func testStoreRandom(m ChunkStore, n int, t *testing.T) { 163 chunks, err := mputRandomChunks(m, n) 164 if err != nil { 165 t.Fatalf("expected no error, got %v", err) 166 } 167 err = mget(m, chunkAddresses(chunks), nil) 168 if err != nil { 169 t.Fatalf("testStore failed: %v", err) 170 } 171 } 172 173 func testStoreCorrect(m ChunkStore, n int, t *testing.T) { 174 chunks, err := mputRandomChunks(m, n) 175 if err != nil { 176 t.Fatalf("expected no error, got %v", err) 177 } 178 f := func(h Address, chunk Chunk) error { 179 if !bytes.Equal(h, chunk.Address()) { 180 return fmt.Errorf("key does not match retrieved chunk Address") 181 } 182 hasher := MakeHashFunc(DefaultHash)() 183 data := chunk.Data() 184 hasher.ResetWithLength(data[:8]) 185 hasher.Write(data[8:]) 186 exp := hasher.Sum(nil) 187 if !bytes.Equal(h, exp) { 188 return fmt.Errorf("key is not hash of chunk data") 189 } 190 return nil 191 } 192 err = mget(m, chunkAddresses(chunks), f) 193 if err != nil { 194 t.Fatalf("testStore failed: %v", err) 195 } 196 } 197 198 func benchmarkStorePut(store ChunkStore, n int, b *testing.B) { 199 chunks := make([]Chunk, n) 200 i := 0 201 f := func(dataSize int64) Chunk { 202 chunk := GenerateRandomChunk(dataSize) 203 chunks[i] = chunk 204 i++ 205 return chunk 206 } 207 208 mput(store, n, f) 209 210 f = func(dataSize int64) Chunk { 211 chunk := chunks[i] 212 i++ 213 return chunk 214 } 215 216 b.ReportAllocs() 217 b.ResetTimer() 218 219 for j := 0; j < b.N; j++ { 220 i = 0 221 mput(store, n, f) 222 } 223 } 224 225 func benchmarkStoreGet(store ChunkStore, n int, b *testing.B) { 226 chunks, err := mputRandomChunks(store, n) 227 if err != nil { 228 b.Fatalf("expected no error, got %v", err) 229 } 230 b.ReportAllocs() 231 b.ResetTimer() 232 addrs := chunkAddresses(chunks) 233 for i := 0; i < b.N; i++ { 234 err := mget(store, addrs, nil) 235 if err != nil { 236 b.Fatalf("mget failed: %v", err) 237 } 238 } 239 } 240 241 // MapChunkStore is a very simple ChunkStore implementation to store chunks in a map in memory. 242 type MapChunkStore struct { 243 chunks map[string]Chunk 244 mu sync.RWMutex 245 } 246 247 func NewMapChunkStore() *MapChunkStore { 248 return &MapChunkStore{ 249 chunks: make(map[string]Chunk), 250 } 251 } 252 253 func (m *MapChunkStore) Put(_ context.Context, ch Chunk) error { 254 m.mu.Lock() 255 defer m.mu.Unlock() 256 m.chunks[ch.Address().Hex()] = ch 257 return nil 258 } 259 260 func (m *MapChunkStore) Get(_ context.Context, ref Address) (Chunk, error) { 261 m.mu.RLock() 262 defer m.mu.RUnlock() 263 chunk := m.chunks[ref.Hex()] 264 if chunk == nil { 265 return nil, ErrChunkNotFound 266 } 267 return chunk, nil 268 } 269 270 // Need to implement Has from SyncChunkStore 271 func (m *MapChunkStore) Has(ctx context.Context, ref Address) bool { 272 m.mu.RLock() 273 defer m.mu.RUnlock() 274 275 _, has := m.chunks[ref.Hex()] 276 return has 277 } 278 279 func (m *MapChunkStore) Close() { 280 } 281 282 func chunkAddresses(chunks []Chunk) []Address { 283 addrs := make([]Address, len(chunks)) 284 for i, ch := range chunks { 285 addrs[i] = ch.Address() 286 } 287 return addrs 288 }