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