nodedb_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. // Copyright 2018 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 enode
  17. import (
  18. "bytes"
  19. "fmt"
  20. "io/ioutil"
  21. "net"
  22. "os"
  23. "path/filepath"
  24. "reflect"
  25. "testing"
  26. "time"
  27. )
  28. var keytestID = HexID("51232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
  29. func TestDBNodeKey(t *testing.T) {
  30. enc := nodeKey(keytestID)
  31. want := []byte{
  32. 'n', ':',
  33. 0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // node id
  34. 0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, //
  35. 0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, //
  36. 0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, //
  37. ':', 'v', '4',
  38. }
  39. if !bytes.Equal(enc, want) {
  40. t.Errorf("wrong encoded key:\ngot %q\nwant %q", enc, want)
  41. }
  42. id, _ := splitNodeKey(enc)
  43. if id != keytestID {
  44. t.Errorf("wrong ID from splitNodeKey")
  45. }
  46. }
  47. func TestDBNodeItemKey(t *testing.T) {
  48. wantIP := net.IP{127, 0, 0, 3}
  49. wantField := "foobar"
  50. enc := nodeItemKey(keytestID, wantIP, wantField)
  51. want := []byte{
  52. 'n', ':',
  53. 0x51, 0x23, 0x2b, 0x8d, 0x78, 0x21, 0x61, 0x7d, // node id
  54. 0x2b, 0x29, 0xb5, 0x4b, 0x81, 0xcd, 0xef, 0xb9, //
  55. 0xb3, 0xe9, 0xc3, 0x7d, 0x7f, 0xd5, 0xf6, 0x32, //
  56. 0x70, 0xbc, 0xc9, 0xe1, 0xa6, 0xf6, 0xa4, 0x39, //
  57. ':', 'v', '4', ':',
  58. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IP
  59. 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x03, //
  60. ':', 'f', 'o', 'o', 'b', 'a', 'r',
  61. }
  62. if !bytes.Equal(enc, want) {
  63. t.Errorf("wrong encoded key:\ngot %q\nwant %q", enc, want)
  64. }
  65. id, ip, field := splitNodeItemKey(enc)
  66. if id != keytestID {
  67. t.Errorf("splitNodeItemKey returned wrong ID: %v", id)
  68. }
  69. if !ip.Equal(wantIP) {
  70. t.Errorf("splitNodeItemKey returned wrong IP: %v", ip)
  71. }
  72. if field != wantField {
  73. t.Errorf("splitNodeItemKey returned wrong field: %q", field)
  74. }
  75. }
  76. var nodeDBInt64Tests = []struct {
  77. key []byte
  78. value int64
  79. }{
  80. {key: []byte{0x01}, value: 1},
  81. {key: []byte{0x02}, value: 2},
  82. {key: []byte{0x03}, value: 3},
  83. }
  84. func TestDBInt64(t *testing.T) {
  85. db, _ := OpenDB("")
  86. defer db.Close()
  87. tests := nodeDBInt64Tests
  88. for i := 0; i < len(tests); i++ {
  89. // Insert the next value
  90. if err := db.storeInt64(tests[i].key, tests[i].value); err != nil {
  91. t.Errorf("test %d: failed to store value: %v", i, err)
  92. }
  93. // Check all existing and non existing values
  94. for j := 0; j < len(tests); j++ {
  95. num := db.fetchInt64(tests[j].key)
  96. switch {
  97. case j <= i && num != tests[j].value:
  98. t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, tests[j].value)
  99. case j > i && num != 0:
  100. t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, 0)
  101. }
  102. }
  103. }
  104. }
  105. func TestDBFetchStore(t *testing.T) {
  106. node := NewV4(
  107. hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
  108. net.IP{192, 168, 0, 1},
  109. 30303,
  110. 30303,
  111. )
  112. inst := time.Now()
  113. num := 314
  114. db, _ := OpenDB("")
  115. defer db.Close()
  116. // Check fetch/store operations on a node ping object
  117. if stored := db.LastPingReceived(node.ID(), node.IP()); stored.Unix() != 0 {
  118. t.Errorf("ping: non-existing object: %v", stored)
  119. }
  120. if err := db.UpdateLastPingReceived(node.ID(), node.IP(), inst); err != nil {
  121. t.Errorf("ping: failed to update: %v", err)
  122. }
  123. if stored := db.LastPingReceived(node.ID(), node.IP()); stored.Unix() != inst.Unix() {
  124. t.Errorf("ping: value mismatch: have %v, want %v", stored, inst)
  125. }
  126. // Check fetch/store operations on a node pong object
  127. if stored := db.LastPongReceived(node.ID(), node.IP()); stored.Unix() != 0 {
  128. t.Errorf("pong: non-existing object: %v", stored)
  129. }
  130. if err := db.UpdateLastPongReceived(node.ID(), node.IP(), inst); err != nil {
  131. t.Errorf("pong: failed to update: %v", err)
  132. }
  133. if stored := db.LastPongReceived(node.ID(), node.IP()); stored.Unix() != inst.Unix() {
  134. t.Errorf("pong: value mismatch: have %v, want %v", stored, inst)
  135. }
  136. // Check fetch/store operations on a node findnode-failure object
  137. if stored := db.FindFails(node.ID(), node.IP()); stored != 0 {
  138. t.Errorf("find-node fails: non-existing object: %v", stored)
  139. }
  140. if err := db.UpdateFindFails(node.ID(), node.IP(), num); err != nil {
  141. t.Errorf("find-node fails: failed to update: %v", err)
  142. }
  143. if stored := db.FindFails(node.ID(), node.IP()); stored != num {
  144. t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num)
  145. }
  146. // Check fetch/store operations on an actual node object
  147. if stored := db.Node(node.ID()); stored != nil {
  148. t.Errorf("node: non-existing object: %v", stored)
  149. }
  150. if err := db.UpdateNode(node); err != nil {
  151. t.Errorf("node: failed to update: %v", err)
  152. }
  153. if stored := db.Node(node.ID()); stored == nil {
  154. t.Errorf("node: not found")
  155. } else if !reflect.DeepEqual(stored, node) {
  156. t.Errorf("node: data mismatch: have %v, want %v", stored, node)
  157. }
  158. }
  159. var nodeDBSeedQueryNodes = []struct {
  160. node *Node
  161. pong time.Time
  162. }{
  163. // This one should not be in the result set because its last
  164. // pong time is too far in the past.
  165. {
  166. node: NewV4(
  167. hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
  168. net.IP{127, 0, 0, 3},
  169. 30303,
  170. 30303,
  171. ),
  172. pong: time.Now().Add(-3 * time.Hour),
  173. },
  174. // This one shouldn't be in the result set because its
  175. // nodeID is the local node's ID.
  176. {
  177. node: NewV4(
  178. hexPubkey("ff93ff820abacd4351b0f14e47b324bc82ff014c226f3f66a53535734a3c150e7e38ca03ef0964ba55acddc768f5e99cd59dea95ddd4defbab1339c92fa319b2"),
  179. net.IP{127, 0, 0, 3},
  180. 30303,
  181. 30303,
  182. ),
  183. pong: time.Now().Add(-4 * time.Second),
  184. },
  185. // These should be in the result set.
  186. {
  187. node: NewV4(
  188. hexPubkey("c2b5eb3f5dde05f815b63777809ee3e7e0cbb20035a6b00ce327191e6eaa8f26a8d461c9112b7ab94698e7361fa19fd647e603e73239002946d76085b6f928d6"),
  189. net.IP{127, 0, 0, 1},
  190. 30303,
  191. 30303,
  192. ),
  193. pong: time.Now().Add(-2 * time.Second),
  194. },
  195. {
  196. node: NewV4(
  197. hexPubkey("6ca1d400c8ddf8acc94bcb0dd254911ad71a57bed5e0ae5aa205beed59b28c2339908e97990c493499613cff8ecf6c3dc7112a8ead220cdcd00d8847ca3db755"),
  198. net.IP{127, 0, 0, 2},
  199. 30303,
  200. 30303,
  201. ),
  202. pong: time.Now().Add(-3 * time.Second),
  203. },
  204. {
  205. node: NewV4(
  206. hexPubkey("234dc63fe4d131212b38236c4c3411288d7bec61cbf7b120ff12c43dc60c96182882f4291d209db66f8a38e986c9c010ff59231a67f9515c7d1668b86b221a47"),
  207. net.IP{127, 0, 0, 3},
  208. 30303,
  209. 30303,
  210. ),
  211. pong: time.Now().Add(-1 * time.Second),
  212. },
  213. {
  214. node: NewV4(
  215. hexPubkey("c013a50b4d1ebce5c377d8af8cb7114fd933ffc9627f96ad56d90fef5b7253ec736fd07ef9a81dc2955a997e54b7bf50afd0aa9f110595e2bec5bb7ce1657004"),
  216. net.IP{127, 0, 0, 3},
  217. 30303,
  218. 30303,
  219. ),
  220. pong: time.Now().Add(-2 * time.Second),
  221. },
  222. {
  223. node: NewV4(
  224. hexPubkey("f141087e3e08af1aeec261ff75f48b5b1637f594ea9ad670e50051646b0416daa3b134c28788cbe98af26992a47652889cd8577ccc108ac02c6a664db2dc1283"),
  225. net.IP{127, 0, 0, 3},
  226. 30303,
  227. 30303,
  228. ),
  229. pong: time.Now().Add(-2 * time.Second),
  230. },
  231. }
  232. func TestDBSeedQuery(t *testing.T) {
  233. // Querying seeds uses seeks an might not find all nodes
  234. // every time when the database is small. Run the test multiple
  235. // times to avoid flakes.
  236. const attempts = 15
  237. var err error
  238. for i := 0; i < attempts; i++ {
  239. if err = testSeedQuery(); err == nil {
  240. return
  241. }
  242. }
  243. if err != nil {
  244. t.Errorf("no successful run in %d attempts: %v", attempts, err)
  245. }
  246. }
  247. func testSeedQuery() error {
  248. db, _ := OpenDB("")
  249. defer db.Close()
  250. // Insert a batch of nodes for querying
  251. for i, seed := range nodeDBSeedQueryNodes {
  252. if err := db.UpdateNode(seed.node); err != nil {
  253. return fmt.Errorf("node %d: failed to insert: %v", i, err)
  254. }
  255. if err := db.UpdateLastPongReceived(seed.node.ID(), seed.node.IP(), seed.pong); err != nil {
  256. return fmt.Errorf("node %d: failed to insert bondTime: %v", i, err)
  257. }
  258. }
  259. // Retrieve the entire batch and check for duplicates
  260. seeds := db.QuerySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour)
  261. have := make(map[ID]struct{})
  262. for _, seed := range seeds {
  263. have[seed.ID()] = struct{}{}
  264. }
  265. want := make(map[ID]struct{})
  266. for _, seed := range nodeDBSeedQueryNodes[1:] {
  267. want[seed.node.ID()] = struct{}{}
  268. }
  269. if len(seeds) != len(want) {
  270. return fmt.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want))
  271. }
  272. for id := range have {
  273. if _, ok := want[id]; !ok {
  274. return fmt.Errorf("extra seed: %v", id)
  275. }
  276. }
  277. for id := range want {
  278. if _, ok := have[id]; !ok {
  279. return fmt.Errorf("missing seed: %v", id)
  280. }
  281. }
  282. return nil
  283. }
  284. func TestDBPersistency(t *testing.T) {
  285. root, err := ioutil.TempDir("", "nodedb-")
  286. if err != nil {
  287. t.Fatalf("failed to create temporary data folder: %v", err)
  288. }
  289. defer os.RemoveAll(root)
  290. var (
  291. testKey = []byte("somekey")
  292. testInt = int64(314)
  293. )
  294. // Create a persistent database and store some values
  295. db, err := OpenDB(filepath.Join(root, "database"))
  296. if err != nil {
  297. t.Fatalf("failed to create persistent database: %v", err)
  298. }
  299. if err := db.storeInt64(testKey, testInt); err != nil {
  300. t.Fatalf("failed to store value: %v.", err)
  301. }
  302. db.Close()
  303. // Reopen the database and check the value
  304. db, err = OpenDB(filepath.Join(root, "database"))
  305. if err != nil {
  306. t.Fatalf("failed to open persistent database: %v", err)
  307. }
  308. if val := db.fetchInt64(testKey); val != testInt {
  309. t.Fatalf("value mismatch: have %v, want %v", val, testInt)
  310. }
  311. db.Close()
  312. }
  313. var nodeDBExpirationNodes = []struct {
  314. node *Node
  315. pong time.Time
  316. storeNode bool
  317. exp bool
  318. }{
  319. // Node has new enough pong time and isn't expired:
  320. {
  321. node: NewV4(
  322. hexPubkey("8d110e2ed4b446d9b5fb50f117e5f37fb7597af455e1dab0e6f045a6eeaa786a6781141659020d38bdc5e698ed3d4d2bafa8b5061810dfa63e8ac038db2e9b67"),
  323. net.IP{127, 0, 0, 1},
  324. 30303,
  325. 30303,
  326. ),
  327. storeNode: true,
  328. pong: time.Now().Add(-dbNodeExpiration + time.Minute),
  329. exp: false,
  330. },
  331. // Node with pong time before expiration is removed:
  332. {
  333. node: NewV4(
  334. hexPubkey("913a205579c32425b220dfba999d215066e5bdbf900226b11da1907eae5e93eb40616d47412cf819664e9eacbdfcca6b0c6e07e09847a38472d4be46ab0c3672"),
  335. net.IP{127, 0, 0, 2},
  336. 30303,
  337. 30303,
  338. ),
  339. storeNode: true,
  340. pong: time.Now().Add(-dbNodeExpiration - time.Minute),
  341. exp: true,
  342. },
  343. // Just pong time, no node stored:
  344. {
  345. node: NewV4(
  346. hexPubkey("b56670e0b6bad2c5dab9f9fe6f061a16cf78d68b6ae2cfda3144262d08d97ce5f46fd8799b6d1f709b1abe718f2863e224488bd7518e5e3b43809ac9bd1138ca"),
  347. net.IP{127, 0, 0, 3},
  348. 30303,
  349. 30303,
  350. ),
  351. storeNode: false,
  352. pong: time.Now().Add(-dbNodeExpiration - time.Minute),
  353. exp: true,
  354. },
  355. // Node with multiple pong times, all older than expiration.
  356. {
  357. node: NewV4(
  358. hexPubkey("29f619cebfd32c9eab34aec797ed5e3fe15b9b45be95b4df3f5fe6a9ae892f433eb08d7698b2ef3621568b0fb70d57b515ab30d4e72583b798298e0f0a66b9d1"),
  359. net.IP{127, 0, 0, 4},
  360. 30303,
  361. 30303,
  362. ),
  363. storeNode: true,
  364. pong: time.Now().Add(-dbNodeExpiration - time.Minute),
  365. exp: true,
  366. },
  367. {
  368. node: NewV4(
  369. hexPubkey("29f619cebfd32c9eab34aec797ed5e3fe15b9b45be95b4df3f5fe6a9ae892f433eb08d7698b2ef3621568b0fb70d57b515ab30d4e72583b798298e0f0a66b9d1"),
  370. net.IP{127, 0, 0, 5},
  371. 30303,
  372. 30303,
  373. ),
  374. storeNode: false,
  375. pong: time.Now().Add(-dbNodeExpiration - 2*time.Minute),
  376. exp: true,
  377. },
  378. // Node with multiple pong times, one newer, one older than expiration.
  379. {
  380. node: NewV4(
  381. hexPubkey("3b73a9e5f4af6c4701c57c73cc8cfa0f4802840b24c11eba92aac3aef65644a3728b4b2aec8199f6d72bd66be2c65861c773129039bd47daa091ca90a6d4c857"),
  382. net.IP{127, 0, 0, 6},
  383. 30303,
  384. 30303,
  385. ),
  386. storeNode: true,
  387. pong: time.Now().Add(-dbNodeExpiration + time.Minute),
  388. exp: false,
  389. },
  390. {
  391. node: NewV4(
  392. hexPubkey("3b73a9e5f4af6c4701c57c73cc8cfa0f4802840b24c11eba92aac3aef65644a3728b4b2aec8199f6d72bd66be2c65861c773129039bd47daa091ca90a6d4c857"),
  393. net.IP{127, 0, 0, 7},
  394. 30303,
  395. 30303,
  396. ),
  397. storeNode: false,
  398. pong: time.Now().Add(-dbNodeExpiration - time.Minute),
  399. exp: true,
  400. },
  401. }
  402. func TestDBExpiration(t *testing.T) {
  403. db, _ := OpenDB("")
  404. defer db.Close()
  405. // Add all the test nodes and set their last pong time.
  406. for i, seed := range nodeDBExpirationNodes {
  407. if seed.storeNode {
  408. if err := db.UpdateNode(seed.node); err != nil {
  409. t.Fatalf("node %d: failed to insert: %v", i, err)
  410. }
  411. }
  412. if err := db.UpdateLastPongReceived(seed.node.ID(), seed.node.IP(), seed.pong); err != nil {
  413. t.Fatalf("node %d: failed to update bondTime: %v", i, err)
  414. }
  415. }
  416. db.expireNodes()
  417. // Check that expired entries have been removed.
  418. unixZeroTime := time.Unix(0, 0)
  419. for i, seed := range nodeDBExpirationNodes {
  420. node := db.Node(seed.node.ID())
  421. pong := db.LastPongReceived(seed.node.ID(), seed.node.IP())
  422. if seed.exp {
  423. if seed.storeNode && node != nil {
  424. t.Errorf("node %d (%s) shouldn't be present after expiration", i, seed.node.ID().TerminalString())
  425. }
  426. if !pong.Equal(unixZeroTime) {
  427. t.Errorf("pong time %d (%s %v) shouldn't be present after expiration", i, seed.node.ID().TerminalString(), seed.node.IP())
  428. }
  429. } else {
  430. if seed.storeNode && node == nil {
  431. t.Errorf("node %d (%s) should be present after expiration", i, seed.node.ID().TerminalString())
  432. }
  433. if !pong.Equal(seed.pong.Truncate(1 * time.Second)) {
  434. t.Errorf("pong time %d (%s) should be %v after expiration, but is %v", i, seed.node.ID().TerminalString(), seed.pong, pong)
  435. }
  436. }
  437. }
  438. }
  439. // This test checks that expiration works when discovery v5 data is present
  440. // in the database.
  441. func TestDBExpireV5(t *testing.T) {
  442. db, _ := OpenDB("")
  443. defer db.Close()
  444. ip := net.IP{127, 0, 0, 1}
  445. db.UpdateFindFailsV5(ID{}, ip, 4)
  446. db.expireNodes()
  447. }