swarm_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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 swarm
  17. import (
  18. "context"
  19. "encoding/hex"
  20. "io/ioutil"
  21. "math/rand"
  22. "os"
  23. "path"
  24. "runtime"
  25. "strings"
  26. "testing"
  27. "time"
  28. "github.com/ethereum/go-ethereum/common"
  29. "github.com/ethereum/go-ethereum/crypto"
  30. "github.com/ethereum/go-ethereum/rpc"
  31. "github.com/ethereum/go-ethereum/swarm/api"
  32. )
  33. // TestNewSwarm validates Swarm fields in repsect to the provided configuration.
  34. func TestNewSwarm(t *testing.T) {
  35. dir, err := ioutil.TempDir("", "swarm")
  36. if err != nil {
  37. t.Fatal(err)
  38. }
  39. defer os.RemoveAll(dir)
  40. // a simple rpc endpoint for testing dialing
  41. ipcEndpoint := path.Join(dir, "TestSwarm.ipc")
  42. // windows namedpipes are not on filesystem but on NPFS
  43. if runtime.GOOS == "windows" {
  44. b := make([]byte, 8)
  45. rand.Read(b)
  46. ipcEndpoint = `\\.\pipe\TestSwarm-` + hex.EncodeToString(b)
  47. }
  48. _, server, err := rpc.StartIPCEndpoint(ipcEndpoint, nil)
  49. if err != nil {
  50. t.Error(err)
  51. }
  52. defer server.Stop()
  53. for _, tc := range []struct {
  54. name string
  55. configure func(*api.Config)
  56. check func(*testing.T, *Swarm, *api.Config)
  57. }{
  58. {
  59. name: "defaults",
  60. configure: nil,
  61. check: func(t *testing.T, s *Swarm, config *api.Config) {
  62. if s.config != config {
  63. t.Error("config is not the same object")
  64. }
  65. if s.backend != nil {
  66. t.Error("backend is not nil")
  67. }
  68. if s.privateKey == nil {
  69. t.Error("private key is not set")
  70. }
  71. if !s.config.HiveParams.Discovery {
  72. t.Error("config.HiveParams.Discovery is false, must be true regardless the configuration")
  73. }
  74. if s.dns != nil {
  75. t.Error("dns initialized, but it should not be")
  76. }
  77. if s.lstore == nil {
  78. t.Error("localstore not initialized")
  79. }
  80. if s.streamer == nil {
  81. t.Error("streamer not initialized")
  82. }
  83. if s.fileStore == nil {
  84. t.Error("fileStore not initialized")
  85. }
  86. if s.lstore.Validators == nil {
  87. t.Error("localstore validators not initialized")
  88. }
  89. if s.bzz == nil {
  90. t.Error("bzz not initialized")
  91. }
  92. if s.ps == nil {
  93. t.Error("pss not initialized")
  94. }
  95. if s.api == nil {
  96. t.Error("api not initialized")
  97. }
  98. if s.sfs == nil {
  99. t.Error("swarm filesystem not initialized")
  100. }
  101. },
  102. },
  103. {
  104. name: "with swap",
  105. configure: func(config *api.Config) {
  106. config.SwapAPI = ipcEndpoint
  107. config.SwapEnabled = true
  108. },
  109. check: func(t *testing.T, s *Swarm, _ *api.Config) {
  110. if s.backend == nil {
  111. t.Error("backend is nil")
  112. }
  113. },
  114. },
  115. {
  116. name: "with swap disabled",
  117. configure: func(config *api.Config) {
  118. config.SwapAPI = ipcEndpoint
  119. config.SwapEnabled = false
  120. },
  121. check: func(t *testing.T, s *Swarm, _ *api.Config) {
  122. if s.backend != nil {
  123. t.Error("backend is not nil")
  124. }
  125. },
  126. },
  127. {
  128. name: "with swap enabled and api endpoint blank",
  129. configure: func(config *api.Config) {
  130. config.SwapAPI = ""
  131. config.SwapEnabled = true
  132. },
  133. check: func(t *testing.T, s *Swarm, _ *api.Config) {
  134. if s.backend != nil {
  135. t.Error("backend is not nil")
  136. }
  137. },
  138. },
  139. {
  140. name: "ens",
  141. configure: func(config *api.Config) {
  142. config.EnsAPIs = []string{
  143. "http://127.0.0.1:8888",
  144. }
  145. },
  146. check: func(t *testing.T, s *Swarm, _ *api.Config) {
  147. if s.dns == nil {
  148. t.Error("dns is not initialized")
  149. }
  150. },
  151. },
  152. } {
  153. t.Run(tc.name, func(t *testing.T) {
  154. config := api.NewConfig()
  155. dir, err := ioutil.TempDir("", "swarm")
  156. if err != nil {
  157. t.Fatal(err)
  158. }
  159. defer os.RemoveAll(dir)
  160. config.Path = dir
  161. privkey, err := crypto.GenerateKey()
  162. if err != nil {
  163. t.Fatal(err)
  164. }
  165. config.Init(privkey)
  166. if tc.configure != nil {
  167. tc.configure(config)
  168. }
  169. s, err := NewSwarm(config, nil)
  170. if err != nil {
  171. t.Fatal(err)
  172. }
  173. if tc.check != nil {
  174. tc.check(t, s, config)
  175. }
  176. })
  177. }
  178. }
  179. func TestParseEnsAPIAddress(t *testing.T) {
  180. for _, x := range []struct {
  181. description string
  182. value string
  183. tld string
  184. endpoint string
  185. addr common.Address
  186. }{
  187. {
  188. description: "IPC endpoint",
  189. value: "/data/testnet/geth.ipc",
  190. endpoint: "/data/testnet/geth.ipc",
  191. },
  192. {
  193. description: "HTTP endpoint",
  194. value: "http://127.0.0.1:1234",
  195. endpoint: "http://127.0.0.1:1234",
  196. },
  197. {
  198. description: "WS endpoint",
  199. value: "ws://127.0.0.1:1234",
  200. endpoint: "ws://127.0.0.1:1234",
  201. },
  202. {
  203. description: "IPC Endpoint and TLD",
  204. value: "test:/data/testnet/geth.ipc",
  205. endpoint: "/data/testnet/geth.ipc",
  206. tld: "test",
  207. },
  208. {
  209. description: "HTTP endpoint and TLD",
  210. value: "test:http://127.0.0.1:1234",
  211. endpoint: "http://127.0.0.1:1234",
  212. tld: "test",
  213. },
  214. {
  215. description: "WS endpoint and TLD",
  216. value: "test:ws://127.0.0.1:1234",
  217. endpoint: "ws://127.0.0.1:1234",
  218. tld: "test",
  219. },
  220. {
  221. description: "IPC Endpoint and contract address",
  222. value: "314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
  223. endpoint: "/data/testnet/geth.ipc",
  224. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  225. },
  226. {
  227. description: "HTTP endpoint and contract address",
  228. value: "314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
  229. endpoint: "http://127.0.0.1:1234",
  230. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  231. },
  232. {
  233. description: "WS endpoint and contract address",
  234. value: "314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:1234",
  235. endpoint: "ws://127.0.0.1:1234",
  236. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  237. },
  238. {
  239. description: "IPC Endpoint, TLD and contract address",
  240. value: "test:314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
  241. endpoint: "/data/testnet/geth.ipc",
  242. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  243. tld: "test",
  244. },
  245. {
  246. description: "HTTP endpoint, TLD and contract address",
  247. value: "eth:314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
  248. endpoint: "http://127.0.0.1:1234",
  249. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  250. tld: "eth",
  251. },
  252. {
  253. description: "WS endpoint, TLD and contract address",
  254. value: "eth:314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:1234",
  255. endpoint: "ws://127.0.0.1:1234",
  256. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  257. tld: "eth",
  258. },
  259. } {
  260. t.Run(x.description, func(t *testing.T) {
  261. tld, endpoint, addr := parseEnsAPIAddress(x.value)
  262. if endpoint != x.endpoint {
  263. t.Errorf("expected Endpoint %q, got %q", x.endpoint, endpoint)
  264. }
  265. if addr != x.addr {
  266. t.Errorf("expected ContractAddress %q, got %q", x.addr.String(), addr.String())
  267. }
  268. if tld != x.tld {
  269. t.Errorf("expected TLD %q, got %q", x.tld, tld)
  270. }
  271. })
  272. }
  273. }
  274. // TestLocalStoreAndRetrieve runs multiple tests where different size files are uploaded
  275. // to a single Swarm instance using API Store and checked against the content returned
  276. // by API Retrieve function.
  277. //
  278. // This test is intended to validate functionality of chunker store and join functions
  279. // and their intergartion into Swarm, without comparing results with ones produced by
  280. // another chunker implementation, as it is done in swarm/storage tests.
  281. func TestLocalStoreAndRetrieve(t *testing.T) {
  282. config := api.NewConfig()
  283. dir, err := ioutil.TempDir("", "node")
  284. if err != nil {
  285. t.Fatal(err)
  286. }
  287. defer os.RemoveAll(dir)
  288. config.Path = dir
  289. privkey, err := crypto.GenerateKey()
  290. if err != nil {
  291. t.Fatal(err)
  292. }
  293. config.Init(privkey)
  294. swarm, err := NewSwarm(config, nil)
  295. if err != nil {
  296. t.Fatal(err)
  297. }
  298. // by default, test only the lonely chunk cases
  299. sizes := []int{1, 60, 4097, 524288 + 1, 7*524288 + 1, 128*524288 + 1}
  300. if *longrunning {
  301. // test broader set of cases if -longruning flag is set
  302. sizes = append(sizes, 83, 179, 253, 1024, 4095, 4096, 8191, 8192, 8193, 12287, 12288, 12289, 123456, 2345678, 67298391, 524288, 524288+4096, 524288+4097, 7*524288, 7*524288+4096, 7*524288+4097, 128*524288, 128*524288+4096, 128*524288+4097, 816778334)
  303. }
  304. for _, n := range sizes {
  305. testLocalStoreAndRetrieve(t, swarm, n, true)
  306. testLocalStoreAndRetrieve(t, swarm, n, false)
  307. }
  308. }
  309. // testLocalStoreAndRetrieve is using a single Swarm instance, to upload
  310. // a file of length n with optional random data using API Store function,
  311. // and checks the output of API Retrieve function on the same instance.
  312. // This is a regression test for issue
  313. // https://github.com/ethersphere/go-ethereum/issues/639
  314. // where pyramid chunker did not split correctly files with lengths that
  315. // are edge cases for chunk and tree parameters, depending whether there
  316. // is a tree chunk with only one data chunk and how the compress functionality
  317. // changed the tree.
  318. func testLocalStoreAndRetrieve(t *testing.T, swarm *Swarm, n int, randomData bool) {
  319. slice := make([]byte, n)
  320. if randomData {
  321. rand.Seed(time.Now().UnixNano())
  322. rand.Read(slice)
  323. }
  324. dataPut := string(slice)
  325. ctx := context.TODO()
  326. k, wait, err := swarm.api.Store(ctx, strings.NewReader(dataPut), int64(len(dataPut)), false)
  327. if err != nil {
  328. t.Fatal(err)
  329. }
  330. if wait != nil {
  331. err = wait(ctx)
  332. if err != nil {
  333. t.Fatal(err)
  334. }
  335. }
  336. r, _ := swarm.api.Retrieve(context.TODO(), k)
  337. d, err := ioutil.ReadAll(r)
  338. if err != nil {
  339. t.Fatal(err)
  340. }
  341. dataGet := string(d)
  342. if len(dataPut) != len(dataGet) {
  343. t.Fatalf("data not matched: length expected %v, got %v", len(dataPut), len(dataGet))
  344. } else {
  345. if dataPut != dataGet {
  346. t.Fatal("data not matched")
  347. }
  348. }
  349. }