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  }