github.com/codingfuture/orig-energi3@v0.8.4/swarm/storage/feed/handler_test.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2018 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 package feed 19 20 import ( 21 "bytes" 22 "context" 23 "flag" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "testing" 28 "time" 29 30 "github.com/ethereum/go-ethereum/crypto" 31 "github.com/ethereum/go-ethereum/log" 32 "github.com/ethereum/go-ethereum/swarm/chunk" 33 "github.com/ethereum/go-ethereum/swarm/storage" 34 "github.com/ethereum/go-ethereum/swarm/storage/feed/lookup" 35 ) 36 37 var ( 38 loglevel = flag.Int("loglevel", 3, "loglevel") 39 startTime = Timestamp{ 40 Time: uint64(4200), 41 } 42 cleanF func() 43 subtopicName = "føø.bar" 44 ) 45 46 func init() { 47 testing.Init() 48 flag.Parse() 49 log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))) 50 } 51 52 // simulated timeProvider 53 type fakeTimeProvider struct { 54 currentTime uint64 55 } 56 57 func (f *fakeTimeProvider) Tick() { 58 f.currentTime++ 59 } 60 61 func (f *fakeTimeProvider) Set(time uint64) { 62 f.currentTime = time 63 } 64 65 func (f *fakeTimeProvider) FastForward(offset uint64) { 66 f.currentTime += offset 67 } 68 69 func (f *fakeTimeProvider) Now() Timestamp { 70 return Timestamp{ 71 Time: f.currentTime, 72 } 73 } 74 75 // make updates and retrieve them based on periods and versions 76 func TestFeedsHandler(t *testing.T) { 77 78 // make fake timeProvider 79 clock := &fakeTimeProvider{ 80 currentTime: startTime.Time, // clock starts at t=4200 81 } 82 83 // signer containing private key 84 signer := newAliceSigner() 85 86 feedsHandler, datadir, teardownTest, err := setupTest(clock, signer) 87 if err != nil { 88 t.Fatal(err) 89 } 90 defer teardownTest() 91 92 // create a new feed 93 ctx, cancel := context.WithCancel(context.Background()) 94 defer cancel() 95 96 topic, _ := NewTopic("Mess with Swarm feeds code and see what ghost catches you", nil) 97 fd := Feed{ 98 Topic: topic, 99 User: signer.Address(), 100 } 101 102 // data for updates: 103 updates := []string{ 104 "blinky", // t=4200 105 "pinky", // t=4242 106 "inky", // t=4284 107 "clyde", // t=4285 108 } 109 110 request := NewFirstRequest(fd.Topic) // this timestamps the update at t = 4200 (start time) 111 chunkAddress := make(map[string]storage.Address) 112 data := []byte(updates[0]) 113 request.SetData(data) 114 if err := request.Sign(signer); err != nil { 115 t.Fatal(err) 116 } 117 chunkAddress[updates[0]], err = feedsHandler.Update(ctx, request) 118 if err != nil { 119 t.Fatal(err) 120 } 121 122 // move the clock ahead 21 seconds 123 clock.FastForward(21) // t=4221 124 125 request, err = feedsHandler.NewRequest(ctx, &request.Feed) // this timestamps the update at t = 4221 126 if err != nil { 127 t.Fatal(err) 128 } 129 if request.Epoch.Base() != 0 || request.Epoch.Level != lookup.HighestLevel-1 { 130 t.Fatalf("Suggested epoch BaseTime should be 0 and Epoch level should be %d", lookup.HighestLevel-1) 131 } 132 133 request.Epoch.Level = lookup.HighestLevel // force level 25 instead of 24 to make it fail 134 data = []byte(updates[1]) 135 request.SetData(data) 136 if err := request.Sign(signer); err != nil { 137 t.Fatal(err) 138 } 139 chunkAddress[updates[1]], err = feedsHandler.Update(ctx, request) 140 if err == nil { 141 t.Fatal("Expected update to fail since an update in this epoch already exists") 142 } 143 144 // move the clock ahead 21 seconds 145 clock.FastForward(21) // t=4242 146 request, err = feedsHandler.NewRequest(ctx, &request.Feed) 147 if err != nil { 148 t.Fatal(err) 149 } 150 request.SetData(data) 151 if err := request.Sign(signer); err != nil { 152 t.Fatal(err) 153 } 154 chunkAddress[updates[1]], err = feedsHandler.Update(ctx, request) 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 // move the clock ahead 42 seconds 160 clock.FastForward(42) // t=4284 161 request, err = feedsHandler.NewRequest(ctx, &request.Feed) 162 if err != nil { 163 t.Fatal(err) 164 } 165 data = []byte(updates[2]) 166 request.SetData(data) 167 if err := request.Sign(signer); err != nil { 168 t.Fatal(err) 169 } 170 chunkAddress[updates[2]], err = feedsHandler.Update(ctx, request) 171 if err != nil { 172 t.Fatal(err) 173 } 174 175 // move the clock ahead 1 second 176 clock.FastForward(1) // t=4285 177 request, err = feedsHandler.NewRequest(ctx, &request.Feed) 178 if err != nil { 179 t.Fatal(err) 180 } 181 if request.Epoch.Base() != 0 || request.Epoch.Level != 22 { 182 t.Fatalf("Expected epoch base time to be %d, got %d. Expected epoch level to be %d, got %d", 0, request.Epoch.Base(), 22, request.Epoch.Level) 183 } 184 data = []byte(updates[3]) 185 request.SetData(data) 186 187 if err := request.Sign(signer); err != nil { 188 t.Fatal(err) 189 } 190 chunkAddress[updates[3]], err = feedsHandler.Update(ctx, request) 191 if err != nil { 192 t.Fatal(err) 193 } 194 195 time.Sleep(time.Second) 196 feedsHandler.Close() 197 198 // check we can retrieve the updates after close 199 clock.FastForward(2000) // t=6285 200 201 feedParams := &HandlerParams{} 202 203 feedsHandler2, err := NewTestHandler(datadir, feedParams) 204 if err != nil { 205 t.Fatal(err) 206 } 207 208 update2, err := feedsHandler2.Lookup(ctx, NewQueryLatest(&request.Feed, lookup.NoClue)) 209 if err != nil { 210 t.Fatal(err) 211 } 212 213 // last update should be "clyde" 214 if !bytes.Equal(update2.data, []byte(updates[len(updates)-1])) { 215 t.Fatalf("feed update data was %v, expected %v", string(update2.data), updates[len(updates)-1]) 216 } 217 if update2.Level != 22 { 218 t.Fatalf("feed update epoch level was %d, expected 22", update2.Level) 219 } 220 if update2.Base() != 0 { 221 t.Fatalf("feed update epoch base time was %d, expected 0", update2.Base()) 222 } 223 log.Debug("Latest lookup", "epoch base time", update2.Base(), "epoch level", update2.Level, "data", update2.data) 224 225 // specific point in time 226 update, err := feedsHandler2.Lookup(ctx, NewQuery(&request.Feed, 4284, lookup.NoClue)) 227 if err != nil { 228 t.Fatal(err) 229 } 230 // check data 231 if !bytes.Equal(update.data, []byte(updates[2])) { 232 t.Fatalf("feed update data (historical) was %v, expected %v", string(update2.data), updates[2]) 233 } 234 log.Debug("Historical lookup", "epoch base time", update2.Base(), "epoch level", update2.Level, "data", update2.data) 235 236 // beyond the first should yield an error 237 update, err = feedsHandler2.Lookup(ctx, NewQuery(&request.Feed, startTime.Time-1, lookup.NoClue)) 238 if err == nil { 239 t.Fatalf("expected previous to fail, returned epoch %s data %v", update.Epoch.String(), update.data) 240 } 241 242 } 243 244 const Day = 60 * 60 * 24 245 const Year = Day * 365 246 const Month = Day * 30 247 248 func generateData(x uint64) []byte { 249 return []byte(fmt.Sprintf("%d", x)) 250 } 251 252 func TestSparseUpdates(t *testing.T) { 253 254 // make fake timeProvider 255 timeProvider := &fakeTimeProvider{ 256 currentTime: startTime.Time, 257 } 258 259 // signer containing private key 260 signer := newAliceSigner() 261 262 rh, datadir, teardownTest, err := setupTest(timeProvider, signer) 263 if err != nil { 264 t.Fatal(err) 265 } 266 defer teardownTest() 267 defer os.RemoveAll(datadir) 268 269 // create a new feed 270 ctx, cancel := context.WithCancel(context.Background()) 271 defer cancel() 272 topic, _ := NewTopic("Very slow updates", nil) 273 fd := Feed{ 274 Topic: topic, 275 User: signer.Address(), 276 } 277 278 // publish one update every 5 years since Unix 0 until today 279 today := uint64(1533799046) 280 var epoch lookup.Epoch 281 var lastUpdateTime uint64 282 for T := uint64(0); T < today; T += 5 * Year { 283 request := NewFirstRequest(fd.Topic) 284 request.Epoch = lookup.GetNextEpoch(epoch, T) 285 request.data = generateData(T) // this generates some data that depends on T, so we can check later 286 request.Sign(signer) 287 if err != nil { 288 t.Fatal(err) 289 } 290 291 if _, err := rh.Update(ctx, request); err != nil { 292 t.Fatal(err) 293 } 294 epoch = request.Epoch 295 lastUpdateTime = T 296 } 297 298 query := NewQuery(&fd, today, lookup.NoClue) 299 300 _, err = rh.Lookup(ctx, query) 301 if err != nil { 302 t.Fatal(err) 303 } 304 305 _, content, err := rh.GetContent(&fd) 306 if err != nil { 307 t.Fatal(err) 308 } 309 310 if !bytes.Equal(generateData(lastUpdateTime), content) { 311 t.Fatalf("Expected to recover last written value %d, got %s", lastUpdateTime, string(content)) 312 } 313 314 // lookup the closest update to 35*Year + 6* Month (~ June 2005): 315 // it should find the update we put on 35*Year, since we were updating every 5 years. 316 317 query.TimeLimit = 35*Year + 6*Month 318 319 _, err = rh.Lookup(ctx, query) 320 if err != nil { 321 t.Fatal(err) 322 } 323 324 _, content, err = rh.GetContent(&fd) 325 if err != nil { 326 t.Fatal(err) 327 } 328 329 if !bytes.Equal(generateData(35*Year), content) { 330 t.Fatalf("Expected to recover %d, got %s", 35*Year, string(content)) 331 } 332 } 333 334 func TestValidator(t *testing.T) { 335 336 // make fake timeProvider 337 timeProvider := &fakeTimeProvider{ 338 currentTime: startTime.Time, 339 } 340 341 // signer containing private key. Alice will be the good girl 342 signer := newAliceSigner() 343 344 // set up sim timeProvider 345 rh, _, teardownTest, err := setupTest(timeProvider, signer) 346 if err != nil { 347 t.Fatal(err) 348 } 349 defer teardownTest() 350 351 // create new feed 352 topic, _ := NewTopic(subtopicName, nil) 353 fd := Feed{ 354 Topic: topic, 355 User: signer.Address(), 356 } 357 mr := NewFirstRequest(fd.Topic) 358 359 // chunk with address 360 data := []byte("foo") 361 mr.SetData(data) 362 if err := mr.Sign(signer); err != nil { 363 t.Fatalf("sign fail: %v", err) 364 } 365 366 chunk, err := mr.toChunk() 367 if err != nil { 368 t.Fatal(err) 369 } 370 if !rh.Validate(chunk) { 371 t.Fatal("Chunk validator fail on update chunk") 372 } 373 374 address := chunk.Address() 375 // mess with the address 376 address[0] = 11 377 address[15] = 99 378 379 if rh.Validate(storage.NewChunk(address, chunk.Data())) { 380 t.Fatal("Expected Validate to fail with false chunk address") 381 } 382 } 383 384 // tests that the content address validator correctly checks the data 385 // tests that feed update chunks are passed through content address validator 386 // there is some redundancy in this test as it also tests content addressed chunks, 387 // which should be evaluated as invalid chunks by this validator 388 func TestValidatorInStore(t *testing.T) { 389 390 // make fake timeProvider 391 TimestampProvider = &fakeTimeProvider{ 392 currentTime: startTime.Time, 393 } 394 395 // signer containing private key 396 signer := newAliceSigner() 397 398 // set up localstore 399 datadir, err := ioutil.TempDir("", "storage-testfeedsvalidator") 400 if err != nil { 401 t.Fatal(err) 402 } 403 defer os.RemoveAll(datadir) 404 405 handlerParams := storage.NewDefaultLocalStoreParams() 406 handlerParams.Init(datadir) 407 store, err := storage.NewLocalStore(handlerParams, nil) 408 if err != nil { 409 t.Fatal(err) 410 } 411 412 // set up Swarm feeds handler and add is as a validator to the localstore 413 fhParams := &HandlerParams{} 414 fh := NewHandler(fhParams) 415 store.Validators = append(store.Validators, fh) 416 417 // create content addressed chunks, one good, one faulty 418 chunks := storage.GenerateRandomChunks(chunk.DefaultSize, 2) 419 goodChunk := chunks[0] 420 badChunk := storage.NewChunk(chunks[1].Address(), goodChunk.Data()) 421 422 topic, _ := NewTopic("xyzzy", nil) 423 fd := Feed{ 424 Topic: topic, 425 User: signer.Address(), 426 } 427 428 // create a feed update chunk with correct publickey 429 id := ID{ 430 Epoch: lookup.Epoch{Time: 42, 431 Level: 1, 432 }, 433 Feed: fd, 434 } 435 436 updateAddr := id.Addr() 437 data := []byte("bar") 438 439 r := new(Request) 440 r.idAddr = updateAddr 441 r.Update.ID = id 442 r.data = data 443 444 r.Sign(signer) 445 446 uglyChunk, err := r.toChunk() 447 if err != nil { 448 t.Fatal(err) 449 } 450 451 // put the chunks in the store and check their error status 452 err = store.Put(context.Background(), goodChunk) 453 if err == nil { 454 t.Fatal("expected error on good content address chunk with feed update validator only, but got nil") 455 } 456 err = store.Put(context.Background(), badChunk) 457 if err == nil { 458 t.Fatal("expected error on bad content address chunk with feed update validator only, but got nil") 459 } 460 err = store.Put(context.Background(), uglyChunk) 461 if err != nil { 462 t.Fatalf("expected no error on feed update chunk with feed update validator only, but got: %s", err) 463 } 464 } 465 466 // create rpc and feeds Handler 467 func setupTest(timeProvider timestampProvider, signer Signer) (fh *TestHandler, datadir string, teardown func(), err error) { 468 469 var fsClean func() 470 var rpcClean func() 471 cleanF = func() { 472 if fsClean != nil { 473 fsClean() 474 } 475 if rpcClean != nil { 476 rpcClean() 477 } 478 } 479 480 // temp datadir 481 datadir, err = ioutil.TempDir("", "fh") 482 if err != nil { 483 return nil, "", nil, err 484 } 485 fsClean = func() { 486 os.RemoveAll(datadir) 487 } 488 489 TimestampProvider = timeProvider 490 fhParams := &HandlerParams{} 491 fh, err = NewTestHandler(datadir, fhParams) 492 return fh, datadir, cleanF, err 493 } 494 495 func newAliceSigner() *GenericSigner { 496 privKey, _ := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") 497 return NewGenericSigner(privKey) 498 } 499 500 func newBobSigner() *GenericSigner { 501 privKey, _ := crypto.HexToECDSA("accedeaccedeaccedeaccedeaccedeaccedeaccedeaccedeaccedeaccedecaca") 502 return NewGenericSigner(privKey) 503 } 504 505 func newCharlieSigner() *GenericSigner { 506 privKey, _ := crypto.HexToECDSA("facadefacadefacadefacadefacadefacadefacadefacadefacadefacadefaca") 507 return NewGenericSigner(privKey) 508 }