manifest_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. // Copyright 2018 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU 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. // go-ethereum 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 General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package main
  17. import (
  18. "bytes"
  19. "io/ioutil"
  20. "os"
  21. "path/filepath"
  22. "testing"
  23. "github.com/ethereum/go-ethereum/swarm/api"
  24. swarm "github.com/ethereum/go-ethereum/swarm/api/client"
  25. )
  26. // TestManifestChange tests manifest add, update and remove
  27. // cli commands without encryption.
  28. func TestManifestChange(t *testing.T) {
  29. testManifestChange(t, false)
  30. }
  31. // TestManifestChange tests manifest add, update and remove
  32. // cli commands with encryption enabled.
  33. func TestManifestChangeEncrypted(t *testing.T) {
  34. testManifestChange(t, true)
  35. }
  36. // testManifestChange performs cli commands:
  37. // - manifest add
  38. // - manifest update
  39. // - manifest remove
  40. // on a manifest, testing the functionality of this
  41. // comands on paths that are in root manifest or a nested one.
  42. // Argument encrypt controls whether to use encryption or not.
  43. func testManifestChange(t *testing.T, encrypt bool) {
  44. t.Parallel()
  45. cluster := newTestCluster(t, 1)
  46. defer cluster.Shutdown()
  47. tmp, err := ioutil.TempDir("", "swarm-manifest-test")
  48. if err != nil {
  49. t.Fatal(err)
  50. }
  51. defer os.RemoveAll(tmp)
  52. origDir := filepath.Join(tmp, "orig")
  53. if err := os.Mkdir(origDir, 0777); err != nil {
  54. t.Fatal(err)
  55. }
  56. indexDataFilename := filepath.Join(origDir, "index.html")
  57. err = ioutil.WriteFile(indexDataFilename, []byte("<h1>Test</h1>"), 0666)
  58. if err != nil {
  59. t.Fatal(err)
  60. }
  61. // Files paths robots.txt and robots.html share the same prefix "robots."
  62. // which will result a manifest with a nested manifest under path "robots.".
  63. // This will allow testing manifest changes on both root and nested manifest.
  64. err = ioutil.WriteFile(filepath.Join(origDir, "robots.txt"), []byte("Disallow: /"), 0666)
  65. if err != nil {
  66. t.Fatal(err)
  67. }
  68. err = ioutil.WriteFile(filepath.Join(origDir, "robots.html"), []byte("<strong>No Robots Allowed</strong>"), 0666)
  69. if err != nil {
  70. t.Fatal(err)
  71. }
  72. err = ioutil.WriteFile(filepath.Join(origDir, "mutants.txt"), []byte("Frank\nMarcus"), 0666)
  73. if err != nil {
  74. t.Fatal(err)
  75. }
  76. args := []string{
  77. "--bzzapi",
  78. cluster.Nodes[0].URL,
  79. "--recursive",
  80. "--defaultpath",
  81. indexDataFilename,
  82. "up",
  83. origDir,
  84. }
  85. if encrypt {
  86. args = append(args, "--encrypt")
  87. }
  88. origManifestHash := runSwarmExpectHash(t, args...)
  89. checkHashLength(t, origManifestHash, encrypt)
  90. client := swarm.NewClient(cluster.Nodes[0].URL)
  91. // upload a new file and use its manifest to add it the original manifest.
  92. t.Run("add", func(t *testing.T) {
  93. humansData := []byte("Ann\nBob")
  94. humansDataFilename := filepath.Join(tmp, "humans.txt")
  95. err = ioutil.WriteFile(humansDataFilename, humansData, 0666)
  96. if err != nil {
  97. t.Fatal(err)
  98. }
  99. humansManifestHash := runSwarmExpectHash(t,
  100. "--bzzapi",
  101. cluster.Nodes[0].URL,
  102. "up",
  103. humansDataFilename,
  104. )
  105. newManifestHash := runSwarmExpectHash(t,
  106. "--bzzapi",
  107. cluster.Nodes[0].URL,
  108. "manifest",
  109. "add",
  110. origManifestHash,
  111. "humans.txt",
  112. humansManifestHash,
  113. )
  114. checkHashLength(t, newManifestHash, encrypt)
  115. newManifest := downloadManifest(t, client, newManifestHash, encrypt)
  116. var found bool
  117. for _, e := range newManifest.Entries {
  118. if e.Path == "humans.txt" {
  119. found = true
  120. if e.Size != int64(len(humansData)) {
  121. t.Errorf("expected humans.txt size %v, got %v", len(humansData), e.Size)
  122. }
  123. if e.ModTime.IsZero() {
  124. t.Errorf("got zero mod time for humans.txt")
  125. }
  126. ct := "text/plain; charset=utf-8"
  127. if e.ContentType != ct {
  128. t.Errorf("expected content type %q, got %q", ct, e.ContentType)
  129. }
  130. break
  131. }
  132. }
  133. if !found {
  134. t.Fatal("no humans.txt in new manifest")
  135. }
  136. checkFile(t, client, newManifestHash, "humans.txt", humansData)
  137. })
  138. // upload a new file and use its manifest to add it the original manifest,
  139. // but ensure that the file will be in the nested manifest of the original one.
  140. t.Run("add nested", func(t *testing.T) {
  141. robotsData := []byte(`{"disallow": "/"}`)
  142. robotsDataFilename := filepath.Join(tmp, "robots.json")
  143. err = ioutil.WriteFile(robotsDataFilename, robotsData, 0666)
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. robotsManifestHash := runSwarmExpectHash(t,
  148. "--bzzapi",
  149. cluster.Nodes[0].URL,
  150. "up",
  151. robotsDataFilename,
  152. )
  153. newManifestHash := runSwarmExpectHash(t,
  154. "--bzzapi",
  155. cluster.Nodes[0].URL,
  156. "manifest",
  157. "add",
  158. origManifestHash,
  159. "robots.json",
  160. robotsManifestHash,
  161. )
  162. checkHashLength(t, newManifestHash, encrypt)
  163. newManifest := downloadManifest(t, client, newManifestHash, encrypt)
  164. var found bool
  165. loop:
  166. for _, e := range newManifest.Entries {
  167. if e.Path == "robots." {
  168. nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
  169. for _, e := range nestedManifest.Entries {
  170. if e.Path == "json" {
  171. found = true
  172. if e.Size != int64(len(robotsData)) {
  173. t.Errorf("expected robots.json size %v, got %v", len(robotsData), e.Size)
  174. }
  175. if e.ModTime.IsZero() {
  176. t.Errorf("got zero mod time for robots.json")
  177. }
  178. ct := "application/json"
  179. if e.ContentType != ct {
  180. t.Errorf("expected content type %q, got %q", ct, e.ContentType)
  181. }
  182. break loop
  183. }
  184. }
  185. }
  186. }
  187. if !found {
  188. t.Fatal("no robots.json in new manifest")
  189. }
  190. checkFile(t, client, newManifestHash, "robots.json", robotsData)
  191. })
  192. // upload a new file and use its manifest to change the file it the original manifest.
  193. t.Run("update", func(t *testing.T) {
  194. indexData := []byte("<h1>Ethereum Swarm</h1>")
  195. indexDataFilename := filepath.Join(tmp, "index.html")
  196. err = ioutil.WriteFile(indexDataFilename, indexData, 0666)
  197. if err != nil {
  198. t.Fatal(err)
  199. }
  200. indexManifestHash := runSwarmExpectHash(t,
  201. "--bzzapi",
  202. cluster.Nodes[0].URL,
  203. "up",
  204. indexDataFilename,
  205. )
  206. newManifestHash := runSwarmExpectHash(t,
  207. "--bzzapi",
  208. cluster.Nodes[0].URL,
  209. "manifest",
  210. "update",
  211. origManifestHash,
  212. "index.html",
  213. indexManifestHash,
  214. )
  215. checkHashLength(t, newManifestHash, encrypt)
  216. newManifest := downloadManifest(t, client, newManifestHash, encrypt)
  217. var found bool
  218. for _, e := range newManifest.Entries {
  219. if e.Path == "index.html" {
  220. found = true
  221. if e.Size != int64(len(indexData)) {
  222. t.Errorf("expected index.html size %v, got %v", len(indexData), e.Size)
  223. }
  224. if e.ModTime.IsZero() {
  225. t.Errorf("got zero mod time for index.html")
  226. }
  227. ct := "text/html; charset=utf-8"
  228. if e.ContentType != ct {
  229. t.Errorf("expected content type %q, got %q", ct, e.ContentType)
  230. }
  231. break
  232. }
  233. }
  234. if !found {
  235. t.Fatal("no index.html in new manifest")
  236. }
  237. checkFile(t, client, newManifestHash, "index.html", indexData)
  238. // check default entry change
  239. checkFile(t, client, newManifestHash, "", indexData)
  240. })
  241. // upload a new file and use its manifest to change the file it the original manifest,
  242. // but ensure that the file is in the nested manifest of the original one.
  243. t.Run("update nested", func(t *testing.T) {
  244. robotsData := []byte(`<string>Only humans allowed!!!</strong>`)
  245. robotsDataFilename := filepath.Join(tmp, "robots.html")
  246. err = ioutil.WriteFile(robotsDataFilename, robotsData, 0666)
  247. if err != nil {
  248. t.Fatal(err)
  249. }
  250. humansManifestHash := runSwarmExpectHash(t,
  251. "--bzzapi",
  252. cluster.Nodes[0].URL,
  253. "up",
  254. robotsDataFilename,
  255. )
  256. newManifestHash := runSwarmExpectHash(t,
  257. "--bzzapi",
  258. cluster.Nodes[0].URL,
  259. "manifest",
  260. "update",
  261. origManifestHash,
  262. "robots.html",
  263. humansManifestHash,
  264. )
  265. checkHashLength(t, newManifestHash, encrypt)
  266. newManifest := downloadManifest(t, client, newManifestHash, encrypt)
  267. var found bool
  268. loop:
  269. for _, e := range newManifest.Entries {
  270. if e.Path == "robots." {
  271. nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
  272. for _, e := range nestedManifest.Entries {
  273. if e.Path == "html" {
  274. found = true
  275. if e.Size != int64(len(robotsData)) {
  276. t.Errorf("expected robots.html size %v, got %v", len(robotsData), e.Size)
  277. }
  278. if e.ModTime.IsZero() {
  279. t.Errorf("got zero mod time for robots.html")
  280. }
  281. ct := "text/html; charset=utf-8"
  282. if e.ContentType != ct {
  283. t.Errorf("expected content type %q, got %q", ct, e.ContentType)
  284. }
  285. break loop
  286. }
  287. }
  288. }
  289. }
  290. if !found {
  291. t.Fatal("no robots.html in new manifest")
  292. }
  293. checkFile(t, client, newManifestHash, "robots.html", robotsData)
  294. })
  295. // remove a file from the manifest.
  296. t.Run("remove", func(t *testing.T) {
  297. newManifestHash := runSwarmExpectHash(t,
  298. "--bzzapi",
  299. cluster.Nodes[0].URL,
  300. "manifest",
  301. "remove",
  302. origManifestHash,
  303. "mutants.txt",
  304. )
  305. checkHashLength(t, newManifestHash, encrypt)
  306. newManifest := downloadManifest(t, client, newManifestHash, encrypt)
  307. var found bool
  308. for _, e := range newManifest.Entries {
  309. if e.Path == "mutants.txt" {
  310. found = true
  311. break
  312. }
  313. }
  314. if found {
  315. t.Fatal("mutants.txt is not removed")
  316. }
  317. })
  318. // remove a file from the manifest, but ensure that the file is in
  319. // the nested manifest of the original one.
  320. t.Run("remove nested", func(t *testing.T) {
  321. newManifestHash := runSwarmExpectHash(t,
  322. "--bzzapi",
  323. cluster.Nodes[0].URL,
  324. "manifest",
  325. "remove",
  326. origManifestHash,
  327. "robots.html",
  328. )
  329. checkHashLength(t, newManifestHash, encrypt)
  330. newManifest := downloadManifest(t, client, newManifestHash, encrypt)
  331. var found bool
  332. loop:
  333. for _, e := range newManifest.Entries {
  334. if e.Path == "robots." {
  335. nestedManifest := downloadManifest(t, client, e.Hash, encrypt)
  336. for _, e := range nestedManifest.Entries {
  337. if e.Path == "html" {
  338. found = true
  339. break loop
  340. }
  341. }
  342. }
  343. }
  344. if found {
  345. t.Fatal("robots.html in not removed")
  346. }
  347. })
  348. }
  349. // TestNestedDefaultEntryUpdate tests if the default entry is updated
  350. // if the file in nested manifest used for it is also updated.
  351. func TestNestedDefaultEntryUpdate(t *testing.T) {
  352. testNestedDefaultEntryUpdate(t, false)
  353. }
  354. // TestNestedDefaultEntryUpdateEncrypted tests if the default entry
  355. // of encrypted upload is updated if the file in nested manifest
  356. // used for it is also updated.
  357. func TestNestedDefaultEntryUpdateEncrypted(t *testing.T) {
  358. testNestedDefaultEntryUpdate(t, true)
  359. }
  360. func testNestedDefaultEntryUpdate(t *testing.T, encrypt bool) {
  361. t.Parallel()
  362. cluster := newTestCluster(t, 1)
  363. defer cluster.Shutdown()
  364. tmp, err := ioutil.TempDir("", "swarm-manifest-test")
  365. if err != nil {
  366. t.Fatal(err)
  367. }
  368. defer os.RemoveAll(tmp)
  369. origDir := filepath.Join(tmp, "orig")
  370. if err := os.Mkdir(origDir, 0777); err != nil {
  371. t.Fatal(err)
  372. }
  373. indexData := []byte("<h1>Test</h1>")
  374. indexDataFilename := filepath.Join(origDir, "index.html")
  375. err = ioutil.WriteFile(indexDataFilename, indexData, 0666)
  376. if err != nil {
  377. t.Fatal(err)
  378. }
  379. // Add another file with common prefix as the default entry to test updates of
  380. // default entry with nested manifests.
  381. err = ioutil.WriteFile(filepath.Join(origDir, "index.txt"), []byte("Test"), 0666)
  382. if err != nil {
  383. t.Fatal(err)
  384. }
  385. args := []string{
  386. "--bzzapi",
  387. cluster.Nodes[0].URL,
  388. "--recursive",
  389. "--defaultpath",
  390. indexDataFilename,
  391. "up",
  392. origDir,
  393. }
  394. if encrypt {
  395. args = append(args, "--encrypt")
  396. }
  397. origManifestHash := runSwarmExpectHash(t, args...)
  398. checkHashLength(t, origManifestHash, encrypt)
  399. client := swarm.NewClient(cluster.Nodes[0].URL)
  400. newIndexData := []byte("<h1>Ethereum Swarm</h1>")
  401. newIndexDataFilename := filepath.Join(tmp, "index.html")
  402. err = ioutil.WriteFile(newIndexDataFilename, newIndexData, 0666)
  403. if err != nil {
  404. t.Fatal(err)
  405. }
  406. newIndexManifestHash := runSwarmExpectHash(t,
  407. "--bzzapi",
  408. cluster.Nodes[0].URL,
  409. "up",
  410. newIndexDataFilename,
  411. )
  412. newManifestHash := runSwarmExpectHash(t,
  413. "--bzzapi",
  414. cluster.Nodes[0].URL,
  415. "manifest",
  416. "update",
  417. origManifestHash,
  418. "index.html",
  419. newIndexManifestHash,
  420. )
  421. checkHashLength(t, newManifestHash, encrypt)
  422. newManifest := downloadManifest(t, client, newManifestHash, encrypt)
  423. var found bool
  424. for _, e := range newManifest.Entries {
  425. if e.Path == "index." {
  426. found = true
  427. newManifest = downloadManifest(t, client, e.Hash, encrypt)
  428. break
  429. }
  430. }
  431. if !found {
  432. t.Fatal("no index. path in new manifest")
  433. }
  434. found = false
  435. for _, e := range newManifest.Entries {
  436. if e.Path == "html" {
  437. found = true
  438. if e.Size != int64(len(newIndexData)) {
  439. t.Errorf("expected index.html size %v, got %v", len(newIndexData), e.Size)
  440. }
  441. if e.ModTime.IsZero() {
  442. t.Errorf("got zero mod time for index.html")
  443. }
  444. ct := "text/html; charset=utf-8"
  445. if e.ContentType != ct {
  446. t.Errorf("expected content type %q, got %q", ct, e.ContentType)
  447. }
  448. break
  449. }
  450. }
  451. if !found {
  452. t.Fatal("no html in new manifest")
  453. }
  454. checkFile(t, client, newManifestHash, "index.html", newIndexData)
  455. // check default entry change
  456. checkFile(t, client, newManifestHash, "", newIndexData)
  457. }
  458. func runSwarmExpectHash(t *testing.T, args ...string) (hash string) {
  459. t.Helper()
  460. hashRegexp := `[a-f\d]{64,128}`
  461. up := runSwarm(t, args...)
  462. _, matches := up.ExpectRegexp(hashRegexp)
  463. up.ExpectExit()
  464. if len(matches) < 1 {
  465. t.Fatal("no matches found")
  466. }
  467. return matches[0]
  468. }
  469. func checkHashLength(t *testing.T, hash string, encrypted bool) {
  470. t.Helper()
  471. l := len(hash)
  472. if encrypted && l != 128 {
  473. t.Errorf("expected hash length 128, got %v", l)
  474. }
  475. if !encrypted && l != 64 {
  476. t.Errorf("expected hash length 64, got %v", l)
  477. }
  478. }
  479. func downloadManifest(t *testing.T, client *swarm.Client, hash string, encrypted bool) (manifest *api.Manifest) {
  480. t.Helper()
  481. m, isEncrypted, err := client.DownloadManifest(hash)
  482. if err != nil {
  483. t.Fatal(err)
  484. }
  485. if encrypted != isEncrypted {
  486. t.Error("new manifest encryption flag is not correct")
  487. }
  488. return m
  489. }
  490. func checkFile(t *testing.T, client *swarm.Client, hash, path string, expected []byte) {
  491. t.Helper()
  492. f, err := client.Download(hash, path)
  493. if err != nil {
  494. t.Fatal(err)
  495. }
  496. got, err := ioutil.ReadAll(f)
  497. if err != nil {
  498. t.Fatal(err)
  499. }
  500. if !bytes.Equal(got, expected) {
  501. t.Errorf("expected file content %q, got %q", expected, got)
  502. }
  503. }