github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/chain/swarm/api/client/client_test.go (about) 1 // Copyright 2017 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 client 18 19 import ( 20 "bytes" 21 "io/ioutil" 22 "os" 23 "path/filepath" 24 "reflect" 25 "sort" 26 "testing" 27 28 "github.com/ethereum/go-ethereum/swarm/storage" 29 "github.com/ethereum/go-ethereum/swarm/storage/feed/lookup" 30 31 "github.com/ethereum/go-ethereum/common" 32 "github.com/ethereum/go-ethereum/crypto" 33 "github.com/ethereum/go-ethereum/swarm/api" 34 swarmhttp "github.com/ethereum/go-ethereum/swarm/api/http" 35 "github.com/ethereum/go-ethereum/swarm/storage/feed" 36 ) 37 38 func serverFunc(api *api.API) swarmhttp.TestServer { 39 return swarmhttp.NewServer(api, "") 40 } 41 42 // TestClientUploadDownloadRaw test uploading and downloading raw data to swarm 43 func TestClientUploadDownloadRaw(t *testing.T) { 44 testClientUploadDownloadRaw(false, t) 45 } 46 func TestClientUploadDownloadRawEncrypted(t *testing.T) { 47 testClientUploadDownloadRaw(true, t) 48 } 49 50 func testClientUploadDownloadRaw(toEncrypt bool, t *testing.T) { 51 srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil) 52 defer srv.Close() 53 54 client := NewClient(srv.URL) 55 56 // upload some raw data 57 data := []byte("foo123") 58 hash, err := client.UploadRaw(bytes.NewReader(data), int64(len(data)), toEncrypt) 59 if err != nil { 60 t.Fatal(err) 61 } 62 63 // check we can download the same data 64 res, isEncrypted, err := client.DownloadRaw(hash) 65 if err != nil { 66 t.Fatal(err) 67 } 68 if isEncrypted != toEncrypt { 69 t.Fatalf("Expected encyption status %v got %v", toEncrypt, isEncrypted) 70 } 71 defer res.Close() 72 gotData, err := ioutil.ReadAll(res) 73 if err != nil { 74 t.Fatal(err) 75 } 76 if !bytes.Equal(gotData, data) { 77 t.Fatalf("expected downloaded data to be %q, got %q", data, gotData) 78 } 79 } 80 81 // TestClientUploadDownloadFiles test uploading and downloading files to swarm 82 // manifests 83 func TestClientUploadDownloadFiles(t *testing.T) { 84 testClientUploadDownloadFiles(false, t) 85 } 86 87 func TestClientUploadDownloadFilesEncrypted(t *testing.T) { 88 testClientUploadDownloadFiles(true, t) 89 } 90 91 func testClientUploadDownloadFiles(toEncrypt bool, t *testing.T) { 92 srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil) 93 defer srv.Close() 94 95 client := NewClient(srv.URL) 96 upload := func(manifest, path string, data []byte) string { 97 file := &File{ 98 ReadCloser: ioutil.NopCloser(bytes.NewReader(data)), 99 ManifestEntry: api.ManifestEntry{ 100 Path: path, 101 ContentType: "text/plain", 102 Size: int64(len(data)), 103 }, 104 } 105 hash, err := client.Upload(file, manifest, toEncrypt) 106 if err != nil { 107 t.Fatal(err) 108 } 109 return hash 110 } 111 checkDownload := func(manifest, path string, expected []byte) { 112 file, err := client.Download(manifest, path) 113 if err != nil { 114 t.Fatal(err) 115 } 116 defer file.Close() 117 if file.Size != int64(len(expected)) { 118 t.Fatalf("expected downloaded file to be %d bytes, got %d", len(expected), file.Size) 119 } 120 if file.ContentType != "text/plain" { 121 t.Fatalf("expected downloaded file to have type %q, got %q", "text/plain", file.ContentType) 122 } 123 data, err := ioutil.ReadAll(file) 124 if err != nil { 125 t.Fatal(err) 126 } 127 if !bytes.Equal(data, expected) { 128 t.Fatalf("expected downloaded data to be %q, got %q", expected, data) 129 } 130 } 131 132 // upload a file to the root of a manifest 133 rootData := []byte("some-data") 134 rootHash := upload("", "", rootData) 135 136 // check we can download the root file 137 checkDownload(rootHash, "", rootData) 138 139 // upload another file to the same manifest 140 otherData := []byte("some-other-data") 141 newHash := upload(rootHash, "some/other/path", otherData) 142 143 // check we can download both files from the new manifest 144 checkDownload(newHash, "", rootData) 145 checkDownload(newHash, "some/other/path", otherData) 146 147 // replace the root file with different data 148 newHash = upload(newHash, "", otherData) 149 150 // check both files have the other data 151 checkDownload(newHash, "", otherData) 152 checkDownload(newHash, "some/other/path", otherData) 153 } 154 155 var testDirFiles = []string{ 156 "file1.txt", 157 "file2.txt", 158 "dir1/file3.txt", 159 "dir1/file4.txt", 160 "dir2/file5.txt", 161 "dir2/dir3/file6.txt", 162 "dir2/dir4/file7.txt", 163 "dir2/dir4/file8.txt", 164 } 165 166 func newTestDirectory(t *testing.T) string { 167 dir, err := ioutil.TempDir("", "swarm-client-test") 168 if err != nil { 169 t.Fatal(err) 170 } 171 172 for _, file := range testDirFiles { 173 path := filepath.Join(dir, file) 174 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { 175 os.RemoveAll(dir) 176 t.Fatalf("error creating dir for %s: %s", path, err) 177 } 178 if err := ioutil.WriteFile(path, []byte(file), 0644); err != nil { 179 os.RemoveAll(dir) 180 t.Fatalf("error writing file %s: %s", path, err) 181 } 182 } 183 184 return dir 185 } 186 187 // TestClientUploadDownloadDirectory tests uploading and downloading a 188 // directory of files to a swarm manifest 189 func TestClientUploadDownloadDirectory(t *testing.T) { 190 srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil) 191 defer srv.Close() 192 193 dir := newTestDirectory(t) 194 defer os.RemoveAll(dir) 195 196 // upload the directory 197 client := NewClient(srv.URL) 198 defaultPath := testDirFiles[0] 199 hash, err := client.UploadDirectory(dir, defaultPath, "", false) 200 if err != nil { 201 t.Fatalf("error uploading directory: %s", err) 202 } 203 204 // check we can download the individual files 205 checkDownloadFile := func(path string, expected []byte) { 206 file, err := client.Download(hash, path) 207 if err != nil { 208 t.Fatal(err) 209 } 210 defer file.Close() 211 data, err := ioutil.ReadAll(file) 212 if err != nil { 213 t.Fatal(err) 214 } 215 if !bytes.Equal(data, expected) { 216 t.Fatalf("expected data to be %q, got %q", expected, data) 217 } 218 } 219 for _, file := range testDirFiles { 220 checkDownloadFile(file, []byte(file)) 221 } 222 223 // check we can download the default path 224 checkDownloadFile("", []byte(testDirFiles[0])) 225 226 // check we can download the directory 227 tmp, err := ioutil.TempDir("", "swarm-client-test") 228 if err != nil { 229 t.Fatal(err) 230 } 231 defer os.RemoveAll(tmp) 232 if err := client.DownloadDirectory(hash, "", tmp, ""); err != nil { 233 t.Fatal(err) 234 } 235 for _, file := range testDirFiles { 236 data, err := ioutil.ReadFile(filepath.Join(tmp, file)) 237 if err != nil { 238 t.Fatal(err) 239 } 240 if !bytes.Equal(data, []byte(file)) { 241 t.Fatalf("expected data to be %q, got %q", file, data) 242 } 243 } 244 } 245 246 // TestClientFileList tests listing files in a swarm manifest 247 func TestClientFileList(t *testing.T) { 248 testClientFileList(false, t) 249 } 250 251 func TestClientFileListEncrypted(t *testing.T) { 252 testClientFileList(true, t) 253 } 254 255 func testClientFileList(toEncrypt bool, t *testing.T) { 256 srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil) 257 defer srv.Close() 258 259 dir := newTestDirectory(t) 260 defer os.RemoveAll(dir) 261 262 client := NewClient(srv.URL) 263 hash, err := client.UploadDirectory(dir, "", "", toEncrypt) 264 if err != nil { 265 t.Fatalf("error uploading directory: %s", err) 266 } 267 268 ls := func(prefix string) []string { 269 list, err := client.List(hash, prefix, "") 270 if err != nil { 271 t.Fatal(err) 272 } 273 paths := make([]string, 0, len(list.CommonPrefixes)+len(list.Entries)) 274 paths = append(paths, list.CommonPrefixes...) 275 for _, entry := range list.Entries { 276 paths = append(paths, entry.Path) 277 } 278 sort.Strings(paths) 279 return paths 280 } 281 282 tests := map[string][]string{ 283 "": {"dir1/", "dir2/", "file1.txt", "file2.txt"}, 284 "file": {"file1.txt", "file2.txt"}, 285 "file1": {"file1.txt"}, 286 "file2.txt": {"file2.txt"}, 287 "file12": {}, 288 "dir": {"dir1/", "dir2/"}, 289 "dir1": {"dir1/"}, 290 "dir1/": {"dir1/file3.txt", "dir1/file4.txt"}, 291 "dir1/file": {"dir1/file3.txt", "dir1/file4.txt"}, 292 "dir1/file3.txt": {"dir1/file3.txt"}, 293 "dir1/file34": {}, 294 "dir2/": {"dir2/dir3/", "dir2/dir4/", "dir2/file5.txt"}, 295 "dir2/file": {"dir2/file5.txt"}, 296 "dir2/dir": {"dir2/dir3/", "dir2/dir4/"}, 297 "dir2/dir3/": {"dir2/dir3/file6.txt"}, 298 "dir2/dir4/": {"dir2/dir4/file7.txt", "dir2/dir4/file8.txt"}, 299 "dir2/dir4/file": {"dir2/dir4/file7.txt", "dir2/dir4/file8.txt"}, 300 "dir2/dir4/file7.txt": {"dir2/dir4/file7.txt"}, 301 "dir2/dir4/file78": {}, 302 } 303 for prefix, expected := range tests { 304 actual := ls(prefix) 305 if !reflect.DeepEqual(actual, expected) { 306 t.Fatalf("expected prefix %q to return %v, got %v", prefix, expected, actual) 307 } 308 } 309 } 310 311 // TestClientMultipartUpload tests uploading files to swarm using a multipart 312 // upload 313 func TestClientMultipartUpload(t *testing.T) { 314 srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil) 315 defer srv.Close() 316 317 // define an uploader which uploads testDirFiles with some data 318 data := []byte("some-data") 319 uploader := UploaderFunc(func(upload UploadFn) error { 320 for _, name := range testDirFiles { 321 file := &File{ 322 ReadCloser: ioutil.NopCloser(bytes.NewReader(data)), 323 ManifestEntry: api.ManifestEntry{ 324 Path: name, 325 ContentType: "text/plain", 326 Size: int64(len(data)), 327 }, 328 } 329 if err := upload(file); err != nil { 330 return err 331 } 332 } 333 return nil 334 }) 335 336 // upload the files as a multipart upload 337 client := NewClient(srv.URL) 338 hash, err := client.MultipartUpload("", uploader) 339 if err != nil { 340 t.Fatal(err) 341 } 342 343 // check we can download the individual files 344 checkDownloadFile := func(path string) { 345 file, err := client.Download(hash, path) 346 if err != nil { 347 t.Fatal(err) 348 } 349 defer file.Close() 350 gotData, err := ioutil.ReadAll(file) 351 if err != nil { 352 t.Fatal(err) 353 } 354 if !bytes.Equal(gotData, data) { 355 t.Fatalf("expected data to be %q, got %q", data, gotData) 356 } 357 } 358 for _, file := range testDirFiles { 359 checkDownloadFile(file) 360 } 361 } 362 363 func newTestSigner() (*feed.GenericSigner, error) { 364 privKey, err := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") 365 if err != nil { 366 return nil, err 367 } 368 return feed.NewGenericSigner(privKey), nil 369 } 370 371 // Test the transparent resolving of feed updates with bzz:// scheme 372 // 373 // First upload data to bzz:, and store the Swarm hash to the resulting manifest in a feed update. 374 // This effectively uses a feed to store a pointer to content rather than the content itself 375 // Retrieving the update with the Swarm hash should return the manifest pointing directly to the data 376 // and raw retrieve of that hash should return the data 377 func TestClientBzzWithFeed(t *testing.T) { 378 379 signer, _ := newTestSigner() 380 381 // Initialize a Swarm test server 382 srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil) 383 swarmClient := NewClient(srv.URL) 384 defer srv.Close() 385 386 // put together some data for our test: 387 dataBytes := []byte(` 388 // 389 // Create some data our manifest will point to. Data that could be very big and wouldn't fit in a feed update. 390 // So what we are going to do is upload it to Swarm bzz:// and obtain a **manifest hash** pointing to it: 391 // 392 // MANIFEST HASH --> DATA 393 // 394 // Then, we store that **manifest hash** into a Swarm Feed update. Once we have done this, 395 // we can use the **feed manifest hash** in bzz:// instead, this way: bzz://feed-manifest-hash. 396 // 397 // FEED MANIFEST HASH --> MANIFEST HASH --> DATA 398 // 399 // Given that we can update the feed at any time with a new **manifest hash** but the **feed manifest hash** 400 // stays constant, we have effectively created a fixed address to changing content. (Applause) 401 // 402 // FEED MANIFEST HASH (the same) --> MANIFEST HASH(2) --> DATA(2) 403 // 404 `) 405 406 // Create a virtual File out of memory containing the above data 407 f := &File{ 408 ReadCloser: ioutil.NopCloser(bytes.NewReader(dataBytes)), 409 ManifestEntry: api.ManifestEntry{ 410 ContentType: "text/plain", 411 Mode: 0660, 412 Size: int64(len(dataBytes)), 413 }, 414 } 415 416 // upload data to bzz:// and retrieve the content-addressed manifest hash, hex-encoded. 417 manifestAddressHex, err := swarmClient.Upload(f, "", false) 418 if err != nil { 419 t.Fatalf("Error creating manifest: %s", err) 420 } 421 422 // convert the hex-encoded manifest hash to a 32-byte slice 423 manifestAddress := common.FromHex(manifestAddressHex) 424 425 if len(manifestAddress) != storage.AddressLength { 426 t.Fatalf("Something went wrong. Got a hash of an unexpected length. Expected %d bytes. Got %d", storage.AddressLength, len(manifestAddress)) 427 } 428 429 // Now create a **feed manifest**. For that, we need a topic: 430 topic, _ := feed.NewTopic("interesting topic indeed", nil) 431 432 // Build a feed request to update data 433 request := feed.NewFirstRequest(topic) 434 435 // Put the 32-byte address of the manifest into the feed update 436 request.SetData(manifestAddress) 437 438 // Sign the update 439 if err := request.Sign(signer); err != nil { 440 t.Fatalf("Error signing update: %s", err) 441 } 442 443 // Publish the update and at the same time request a **feed manifest** to be created 444 feedManifestAddressHex, err := swarmClient.CreateFeedWithManifest(request) 445 if err != nil { 446 t.Fatalf("Error creating feed manifest: %s", err) 447 } 448 449 // Check we have received the exact **feed manifest** to be expected 450 // given the topic and user signing the updates: 451 correctFeedManifestAddrHex := "747c402e5b9dc715a25a4393147512167bab018a007fad7cdcd9adc7fce1ced2" 452 if feedManifestAddressHex != correctFeedManifestAddrHex { 453 t.Fatalf("Response feed manifest mismatch, expected '%s', got '%s'", correctFeedManifestAddrHex, feedManifestAddressHex) 454 } 455 456 // Check we get a not found error when trying to get feed updates with a made-up manifest 457 _, err = swarmClient.QueryFeed(nil, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") 458 if err != ErrNoFeedUpdatesFound { 459 t.Fatalf("Expected to receive ErrNoFeedUpdatesFound error. Got: %s", err) 460 } 461 462 // If we query the feed directly we should get **manifest hash** back: 463 reader, err := swarmClient.QueryFeed(nil, correctFeedManifestAddrHex) 464 if err != nil { 465 t.Fatalf("Error retrieving feed updates: %s", err) 466 } 467 defer reader.Close() 468 gotData, err := ioutil.ReadAll(reader) 469 if err != nil { 470 t.Fatal(err) 471 } 472 473 //Check that indeed the **manifest hash** is retrieved 474 if !bytes.Equal(manifestAddress, gotData) { 475 t.Fatalf("Expected: %v, got %v", manifestAddress, gotData) 476 } 477 478 // Now the final test we were looking for: Use bzz://<feed-manifest> and that should resolve all manifests 479 // and return the original data directly: 480 f, err = swarmClient.Download(feedManifestAddressHex, "") 481 if err != nil { 482 t.Fatal(err) 483 } 484 gotData, err = ioutil.ReadAll(f) 485 if err != nil { 486 t.Fatal(err) 487 } 488 489 // Check that we get back the original data: 490 if !bytes.Equal(dataBytes, gotData) { 491 t.Fatalf("Expected: %v, got %v", manifestAddress, gotData) 492 } 493 } 494 495 // TestClientCreateUpdateFeed will check that feeds can be created and updated via the HTTP client. 496 func TestClientCreateUpdateFeed(t *testing.T) { 497 498 signer, _ := newTestSigner() 499 500 srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil) 501 client := NewClient(srv.URL) 502 defer srv.Close() 503 504 // set raw data for the feed update 505 databytes := []byte("En un lugar de La Mancha, de cuyo nombre no quiero acordarme...") 506 507 // our feed topic name 508 topic, _ := feed.NewTopic("El Quijote", nil) 509 createRequest := feed.NewFirstRequest(topic) 510 511 createRequest.SetData(databytes) 512 if err := createRequest.Sign(signer); err != nil { 513 t.Fatalf("Error signing update: %s", err) 514 } 515 516 feedManifestHash, err := client.CreateFeedWithManifest(createRequest) 517 if err != nil { 518 t.Fatal(err) 519 } 520 521 correctManifestAddrHex := "0e9b645ebc3da167b1d56399adc3276f7a08229301b72a03336be0e7d4b71882" 522 if feedManifestHash != correctManifestAddrHex { 523 t.Fatalf("Response feed manifest mismatch, expected '%s', got '%s'", correctManifestAddrHex, feedManifestHash) 524 } 525 526 reader, err := client.QueryFeed(nil, correctManifestAddrHex) 527 if err != nil { 528 t.Fatalf("Error retrieving feed updates: %s", err) 529 } 530 defer reader.Close() 531 gotData, err := ioutil.ReadAll(reader) 532 if err != nil { 533 t.Fatal(err) 534 } 535 if !bytes.Equal(databytes, gotData) { 536 t.Fatalf("Expected: %v, got %v", databytes, gotData) 537 } 538 539 // define different data 540 databytes = []byte("... no ha mucho tiempo que vivĂa un hidalgo de los de lanza en astillero ...") 541 542 updateRequest, err := client.GetFeedRequest(nil, correctManifestAddrHex) 543 if err != nil { 544 t.Fatalf("Error retrieving update request template: %s", err) 545 } 546 547 updateRequest.SetData(databytes) 548 if err := updateRequest.Sign(signer); err != nil { 549 t.Fatalf("Error signing update: %s", err) 550 } 551 552 if err = client.UpdateFeed(updateRequest); err != nil { 553 t.Fatalf("Error updating feed: %s", err) 554 } 555 556 reader, err = client.QueryFeed(nil, correctManifestAddrHex) 557 if err != nil { 558 t.Fatalf("Error retrieving feed updates: %s", err) 559 } 560 defer reader.Close() 561 gotData, err = ioutil.ReadAll(reader) 562 if err != nil { 563 t.Fatal(err) 564 } 565 if !bytes.Equal(databytes, gotData) { 566 t.Fatalf("Expected: %v, got %v", databytes, gotData) 567 } 568 569 // now try retrieving feed updates without a manifest 570 571 fd := &feed.Feed{ 572 Topic: topic, 573 User: signer.Address(), 574 } 575 576 lookupParams := feed.NewQueryLatest(fd, lookup.NoClue) 577 reader, err = client.QueryFeed(lookupParams, "") 578 if err != nil { 579 t.Fatalf("Error retrieving feed updates: %s", err) 580 } 581 defer reader.Close() 582 gotData, err = ioutil.ReadAll(reader) 583 if err != nil { 584 t.Fatal(err) 585 } 586 if !bytes.Equal(databytes, gotData) { 587 t.Fatalf("Expected: %v, got %v", databytes, gotData) 588 } 589 }