swarm_test.go 10 KB

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