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 }