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