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 }