network_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  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 simulations
  17. import (
  18. "context"
  19. "encoding/json"
  20. "fmt"
  21. "reflect"
  22. "strconv"
  23. "strings"
  24. "testing"
  25. "time"
  26. "github.com/ethereum/go-ethereum/log"
  27. "github.com/ethereum/go-ethereum/node"
  28. "github.com/ethereum/go-ethereum/p2p/enode"
  29. "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
  30. )
  31. // Tests that a created snapshot with a minimal service only contains the expected connections
  32. // and that a network when loaded with this snapshot only contains those same connections
  33. func TestSnapshot(t *testing.T) {
  34. // PART I
  35. // create snapshot from ring network
  36. // this is a minimal service, whose protocol will take exactly one message OR close of connection before quitting
  37. adapter := adapters.NewSimAdapter(adapters.Services{
  38. "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
  39. return NewNoopService(nil), nil
  40. },
  41. })
  42. // create network
  43. network := NewNetwork(adapter, &NetworkConfig{
  44. DefaultService: "noopwoop",
  45. })
  46. // \todo consider making a member of network, set to true threadsafe when shutdown
  47. runningOne := true
  48. defer func() {
  49. if runningOne {
  50. network.Shutdown()
  51. }
  52. }()
  53. // create and start nodes
  54. nodeCount := 20
  55. ids := make([]enode.ID, nodeCount)
  56. for i := 0; i < nodeCount; i++ {
  57. conf := adapters.RandomNodeConfig()
  58. node, err := network.NewNodeWithConfig(conf)
  59. if err != nil {
  60. t.Fatalf("error creating node: %s", err)
  61. }
  62. if err := network.Start(node.ID()); err != nil {
  63. t.Fatalf("error starting node: %s", err)
  64. }
  65. ids[i] = node.ID()
  66. }
  67. // subscribe to peer events
  68. evC := make(chan *Event)
  69. sub := network.Events().Subscribe(evC)
  70. defer sub.Unsubscribe()
  71. // connect nodes in a ring
  72. // spawn separate thread to avoid deadlock in the event listeners
  73. go func() {
  74. for i, id := range ids {
  75. peerID := ids[(i+1)%len(ids)]
  76. if err := network.Connect(id, peerID); err != nil {
  77. t.Fatal(err)
  78. }
  79. }
  80. }()
  81. // collect connection events up to expected number
  82. ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
  83. defer cancel()
  84. checkIds := make(map[enode.ID][]enode.ID)
  85. connEventCount := nodeCount
  86. OUTER:
  87. for {
  88. select {
  89. case <-ctx.Done():
  90. t.Fatal(ctx.Err())
  91. case ev := <-evC:
  92. if ev.Type == EventTypeConn && !ev.Control {
  93. // fail on any disconnect
  94. if !ev.Conn.Up {
  95. t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
  96. }
  97. checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
  98. checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
  99. connEventCount--
  100. log.Debug("ev", "count", connEventCount)
  101. if connEventCount == 0 {
  102. break OUTER
  103. }
  104. }
  105. }
  106. }
  107. // create snapshot of current network
  108. snap, err := network.Snapshot()
  109. if err != nil {
  110. t.Fatal(err)
  111. }
  112. j, err := json.Marshal(snap)
  113. if err != nil {
  114. t.Fatal(err)
  115. }
  116. log.Debug("snapshot taken", "nodes", len(snap.Nodes), "conns", len(snap.Conns), "json", string(j))
  117. // verify that the snap element numbers check out
  118. if len(checkIds) != len(snap.Conns) || len(checkIds) != len(snap.Nodes) {
  119. t.Fatalf("snapshot wrong node,conn counts %d,%d != %d", len(snap.Nodes), len(snap.Conns), len(checkIds))
  120. }
  121. // shut down sim network
  122. runningOne = false
  123. sub.Unsubscribe()
  124. network.Shutdown()
  125. // check that we have all the expected connections in the snapshot
  126. for nodid, nodConns := range checkIds {
  127. for _, nodConn := range nodConns {
  128. var match bool
  129. for _, snapConn := range snap.Conns {
  130. if snapConn.One == nodid && snapConn.Other == nodConn {
  131. match = true
  132. break
  133. } else if snapConn.Other == nodid && snapConn.One == nodConn {
  134. match = true
  135. break
  136. }
  137. }
  138. if !match {
  139. t.Fatalf("snapshot missing conn %v -> %v", nodid, nodConn)
  140. }
  141. }
  142. }
  143. log.Info("snapshot checked")
  144. // PART II
  145. // load snapshot and verify that exactly same connections are formed
  146. adapter = adapters.NewSimAdapter(adapters.Services{
  147. "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
  148. return NewNoopService(nil), nil
  149. },
  150. })
  151. network = NewNetwork(adapter, &NetworkConfig{
  152. DefaultService: "noopwoop",
  153. })
  154. defer func() {
  155. network.Shutdown()
  156. }()
  157. // subscribe to peer events
  158. // every node up and conn up event will generate one additional control event
  159. // therefore multiply the count by two
  160. evC = make(chan *Event, (len(snap.Conns)*2)+(len(snap.Nodes)*2))
  161. sub = network.Events().Subscribe(evC)
  162. defer sub.Unsubscribe()
  163. // load the snapshot
  164. // spawn separate thread to avoid deadlock in the event listeners
  165. err = network.Load(snap)
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. // collect connection events up to expected number
  170. ctx, cancel = context.WithTimeout(context.TODO(), time.Second*3)
  171. defer cancel()
  172. connEventCount = nodeCount
  173. OuterTwo:
  174. for {
  175. select {
  176. case <-ctx.Done():
  177. t.Fatal(ctx.Err())
  178. case ev := <-evC:
  179. if ev.Type == EventTypeConn && !ev.Control {
  180. // fail on any disconnect
  181. if !ev.Conn.Up {
  182. t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
  183. }
  184. log.Debug("conn", "on", ev.Conn.One, "other", ev.Conn.Other)
  185. checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
  186. checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
  187. connEventCount--
  188. log.Debug("ev", "count", connEventCount)
  189. if connEventCount == 0 {
  190. break OuterTwo
  191. }
  192. }
  193. }
  194. }
  195. // check that we have all expected connections in the network
  196. for _, snapConn := range snap.Conns {
  197. var match bool
  198. for nodid, nodConns := range checkIds {
  199. for _, nodConn := range nodConns {
  200. if snapConn.One == nodid && snapConn.Other == nodConn {
  201. match = true
  202. break
  203. } else if snapConn.Other == nodid && snapConn.One == nodConn {
  204. match = true
  205. break
  206. }
  207. }
  208. }
  209. if !match {
  210. t.Fatalf("network missing conn %v -> %v", snapConn.One, snapConn.Other)
  211. }
  212. }
  213. // verify that network didn't generate any other additional connection events after the ones we have collected within a reasonable period of time
  214. ctx, cancel = context.WithTimeout(context.TODO(), time.Second)
  215. defer cancel()
  216. select {
  217. case <-ctx.Done():
  218. case ev := <-evC:
  219. if ev.Type == EventTypeConn {
  220. t.Fatalf("Superfluous conn found %v -> %v", ev.Conn.One, ev.Conn.Other)
  221. }
  222. }
  223. // This test validates if all connections from the snapshot
  224. // are created in the network.
  225. t.Run("conns after load", func(t *testing.T) {
  226. // Create new network.
  227. n := NewNetwork(
  228. adapters.NewSimAdapter(adapters.Services{
  229. "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
  230. return NewNoopService(nil), nil
  231. },
  232. }),
  233. &NetworkConfig{
  234. DefaultService: "noopwoop",
  235. },
  236. )
  237. defer n.Shutdown()
  238. // Load the same snapshot.
  239. err := n.Load(snap)
  240. if err != nil {
  241. t.Fatal(err)
  242. }
  243. // Check every connection from the snapshot
  244. // if it is in the network, too.
  245. for _, c := range snap.Conns {
  246. if n.GetConn(c.One, c.Other) == nil {
  247. t.Errorf("missing connection: %s -> %s", c.One, c.Other)
  248. }
  249. }
  250. })
  251. }
  252. // TestNetworkSimulation creates a multi-node simulation network with each node
  253. // connected in a ring topology, checks that all nodes successfully handshake
  254. // with each other and that a snapshot fully represents the desired topology
  255. func TestNetworkSimulation(t *testing.T) {
  256. // create simulation network with 20 testService nodes
  257. adapter := adapters.NewSimAdapter(adapters.Services{
  258. "test": newTestService,
  259. })
  260. network := NewNetwork(adapter, &NetworkConfig{
  261. DefaultService: "test",
  262. })
  263. defer network.Shutdown()
  264. nodeCount := 20
  265. ids := make([]enode.ID, nodeCount)
  266. for i := 0; i < nodeCount; i++ {
  267. conf := adapters.RandomNodeConfig()
  268. node, err := network.NewNodeWithConfig(conf)
  269. if err != nil {
  270. t.Fatalf("error creating node: %s", err)
  271. }
  272. if err := network.Start(node.ID()); err != nil {
  273. t.Fatalf("error starting node: %s", err)
  274. }
  275. ids[i] = node.ID()
  276. }
  277. // perform a check which connects the nodes in a ring (so each node is
  278. // connected to exactly two peers) and then checks that all nodes
  279. // performed two handshakes by checking their peerCount
  280. action := func(_ context.Context) error {
  281. for i, id := range ids {
  282. peerID := ids[(i+1)%len(ids)]
  283. if err := network.Connect(id, peerID); err != nil {
  284. return err
  285. }
  286. }
  287. return nil
  288. }
  289. check := func(ctx context.Context, id enode.ID) (bool, error) {
  290. // check we haven't run out of time
  291. select {
  292. case <-ctx.Done():
  293. return false, ctx.Err()
  294. default:
  295. }
  296. // get the node
  297. node := network.GetNode(id)
  298. if node == nil {
  299. return false, fmt.Errorf("unknown node: %s", id)
  300. }
  301. // check it has exactly two peers
  302. client, err := node.Client()
  303. if err != nil {
  304. return false, err
  305. }
  306. var peerCount int64
  307. if err := client.CallContext(ctx, &peerCount, "test_peerCount"); err != nil {
  308. return false, err
  309. }
  310. switch {
  311. case peerCount < 2:
  312. return false, nil
  313. case peerCount == 2:
  314. return true, nil
  315. default:
  316. return false, fmt.Errorf("unexpected peerCount: %d", peerCount)
  317. }
  318. }
  319. timeout := 30 * time.Second
  320. ctx, cancel := context.WithTimeout(context.Background(), timeout)
  321. defer cancel()
  322. // trigger a check every 100ms
  323. trigger := make(chan enode.ID)
  324. go triggerChecks(ctx, ids, trigger, 100*time.Millisecond)
  325. result := NewSimulation(network).Run(ctx, &Step{
  326. Action: action,
  327. Trigger: trigger,
  328. Expect: &Expectation{
  329. Nodes: ids,
  330. Check: check,
  331. },
  332. })
  333. if result.Error != nil {
  334. t.Fatalf("simulation failed: %s", result.Error)
  335. }
  336. // take a network snapshot and check it contains the correct topology
  337. snap, err := network.Snapshot()
  338. if err != nil {
  339. t.Fatal(err)
  340. }
  341. if len(snap.Nodes) != nodeCount {
  342. t.Fatalf("expected snapshot to contain %d nodes, got %d", nodeCount, len(snap.Nodes))
  343. }
  344. if len(snap.Conns) != nodeCount {
  345. t.Fatalf("expected snapshot to contain %d connections, got %d", nodeCount, len(snap.Conns))
  346. }
  347. for i, id := range ids {
  348. conn := snap.Conns[i]
  349. if conn.One != id {
  350. t.Fatalf("expected conn[%d].One to be %s, got %s", i, id, conn.One)
  351. }
  352. peerID := ids[(i+1)%len(ids)]
  353. if conn.Other != peerID {
  354. t.Fatalf("expected conn[%d].Other to be %s, got %s", i, peerID, conn.Other)
  355. }
  356. }
  357. }
  358. func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, interval time.Duration) {
  359. tick := time.NewTicker(interval)
  360. defer tick.Stop()
  361. for {
  362. select {
  363. case <-tick.C:
  364. for _, id := range ids {
  365. select {
  366. case trigger <- id:
  367. case <-ctx.Done():
  368. return
  369. }
  370. }
  371. case <-ctx.Done():
  372. return
  373. }
  374. }
  375. }
  376. // \todo: refactor to implement shapshots
  377. // and connect configuration methods once these are moved from
  378. // swarm/network/simulations/connect.go
  379. func BenchmarkMinimalService(b *testing.B) {
  380. b.Run("ring/32", benchmarkMinimalServiceTmp)
  381. }
  382. func benchmarkMinimalServiceTmp(b *testing.B) {
  383. // stop timer to discard setup time pollution
  384. args := strings.Split(b.Name(), "/")
  385. nodeCount, err := strconv.ParseInt(args[2], 10, 16)
  386. if err != nil {
  387. b.Fatal(err)
  388. }
  389. for i := 0; i < b.N; i++ {
  390. // this is a minimal service, whose protocol will close a channel upon run of protocol
  391. // making it possible to bench the time it takes for the service to start and protocol actually to be run
  392. protoCMap := make(map[enode.ID]map[enode.ID]chan struct{})
  393. adapter := adapters.NewSimAdapter(adapters.Services{
  394. "noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
  395. protoCMap[ctx.Config.ID] = make(map[enode.ID]chan struct{})
  396. svc := NewNoopService(protoCMap[ctx.Config.ID])
  397. return svc, nil
  398. },
  399. })
  400. // create network
  401. network := NewNetwork(adapter, &NetworkConfig{
  402. DefaultService: "noopwoop",
  403. })
  404. defer network.Shutdown()
  405. // create and start nodes
  406. ids := make([]enode.ID, nodeCount)
  407. for i := 0; i < int(nodeCount); i++ {
  408. conf := adapters.RandomNodeConfig()
  409. node, err := network.NewNodeWithConfig(conf)
  410. if err != nil {
  411. b.Fatalf("error creating node: %s", err)
  412. }
  413. if err := network.Start(node.ID()); err != nil {
  414. b.Fatalf("error starting node: %s", err)
  415. }
  416. ids[i] = node.ID()
  417. }
  418. // ready, set, go
  419. b.ResetTimer()
  420. // connect nodes in a ring
  421. for i, id := range ids {
  422. peerID := ids[(i+1)%len(ids)]
  423. if err := network.Connect(id, peerID); err != nil {
  424. b.Fatal(err)
  425. }
  426. }
  427. // wait for all protocols to signal to close down
  428. ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
  429. defer cancel()
  430. for nodid, peers := range protoCMap {
  431. for peerid, peerC := range peers {
  432. log.Debug("getting ", "node", nodid, "peer", peerid)
  433. select {
  434. case <-ctx.Done():
  435. b.Fatal(ctx.Err())
  436. case <-peerC:
  437. }
  438. }
  439. }
  440. }
  441. }
  442. func TestNode_UnmarshalJSON(t *testing.T) {
  443. t.Run(
  444. "test unmarshal of Node up field",
  445. func(t *testing.T) {
  446. runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONUpField())
  447. },
  448. )
  449. t.Run(
  450. "test unmarshal of Node Config field",
  451. func(t *testing.T) {
  452. runNodeUnmarshalJSON(t, casesNodeUnmarshalJSONConfigField())
  453. },
  454. )
  455. }
  456. func runNodeUnmarshalJSON(t *testing.T, tests []nodeUnmarshalTestCase) {
  457. t.Helper()
  458. for _, tt := range tests {
  459. t.Run(tt.name, func(t *testing.T) {
  460. var got Node
  461. if err := got.UnmarshalJSON([]byte(tt.marshaled)); err != nil {
  462. expectErrorMessageToContain(t, err, tt.wantErr)
  463. }
  464. expectNodeEquality(t, got, tt.want)
  465. })
  466. }
  467. }
  468. type nodeUnmarshalTestCase struct {
  469. name string
  470. marshaled string
  471. want Node
  472. wantErr string
  473. }
  474. func expectErrorMessageToContain(t *testing.T, got error, want string) {
  475. t.Helper()
  476. if got == nil && want == "" {
  477. return
  478. }
  479. if got == nil && want != "" {
  480. t.Errorf("error was expected, got: nil, want: %v", want)
  481. return
  482. }
  483. if !strings.Contains(got.Error(), want) {
  484. t.Errorf(
  485. "unexpected error message, got %v, want: %v",
  486. want,
  487. got,
  488. )
  489. }
  490. }
  491. func expectNodeEquality(t *testing.T, got Node, want Node) {
  492. t.Helper()
  493. if !reflect.DeepEqual(got, want) {
  494. t.Errorf("Node.UnmarshalJSON() = %v, want %v", got, want)
  495. }
  496. }
  497. func casesNodeUnmarshalJSONUpField() []nodeUnmarshalTestCase {
  498. return []nodeUnmarshalTestCase{
  499. {
  500. name: "empty json",
  501. marshaled: "{}",
  502. want: Node{
  503. up: false,
  504. },
  505. },
  506. {
  507. name: "a stopped node",
  508. marshaled: "{\"up\": false}",
  509. want: Node{
  510. up: false,
  511. },
  512. },
  513. {
  514. name: "a running node",
  515. marshaled: "{\"up\": true}",
  516. want: Node{
  517. up: true,
  518. },
  519. },
  520. {
  521. name: "invalid JSON value on valid key",
  522. marshaled: "{\"up\": foo}",
  523. wantErr: "invalid character",
  524. },
  525. {
  526. name: "invalid JSON key and value",
  527. marshaled: "{foo: bar}",
  528. wantErr: "invalid character",
  529. },
  530. {
  531. name: "bool value expected but got something else (string)",
  532. marshaled: "{\"up\": \"true\"}",
  533. wantErr: "cannot unmarshal string into Go struct",
  534. },
  535. }
  536. }
  537. func casesNodeUnmarshalJSONConfigField() []nodeUnmarshalTestCase {
  538. // Don't do a big fuss around testing, as adapters.NodeConfig should
  539. // handle it's own serialization. Just do a sanity check.
  540. return []nodeUnmarshalTestCase{
  541. {
  542. name: "Config field is omitted",
  543. marshaled: "{}",
  544. want: Node{
  545. Config: nil,
  546. },
  547. },
  548. {
  549. name: "Config field is nil",
  550. marshaled: "{\"config\": nil}",
  551. want: Node{
  552. Config: nil,
  553. },
  554. },
  555. {
  556. name: "a non default Config field",
  557. marshaled: "{\"config\":{\"name\":\"node_ecdd0\",\"port\":44665}}",
  558. want: Node{
  559. Config: &adapters.NodeConfig{
  560. Name: "node_ecdd0",
  561. Port: 44665,
  562. },
  563. },
  564. },
  565. }
  566. }