github.com/carter-ya/go-ethereum@v0.0.0-20230628080049-d2309be3983b/cmd/geth/les_test.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU 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  // go-ethereum 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 General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"os"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strings"
    26  	"sync/atomic"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/ethereum/go-ethereum/p2p"
    31  	"github.com/ethereum/go-ethereum/rpc"
    32  )
    33  
    34  type gethrpc struct {
    35  	name     string
    36  	rpc      *rpc.Client
    37  	geth     *testgeth
    38  	nodeInfo *p2p.NodeInfo
    39  }
    40  
    41  func (g *gethrpc) killAndWait() {
    42  	g.geth.Kill()
    43  	g.geth.WaitExit()
    44  }
    45  
    46  func (g *gethrpc) callRPC(result interface{}, method string, args ...interface{}) {
    47  	if err := g.rpc.Call(&result, method, args...); err != nil {
    48  		g.geth.Fatalf("callRPC %v: %v", method, err)
    49  	}
    50  }
    51  
    52  func (g *gethrpc) addPeer(peer *gethrpc) {
    53  	g.geth.Logf("%v.addPeer(%v)", g.name, peer.name)
    54  	enode := peer.getNodeInfo().Enode
    55  	peerCh := make(chan *p2p.PeerEvent)
    56  	sub, err := g.rpc.Subscribe(context.Background(), "admin", peerCh, "peerEvents")
    57  	if err != nil {
    58  		g.geth.Fatalf("subscribe %v: %v", g.name, err)
    59  	}
    60  	defer sub.Unsubscribe()
    61  	g.callRPC(nil, "admin_addPeer", enode)
    62  	dur := 14 * time.Second
    63  	timeout := time.After(dur)
    64  	select {
    65  	case ev := <-peerCh:
    66  		g.geth.Logf("%v received event: type=%v, peer=%v", g.name, ev.Type, ev.Peer)
    67  	case err := <-sub.Err():
    68  		g.geth.Fatalf("%v sub error: %v", g.name, err)
    69  	case <-timeout:
    70  		g.geth.Error("timeout adding peer after", dur)
    71  	}
    72  }
    73  
    74  // Use this function instead of `g.nodeInfo` directly
    75  func (g *gethrpc) getNodeInfo() *p2p.NodeInfo {
    76  	if g.nodeInfo != nil {
    77  		return g.nodeInfo
    78  	}
    79  	g.nodeInfo = &p2p.NodeInfo{}
    80  	g.callRPC(&g.nodeInfo, "admin_nodeInfo")
    81  	return g.nodeInfo
    82  }
    83  
    84  // ipcEndpoint resolves an IPC endpoint based on a configured value, taking into
    85  // account the set data folders as well as the designated platform we're currently
    86  // running on.
    87  func ipcEndpoint(ipcPath, datadir string) string {
    88  	// On windows we can only use plain top-level pipes
    89  	if runtime.GOOS == "windows" {
    90  		if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
    91  			return ipcPath
    92  		}
    93  		return `\\.\pipe\` + ipcPath
    94  	}
    95  	// Resolve names into the data directory full paths otherwise
    96  	if filepath.Base(ipcPath) == ipcPath {
    97  		if datadir == "" {
    98  			return filepath.Join(os.TempDir(), ipcPath)
    99  		}
   100  		return filepath.Join(datadir, ipcPath)
   101  	}
   102  	return ipcPath
   103  }
   104  
   105  // nextIPC ensures that each ipc pipe gets a unique name.
   106  // On linux, it works well to use ipc pipes all over the filesystem (in datadirs),
   107  // but windows require pipes to sit in "\\.\pipe\". Therefore, to run several
   108  // nodes simultaneously, we need to distinguish between them, which we do by
   109  // the pipe filename instead of folder.
   110  var nextIPC = uint32(0)
   111  
   112  func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
   113  	ipcName := fmt.Sprintf("geth-%d.ipc", atomic.AddUint32(&nextIPC, 1))
   114  	args = append([]string{"--networkid=42", "--port=0", "--authrpc.port", "0", "--ipcpath", ipcName}, args...)
   115  	t.Logf("Starting %v with rpc: %v", name, args)
   116  
   117  	g := &gethrpc{
   118  		name: name,
   119  		geth: runGeth(t, args...),
   120  	}
   121  	ipcpath := ipcEndpoint(ipcName, g.geth.Datadir)
   122  	// We can't know exactly how long geth will take to start, so we try 10
   123  	// times over a 5 second period.
   124  	var err error
   125  	for i := 0; i < 10; i++ {
   126  		time.Sleep(500 * time.Millisecond)
   127  		if g.rpc, err = rpc.Dial(ipcpath); err == nil {
   128  			return g
   129  		}
   130  	}
   131  	t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
   132  	return nil
   133  }
   134  
   135  func initGeth(t *testing.T) string {
   136  	args := []string{"--networkid=42", "init", "./testdata/clique.json"}
   137  	t.Logf("Initializing geth: %v ", args)
   138  	g := runGeth(t, args...)
   139  	datadir := g.Datadir
   140  	g.WaitExit()
   141  	return datadir
   142  }
   143  
   144  func startLightServer(t *testing.T) *gethrpc {
   145  	datadir := initGeth(t)
   146  	t.Logf("Importing keys to geth")
   147  	runGeth(t, "account", "import", "--datadir", datadir, "--password", "./testdata/password.txt", "--lightkdf", "./testdata/key.prv").WaitExit()
   148  	account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
   149  	server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1", "--verbosity=4")
   150  	return server
   151  }
   152  
   153  func startClient(t *testing.T, name string) *gethrpc {
   154  	datadir := initGeth(t)
   155  	return startGethWithIpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1", "--verbosity=4")
   156  }
   157  
   158  func TestPriorityClient(t *testing.T) {
   159  	lightServer := startLightServer(t)
   160  	defer lightServer.killAndWait()
   161  
   162  	// Start client and add lightServer as peer
   163  	freeCli := startClient(t, "freeCli")
   164  	defer freeCli.killAndWait()
   165  	freeCli.addPeer(lightServer)
   166  
   167  	var peers []*p2p.PeerInfo
   168  	freeCli.callRPC(&peers, "admin_peers")
   169  	if len(peers) != 1 {
   170  		t.Errorf("Expected: # of client peers == 1, actual: %v", len(peers))
   171  		return
   172  	}
   173  
   174  	// Set up priority client, get its nodeID, increase its balance on the lightServer
   175  	prioCli := startClient(t, "prioCli")
   176  	defer prioCli.killAndWait()
   177  	// 3_000_000_000 once we move to Go 1.13
   178  	tokens := uint64(3000000000)
   179  	lightServer.callRPC(nil, "les_addBalance", prioCli.getNodeInfo().ID, tokens)
   180  	prioCli.addPeer(lightServer)
   181  
   182  	// Check if priority client is actually syncing and the regular client got kicked out
   183  	prioCli.callRPC(&peers, "admin_peers")
   184  	if len(peers) != 1 {
   185  		t.Errorf("Expected: # of prio peers == 1, actual: %v", len(peers))
   186  	}
   187  
   188  	nodes := map[string]*gethrpc{
   189  		lightServer.getNodeInfo().ID: lightServer,
   190  		freeCli.getNodeInfo().ID:     freeCli,
   191  		prioCli.getNodeInfo().ID:     prioCli,
   192  	}
   193  	time.Sleep(1 * time.Second)
   194  	lightServer.callRPC(&peers, "admin_peers")
   195  	peersWithNames := make(map[string]string)
   196  	for _, p := range peers {
   197  		peersWithNames[nodes[p.ID].name] = p.ID
   198  	}
   199  	if _, freeClientFound := peersWithNames[freeCli.name]; freeClientFound {
   200  		t.Error("client is still a peer of lightServer", peersWithNames)
   201  	}
   202  	if _, prioClientFound := peersWithNames[prioCli.name]; !prioClientFound {
   203  		t.Error("prio client is not among lightServer peers", peersWithNames)
   204  	}
   205  }