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