client_test.go 18 KB

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