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