github.com/MetalBlockchain/subnet-evm@v0.4.9/rpc/subscription_test.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2016 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package rpc 28 29 import ( 30 "encoding/json" 31 "fmt" 32 "net" 33 "strings" 34 "testing" 35 "time" 36 ) 37 38 func TestNewID(t *testing.T) { 39 hexchars := "0123456789ABCDEFabcdef" 40 for i := 0; i < 100; i++ { 41 id := string(NewID()) 42 if !strings.HasPrefix(id, "0x") { 43 t.Fatalf("invalid ID prefix, want '0x...', got %s", id) 44 } 45 46 id = id[2:] 47 if len(id) == 0 || len(id) > 32 { 48 t.Fatalf("invalid ID length, want len(id) > 0 && len(id) <= 32), got %d", len(id)) 49 } 50 51 for i := 0; i < len(id); i++ { 52 if strings.IndexByte(hexchars, id[i]) == -1 { 53 t.Fatalf("unexpected byte, want any valid hex char, got %c", id[i]) 54 } 55 } 56 } 57 } 58 59 func TestSubscriptions(t *testing.T) { 60 var ( 61 namespaces = []string{"eth", "bzz"} 62 service = ¬ificationTestService{} 63 subCount = len(namespaces) 64 notificationCount = 3 65 66 server = NewServer(0) 67 clientConn, serverConn = net.Pipe() 68 out = json.NewEncoder(clientConn) 69 in = json.NewDecoder(clientConn) 70 successes = make(chan subConfirmation) 71 notifications = make(chan subscriptionResult) 72 errors = make(chan error, subCount*notificationCount+1) 73 ) 74 75 // setup and start server 76 for _, namespace := range namespaces { 77 if err := server.RegisterName(namespace, service); err != nil { 78 t.Fatalf("unable to register test service %v", err) 79 } 80 } 81 go server.ServeCodec(NewCodec(serverConn), 0, 0, 0, 0) 82 defer server.Stop() 83 84 // wait for message and write them to the given channels 85 go waitForMessages(in, successes, notifications, errors) 86 87 // create subscriptions one by one 88 for i, namespace := range namespaces { 89 request := map[string]interface{}{ 90 "id": i, 91 "method": fmt.Sprintf("%s_subscribe", namespace), 92 "version": "2.0", 93 "params": []interface{}{"someSubscription", notificationCount, i}, 94 } 95 if err := out.Encode(&request); err != nil { 96 t.Fatalf("Could not create subscription: %v", err) 97 } 98 } 99 100 timeout := time.After(30 * time.Second) 101 subids := make(map[string]string, subCount) 102 count := make(map[string]int, subCount) 103 allReceived := func() bool { 104 done := len(count) == subCount 105 for _, c := range count { 106 if c < notificationCount { 107 done = false 108 } 109 } 110 return done 111 } 112 for !allReceived() { 113 select { 114 case confirmation := <-successes: // subscription created 115 subids[namespaces[confirmation.reqid]] = string(confirmation.subid) 116 case notification := <-notifications: 117 count[notification.ID]++ 118 case err := <-errors: 119 t.Fatal(err) 120 case <-timeout: 121 for _, namespace := range namespaces { 122 subid, found := subids[namespace] 123 if !found { 124 t.Errorf("subscription for %q not created", namespace) 125 continue 126 } 127 if count, found := count[subid]; !found || count < notificationCount { 128 t.Errorf("didn't receive all notifications (%d<%d) in time for namespace %q", count, notificationCount, namespace) 129 } 130 } 131 t.Fatal("timed out") 132 } 133 } 134 } 135 136 // This test checks that unsubscribing works. 137 func TestServerUnsubscribe(t *testing.T) { 138 p1, p2 := net.Pipe() 139 defer p2.Close() 140 141 // Start the server. 142 server := newTestServer() 143 service := ¬ificationTestService{unsubscribed: make(chan string, 1)} 144 server.RegisterName("nftest2", service) 145 go server.ServeCodec(NewCodec(p1), 0, 0, 0, 0) 146 147 // Subscribe. 148 p2.SetDeadline(time.Now().Add(10 * time.Second)) 149 p2.Write([]byte(`{"jsonrpc":"2.0","id":1,"method":"nftest2_subscribe","params":["someSubscription",0,10]}`)) 150 151 // Handle received messages. 152 var ( 153 resps = make(chan subConfirmation) 154 notifications = make(chan subscriptionResult) 155 errors = make(chan error, 1) 156 ) 157 go waitForMessages(json.NewDecoder(p2), resps, notifications, errors) 158 159 // Receive the subscription ID. 160 var sub subConfirmation 161 select { 162 case sub = <-resps: 163 case err := <-errors: 164 t.Fatal(err) 165 } 166 167 // Unsubscribe and check that it is handled on the server side. 168 p2.Write([]byte(`{"jsonrpc":"2.0","method":"nftest2_unsubscribe","params":["` + sub.subid + `"]}`)) 169 for { 170 select { 171 case id := <-service.unsubscribed: 172 if id != string(sub.subid) { 173 t.Errorf("wrong subscription ID unsubscribed") 174 } 175 return 176 case err := <-errors: 177 t.Fatal(err) 178 case <-notifications: 179 // drop notifications 180 } 181 } 182 } 183 184 type subConfirmation struct { 185 reqid int 186 subid ID 187 } 188 189 // waitForMessages reads RPC messages from 'in' and dispatches them into the given channels. 190 // It stops if there is an error. 191 func waitForMessages(in *json.Decoder, successes chan subConfirmation, notifications chan subscriptionResult, errors chan error) { 192 for { 193 resp, notification, err := readAndValidateMessage(in) 194 if err != nil { 195 errors <- err 196 return 197 } else if resp != nil { 198 successes <- *resp 199 } else { 200 notifications <- *notification 201 } 202 } 203 } 204 205 func readAndValidateMessage(in *json.Decoder) (*subConfirmation, *subscriptionResult, error) { 206 var msg jsonrpcMessage 207 if err := in.Decode(&msg); err != nil { 208 return nil, nil, fmt.Errorf("decode error: %v", err) 209 } 210 switch { 211 case msg.isNotification(): 212 var res subscriptionResult 213 if err := json.Unmarshal(msg.Params, &res); err != nil { 214 return nil, nil, fmt.Errorf("invalid subscription result: %v", err) 215 } 216 return nil, &res, nil 217 case msg.isResponse(): 218 var c subConfirmation 219 if msg.Error != nil { 220 return nil, nil, msg.Error 221 } else if err := json.Unmarshal(msg.Result, &c.subid); err != nil { 222 return nil, nil, fmt.Errorf("invalid response: %v", err) 223 } else { 224 json.Unmarshal(msg.ID, &c.reqid) 225 return &c, nil, nil 226 } 227 default: 228 return nil, nil, fmt.Errorf("unrecognized message: %v", msg) 229 } 230 }