node_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. // Copyright 2015 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 discover
  17. import (
  18. "bytes"
  19. "fmt"
  20. "math/big"
  21. "math/rand"
  22. "net"
  23. "reflect"
  24. "strings"
  25. "testing"
  26. "testing/quick"
  27. "time"
  28. "github.com/ethereum/go-ethereum/common"
  29. "github.com/ethereum/go-ethereum/crypto"
  30. )
  31. func ExampleNewNode() {
  32. id := MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439")
  33. // Complete nodes contain UDP and TCP endpoints:
  34. n1 := NewNode(id, net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 52150, 30303)
  35. fmt.Println("n1:", n1)
  36. fmt.Println("n1.Incomplete() ->", n1.Incomplete())
  37. // An incomplete node can be created by passing zero values
  38. // for all parameters except id.
  39. n2 := NewNode(id, nil, 0, 0)
  40. fmt.Println("n2:", n2)
  41. fmt.Println("n2.Incomplete() ->", n2.Incomplete())
  42. // Output:
  43. // n1: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:30303?discport=52150
  44. // n1.Incomplete() -> false
  45. // n2: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439
  46. // n2.Incomplete() -> true
  47. }
  48. var parseNodeTests = []struct {
  49. rawurl string
  50. wantError string
  51. wantResult *Node
  52. }{
  53. {
  54. rawurl: "http://foobar",
  55. wantError: `invalid URL scheme, want "enode"`,
  56. },
  57. {
  58. rawurl: "enode://01010101@123.124.125.126:3",
  59. wantError: `invalid node ID (wrong length, want 128 hex chars)`,
  60. },
  61. // Complete nodes with IP address.
  62. {
  63. rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3",
  64. wantError: `invalid IP address`,
  65. },
  66. {
  67. rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo",
  68. wantError: `invalid port`,
  69. },
  70. {
  71. rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo",
  72. wantError: `invalid discport in query`,
  73. },
  74. {
  75. rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
  76. wantResult: NewNode(
  77. MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
  78. net.IP{0x7f, 0x0, 0x0, 0x1},
  79. 52150,
  80. 52150,
  81. ),
  82. },
  83. {
  84. rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150",
  85. wantResult: NewNode(
  86. MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
  87. net.ParseIP("::"),
  88. 52150,
  89. 52150,
  90. ),
  91. },
  92. {
  93. rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150",
  94. wantResult: NewNode(
  95. MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
  96. net.ParseIP("2001:db8:3c4d:15::abcd:ef12"),
  97. 52150,
  98. 52150,
  99. ),
  100. },
  101. {
  102. rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334",
  103. wantResult: NewNode(
  104. MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
  105. net.IP{0x7f, 0x0, 0x0, 0x1},
  106. 22334,
  107. 52150,
  108. ),
  109. },
  110. // Incomplete nodes with no address.
  111. {
  112. rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
  113. wantResult: NewNode(
  114. MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
  115. nil, 0, 0,
  116. ),
  117. },
  118. {
  119. rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439",
  120. wantResult: NewNode(
  121. MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"),
  122. nil, 0, 0,
  123. ),
  124. },
  125. // Invalid URLs
  126. {
  127. rawurl: "01010101",
  128. wantError: `invalid node ID (wrong length, want 128 hex chars)`,
  129. },
  130. {
  131. rawurl: "enode://01010101",
  132. wantError: `invalid node ID (wrong length, want 128 hex chars)`,
  133. },
  134. {
  135. // This test checks that errors from url.Parse are handled.
  136. rawurl: "://foo",
  137. wantError: `parse ://foo: missing protocol scheme`,
  138. },
  139. }
  140. func TestParseNode(t *testing.T) {
  141. for _, test := range parseNodeTests {
  142. n, err := ParseNode(test.rawurl)
  143. if test.wantError != "" {
  144. if err == nil {
  145. t.Errorf("test %q:\n got nil error, expected %#q", test.rawurl, test.wantError)
  146. continue
  147. } else if err.Error() != test.wantError {
  148. t.Errorf("test %q:\n got error %#q, expected %#q", test.rawurl, err.Error(), test.wantError)
  149. continue
  150. }
  151. } else {
  152. if err != nil {
  153. t.Errorf("test %q:\n unexpected error: %v", test.rawurl, err)
  154. continue
  155. }
  156. if !reflect.DeepEqual(n, test.wantResult) {
  157. t.Errorf("test %q:\n result mismatch:\ngot: %#v, want: %#v", test.rawurl, n, test.wantResult)
  158. }
  159. }
  160. }
  161. }
  162. func TestNodeString(t *testing.T) {
  163. for i, test := range parseNodeTests {
  164. if test.wantError == "" && strings.HasPrefix(test.rawurl, "enode://") {
  165. str := test.wantResult.String()
  166. if str != test.rawurl {
  167. t.Errorf("test %d: Node.String() mismatch:\ngot: %s\nwant: %s", i, str, test.rawurl)
  168. }
  169. }
  170. }
  171. }
  172. func TestHexID(t *testing.T) {
  173. ref := NodeID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 106, 217, 182, 31, 165, 174, 1, 67, 7, 235, 220, 150, 66, 83, 173, 205, 159, 44, 10, 57, 42, 161, 26, 188}
  174. id1 := MustHexID("0x000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
  175. id2 := MustHexID("000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc")
  176. if id1 != ref {
  177. t.Errorf("wrong id1\ngot %v\nwant %v", id1[:], ref[:])
  178. }
  179. if id2 != ref {
  180. t.Errorf("wrong id2\ngot %v\nwant %v", id2[:], ref[:])
  181. }
  182. }
  183. func TestNodeID_textEncoding(t *testing.T) {
  184. ref := NodeID{
  185. 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
  186. 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
  187. 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30,
  188. 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40,
  189. 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50,
  190. 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60,
  191. 0x61, 0x62, 0x63, 0x64,
  192. }
  193. hex := "01020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364"
  194. text, err := ref.MarshalText()
  195. if err != nil {
  196. t.Fatal(err)
  197. }
  198. if !bytes.Equal(text, []byte(hex)) {
  199. t.Fatalf("text encoding did not match\nexpected: %s\ngot: %s", hex, text)
  200. }
  201. id := new(NodeID)
  202. if err := id.UnmarshalText(text); err != nil {
  203. t.Fatal(err)
  204. }
  205. if *id != ref {
  206. t.Fatalf("text decoding did not match\nexpected: %s\ngot: %s", ref, id)
  207. }
  208. }
  209. func TestNodeID_recover(t *testing.T) {
  210. prv := newkey()
  211. hash := make([]byte, 32)
  212. sig, err := crypto.Sign(hash, prv)
  213. if err != nil {
  214. t.Fatalf("signing error: %v", err)
  215. }
  216. pub := PubkeyID(&prv.PublicKey)
  217. recpub, err := recoverNodeID(hash, sig)
  218. if err != nil {
  219. t.Fatalf("recovery error: %v", err)
  220. }
  221. if pub != recpub {
  222. t.Errorf("recovered wrong pubkey:\ngot: %v\nwant: %v", recpub, pub)
  223. }
  224. ecdsa, err := pub.Pubkey()
  225. if err != nil {
  226. t.Errorf("Pubkey error: %v", err)
  227. }
  228. if !reflect.DeepEqual(ecdsa, &prv.PublicKey) {
  229. t.Errorf("Pubkey mismatch:\n got: %#v\n want: %#v", ecdsa, &prv.PublicKey)
  230. }
  231. }
  232. func TestNodeID_pubkeyBad(t *testing.T) {
  233. ecdsa, err := NodeID{}.Pubkey()
  234. if err == nil {
  235. t.Error("expected error for zero ID")
  236. }
  237. if ecdsa != nil {
  238. t.Error("expected nil result")
  239. }
  240. }
  241. func TestNodeID_distcmp(t *testing.T) {
  242. distcmpBig := func(target, a, b common.Hash) int {
  243. tbig := new(big.Int).SetBytes(target[:])
  244. abig := new(big.Int).SetBytes(a[:])
  245. bbig := new(big.Int).SetBytes(b[:])
  246. return new(big.Int).Xor(tbig, abig).Cmp(new(big.Int).Xor(tbig, bbig))
  247. }
  248. if err := quick.CheckEqual(distcmp, distcmpBig, quickcfg()); err != nil {
  249. t.Error(err)
  250. }
  251. }
  252. // the random tests is likely to miss the case where they're equal.
  253. func TestNodeID_distcmpEqual(t *testing.T) {
  254. base := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
  255. x := common.Hash{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}
  256. if distcmp(base, x, x) != 0 {
  257. t.Errorf("distcmp(base, x, x) != 0")
  258. }
  259. }
  260. func TestNodeID_logdist(t *testing.T) {
  261. logdistBig := func(a, b common.Hash) int {
  262. abig, bbig := new(big.Int).SetBytes(a[:]), new(big.Int).SetBytes(b[:])
  263. return new(big.Int).Xor(abig, bbig).BitLen()
  264. }
  265. if err := quick.CheckEqual(logdist, logdistBig, quickcfg()); err != nil {
  266. t.Error(err)
  267. }
  268. }
  269. // the random tests is likely to miss the case where they're equal.
  270. func TestNodeID_logdistEqual(t *testing.T) {
  271. x := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
  272. if logdist(x, x) != 0 {
  273. t.Errorf("logdist(x, x) != 0")
  274. }
  275. }
  276. func TestNodeID_hashAtDistance(t *testing.T) {
  277. // we don't use quick.Check here because its output isn't
  278. // very helpful when the test fails.
  279. cfg := quickcfg()
  280. for i := 0; i < cfg.MaxCount; i++ {
  281. a := gen(common.Hash{}, cfg.Rand).(common.Hash)
  282. dist := cfg.Rand.Intn(len(common.Hash{}) * 8)
  283. result := hashAtDistance(a, dist)
  284. actualdist := logdist(result, a)
  285. if dist != actualdist {
  286. t.Log("a: ", a)
  287. t.Log("result:", result)
  288. t.Fatalf("#%d: distance of result is %d, want %d", i, actualdist, dist)
  289. }
  290. }
  291. }
  292. func quickcfg() *quick.Config {
  293. return &quick.Config{
  294. MaxCount: 5000,
  295. Rand: rand.New(rand.NewSource(time.Now().Unix())),
  296. }
  297. }
  298. // TODO: The Generate method can be dropped when we require Go >= 1.5
  299. // because testing/quick learned to generate arrays in 1.5.
  300. func (NodeID) Generate(rand *rand.Rand, size int) reflect.Value {
  301. var id NodeID
  302. m := rand.Intn(len(id))
  303. for i := len(id) - 1; i > m; i-- {
  304. id[i] = byte(rand.Uint32())
  305. }
  306. return reflect.ValueOf(id)
  307. }