github.com/core-coin/go-core/v2@v2.1.9/cmd/gocore/les_test.go (about)

     1  // Copyright 2023 by the Authors
     2  // This file is part of go-core.
     3  //
     4  // go-core 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-core 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-core. 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/core-coin/go-core/v2/p2p"
    31  	"github.com/core-coin/go-core/v2/rpc"
    32  )
    33  
    34  type gocorerpc struct {
    35  	name     string
    36  	rpc      *rpc.Client
    37  	gocore   *testgocore
    38  	nodeInfo *p2p.NodeInfo
    39  }
    40  
    41  func (g *gocorerpc) killAndWait() {
    42  	g.gocore.Kill()
    43  	g.gocore.WaitExit()
    44  }
    45  
    46  func (g *gocorerpc) callRPC(result interface{}, method string, args ...interface{}) {
    47  	if err := g.rpc.Call(&result, method, args...); err != nil {
    48  		g.gocore.Fatalf("callRPC %v: %v", method, err)
    49  	}
    50  }
    51  
    52  func (g *gocorerpc) addPeer(peer *gocorerpc) {
    53  	g.gocore.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.gocore.Fatalf("subscribe %v: %v", g.name, err)
    59  	}
    60  	defer sub.Unsubscribe()
    61  	g.callRPC(nil, "admin_addPeer", enode)
    62  	dur := 30 * time.Second
    63  	timeout := time.After(dur)
    64  	select {
    65  	case ev := <-peerCh:
    66  		g.gocore.Logf("%v received event: type=%v, peer=%v", g.name, ev.Type, ev.Peer)
    67  	case err := <-sub.Err():
    68  		g.gocore.Fatalf("%v sub error: %v", g.name, err)
    69  	case <-timeout:
    70  		g.gocore.Error("timeout adding peer after", dur)
    71  	}
    72  }
    73  
    74  // Use this function instead of `g.nodeInfo` directly
    75  func (g *gocorerpc) 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  func (g *gocorerpc) waitSynced() {
    85  	// Check if it's synced now
    86  	var result interface{}
    87  	g.callRPC(&result, "xcb_syncing")
    88  	syncing, ok := result.(bool)
    89  	if ok && !syncing {
    90  		g.gocore.Logf("%v already synced", g.name)
    91  		return
    92  	}
    93  
    94  	// Actually wait, subscribe to the event
    95  	ch := make(chan interface{})
    96  	sub, err := g.rpc.Subscribe(context.Background(), "xcb", ch, "syncing")
    97  	if err != nil {
    98  		g.gocore.Fatalf("%v syncing: %v", g.name, err)
    99  	}
   100  	defer sub.Unsubscribe()
   101  	timeout := time.After(10 * time.Second)
   102  	select {
   103  	case ev := <-ch:
   104  		g.gocore.Log("'syncing' event", ev)
   105  		syncing, ok := ev.(bool)
   106  		if ok && !syncing {
   107  			break
   108  		}
   109  		g.gocore.Log("Other 'syncing' event", ev)
   110  	case err := <-sub.Err():
   111  		g.gocore.Fatalf("%v notification: %v", g.name, err)
   112  		break
   113  	case <-timeout:
   114  		g.gocore.Fatalf("%v timeout syncing", g.name)
   115  		break
   116  	}
   117  }
   118  
   119  // ipcEndpoint resolves an IPC endpoint based on a configured value, taking into
   120  // account the set data folders as well as the designated platform we're currently
   121  // running on.
   122  func ipcEndpoint(ipcPath, datadir string) string {
   123  	// On windows we can only use plain top-level pipes
   124  	if runtime.GOOS == "windows" {
   125  		if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
   126  			return ipcPath
   127  		}
   128  		return `\\.\pipe\` + ipcPath
   129  	}
   130  	// Resolve names into the data directory full paths otherwise
   131  	if filepath.Base(ipcPath) == ipcPath {
   132  		if datadir == "" {
   133  			return filepath.Join(os.TempDir(), ipcPath)
   134  		}
   135  		return filepath.Join(datadir, ipcPath)
   136  	}
   137  	return ipcPath
   138  }
   139  
   140  // nextIPC ensures that each ipc pipe gets a unique name.
   141  // On linux, it works well to use ipc pipes all over the filesystem (in datadirs),
   142  // but windows require pipes to sit in "\\.\pipe\". Therefore, to run several
   143  // nodes simultaneously, we need to distinguish between them, which we do by
   144  // the pipe filename instead of folder.
   145  var nextIPC = uint32(0)
   146  
   147  func startGocoreWithIpc(t *testing.T, name string, args ...string) *gocorerpc {
   148  	ipcName := fmt.Sprintf("gocore-%d.ipc", atomic.AddUint32(&nextIPC, 1))
   149  	args = append([]string{"--networkid=42", "--port=0", "--ipcpath", ipcName}, args...)
   150  	t.Logf("Starting %v with rpc: %v", name, args)
   151  
   152  	g := &gocorerpc{
   153  		name:   name,
   154  		gocore: runGocore(t, args...),
   155  	}
   156  	// wait before we can attach to it. TODO: probe for it properly
   157  	time.Sleep(10 * time.Second)
   158  	var err error
   159  	ipcpath := ipcEndpoint(ipcName, g.gocore.Datadir)
   160  	if g.rpc, err = rpc.Dial(ipcpath); err != nil {
   161  		t.Fatalf("%v rpc connect to %v: %v", name, ipcpath, err)
   162  	}
   163  	return g
   164  }
   165  
   166  func initGocore(t *testing.T) string {
   167  	args := []string{"--networkid=42", "init", "./testdata/clique.json"}
   168  	t.Logf("Initializing gocore: %v ", args)
   169  	g := runGocore(t, args...)
   170  	datadir := g.Datadir
   171  	g.WaitExit()
   172  	return datadir
   173  }
   174  
   175  func startLightServer(t *testing.T) *gocorerpc {
   176  	datadir := initGocore(t)
   177  	t.Logf("Importing keys to gocore")
   178  	runGocore(t, "--datadir", datadir, "--networkid=42", "--password", "./testdata/password.txt", "account", "import", "./testdata/key.prv", "--lightkdf").WaitExit()
   179  	account := "ce4404ad003b70526a4a1ac8ba121f2f96d95b6e11e0" // 9c0c5806dc0b007ed080762cadde92b3e4fefb9034f0a0e3e279a1d4139e38fd20364df61bc0eb34104e01764ae028470a73a167c6e624d443
   180  	server := startGocoreWithIpc(t, "lightserver", "--networkid=42", "--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=5")
   181  	return server
   182  }
   183  
   184  func startClient(t *testing.T, name string) *gocorerpc {
   185  	datadir := initGocore(t)
   186  	return startGocoreWithIpc(t, name, "--networkid=42", "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1", "--verbosity=5")
   187  }
   188  
   189  func TestPriorityClient(t *testing.T) {
   190  	t.Skip()
   191  	lightServer := startLightServer(t)
   192  	defer lightServer.killAndWait()
   193  
   194  	// Start client and add lightServer as peer
   195  	freeCli := startClient(t, "freeCli")
   196  	defer freeCli.killAndWait()
   197  	freeCli.addPeer(lightServer)
   198  
   199  	var peers []*p2p.PeerInfo
   200  	freeCli.callRPC(&peers, "admin_peers")
   201  	if len(peers) != 1 {
   202  		t.Errorf("Expected: # of client peers == 1, actual: %v", len(peers))
   203  		return
   204  	}
   205  
   206  	// Set up priority client, get its nodeID, increase its balance on the lightServer
   207  	prioCli := startClient(t, "prioCli")
   208  	defer prioCli.killAndWait()
   209  	// 3_000_000_000 once we move to Go 1.13
   210  	tokens := uint64(3000000000)
   211  	lightServer.callRPC(nil, "les_addBalance", prioCli.getNodeInfo().ID, tokens)
   212  	prioCli.addPeer(lightServer)
   213  
   214  	// Check if priority client is actually syncing and the regular client got kicked out
   215  	prioCli.callRPC(&peers, "admin_peers")
   216  	if len(peers) != 1 {
   217  		t.Errorf("Expected: # of prio peers == 1, actual: %v", len(peers))
   218  	}
   219  
   220  	nodes := map[string]*gocorerpc{
   221  		lightServer.getNodeInfo().ID: lightServer,
   222  		freeCli.getNodeInfo().ID:     freeCli,
   223  		prioCli.getNodeInfo().ID:     prioCli,
   224  	}
   225  	time.Sleep(8 * time.Second)
   226  	lightServer.callRPC(&peers, "admin_peers")
   227  	peersWithNames := make(map[string]string)
   228  	for _, p := range peers {
   229  		peersWithNames[nodes[p.ID].name] = p.ID
   230  	}
   231  	if _, freeClientFound := peersWithNames[freeCli.name]; freeClientFound {
   232  		t.Error("client is still a peer of lightServer", peersWithNames)
   233  	}
   234  	if _, prioClientFound := peersWithNames[prioCli.name]; !prioClientFound {
   235  		t.Error("prio client is not among lightServer peers", peersWithNames)
   236  	}
   237  }