github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/p2p/discv5/node_test.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package discv5 13 14 import ( 15 "fmt" 16 "math/big" 17 "math/rand" 18 "net" 19 "reflect" 20 "strings" 21 "testing" 22 "testing/quick" 23 "time" 24 25 "github.com/Sberex/go-sberex/common" 26 "github.com/Sberex/go-sberex/crypto" 27 ) 28 29 func ExampleNewNode() { 30 id := MustHexID("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439") 31 32 // Complete nodes contain UDP and TCP endpoints: 33 n1 := NewNode(id, net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 52150, 30303) 34 fmt.Println("n1:", n1) 35 fmt.Println("n1.Incomplete() ->", n1.Incomplete()) 36 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 43 // Output: 44 // n1: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:30303?discport=52150 45 // n1.Incomplete() -> false 46 // n2: enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439 47 // n2.Incomplete() -> true 48 } 49 50 var parseNodeTests = []struct { 51 rawurl string 52 wantError string 53 wantResult *Node 54 }{ 55 { 56 rawurl: "http://foobar", 57 wantError: `invalid URL scheme, want "enode"`, 58 }, 59 { 60 rawurl: "enode://01010101@123.124.125.126:3", 61 wantError: `invalid node ID (wrong length, want 128 hex chars)`, 62 }, 63 // Complete nodes with IP address. 64 { 65 rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3", 66 wantError: `invalid IP address`, 67 }, 68 { 69 rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo", 70 wantError: `invalid port`, 71 }, 72 { 73 rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo", 74 wantError: `invalid discport in query`, 75 }, 76 { 77 rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150", 78 wantResult: NewNode( 79 MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 80 net.IP{0x7f, 0x0, 0x0, 0x1}, 81 52150, 82 52150, 83 ), 84 }, 85 { 86 rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", 87 wantResult: NewNode( 88 MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 89 net.ParseIP("::"), 90 52150, 91 52150, 92 ), 93 }, 94 { 95 rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150", 96 wantResult: NewNode( 97 MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 98 net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), 99 52150, 100 52150, 101 ), 102 }, 103 { 104 rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334", 105 wantResult: NewNode( 106 MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 107 net.IP{0x7f, 0x0, 0x0, 0x1}, 108 22334, 109 52150, 110 ), 111 }, 112 // Incomplete nodes with no address. 113 { 114 rawurl: "1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", 115 wantResult: NewNode( 116 MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 117 nil, 0, 0, 118 ), 119 }, 120 { 121 rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", 122 wantResult: NewNode( 123 MustHexID("0x1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), 124 nil, 0, 0, 125 ), 126 }, 127 // Invalid URLs 128 { 129 rawurl: "01010101", 130 wantError: `invalid node ID (wrong length, want 128 hex chars)`, 131 }, 132 { 133 rawurl: "enode://01010101", 134 wantError: `invalid node ID (wrong length, want 128 hex chars)`, 135 }, 136 { 137 // This test checks that errors from url.Parse are handled. 138 rawurl: "://foo", 139 wantError: `parse ://foo: missing protocol scheme`, 140 }, 141 } 142 143 func TestParseNode(t *testing.T) { 144 for _, test := range parseNodeTests { 145 n, err := ParseNode(test.rawurl) 146 if test.wantError != "" { 147 if err == nil { 148 t.Errorf("test %q:\n got nil error, expected %#q", test.rawurl, test.wantError) 149 continue 150 } else if err.Error() != test.wantError { 151 t.Errorf("test %q:\n got error %#q, expected %#q", test.rawurl, err.Error(), test.wantError) 152 continue 153 } 154 } else { 155 if err != nil { 156 t.Errorf("test %q:\n unexpected error: %v", test.rawurl, err) 157 continue 158 } 159 if !reflect.DeepEqual(n, test.wantResult) { 160 t.Errorf("test %q:\n result mismatch:\ngot: %#v, want: %#v", test.rawurl, n, test.wantResult) 161 } 162 } 163 } 164 } 165 166 func TestNodeString(t *testing.T) { 167 for i, test := range parseNodeTests { 168 if test.wantError == "" && strings.HasPrefix(test.rawurl, "enode://") { 169 str := test.wantResult.String() 170 if str != test.rawurl { 171 t.Errorf("test %d: Node.String() mismatch:\ngot: %s\nwant: %s", i, str, test.rawurl) 172 } 173 } 174 } 175 } 176 177 func TestHexID(t *testing.T) { 178 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} 179 id1 := MustHexID("0x000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") 180 id2 := MustHexID("000000000000000000000000000000000000000000000000000000000000000000000000000000806ad9b61fa5ae014307ebdc964253adcd9f2c0a392aa11abc") 181 182 if id1 != ref { 183 t.Errorf("wrong id1\ngot %v\nwant %v", id1[:], ref[:]) 184 } 185 if id2 != ref { 186 t.Errorf("wrong id2\ngot %v\nwant %v", id2[:], ref[:]) 187 } 188 } 189 190 func TestNodeID_recover(t *testing.T) { 191 prv := newkey() 192 hash := make([]byte, 32) 193 sig, err := crypto.Sign(hash, prv) 194 if err != nil { 195 t.Fatalf("signing error: %v", err) 196 } 197 198 pub := PubkeyID(&prv.PublicKey) 199 recpub, err := recoverNodeID(hash, sig) 200 if err != nil { 201 t.Fatalf("recovery error: %v", err) 202 } 203 if pub != recpub { 204 t.Errorf("recovered wrong pubkey:\ngot: %v\nwant: %v", recpub, pub) 205 } 206 207 ecdsa, err := pub.Pubkey() 208 if err != nil { 209 t.Errorf("Pubkey error: %v", err) 210 } 211 if !reflect.DeepEqual(ecdsa, &prv.PublicKey) { 212 t.Errorf("Pubkey mismatch:\n got: %#v\n want: %#v", ecdsa, &prv.PublicKey) 213 } 214 } 215 216 func TestNodeID_pubkeyBad(t *testing.T) { 217 ecdsa, err := NodeID{}.Pubkey() 218 if err == nil { 219 t.Error("expected error for zero ID") 220 } 221 if ecdsa != nil { 222 t.Error("expected nil result") 223 } 224 } 225 226 func TestNodeID_distcmp(t *testing.T) { 227 distcmpBig := func(target, a, b common.Hash) int { 228 tbig := new(big.Int).SetBytes(target[:]) 229 abig := new(big.Int).SetBytes(a[:]) 230 bbig := new(big.Int).SetBytes(b[:]) 231 return new(big.Int).Xor(tbig, abig).Cmp(new(big.Int).Xor(tbig, bbig)) 232 } 233 if err := quick.CheckEqual(distcmp, distcmpBig, quickcfg()); err != nil { 234 t.Error(err) 235 } 236 } 237 238 // the random tests is likely to miss the case where they're equal. 239 func TestNodeID_distcmpEqual(t *testing.T) { 240 base := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} 241 x := common.Hash{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0} 242 if distcmp(base, x, x) != 0 { 243 t.Errorf("distcmp(base, x, x) != 0") 244 } 245 } 246 247 func TestNodeID_logdist(t *testing.T) { 248 logdistBig := func(a, b common.Hash) int { 249 abig, bbig := new(big.Int).SetBytes(a[:]), new(big.Int).SetBytes(b[:]) 250 return new(big.Int).Xor(abig, bbig).BitLen() 251 } 252 if err := quick.CheckEqual(logdist, logdistBig, 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_logdistEqual(t *testing.T) { 259 x := common.Hash{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} 260 if logdist(x, x) != 0 { 261 t.Errorf("logdist(x, x) != 0") 262 } 263 } 264 265 func TestNodeID_hashAtDistance(t *testing.T) { 266 // we don't use quick.Check here because its output isn't 267 // very helpful when the test fails. 268 cfg := quickcfg() 269 for i := 0; i < cfg.MaxCount; i++ { 270 a := gen(common.Hash{}, cfg.Rand).(common.Hash) 271 dist := cfg.Rand.Intn(len(common.Hash{}) * 8) 272 result := hashAtDistance(a, dist) 273 actualdist := logdist(result, a) 274 275 if dist != actualdist { 276 t.Log("a: ", a) 277 t.Log("result:", result) 278 t.Fatalf("#%d: distance of result is %d, want %d", i, actualdist, dist) 279 } 280 } 281 } 282 283 func quickcfg() *quick.Config { 284 return &quick.Config{ 285 MaxCount: 5000, 286 Rand: rand.New(rand.NewSource(time.Now().Unix())), 287 } 288 } 289 290 // TODO: The Generate method can be dropped when we require Go >= 1.5 291 // because testing/quick learned to generate arrays in 1.5. 292 293 func (NodeID) Generate(rand *rand.Rand, size int) reflect.Value { 294 var id NodeID 295 m := rand.Intn(len(id)) 296 for i := len(id) - 1; i > m; i-- { 297 id[i] = byte(rand.Uint32()) 298 } 299 return reflect.ValueOf(id) 300 }