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