github.com/snowblossomcoin/go-ethereum@v1.9.25/cmd/geth/les_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "path/filepath" 6 "testing" 7 "time" 8 9 "github.com/ethereum/go-ethereum/p2p" 10 "github.com/ethereum/go-ethereum/rpc" 11 ) 12 13 type gethrpc struct { 14 name string 15 rpc *rpc.Client 16 geth *testgeth 17 nodeInfo *p2p.NodeInfo 18 } 19 20 func (g *gethrpc) killAndWait() { 21 g.geth.Kill() 22 g.geth.WaitExit() 23 } 24 25 func (g *gethrpc) callRPC(result interface{}, method string, args ...interface{}) { 26 if err := g.rpc.Call(&result, method, args...); err != nil { 27 g.geth.Fatalf("callRPC %v: %v", method, err) 28 } 29 } 30 31 func (g *gethrpc) addPeer(peer *gethrpc) { 32 g.geth.Logf("%v.addPeer(%v)", g.name, peer.name) 33 enode := peer.getNodeInfo().Enode 34 peerCh := make(chan *p2p.PeerEvent) 35 sub, err := g.rpc.Subscribe(context.Background(), "admin", peerCh, "peerEvents") 36 if err != nil { 37 g.geth.Fatalf("subscribe %v: %v", g.name, err) 38 } 39 defer sub.Unsubscribe() 40 g.callRPC(nil, "admin_addPeer", enode) 41 dur := 14 * time.Second 42 timeout := time.After(dur) 43 select { 44 case ev := <-peerCh: 45 g.geth.Logf("%v received event: type=%v, peer=%v", g.name, ev.Type, ev.Peer) 46 case err := <-sub.Err(): 47 g.geth.Fatalf("%v sub error: %v", g.name, err) 48 case <-timeout: 49 g.geth.Error("timeout adding peer after", dur) 50 } 51 } 52 53 // Use this function instead of `g.nodeInfo` directly 54 func (g *gethrpc) getNodeInfo() *p2p.NodeInfo { 55 if g.nodeInfo != nil { 56 return g.nodeInfo 57 } 58 g.nodeInfo = &p2p.NodeInfo{} 59 g.callRPC(&g.nodeInfo, "admin_nodeInfo") 60 return g.nodeInfo 61 } 62 63 func (g *gethrpc) waitSynced() { 64 // Check if it's synced now 65 var result interface{} 66 g.callRPC(&result, "eth_syncing") 67 syncing, ok := result.(bool) 68 if ok && !syncing { 69 g.geth.Logf("%v already synced", g.name) 70 return 71 } 72 73 // Actually wait, subscribe to the event 74 ch := make(chan interface{}) 75 sub, err := g.rpc.Subscribe(context.Background(), "eth", ch, "syncing") 76 if err != nil { 77 g.geth.Fatalf("%v syncing: %v", g.name, err) 78 } 79 defer sub.Unsubscribe() 80 timeout := time.After(4 * time.Second) 81 select { 82 case ev := <-ch: 83 g.geth.Log("'syncing' event", ev) 84 syncing, ok := ev.(bool) 85 if ok && !syncing { 86 break 87 } 88 g.geth.Log("Other 'syncing' event", ev) 89 case err := <-sub.Err(): 90 g.geth.Fatalf("%v notification: %v", g.name, err) 91 break 92 case <-timeout: 93 g.geth.Fatalf("%v timeout syncing", g.name) 94 break 95 } 96 } 97 98 func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc { 99 g := &gethrpc{name: name} 100 args = append([]string{"--networkid=42", "--port=0", "--nousb"}, args...) 101 t.Logf("Starting %v with rpc: %v", name, args) 102 g.geth = runGeth(t, args...) 103 // wait before we can attach to it. TODO: probe for it properly 104 time.Sleep(1 * time.Second) 105 var err error 106 ipcpath := filepath.Join(g.geth.Datadir, "geth.ipc") 107 g.rpc, err = rpc.Dial(ipcpath) 108 if err != nil { 109 t.Fatalf("%v rpc connect: %v", name, err) 110 } 111 return g 112 } 113 114 func initGeth(t *testing.T) string { 115 g := runGeth(t, "--nousb", "--networkid=42", "init", "./testdata/clique.json") 116 datadir := g.Datadir 117 g.WaitExit() 118 return datadir 119 } 120 121 func startLightServer(t *testing.T) *gethrpc { 122 datadir := initGeth(t) 123 runGeth(t, "--nousb", "--datadir", datadir, "--password", "./testdata/password.txt", "account", "import", "./testdata/key.prv").WaitExit() 124 account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105" 125 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") 126 return server 127 } 128 129 func startClient(t *testing.T, name string) *gethrpc { 130 datadir := initGeth(t) 131 return startGethWithIpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1") 132 } 133 134 func TestPriorityClient(t *testing.T) { 135 lightServer := startLightServer(t) 136 defer lightServer.killAndWait() 137 138 // Start client and add lightServer as peer 139 freeCli := startClient(t, "freeCli") 140 defer freeCli.killAndWait() 141 freeCli.addPeer(lightServer) 142 143 var peers []*p2p.PeerInfo 144 freeCli.callRPC(&peers, "admin_peers") 145 if len(peers) != 1 { 146 t.Errorf("Expected: # of client peers == 1, actual: %v", len(peers)) 147 return 148 } 149 150 // Set up priority client, get its nodeID, increase its balance on the lightServer 151 prioCli := startClient(t, "prioCli") 152 defer prioCli.killAndWait() 153 // 3_000_000_000 once we move to Go 1.13 154 tokens := 3000000000 155 lightServer.callRPC(nil, "les_addBalance", prioCli.getNodeInfo().ID, tokens) 156 prioCli.addPeer(lightServer) 157 158 // Check if priority client is actually syncing and the regular client got kicked out 159 prioCli.callRPC(&peers, "admin_peers") 160 if len(peers) != 1 { 161 t.Errorf("Expected: # of prio peers == 1, actual: %v", len(peers)) 162 } 163 164 nodes := map[string]*gethrpc{ 165 lightServer.getNodeInfo().ID: lightServer, 166 freeCli.getNodeInfo().ID: freeCli, 167 prioCli.getNodeInfo().ID: prioCli, 168 } 169 time.Sleep(1 * time.Second) 170 lightServer.callRPC(&peers, "admin_peers") 171 peersWithNames := make(map[string]string) 172 for _, p := range peers { 173 peersWithNames[nodes[p.ID].name] = p.ID 174 } 175 if _, freeClientFound := peersWithNames[freeCli.name]; freeClientFound { 176 t.Error("client is still a peer of lightServer", peersWithNames) 177 } 178 if _, prioClientFound := peersWithNames[prioCli.name]; !prioClientFound { 179 t.Error("prio client is not among lightServer peers", peersWithNames) 180 } 181 }