github.com/amazechain/amc@v0.1.3/cmd/evmsdk/ws.go (about) 1 // Copyright 2023 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser 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 // The AmazeChain library 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 Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package evmsdk 18 19 import ( 20 "encoding/json" 21 "io/ioutil" 22 "runtime" 23 24 "github.com/gorilla/websocket" 25 ) 26 27 type WebSocketService struct { 28 addr string 29 acc string 30 readConn *websocket.Conn 31 writeConn *websocket.Conn 32 } 33 34 func NewWebSocketService(addr, acc string) (*WebSocketService, error) { 35 simpleLog("init ws read conn") 36 readConn, readResp, err := websocket.DefaultDialer.Dial(addr, nil) 37 if err != nil { 38 simpleLog("init ws read conn error,err=%+v", err) 39 return nil, err 40 } 41 if readResp != nil { 42 readRespBytes, err := ioutil.ReadAll(readResp.Body) 43 if err != nil { 44 simpleLog("readresp error", "err", err) 45 } 46 simpleLog("readresp", "readRespBytes", string(readRespBytes)) 47 } 48 readConn.SetPongHandler(func(appData string) error { simpleLog("read pong", appData); return nil }) 49 readConn.SetPingHandler(func(appData string) error { simpleLog("read ping", appData); return nil }) 50 simpleLog("init ws read conn") 51 52 writeConn, writeResp, err := websocket.DefaultDialer.Dial(addr, nil) 53 if err != nil { 54 simpleLog("init ws write conn error,err=%+v", err) 55 return nil, err 56 } 57 if writeResp != nil { 58 readRespBytes, err := ioutil.ReadAll(writeResp.Body) 59 if err != nil { 60 simpleLog("writeresp error", "err", err) 61 } 62 simpleLog("writeresp", "writeRespBytes", string(readRespBytes)) 63 } 64 writeConn.SetPongHandler(func(appData string) error { simpleLog("write pong", appData); return nil }) 65 writeConn.SetPingHandler(func(appData string) error { simpleLog("write ping", appData); return nil }) 66 simpleLog("init dial ws done") 67 return &WebSocketService{ 68 addr: addr, 69 readConn: readConn, 70 writeConn: writeConn, 71 acc: acc, 72 }, nil 73 } 74 75 func (ws *WebSocketService) Chans(pubk string) (<-chan []byte, chan<- []byte, error) { 76 simpleLog("ping read conn") 77 err := ws.readConn.PingHandler()("") 78 if err != nil { 79 simpleLog("ping read conn error,err=%+v", err) 80 return nil, nil, err 81 } 82 simpleLog("ping done") 83 msg := `{ 84 "jsonrpc": "2.0", 85 "method": "eth_subscribe", 86 "params": [ 87 "minedBlock", 88 "` + ws.acc + `" 89 ], 90 "id": 1 91 }` 92 simpleLog("minedBlock message:%+v", msg) 93 94 if err := ws.readConn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil { 95 simpleLog("minedBlock message write message error,err=%+v", err) 96 return nil, nil, err 97 } 98 99 _, msg0, err := ws.readConn.ReadMessage() 100 if err != nil { 101 simpleLog("init read conn readmessage error,err=", err) 102 return nil, nil, err 103 } 104 msg0bean := new(JSONRPCRequest) 105 if err := json.Unmarshal(msg0, msg0bean); err != nil { 106 simpleLog("unmarshal message0 error,err=", err) 107 return nil, nil, err 108 } 109 if msg0bean.Error != nil { 110 if msgCode := msg0bean.Error["code"]; msgCode != nil { 111 if msgCodeFloat, ok := msgCode.(float64); ok && msgCodeFloat != 0 { 112 simpleLogf("init read conn error,code=%+v ,message=%+v", msgCodeFloat, msg0bean.Error["message"]) 113 return nil, nil, &InnerError{Code: int(msgCodeFloat), Msg: msg0bean.Error["message"].(string)} 114 } 115 } 116 } 117 118 chO, chI := make(chan []byte, 0), make(chan []byte, 0) 119 go func() { 120 defer func() { 121 if err := recover(); err != nil { 122 buf := make([]byte, 4096) 123 runtime.Stack(buf, true) 124 simpleLog("readconn goroutine down", "err", err) 125 simpleLog(string(buf)) 126 } 127 EE.Stop() 128 }() 129 for { 130 msgType, b, err := ws.readConn.ReadMessage() 131 if err != nil { 132 simpleLog("ws closed:", err.Error()) 133 close(chO) 134 return 135 } 136 if msgType == websocket.TextMessage { 137 chO <- b 138 } 139 simpleLog("received msg:", string(b)) 140 } 141 }() 142 go func() { 143 defer func() { 144 if err := recover(); err != nil { 145 buf := make([]byte, 4096) 146 runtime.Stack(buf, true) 147 simpleLog("writeconn goroutine down", "err", err) 148 simpleLog(string(buf)) 149 } 150 EE.Stop() 151 }() 152 for msg := range chI { 153 /* 154 { 155 "jsonrpc":"2.0", 156 "method":"eth_submitSign", 157 "params":[{ 158 "number":11111, 159 "stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000", 160 "sign":"0xb4dd42744e40aa50bc0e747a52dcf8d1196c08a0f9fcfd8fda0aaa413a47bbdda20d1a76298642ee68b1f1009df149680e4e4b14f8c488faa4010ad9f26a81379c93d7f0da5596192da203c31a6301cd50d53734e537d828fe7f9593eb7035d3", 161 "publicKey":"0xa8a236acd9b7ea68c1858a3059e4aad7698842f7de49ace8dc49acc922bd6d543792e98b7b74fa380bf622671792d57a" 162 }], 163 "id":1 164 } 165 */ 166 wrappedRequest, err := ws.unwrapJSONRPCRequest(msg) 167 if err != nil { 168 simpleLog("wrapJSONRPCRequest error,err=", err) 169 continue 170 } 171 if err := ws.writeConn.WriteMessage(websocket.TextMessage, wrappedRequest); err != nil { 172 simpleLog("ws producer stopped") 173 return 174 } 175 simpleLog("sending msg:", string(wrappedRequest)) 176 } 177 }() 178 return chO, chI, nil 179 } 180 181 type JSONRPCRequest struct { 182 JsonRpc string `json:"jsonrpc"` 183 Method string `json:"method"` 184 ID int `json:"id"` 185 Params []json.RawMessage `json:"params"` 186 Error map[string]interface{} `json:"error"` 187 } 188 189 func (ws *WebSocketService) unwrapJSONRPCRequest(in []byte) ([]byte, error) { 190 d := &JSONRPCRequest{ 191 JsonRpc: "2.0", 192 Method: "eth_submitSign", 193 ID: 1, 194 Params: make([]json.RawMessage, 1), 195 } 196 d.Params[0] = in 197 return json.Marshal(d) 198 199 }