swarm_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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.netStore == nil {
  78. t.Error("netStore 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.bzz == nil {
  87. t.Error("bzz not initialized")
  88. }
  89. if s.ps == nil {
  90. t.Error("pss not initialized")
  91. }
  92. if s.api == nil {
  93. t.Error("api not initialized")
  94. }
  95. if s.sfs == nil {
  96. t.Error("swarm filesystem not initialized")
  97. }
  98. },
  99. },
  100. {
  101. name: "with swap",
  102. configure: func(config *api.Config) {
  103. config.SwapAPI = ipcEndpoint
  104. config.SwapEnabled = true
  105. },
  106. check: func(t *testing.T, s *Swarm, _ *api.Config) {
  107. if s.backend == nil {
  108. t.Error("backend is nil")
  109. }
  110. },
  111. },
  112. {
  113. name: "with swap disabled",
  114. configure: func(config *api.Config) {
  115. config.SwapAPI = ipcEndpoint
  116. config.SwapEnabled = false
  117. },
  118. check: func(t *testing.T, s *Swarm, _ *api.Config) {
  119. if s.backend != nil {
  120. t.Error("backend is not nil")
  121. }
  122. },
  123. },
  124. {
  125. name: "with swap enabled and api endpoint blank",
  126. configure: func(config *api.Config) {
  127. config.SwapAPI = ""
  128. config.SwapEnabled = true
  129. },
  130. check: func(t *testing.T, s *Swarm, _ *api.Config) {
  131. if s.backend != nil {
  132. t.Error("backend is not nil")
  133. }
  134. },
  135. },
  136. {
  137. name: "ens",
  138. configure: func(config *api.Config) {
  139. config.EnsAPIs = []string{
  140. "http://127.0.0.1:8888",
  141. }
  142. },
  143. check: func(t *testing.T, s *Swarm, _ *api.Config) {
  144. if s.dns == nil {
  145. t.Error("dns is not initialized")
  146. }
  147. },
  148. },
  149. } {
  150. t.Run(tc.name, func(t *testing.T) {
  151. config := api.NewConfig()
  152. dir, err := ioutil.TempDir("", "swarm")
  153. if err != nil {
  154. t.Fatal(err)
  155. }
  156. defer os.RemoveAll(dir)
  157. config.Path = dir
  158. privkey, err := crypto.GenerateKey()
  159. if err != nil {
  160. t.Fatal(err)
  161. }
  162. config.Init(privkey)
  163. if tc.configure != nil {
  164. tc.configure(config)
  165. }
  166. s, err := NewSwarm(config, nil)
  167. if err != nil {
  168. t.Fatal(err)
  169. }
  170. if tc.check != nil {
  171. tc.check(t, s, config)
  172. }
  173. })
  174. }
  175. }
  176. func TestParseEnsAPIAddress(t *testing.T) {
  177. for _, x := range []struct {
  178. description string
  179. value string
  180. tld string
  181. endpoint string
  182. addr common.Address
  183. }{
  184. {
  185. description: "IPC endpoint",
  186. value: "/data/testnet/geth.ipc",
  187. endpoint: "/data/testnet/geth.ipc",
  188. },
  189. {
  190. description: "HTTP endpoint",
  191. value: "http://127.0.0.1:1234",
  192. endpoint: "http://127.0.0.1:1234",
  193. },
  194. {
  195. description: "WS endpoint",
  196. value: "ws://127.0.0.1:1234",
  197. endpoint: "ws://127.0.0.1:1234",
  198. },
  199. {
  200. description: "IPC Endpoint and TLD",
  201. value: "test:/data/testnet/geth.ipc",
  202. endpoint: "/data/testnet/geth.ipc",
  203. tld: "test",
  204. },
  205. {
  206. description: "HTTP endpoint and TLD",
  207. value: "test:http://127.0.0.1:1234",
  208. endpoint: "http://127.0.0.1:1234",
  209. tld: "test",
  210. },
  211. {
  212. description: "WS endpoint and TLD",
  213. value: "test:ws://127.0.0.1:1234",
  214. endpoint: "ws://127.0.0.1:1234",
  215. tld: "test",
  216. },
  217. {
  218. description: "IPC Endpoint and contract address",
  219. value: "314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
  220. endpoint: "/data/testnet/geth.ipc",
  221. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  222. },
  223. {
  224. description: "HTTP endpoint and contract address",
  225. value: "314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
  226. endpoint: "http://127.0.0.1:1234",
  227. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  228. },
  229. {
  230. description: "WS endpoint and contract address",
  231. value: "314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:1234",
  232. endpoint: "ws://127.0.0.1:1234",
  233. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  234. },
  235. {
  236. description: "IPC Endpoint, TLD and contract address",
  237. value: "test:314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
  238. endpoint: "/data/testnet/geth.ipc",
  239. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  240. tld: "test",
  241. },
  242. {
  243. description: "HTTP endpoint, TLD and contract address",
  244. value: "eth:314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
  245. endpoint: "http://127.0.0.1:1234",
  246. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  247. tld: "eth",
  248. },
  249. {
  250. description: "WS endpoint, TLD and contract address",
  251. value: "eth:314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:1234",
  252. endpoint: "ws://127.0.0.1:1234",
  253. addr: common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
  254. tld: "eth",
  255. },
  256. } {
  257. t.Run(x.description, func(t *testing.T) {
  258. tld, endpoint, addr := parseEnsAPIAddress(x.value)
  259. if endpoint != x.endpoint {
  260. t.Errorf("expected Endpoint %q, got %q", x.endpoint, endpoint)
  261. }
  262. if addr != x.addr {
  263. t.Errorf("expected ContractAddress %q, got %q", x.addr.String(), addr.String())
  264. }
  265. if tld != x.tld {
  266. t.Errorf("expected TLD %q, got %q", x.tld, tld)
  267. }
  268. })
  269. }
  270. }
  271. // TestLocalStoreAndRetrieve runs multiple tests where different size files are uploaded
  272. // to a single Swarm instance using API Store and checked against the content returned
  273. // by API Retrieve function.
  274. //
  275. // This test is intended to validate functionality of chunker store and join functions
  276. // and their intergartion into Swarm, without comparing results with ones produced by
  277. // another chunker implementation, as it is done in swarm/storage tests.
  278. func TestLocalStoreAndRetrieve(t *testing.T) {
  279. config := api.NewConfig()
  280. dir, err := ioutil.TempDir("", "node")
  281. if err != nil {
  282. t.Fatal(err)
  283. }
  284. defer os.RemoveAll(dir)
  285. config.Path = dir
  286. privkey, err := crypto.GenerateKey()
  287. if err != nil {
  288. t.Fatal(err)
  289. }
  290. config.Init(privkey)
  291. swarm, err := NewSwarm(config, nil)
  292. if err != nil {
  293. t.Fatal(err)
  294. }
  295. // by default, test only the lonely chunk cases
  296. sizes := []int{1, 60, 4097, 524288 + 1, 7*524288 + 1}
  297. if *longrunning {
  298. // test broader set of cases if -longruning flag is set
  299. 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+1, 128*524288, 128*524288+4096, 128*524288+4097, 816778334)
  300. }
  301. for _, n := range sizes {
  302. testLocalStoreAndRetrieve(t, swarm, n, true)
  303. testLocalStoreAndRetrieve(t, swarm, n, false)
  304. }
  305. }
  306. // testLocalStoreAndRetrieve is using a single Swarm instance, to upload
  307. // a file of length n with optional random data using API Store function,
  308. // and checks the output of API Retrieve function on the same instance.
  309. // This is a regression test for issue
  310. // https://github.com/ethersphere/go-ethereum/issues/639
  311. // where pyramid chunker did not split correctly files with lengths that
  312. // are edge cases for chunk and tree parameters, depending whether there
  313. // is a tree chunk with only one data chunk and how the compress functionality
  314. // changed the tree.
  315. func testLocalStoreAndRetrieve(t *testing.T, swarm *Swarm, n int, randomData bool) {
  316. slice := make([]byte, n)
  317. if randomData {
  318. rand.Seed(time.Now().UnixNano())
  319. rand.Read(slice)
  320. }
  321. dataPut := string(slice)
  322. ctx := context.TODO()
  323. k, wait, err := swarm.api.Store(ctx, strings.NewReader(dataPut), int64(len(dataPut)), false)
  324. if err != nil {
  325. t.Fatal(err)
  326. }
  327. if wait != nil {
  328. err = wait(ctx)
  329. if err != nil {
  330. t.Fatal(err)
  331. }
  332. }
  333. r, _ := swarm.api.Retrieve(context.TODO(), k)
  334. d, err := ioutil.ReadAll(r)
  335. if err != nil {
  336. t.Fatal(err)
  337. }
  338. dataGet := string(d)
  339. if len(dataPut) != len(dataGet) {
  340. t.Fatalf("data not matched: length expected %v, got %v", len(dataPut), len(dataGet))
  341. } else {
  342. if dataPut != dataGet {
  343. t.Fatal("data not matched")
  344. }
  345. }
  346. }