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  }