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  }