github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/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/neatio-net/neatio/utilities/common"
    16  	"github.com/neatio-net/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  }