You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

380 lines
11 KiB

  1. package p2p_test
  2. import (
  3. "net"
  4. "strings"
  5. "testing"
  6. "github.com/stretchr/testify/require"
  7. "github.com/tendermint/tendermint/crypto/ed25519"
  8. "github.com/tendermint/tendermint/internal/p2p"
  9. )
  10. func TestNewNodeID(t *testing.T) {
  11. // Most tests are in TestNodeID_Validate, this just checks that it's validated.
  12. testcases := []struct {
  13. input string
  14. expect p2p.NodeID
  15. ok bool
  16. }{
  17. {"", "", false},
  18. {"foo", "", false},
  19. {"00112233445566778899aabbccddeeff00112233", "00112233445566778899aabbccddeeff00112233", true},
  20. {"00112233445566778899AABBCCDDEEFF00112233", "00112233445566778899aabbccddeeff00112233", true},
  21. {"00112233445566778899aabbccddeeff0011223", "", false},
  22. {"00112233445566778899aabbccddeeff0011223g", "", false},
  23. }
  24. for _, tc := range testcases {
  25. tc := tc
  26. t.Run(tc.input, func(t *testing.T) {
  27. id, err := p2p.NewNodeID(tc.input)
  28. if !tc.ok {
  29. require.Error(t, err)
  30. } else {
  31. require.NoError(t, err)
  32. require.Equal(t, id, tc.expect)
  33. }
  34. })
  35. }
  36. }
  37. func TestNewNodeIDFromPubKey(t *testing.T) {
  38. privKey := ed25519.GenPrivKeyFromSecret([]byte("foo"))
  39. nodeID := p2p.NodeIDFromPubKey(privKey.PubKey())
  40. require.Equal(t, p2p.NodeID("045f5600654182cfeaccfe6cb19f0642e8a59898"), nodeID)
  41. require.NoError(t, nodeID.Validate())
  42. }
  43. func TestNodeID_Bytes(t *testing.T) {
  44. testcases := []struct {
  45. nodeID p2p.NodeID
  46. expect []byte
  47. ok bool
  48. }{
  49. {"", []byte{}, true},
  50. {"01f0", []byte{0x01, 0xf0}, true},
  51. {"01F0", []byte{0x01, 0xf0}, true},
  52. {"01F", nil, false},
  53. {"01g0", nil, false},
  54. }
  55. for _, tc := range testcases {
  56. tc := tc
  57. t.Run(string(tc.nodeID), func(t *testing.T) {
  58. bz, err := tc.nodeID.Bytes()
  59. if tc.ok {
  60. require.NoError(t, err)
  61. require.Equal(t, tc.expect, bz)
  62. } else {
  63. require.Error(t, err)
  64. }
  65. })
  66. }
  67. }
  68. func TestNodeID_Validate(t *testing.T) {
  69. testcases := []struct {
  70. nodeID p2p.NodeID
  71. ok bool
  72. }{
  73. {"", false},
  74. {"00", false},
  75. {"00112233445566778899aabbccddeeff00112233", true},
  76. {"00112233445566778899aabbccddeeff001122334", false},
  77. {"00112233445566778899aabbccddeeffgg001122", false},
  78. {"00112233445566778899AABBCCDDEEFF00112233", false},
  79. }
  80. for _, tc := range testcases {
  81. tc := tc
  82. t.Run(string(tc.nodeID), func(t *testing.T) {
  83. err := tc.nodeID.Validate()
  84. if tc.ok {
  85. require.NoError(t, err)
  86. } else {
  87. require.Error(t, err)
  88. }
  89. })
  90. }
  91. }
  92. func TestParseNodeAddress(t *testing.T) {
  93. user := "00112233445566778899aabbccddeeff00112233"
  94. id := p2p.NodeID(user)
  95. testcases := []struct {
  96. url string
  97. expect p2p.NodeAddress
  98. ok bool
  99. }{
  100. // Valid addresses.
  101. {
  102. "mconn://" + user + "@127.0.0.1:26657/some/path?foo=bar",
  103. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "127.0.0.1", Port: 26657, Path: "/some/path?foo=bar"},
  104. true,
  105. },
  106. {
  107. "TCP://" + strings.ToUpper(user) + "@hostname.DOMAIN:8080/Path/%f0%9f%91%8B#Anchor",
  108. p2p.NodeAddress{Protocol: "tcp", NodeID: id, Hostname: "hostname.domain", Port: 8080, Path: "/Path/👋#Anchor"},
  109. true,
  110. },
  111. {
  112. user + "@127.0.0.1",
  113. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "127.0.0.1"},
  114. true,
  115. },
  116. {
  117. user + "@hostname.domain",
  118. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "hostname.domain"},
  119. true,
  120. },
  121. {
  122. user + "@hostname.domain:80",
  123. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "hostname.domain", Port: 80},
  124. true,
  125. },
  126. {
  127. user + "@%F0%9F%91%8B",
  128. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "👋"},
  129. true,
  130. },
  131. {
  132. user + "@%F0%9F%91%8B:80/path",
  133. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "👋", Port: 80, Path: "/path"},
  134. true,
  135. },
  136. {
  137. user + "@127.0.0.1:26657",
  138. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "127.0.0.1", Port: 26657},
  139. true,
  140. },
  141. {
  142. user + "@127.0.0.1:26657/path",
  143. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "127.0.0.1", Port: 26657, Path: "/path"},
  144. true,
  145. },
  146. {
  147. user + "@0.0.0.0:0",
  148. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "0.0.0.0", Port: 0},
  149. true,
  150. },
  151. {
  152. "memory:" + user,
  153. p2p.NodeAddress{Protocol: "memory", NodeID: id},
  154. true,
  155. },
  156. {
  157. user + "@[1::]",
  158. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "1::"},
  159. true,
  160. },
  161. {
  162. "mconn://" + user + "@[fd80:b10c::2]:26657",
  163. p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "fd80:b10c::2", Port: 26657},
  164. true,
  165. },
  166. // Invalid addresses.
  167. {"", p2p.NodeAddress{}, false},
  168. {"127.0.0.1", p2p.NodeAddress{}, false},
  169. {"hostname", p2p.NodeAddress{}, false},
  170. {"scheme:", p2p.NodeAddress{}, false},
  171. {"memory:foo", p2p.NodeAddress{}, false},
  172. {user + "@%F%F0", p2p.NodeAddress{}, false},
  173. {"//" + user + "@127.0.0.1", p2p.NodeAddress{}, false},
  174. {"://" + user + "@127.0.0.1", p2p.NodeAddress{}, false},
  175. {"mconn://foo@127.0.0.1", p2p.NodeAddress{}, false},
  176. {"mconn://" + user + "@127.0.0.1:65536", p2p.NodeAddress{}, false},
  177. {"mconn://" + user + "@:80", p2p.NodeAddress{}, false},
  178. }
  179. for _, tc := range testcases {
  180. tc := tc
  181. t.Run(tc.url, func(t *testing.T) {
  182. address, err := p2p.ParseNodeAddress(tc.url)
  183. if !tc.ok {
  184. require.Error(t, err)
  185. } else {
  186. require.NoError(t, err)
  187. require.Equal(t, tc.expect, address)
  188. }
  189. })
  190. }
  191. }
  192. func TestNodeAddress_Resolve(t *testing.T) {
  193. id := p2p.NodeID("00112233445566778899aabbccddeeff00112233")
  194. testcases := []struct {
  195. address p2p.NodeAddress
  196. expect p2p.Endpoint
  197. ok bool
  198. }{
  199. // Valid networked addresses (with hostname).
  200. {
  201. p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1", Port: 80, Path: "/path"},
  202. p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
  203. true,
  204. },
  205. {
  206. p2p.NodeAddress{Protocol: "tcp", Hostname: "localhost", Port: 80, Path: "/path"},
  207. p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
  208. true,
  209. },
  210. {
  211. p2p.NodeAddress{Protocol: "tcp", Hostname: "localhost", Port: 80, Path: "/path"},
  212. p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback, Port: 80, Path: "/path"},
  213. true,
  214. },
  215. {
  216. p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1"},
  217. p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1)},
  218. true,
  219. },
  220. {
  221. p2p.NodeAddress{Protocol: "tcp", Hostname: "::1"},
  222. p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback},
  223. true,
  224. },
  225. {
  226. p2p.NodeAddress{Protocol: "tcp", Hostname: "8.8.8.8"},
  227. p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(8, 8, 8, 8)},
  228. true,
  229. },
  230. {
  231. p2p.NodeAddress{Protocol: "tcp", Hostname: "2001:0db8::ff00:0042:8329"},
  232. p2p.Endpoint{Protocol: "tcp", IP: []byte{
  233. 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29}},
  234. true,
  235. },
  236. {
  237. p2p.NodeAddress{Protocol: "tcp", Hostname: "some.missing.host.tendermint.com"},
  238. p2p.Endpoint{},
  239. false,
  240. },
  241. // Valid non-networked addresses.
  242. {
  243. p2p.NodeAddress{Protocol: "memory", NodeID: id},
  244. p2p.Endpoint{Protocol: "memory", Path: string(id)},
  245. true,
  246. },
  247. {
  248. p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: string(id)},
  249. p2p.Endpoint{Protocol: "memory", Path: string(id)},
  250. true,
  251. },
  252. // Invalid addresses.
  253. {p2p.NodeAddress{}, p2p.Endpoint{}, false},
  254. {p2p.NodeAddress{Hostname: "127.0.0.1"}, p2p.Endpoint{}, false},
  255. {p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1:80"}, p2p.Endpoint{}, false},
  256. {p2p.NodeAddress{Protocol: "memory"}, p2p.Endpoint{}, false},
  257. {p2p.NodeAddress{Protocol: "memory", Path: string(id)}, p2p.Endpoint{}, false},
  258. {p2p.NodeAddress{Protocol: "tcp", Hostname: "💥"}, p2p.Endpoint{}, false},
  259. }
  260. for _, tc := range testcases {
  261. tc := tc
  262. t.Run(tc.address.String(), func(t *testing.T) {
  263. endpoints, err := tc.address.Resolve(ctx)
  264. if !tc.ok {
  265. require.Error(t, err)
  266. return
  267. }
  268. require.Contains(t, endpoints, tc.expect)
  269. })
  270. }
  271. }
  272. func TestNodeAddress_String(t *testing.T) {
  273. id := p2p.NodeID("00112233445566778899aabbccddeeff00112233")
  274. user := string(id)
  275. testcases := []struct {
  276. address p2p.NodeAddress
  277. expect string
  278. }{
  279. // Valid networked addresses (with hostname).
  280. {
  281. p2p.NodeAddress{Protocol: "tcp", NodeID: id, Hostname: "host", Port: 80, Path: "/path/sub?foo=bar&x=y#anchor"},
  282. "tcp://" + user + "@host:80/path/sub%3Ffoo=bar&x=y%23anchor",
  283. },
  284. {
  285. p2p.NodeAddress{Protocol: "tcp", NodeID: id, Hostname: "host.domain"},
  286. "tcp://" + user + "@host.domain",
  287. },
  288. {
  289. p2p.NodeAddress{NodeID: id, Hostname: "host", Port: 80, Path: "foo/bar"},
  290. user + "@host:80/foo/bar",
  291. },
  292. // Valid non-networked addresses (without hostname).
  293. {
  294. p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: string(id)},
  295. "memory:" + user,
  296. },
  297. {
  298. p2p.NodeAddress{Protocol: "memory", NodeID: id},
  299. "memory:" + user,
  300. },
  301. // Addresses with weird contents, which are technically fine (not harmful).
  302. {
  303. p2p.NodeAddress{Protocol: "💬", NodeID: "👨", Hostname: "💻", Port: 80, Path: "🛣"},
  304. "💬://%F0%9F%91%A8@%F0%9F%92%BB:80/%F0%9F%9B%A3",
  305. },
  306. // Partial (invalid) addresses.
  307. {p2p.NodeAddress{}, ""},
  308. {p2p.NodeAddress{NodeID: id}, user + "@"},
  309. {p2p.NodeAddress{Protocol: "tcp"}, "tcp:"},
  310. {p2p.NodeAddress{Hostname: "host"}, "host"},
  311. {p2p.NodeAddress{Port: 80}, ""},
  312. {p2p.NodeAddress{Path: "path"}, "/path"},
  313. {p2p.NodeAddress{NodeID: id, Port: 80}, user + "@"},
  314. {p2p.NodeAddress{Protocol: "tcp", Hostname: "host"}, "tcp://host"},
  315. {
  316. p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: "path"},
  317. "memory://00112233445566778899aabbccddeeff00112233@/path",
  318. },
  319. {
  320. p2p.NodeAddress{Protocol: "memory", NodeID: id, Port: 80},
  321. "memory:00112233445566778899aabbccddeeff00112233",
  322. },
  323. }
  324. for _, tc := range testcases {
  325. tc := tc
  326. t.Run(tc.address.String(), func(t *testing.T) {
  327. require.Equal(t, tc.expect, tc.address.String())
  328. })
  329. }
  330. }
  331. func TestNodeAddress_Validate(t *testing.T) {
  332. id := p2p.NodeID("00112233445566778899aabbccddeeff00112233")
  333. testcases := []struct {
  334. address p2p.NodeAddress
  335. ok bool
  336. }{
  337. // Valid addresses.
  338. {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "host", Port: 80, Path: "/path"}, true},
  339. {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "host"}, true},
  340. {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Path: "path"}, true},
  341. {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Hostname: "👋", Path: "👋"}, true},
  342. // Invalid addresses.
  343. {p2p.NodeAddress{}, false},
  344. {p2p.NodeAddress{NodeID: "foo", Hostname: "host"}, false},
  345. {p2p.NodeAddress{Protocol: "mconn", NodeID: id}, true},
  346. {p2p.NodeAddress{Protocol: "mconn", NodeID: "foo", Hostname: "host"}, false},
  347. {p2p.NodeAddress{Protocol: "mconn", NodeID: id, Port: 80, Path: "path"}, false},
  348. }
  349. for _, tc := range testcases {
  350. tc := tc
  351. t.Run(tc.address.String(), func(t *testing.T) {
  352. err := tc.address.Validate()
  353. if tc.ok {
  354. require.NoError(t, err)
  355. } else {
  356. require.Error(t, err)
  357. }
  358. })
  359. }
  360. }