From 71592e4b5293d1657e745f8ad556f9583a22cc0a Mon Sep 17 00:00:00 2001 From: billfort Date: Wed, 17 Apr 2024 19:01:51 +0800 Subject: [PATCH] Support WebRTC communication Signed-off-by: billfort --- api/common/interfaces.go | 65 ++++- api/httpjson/RPCserver.go | 1 + api/httpjson/client/client.go | 33 +++ api/webrtc/webrtc.go | 395 +++++++++++++++++++++++++++ api/websocket/server/relay.go | 32 +-- api/websocket/server/server.go | 366 +++++++++++-------------- api/websocket/server/wsserver.go | 117 ++++++++ api/websocket/session/session.go | 15 +- api/websocket/session/sessionlist.go | 4 +- api/websocket/websocket.go | 8 +- cmd/nknd/commands/root.go | 1 + config.local.json | 5 + config.mainnet.json | 5 + config.testnet.json | 5 + config/config.go | 10 + go.mod | 30 +- go.sum | 115 +++++++- 17 files changed, 945 insertions(+), 262 deletions(-) create mode 100644 api/webrtc/webrtc.go create mode 100644 api/websocket/server/wsserver.go diff --git a/api/common/interfaces.go b/api/common/interfaces.go index e878ce226..e4e0dd7e1 100644 --- a/api/common/interfaces.go +++ b/api/common/interfaces.go @@ -7,9 +7,13 @@ import ( "encoding/json" "fmt" "net" + "net/url" "strings" + "time" "github.com/nknorg/nkn/v2/api/common/errcode" + "github.com/nknorg/nkn/v2/api/httpjson/client" + "github.com/nknorg/nkn/v2/api/webrtc" "github.com/nknorg/nkn/v2/block" "github.com/nknorg/nkn/v2/chain" "github.com/nknorg/nkn/v2/common" @@ -412,12 +416,13 @@ func getVersion(s Serverer, params map[string]interface{}, ctx context.Context) return respPacking(errcode.SUCCESS, config.Version) } -func NodeInfo(wsAddr, rpcAddr string, pubkey, id []byte) map[string]string { +func NodeInfo(wsAddr, rpcAddr string, pubkey, id []byte, sdp string) map[string]string { nodeInfo := make(map[string]string) nodeInfo["addr"] = wsAddr nodeInfo["rpcAddr"] = rpcAddr nodeInfo["pubkey"] = hex.EncodeToString(pubkey) nodeInfo["id"] = hex.EncodeToString(id) + nodeInfo["sdp"] = sdp return nodeInfo } @@ -444,7 +449,7 @@ func getWsAddr(s Serverer, params map[string]interface{}, ctx context.Context) m return respPacking(errcode.INTERNAL_ERROR, err.Error()) } - return respPacking(errcode.SUCCESS, NodeInfo(wsAddr, rpcAddr, pubkey, id)) + return respPacking(errcode.SUCCESS, NodeInfo(wsAddr, rpcAddr, pubkey, id, "")) } func getWssAddr(s Serverer, params map[string]interface{}, ctx context.Context) map[string]interface{} { @@ -467,7 +472,7 @@ func getWssAddr(s Serverer, params map[string]interface{}, ctx context.Context) return respPacking(errcode.INTERNAL_ERROR, err.Error()) } - return respPacking(errcode.SUCCESS, NodeInfo(wsAddr, rpcAddr, pubkey, id)) + return respPacking(errcode.SUCCESS, NodeInfo(wsAddr, rpcAddr, pubkey, id, "")) } // getBalanceByAddr gets balance by address @@ -953,6 +958,59 @@ func findSuccessorAddr(s Serverer, params map[string]interface{}, ctx context.Co return respPacking(errcode.SUCCESS, addrs[0]) } +// getPeerAddr get a node address +// params: {"address":
} +// return: {"resultOrData":|, "error":} +func getPeerAddr(s Serverer, params map[string]interface{}, ctx context.Context) map[string]interface{} { + if len(params) < 1 { + return RespPacking("length of params is less than 1", errcode.INVALID_PARAMS) + } + + str, ok := params["address"].(string) + if !ok { + return RespPacking("address should be a string", errcode.INTERNAL_ERROR) + } + + clientID, _, _, err := address.ParseClientAddress(str) + if err != nil { + return RespPacking(err.Error(), errcode.INTERNAL_ERROR) + } + + wsAddr, rpcAddr, pubkey, id, err := s.GetNetNode().FindWsAddr(clientID) + if err != nil { + return RespPacking(err.Error(), errcode.INTERNAL_ERROR) + } + + n := s.GetNetNode() + if n == nil { + return nil + } + + if n.GetWsAddr() == wsAddr { + offer := params["offer"].(string) + peer := webrtc.NewPeer(config.Parameters.StunList) + + err = peer.Answer(offer) + if err != nil { + return RespPacking(err.Error(), errcode.INTERNAL_ERROR) + } + select { + case answer := <-peer.OnSdp: + return RespPacking(NodeInfo(wsAddr, rpcAddr, pubkey, id, answer), errcode.SUCCESS) + case <-time.After(10 * time.Second): + return RespPacking(fmt.Errorf("webrtc, wait for sdp time out"), errcode.INTERNAL_ERROR) + } + } + + reqAddr := (&url.URL{Scheme: "http", Host: rpcAddr}).String() + wsAddr, rpcAddr, pubkey, id, sdp, err := client.GetPeerAddr(reqAddr, params) + if err != nil { + return RespPacking(err.Error(), errcode.INTERNAL_ERROR) + } + + return RespPacking(NodeInfo(wsAddr, rpcAddr, pubkey, id, sdp), errcode.SUCCESS) +} + var InitialAPIHandlers = map[string]APIHandler{ "getlatestblockhash": {Handler: getLatestBlockHash, AccessCtrl: BIT_JSONRPC}, "getblock": {Handler: getBlock, AccessCtrl: BIT_JSONRPC}, @@ -983,4 +1041,5 @@ var InitialAPIHandlers = map[string]APIHandler{ "findsuccessoraddr": {Handler: findSuccessorAddr, AccessCtrl: BIT_JSONRPC}, "findsuccessoraddrs": {Handler: findSuccessorAddrs, AccessCtrl: BIT_JSONRPC}, "getregistrant": {Handler: getRegistrant, AccessCtrl: BIT_JSONRPC}, + "getpeeraddr": {Handler: getPeerAddr, AccessCtrl: BIT_JSONRPC}, } diff --git a/api/httpjson/RPCserver.go b/api/httpjson/RPCserver.go index 96fd9d555..217b4cbd6 100644 --- a/api/httpjson/RPCserver.go +++ b/api/httpjson/RPCserver.go @@ -130,6 +130,7 @@ func (s *RPCServer) Handle(w http.ResponseWriter, r *http.Request) { } method, ok = request["method"].(string) if !ok { + log.Warning("RPC Server - No function to call for ", method) code = errcode.INVALID_METHOD } if request["params"] != nil { diff --git a/api/httpjson/client/client.go b/api/httpjson/client/client.go index 650f61a36..562e9b954 100644 --- a/api/httpjson/client/client.go +++ b/api/httpjson/client/client.go @@ -222,3 +222,36 @@ func GetNonceByAddr(remote, addr string, txPool bool) (uint64, uint32, error) { return nonce, ret.Result.CurrentHeight, nil } + +func GetPeerAddr(remote string, params map[string]interface{}) (string, string, []byte, []byte, string, error) { + fmt.Println("......GetPeerAddr, remote: ", remote) + resp, err := Call(remote, "getpeeraddr", 0, params) + if err != nil { + return "", "", nil, nil, "", err + } + + // log.Infof("Node-to-Node GetPeerAddr got resp: %v from %s\n", string(resp), remote) + + var ret struct { + Result struct { + Addr string `json:"addr"` + RpcAddr string `json:"rpcAddr"` + Pubkey []byte `json:"pubkey"` + Id []byte `json:"id"` + Sdp string `json:"sdp"` + } `json:"result"` + Err map[string]interface{} `json:"error"` + } + + if err := json.Unmarshal(resp, &ret); err != nil { + log.Error("Node-to-Node GetPeerAddr json.Unmarshal error: ", err) + return "", "", nil, nil, "", err + } + if len(ret.Err) != 0 { // resp.error NOT empty + log.Error("Node-to-Node GetPeerAddr ret.Err: ", ret.Err) + return "", "", nil, nil, "", fmt.Errorf("GetPeerAddr(%s) resp error: %v", remote, string(resp)) + } + + fmt.Printf("......GetPeerAddr got result: %+v\n", ret.Result) + return ret.Result.Addr, ret.Result.RpcAddr, ret.Result.Pubkey, ret.Result.Id, ret.Result.Sdp, nil +} diff --git a/api/webrtc/webrtc.go b/api/webrtc/webrtc.go new file mode 100644 index 000000000..ac76d3823 --- /dev/null +++ b/api/webrtc/webrtc.go @@ -0,0 +1,395 @@ +package webrtc + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "os" + "sync" + "time" + + "github.com/nknorg/nkn/v2/api/ratelimiter" + "github.com/nknorg/nkn/v2/api/websocket/session" + "github.com/nknorg/nkn/v2/config" + "github.com/nknorg/nkn/v2/util/log" + "github.com/pion/webrtc/v4" +) + +// compitable to websocket +const ( + UnknownMessage = 0 + TextMessage = 1 + BinaryMessage = 2 + CloseMessage = 8 + PingMessage = 9 + PongMessage = 10 + + PingData = "ping" + PongData = "pong" +) + +var NewConnection func(conn session.Conn, r *http.Request) + +type DataChannelMessage struct { + messageType int + data []byte +} + +type Peer struct { + pc *webrtc.PeerConnection + dc *webrtc.DataChannel + offer string + answer string + OnSdp chan string + OnMessage chan DataChannelMessage + + mutex sync.RWMutex + isConnected bool + readDeadline time.Time + writeDeadline time.Time + readLimit int64 + pongHandler func(string) error +} + +func NewPeer(urls []string) *Peer { + p := &Peer{ + OnSdp: make(chan string, 1), + isConnected: false, + OnMessage: make(chan DataChannelMessage, 128), + } + + config := webrtc.Configuration{ + ICEServers: []webrtc.ICEServer{ + { + URLs: urls, + }, + }, + } + var err error + pc, err := webrtc.NewPeerConnection(config) + if err != nil { + log.Error("NewPeerConnection error: ", err) + return nil + } + + p.pc = pc + return p +} + +func (c *Peer) Offer(label string) error { + if c.pc == nil { + return fmt.Errorf("PeerConnection not available") + } + + c.pc.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate == nil { + localDesc := c.pc.LocalDescription() + encodedDescr, err := Encode(localDesc) + if err != nil { + log.Error("WebRTC OnICECandidate error: ", err) + return + } + c.offer = encodedDescr + c.OnSdp <- encodedDescr + } + }) + + dc, err := c.pc.CreateDataChannel(label, nil) + if err != nil { + return err + } + dc.OnOpen(func() { + log.Debugf("data channel %v has been opened\n", dc.Label()) + c.mutex.Lock() + defer c.mutex.Unlock() + c.isConnected = true + }) + dc.OnMessage(func(msg webrtc.DataChannelMessage) { + var dcmsg DataChannelMessage + if msg.IsString { + dcmsg.messageType = TextMessage + if string(msg.Data) == PongData { + if c.pongHandler != nil { + c.pongHandler(PongData) + } else { + log.Info("Pong handler not set") + } + return + } else if string(msg.Data) == PingData { + dc.SendText(PongData) + return + } + } else { + dcmsg.messageType = BinaryMessage + } + dcmsg.data = msg.Data + c.OnMessage <- dcmsg + }) + dc.OnClose(func() { + c.mutex.Lock() + defer c.mutex.Unlock() + c.isConnected = false + }) + + dc.OnError(func(err error) { + log.Errorf("Data Channel %s error: %s\n", dc.Label(), err.Error()) + }) + + offer, err := c.pc.CreateOffer(&webrtc.OfferOptions{ICERestart: false}) + if err != nil { + return nil + } + if err = c.pc.SetLocalDescription(offer); err != nil { + return nil + } + + c.dc = dc + return nil +} + +func (c *Peer) Answer(offerSdp string) error { + offer := webrtc.SessionDescription{} + err := Decode(offerSdp, &offer) + if err != nil { + return err + } + + sdp, err := offer.Unmarshal() + if err != nil { + return err + } + limiter := ratelimiter.GetLimiter("webrtc:"+sdp.Origin.UnicastAddress, config.Parameters.WsIPRateLimit, int(config.Parameters.WsIPRateBurst)) + if !limiter.Allow() { + return fmt.Errorf("webrtc connection limit of %s reached", sdp.Origin.UnicastAddress) + } + + if err := c.pc.SetRemoteDescription(offer); err != nil { + return err + } + + answer, err := c.pc.CreateAnswer(nil) + if err != nil { + return err + } + + if err := c.pc.SetLocalDescription(answer); err != nil { + return err + } + + c.pc.OnICECandidate(func(candidate *webrtc.ICECandidate) { + if candidate == nil { + localDesc := c.pc.LocalDescription() + encodedDescr, err := Encode(localDesc) + if err != nil { + log.Error("WebRTC OnICECandidate error: ", err) + return + } + c.answer = encodedDescr + c.OnSdp <- encodedDescr + } + }) + + c.pc.OnDataChannel(func(dc *webrtc.DataChannel) { + dc.OnOpen(func() { + c.dc = dc + + c.mutex.Lock() + c.isConnected = true + c.mutex.Unlock() + + if NewConnection != nil { + go NewConnection(c, nil) + } + }) + + dc.OnMessage(func(msg webrtc.DataChannelMessage) { + var dcmsg DataChannelMessage + if msg.IsString { + dcmsg.messageType = TextMessage + if string(msg.Data) == PingData { + dc.SendText(PongData) + return + } else if string(msg.Data) == PongData { + if c.pongHandler != nil { + c.pongHandler(PongData) + } else { + log.Info("Pong handler not set") + } + return + } + } else { + dcmsg.messageType = BinaryMessage + } + dcmsg.data = msg.Data + c.OnMessage <- dcmsg + }) + + dc.OnClose(func() { + c.mutex.Lock() + c.isConnected = false + c.mutex.Unlock() + }) + + dc.OnError(func(err error) { + log.Errorf("Data Channel %v error: %s\n", dc.Label(), err.Error()) + }) + }) + + return nil +} + +func (c *Peer) IsConnected() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + return c.isConnected +} + +func (c *Peer) SetRemoteSdp(sdp string) error { + answer := webrtc.SessionDescription{} + err := Decode(sdp, &answer) + if err != nil { + return err + } + return c.pc.SetRemoteDescription(answer) +} + +func (c *Peer) WriteMessage(messageType int, data []byte) (err error) { + if c.dc == nil { + return fmt.Errorf("DataChannel not available") + } + + if messageType == PingMessage { + return c.dc.SendText(PingData) + } + if messageType == PongMessage { + return c.dc.SendText(PongData) + } + if data == nil { + return fmt.Errorf("data to send is nil") + } + + if c.writeDeadline.IsZero() { + if messageType == TextMessage { + err = c.dc.SendText(string(data)) + } else { + err = c.dc.Send(data) + } + return err + } + + var dur time.Duration + now := time.Now() + switch { + case c.writeDeadline.Before(now), c.writeDeadline == now: + return os.ErrDeadlineExceeded + + case c.writeDeadline.After(now): + writeResult := make(chan error) + go func() { + if messageType == TextMessage { + err = c.dc.SendText(string(data)) + } else { + err = c.dc.Send(data) + } + + writeResult <- err + }() + + dur = time.Until(c.writeDeadline) + select { + case err = <-writeResult: + return err + case <-time.After(dur): + return os.ErrDeadlineExceeded + } + } + + return fmt.Errorf("unknown error") +} + +// WriteJSON writes the JSON encoding of v as a message. +func (c *Peer) WriteJSON(v interface{}) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + return c.WriteMessage(TextMessage, b) +} + +func (c *Peer) ReadMessage() (messageType int, data []byte, err error) { + if c.readDeadline.IsZero() { + msg := <-c.OnMessage + return msg.messageType, msg.data, nil + } + + now := time.Now() + switch { + case c.readDeadline.After(now): + for { + oldReadDeadline := c.readDeadline + dur := time.Until(c.readDeadline) + select { + case msg := <-c.OnMessage: + return msg.messageType, msg.data, nil + case <-time.After(dur): + if c.readDeadline.After(oldReadDeadline) { + continue + } + return UnknownMessage, nil, os.ErrDeadlineExceeded + } + } + + case c.readDeadline.Before(now), c.readDeadline == now: + return UnknownMessage, nil, os.ErrDeadlineExceeded + } + + return UnknownMessage, nil, fmt.Errorf("unknown error") +} + +func (c *Peer) SetWriteDeadline(t time.Time) error { + c.writeDeadline = t + return nil +} + +func (c *Peer) SetReadDeadline(t time.Time) error { + c.readDeadline = t + return nil +} + +func (c *Peer) SetReadLimit(l int64) { + c.readLimit = l +} + +func (c *Peer) Close() error { + if c.dc != nil { + if err := c.dc.Close(); err != nil { + return err + } + } + return c.pc.Close() +} + +func (c *Peer) SetPongHandler(f func(string) error) { + c.pongHandler = f +} + +// Encode the input in base64 +func Encode(obj interface{}) (string, error) { + b, err := json.Marshal(obj) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(b), nil +} + +// Decode the input from base64 +func Decode(in string, obj interface{}) error { + b, err := base64.StdEncoding.DecodeString(in) + if err != nil { + return err + } + + return json.Unmarshal(b, obj) +} diff --git a/api/websocket/server/relay.go b/api/websocket/server/relay.go index 1900de4ea..d3bb83b6c 100644 --- a/api/websocket/server/relay.go +++ b/api/websocket/server/relay.go @@ -18,7 +18,7 @@ type sigChainInfo struct { sigChainLen int } -func (ws *WsServer) sendOutboundRelayMessage(srcAddrStrPtr *string, msg *pb.OutboundMessage) { +func (ms *MsgServer) sendOutboundRelayMessage(srcAddrStrPtr *string, msg *pb.OutboundMessage) { if srcAddrStrPtr == nil { log.Warningf("src addr is nil") return @@ -56,15 +56,15 @@ func (ws *WsServer) sendOutboundRelayMessage(srcAddrStrPtr *string, msg *pb.Outb } else { payload = payloads[0] } - err := ws.localNode.SendRelayMessage(*srcAddrStrPtr, dest, payload, msg.Signatures[i], msg.BlockHash, msg.Nonce, msg.MaxHoldingSeconds) + err := ms.localNode.SendRelayMessage(*srcAddrStrPtr, dest, payload, msg.Signatures[i], msg.BlockHash, msg.Nonce, msg.MaxHoldingSeconds) if err != nil { log.Error("Send relay message error:", err) } } } -func (ws *WsServer) sendInboundMessage(clientID string, inboundMsg *pb.InboundMessage) bool { - clients := ws.SessionList.GetSessionsById(clientID) +func (ms *MsgServer) sendInboundMessage(clientID string, inboundMsg *pb.InboundMessage) bool { + clients := ms.SessionList.GetSessionsById(clientID) if clients == nil { log.Debugf("Client Not Online: %s", clientID) return false @@ -105,7 +105,7 @@ func (ws *WsServer) sendInboundMessage(clientID string, inboundMsg *pb.InboundMe return success } -func (ws *WsServer) sendInboundRelayMessage(relayMessage *pb.Relay, shouldSign bool) { +func (ms *MsgServer) sendInboundRelayMessage(relayMessage *pb.Relay, shouldSign bool) { clientID := relayMessage.DestId msg := &pb.InboundMessage{ Src: address.AssembleClientAddress(relayMessage.SrcIdentifier, relayMessage.SrcPubkey), @@ -119,34 +119,34 @@ func (ws *WsServer) sendInboundRelayMessage(relayMessage *pb.Relay, shouldSign b } } - success := ws.sendInboundMessage(hex.EncodeToString(clientID), msg) + success := ms.sendInboundMessage(hex.EncodeToString(clientID), msg) if success { if shouldSign { - ws.sigChainCache.Add(relayMessage.LastHash, &sigChainInfo{ + ms.sigChainCache.Add(relayMessage.LastHash, &sigChainInfo{ blockHash: relayMessage.BlockHash, sigChainLen: int(relayMessage.SigChainLen), }) } if time.Duration(relayMessage.MaxHoldingSeconds) > pongTimeout/time.Second { - ok := ws.messageDeliveredCache.Push(relayMessage) + ok := ms.messageDeliveredCache.Push(relayMessage) if !ok { log.Warningf("MessageDeliveredCache full, discarding messages.") } } } else if relayMessage.MaxHoldingSeconds > 0 { - ws.messageBuffer.AddMessage(clientID, relayMessage) + ms.messageBuffer.AddMessage(clientID, relayMessage) } } -func (ws *WsServer) startCheckingLostMessages() { +func (ms *MsgServer) startCheckingLostMessages() { for { - v, ok := ws.messageDeliveredCache.Pop() + v, ok := ms.messageDeliveredCache.Pop() if !ok { break } if relayMessage, ok := v.(*pb.Relay); ok { clientID := relayMessage.DestId - clients := ws.SessionList.GetSessionsById(hex.EncodeToString(clientID)) + clients := ms.SessionList.GetSessionsById(hex.EncodeToString(clientID)) if len(clients) > 0 { threshold := time.Now().Add(-pongTimeout) success := false @@ -157,17 +157,17 @@ func (ws *WsServer) startCheckingLostMessages() { } } if !success { - ws.sendInboundRelayMessage(relayMessage, false) + ms.sendInboundRelayMessage(relayMessage, false) } continue } - ws.messageBuffer.AddMessage(clientID, relayMessage) + ms.messageBuffer.AddMessage(clientID, relayMessage) } } } -func (ws *WsServer) handleReceipt(receipt *pb.Receipt) error { - v, ok := ws.sigChainCache.Get(receipt.PrevHash) +func (ms *MsgServer) handleReceipt(receipt *pb.Receipt) error { + v, ok := ms.sigChainCache.Get(receipt.PrevHash) if !ok { return fmt.Errorf("sigchain info with last hash %x not found in cache", receipt.PrevHash) } diff --git a/api/websocket/server/server.go b/api/websocket/server/server.go index a0d79737f..2e56998af 100644 --- a/api/websocket/server/server.go +++ b/api/websocket/server/server.go @@ -20,7 +20,7 @@ import ( "github.com/golang/protobuf/proto" api "github.com/nknorg/nkn/v2/api/common" "github.com/nknorg/nkn/v2/api/common/errcode" - "github.com/nknorg/nkn/v2/api/ratelimiter" + "github.com/nknorg/nkn/v2/api/webrtc" "github.com/nknorg/nkn/v2/api/websocket/messagebuffer" "github.com/nknorg/nkn/v2/api/websocket/session" "github.com/nknorg/nkn/v2/chain" @@ -53,13 +53,8 @@ type Handler struct { pushFlag bool } -type WsServer struct { +type MsgServer struct { sync.RWMutex - Upgrader websocket.Upgrader - listener net.Listener - tlsListener net.Listener - server *http.Server - tlsServer *http.Server SessionList *session.SessionList ActionMap map[string]Handler TxHashMap map[string]string //key: txHash value:sessionid @@ -68,11 +63,11 @@ type WsServer struct { messageBuffer *messagebuffer.MessageBuffer messageDeliveredCache *DelayedChan sigChainCache common.Cache + ws *wsServer } -func InitWsServer(localNode node.ILocalNode, wallet *vault.Wallet) *WsServer { - ws := &WsServer{ - Upgrader: websocket.Upgrader{}, +func InitMsgServer(localNode node.ILocalNode, wallet *vault.Wallet) *MsgServer { + ws := &MsgServer{ SessionList: session.NewSessionList(), TxHashMap: make(map[string]string), localNode: localNode, @@ -80,68 +75,31 @@ func InitWsServer(localNode node.ILocalNode, wallet *vault.Wallet) *WsServer { messageBuffer: messagebuffer.NewMessageBuffer(true), messageDeliveredCache: NewDelayedChan(messageDeliveredCacheSize, pongTimeout), sigChainCache: common.NewGoCache(sigChainCacheExpiration, sigChainCacheCleanupInterval), + ws: &wsServer{}, } return ws } -func (ws *WsServer) Start(wssCertReady chan struct{}) error { - if config.Parameters.HttpWsPort == 0 { - log.Error("Not configure HttpWsPort port ") - return nil - } - ws.registryMethod() - ws.Upgrader.CheckOrigin = func(r *http.Request) bool { - return true - } +func (ms *MsgServer) Start(wssCertReady chan struct{}) error { - var err error + ms.ws.Start(ms, wssCertReady) + ms.registryMethod() - ws.listener, err = net.Listen("tcp", ":"+strconv.Itoa(int(config.Parameters.HttpWsPort))) - if err != nil { - log.Error("net.Listen: ", err.Error()) - return err - } - - event.Queue.Subscribe(event.SendInboundMessageToClient, ws.sendInboundRelayMessageToClient) + go ms.startCheckingLostMessages() + go ms.startCheckingWrongClients() - ws.server = &http.Server{Handler: http.HandlerFunc(ws.websocketHandler)} - go ws.server.Serve(ws.listener) + event.Queue.Subscribe(event.SendInboundMessageToClient, ms.sendInboundRelayMessageToClient) - go func(wssCertReady chan struct{}) { - if wssCertReady == nil { - return - } - for { - select { - case <-wssCertReady: - log.Info("wss cert received") - ws.tlsListener, err = ws.initTlsListen() - if err != nil { - log.Error("Https Cert: ", err.Error()) - } - err = ws.server.Serve(ws.tlsListener) - if err != nil { - log.Error(err) - } - return - case <-time.After(300 * time.Second): - log.Info("wss server is unavailable yet") - } - } - }(wssCertReady) - - go ws.startCheckingLostMessages() - - go ws.startCheckingWrongClients() + webrtc.NewConnection = ms.newConnection return nil } -func (ws *WsServer) registryMethod() { +func (ms *MsgServer) registryMethod() { gettxhashmap := func(s api.Serverer, cmd map[string]interface{}, ctx context.Context) map[string]interface{} { - ws.Lock() - defer ws.Unlock() - resp := api.RespPacking(len(ws.TxHashMap), errcode.SUCCESS) + ms.Lock() + defer ms.Unlock() + resp := api.RespPacking(len(ms.TxHashMap), errcode.SUCCESS) return resp } @@ -151,7 +109,7 @@ func (ws *WsServer) registryMethod() { } getsessioncount := func(s api.Serverer, cmd map[string]interface{}, ctx context.Context) map[string]interface{} { - return api.RespPacking(ws.SessionList.GetSessionCount(), errcode.SUCCESS) + return api.RespPacking(ms.SessionList.GetSessionCount(), errcode.SUCCESS) } setClient := func(s api.Serverer, cmd map[string]interface{}, ctx context.Context) map[string]interface{} { @@ -191,7 +149,7 @@ func (ws *WsServer) registryMethod() { } if wsAddr != localAddr { - return api.RespPacking(api.NodeInfo(wsAddr, rpcAddr, pubkey, id), errcode.WRONG_NODE) + return api.RespPacking(api.NodeInfo(wsAddr, rpcAddr, pubkey, id, ""), errcode.WRONG_NODE) } // client auth @@ -227,7 +185,7 @@ func (ws *WsServer) registryMethod() { go func() { log.Warning("Client signature is not right, close its conneciton now") time.Sleep(3 * time.Second) // sleep several second, let response reach client - ws.SessionList.CloseSession(sess) // close this session + ms.SessionList.CloseSession(sess) // close this session }() return api.RespPacking(nil, errcode.INVALID_SIGNATURE) } @@ -236,7 +194,7 @@ func (ws *WsServer) registryMethod() { } newSessionID := hex.EncodeToString(clientID) - session, err := ws.SessionList.ChangeSessionToClient(cmd["Userid"].(string), newSessionID) + session, err := ms.SessionList.ChangeSessionToClient(cmd["Userid"].(string), newSessionID) if err != nil { log.Error("Change session id error: ", err) return api.RespPacking(nil, errcode.INTERNAL_ERROR) @@ -244,9 +202,9 @@ func (ws *WsServer) registryMethod() { session.SetClient(clientID, pubKey, &addrStr, isTlsClient) go func() { - messages := ws.messageBuffer.PopMessages(clientID) + messages := ms.messageBuffer.PopMessages(clientID) for _, message := range messages { - ws.sendInboundRelayMessage(message, true) + ms.sendInboundRelayMessage(message, true) } }() @@ -260,7 +218,7 @@ func (ws *WsServer) registryMethod() { } res := make(map[string]interface{}) - res["node"] = api.NodeInfo(wsAddr, rpcAddr, pubkey, id) + res["node"] = api.NodeInfo(wsAddr, rpcAddr, pubkey, id, "") res["sigChainBlockHash"] = hex.EncodeToString(sigChainBlockHash.ToArray()) return api.RespPacking(res, errcode.SUCCESS) @@ -279,101 +237,10 @@ func (ws *WsServer) registryMethod() { } } - ws.ActionMap = actionMap -} - -func (ws *WsServer) Stop() { - if ws.server != nil { - ws.server.Shutdown(context.Background()) - log.Error("Close websocket ") - } -} - -// websocketHandler -func (ws *WsServer) websocketHandler(w http.ResponseWriter, r *http.Request) { - host, _, err := net.SplitHostPort(r.RemoteAddr) - if err == nil { - limiter := ratelimiter.GetLimiter("ws:"+host, config.Parameters.WsIPRateLimit, int(config.Parameters.WsIPRateBurst)) - if !limiter.Allow() { - log.Infof("Ws connection limit of %s reached", host) - w.WriteHeader(http.StatusTooManyRequests) - return - } - } - - wsConn, err := ws.Upgrader.Upgrade(w, r, nil) - if err != nil { - log.Error("websocket Upgrader: ", err) - return - } - defer wsConn.Close() - - sess, err := ws.SessionList.NewSession(wsConn) - if err != nil { - log.Error("websocket NewSession:", err) - return - } - - defer func() { - ws.deleteTxHashs(sess.GetSessionId()) - ws.SessionList.CloseSession(sess) - if err := recover(); err != nil { - log.Error("websocket recover:", err) - } - }() - - wsConn.SetReadLimit(maxMessageSize) - wsConn.SetReadDeadline(time.Now().Add(pongTimeout)) - wsConn.SetPongHandler(func(string) error { - wsConn.SetReadDeadline(time.Now().Add(pongTimeout)) - sess.UpdateLastReadTime() - return nil - }) - - // client auth - err = ws.sendClientAuthChallenge(sess) - if err != nil { - log.Error("send client auth challenge: ", err) - return - } - - done := make(chan struct{}) - defer close(done) - go func() { - ticker := time.NewTicker(pingInterval) - defer ticker.Stop() - var err error - for { - select { - case <-ticker.C: - err = sess.Ping() - if err != nil { - return - } - case <-done: - return - } - } - }() - - for { - messageType, bysMsg, err := wsConn.ReadMessage() - if err != nil { - log.Debugf("websocket read message error: %v", err) - break - } - - wsConn.SetReadDeadline(time.Now().Add(pongTimeout)) - sess.UpdateLastReadTime() - - err = ws.OnDataHandle(sess, messageType, bysMsg, r) - if err != nil { - log.Error(err) - } - } + ms.ActionMap = actionMap } -func (ws *WsServer) IsValidMsg(reqMsg map[string]interface{}) bool { +func (ms *MsgServer) IsValidMsg(reqMsg map[string]interface{}) bool { if _, ok := reqMsg["Hash"].(string); !ok && reqMsg["Hash"] != nil { return false } @@ -386,7 +253,7 @@ func (ws *WsServer) IsValidMsg(reqMsg map[string]interface{}) bool { return true } -func (ws *WsServer) OnDataHandle(curSession *session.Session, messageType int, bysMsg []byte, r *http.Request) error { +func (ms *MsgServer) OnDataHandle(curSession *session.Session, messageType int, bysMsg []byte, httpr *http.Request) error { if messageType == websocket.BinaryMessage { msg := &pb.ClientMessage{} err := proto.Unmarshal(bysMsg, msg) @@ -422,14 +289,14 @@ func (ws *WsServer) OnDataHandle(curSession *session.Session, messageType int, b if err != nil { return fmt.Errorf("Unmarshal outbound message error: %v", err) } - ws.sendOutboundRelayMessage(curSession.GetAddrStr(), outboundMsg) + ms.sendOutboundRelayMessage(curSession.GetAddrStr(), outboundMsg) case pb.ClientMessageType_RECEIPT: receipt := &pb.Receipt{} err = proto.Unmarshal(b, receipt) if err != nil { return fmt.Errorf("Unmarshal receipt error: %v", err) } - err = ws.handleReceipt(receipt) + err = ms.handleReceipt(receipt) if err != nil { return fmt.Errorf("Handle receipt error: %v", err) } @@ -444,24 +311,24 @@ func (ws *WsServer) OnDataHandle(curSession *session.Session, messageType int, b if err := json.Unmarshal(bysMsg, &req); err != nil { resp := api.ResponsePack(errcode.ILLEGAL_DATAFORMAT) - ws.respondToSession(curSession, resp) + ms.respondToSession(curSession, resp) return fmt.Errorf("websocket OnDataHandle: %v", err) } actionName, ok := req["Action"].(string) if !ok { resp := api.ResponsePack(errcode.INVALID_METHOD) - ws.respondToSession(curSession, resp) + ms.respondToSession(curSession, resp) return nil } - action, ok := ws.ActionMap[actionName] + action, ok := ms.ActionMap[actionName] if !ok { resp := api.ResponsePack(errcode.INVALID_METHOD) - ws.respondToSession(curSession, resp) + ms.respondToSession(curSession, resp) return nil } - if !ws.IsValidMsg(req) { + if !ms.IsValidMsg(req) { resp := api.ResponsePack(errcode.INVALID_PARAMS) - ws.respondToSession(curSession, resp) + ms.respondToSession(curSession, resp) return nil } if height, ok := req["Height"].(float64); ok { @@ -471,39 +338,45 @@ func (ws *WsServer) OnDataHandle(curSession *session.Session, messageType int, b req["Raw"] = strconv.FormatInt(int64(raw), 10) } req["Userid"] = curSession.GetSessionId() - req["IsTls"] = r.TLS != nil + ctx := context.Background() + if httpr != nil { + req["IsTls"] = httpr.TLS != nil + ctx = httpr.Context() + } else { + req["IsTls"] = false + } req["session"] = curSession - ret := action.handler(ws, req, r.Context()) + ret := action.handler(ms, req, ctx) resp := api.ResponsePack(ret["error"].(errcode.ErrCode)) resp["Action"] = actionName resp["Result"] = ret["resultOrData"] if txHash, ok := resp["Result"].(string); ok && action.pushFlag { - ws.Lock() - defer ws.Unlock() - ws.TxHashMap[txHash] = curSession.GetSessionId() + ms.Lock() + defer ms.Unlock() + ms.TxHashMap[txHash] = curSession.GetSessionId() } - ws.respondToSession(curSession, resp) + ms.respondToSession(curSession, resp) return nil } -func (ws *WsServer) SetTxHashMap(txhash string, sessionid string) { - ws.Lock() - defer ws.Unlock() - ws.TxHashMap[txhash] = sessionid +func (ms *MsgServer) SetTxHashMap(txhash string, sessionid string) { + ms.Lock() + defer ms.Unlock() + ms.TxHashMap[txhash] = sessionid } -func (ws *WsServer) deleteTxHashs(sSessionId string) { - ws.Lock() - defer ws.Unlock() - for k, v := range ws.TxHashMap { +func (ms *MsgServer) deleteTxHashs(sSessionId string) { + ms.Lock() + defer ms.Unlock() + for k, v := range ms.TxHashMap { if v == sSessionId { - delete(ws.TxHashMap, k) + delete(ms.TxHashMap, k) } } } -func (ws *WsServer) respondToSession(session *session.Session, resp map[string]interface{}) error { +func (ms *MsgServer) respondToSession(session *session.Session, resp map[string]interface{}) error { resp["Desc"] = errcode.ErrMessage[resp["Error"].(errcode.ErrCode)] data, err := json.Marshal(resp) if err != nil { @@ -514,46 +387,46 @@ func (ws *WsServer) respondToSession(session *session.Session, resp map[string]i return err } -func (ws *WsServer) respondToId(sSessionId string, resp map[string]interface{}) { - sessions := ws.SessionList.GetSessionsById(sSessionId) +func (ms *MsgServer) respondToId(sSessionId string, resp map[string]interface{}) { + sessions := ms.SessionList.GetSessionsById(sSessionId) if sessions == nil { log.Error("websocket sessionId Not Exist: " + sSessionId) return } for _, session := range sessions { - ws.respondToSession(session, resp) + ms.respondToSession(session, resp) } } -func (ws *WsServer) PushTxResult(txHashStr string, resp map[string]interface{}) { - ws.Lock() - defer ws.Unlock() - sSessionId := ws.TxHashMap[txHashStr] - delete(ws.TxHashMap, txHashStr) +func (ms *MsgServer) PushTxResult(txHashStr string, resp map[string]interface{}) { + ms.Lock() + defer ms.Unlock() + sSessionId := ms.TxHashMap[txHashStr] + delete(ms.TxHashMap, txHashStr) if len(sSessionId) > 0 { - ws.respondToId(sSessionId, resp) + ms.respondToId(sSessionId, resp) } - ws.PushResult(resp) + ms.PushResult(resp) } -func (ws *WsServer) PushResult(resp map[string]interface{}) { +func (ms *MsgServer) PushResult(resp map[string]interface{}) { resp["Desc"] = errcode.ErrMessage[resp["Error"].(errcode.ErrCode)] data, err := json.Marshal(resp) if err != nil { log.Error("Websocket PushResult:", err) return } - ws.Broadcast(data) + ms.Broadcast(data) } -func (ws *WsServer) Broadcast(data []byte) error { - ws.SessionList.ForEachSession(func(s *session.Session) { +func (ms *MsgServer) Broadcast(data []byte) error { + ms.SessionList.ForEachSession(func(s *session.Session) { s.SendText(data) }) return nil } -func (ws *WsServer) initTlsListen() (net.Listener, error) { +func (ms *MsgServer) initTlsListen() (net.Listener, error) { tlsConfig := &tls.Config{ GetCertificate: api.GetWssCertificate, } @@ -566,23 +439,23 @@ func (ws *WsServer) initTlsListen() (net.Listener, error) { return listener, nil } -func (ws *WsServer) GetClientsById(cliendID []byte) []*session.Session { - sessions := ws.SessionList.GetSessionsById(hex.EncodeToString(cliendID)) +func (ms *MsgServer) GetClientsById(cliendID []byte) []*session.Session { + sessions := ms.SessionList.GetSessionsById(hex.EncodeToString(cliendID)) return sessions } -func (ws *WsServer) GetNetNode() node.ILocalNode { - return ws.localNode +func (ms *MsgServer) GetNetNode() node.ILocalNode { + return ms.localNode } -func (ws *WsServer) NotifyWrongClients() { - ws.SessionList.ForEachClient(func(client *session.Session) { +func (ms *MsgServer) NotifyWrongClients() { + ms.SessionList.ForEachClient(func(client *session.Session) { clientID := client.GetID() if clientID == nil { return } - localNode := ws.GetNetNode() + localNode := ms.GetNetNode() var wsAddr, rpcAddr, localAddr string var pubkey, id []byte @@ -602,29 +475,29 @@ func (ws *WsServer) NotifyWrongClients() { if wsAddr != localAddr { resp := api.ResponsePack(errcode.WRONG_NODE) - resp["Result"] = api.NodeInfo(wsAddr, rpcAddr, pubkey, id) - ws.respondToSession(client, resp) + resp["Result"] = api.NodeInfo(wsAddr, rpcAddr, pubkey, id, "") + ms.respondToSession(client, resp) } }) } -func (ws *WsServer) startCheckingWrongClients() { +func (ms *MsgServer) startCheckingWrongClients() { for { time.Sleep(checkWrongClientsInterval) - ws.NotifyWrongClients() + ms.NotifyWrongClients() } } -func (ws *WsServer) sendInboundRelayMessageToClient(v interface{}) { +func (ms *MsgServer) sendInboundRelayMessageToClient(v interface{}) { if msg, ok := v.(*pb.Relay); ok { - ws.sendInboundRelayMessage(msg, true) + ms.sendInboundRelayMessage(msg, true) } else { log.Error("Decode relay message failed") } } // client auth, generate challenge -func (ws *WsServer) sendClientAuthChallenge(sess *session.Session) error { +func (ms *MsgServer) sendClientAuthChallenge(sess *session.Session) error { resp := api.ResponsePack(errcode.SUCCESS) resp["Action"] = "authChallenge" @@ -633,6 +506,71 @@ func (ws *WsServer) sendClientAuthChallenge(sess *session.Session) error { resp["Challenge"] = hex.EncodeToString(challenge) sess.Challenge = challenge // save this challenge for verifying later. - err := ws.respondToSession(sess, resp) + err := ms.respondToSession(sess, resp) return err } + +func (ms *MsgServer) newConnection(conn session.Conn, r *http.Request) { + sess, err := ms.SessionList.NewSession(conn) + if err != nil { + log.Error("websocket NewSession:", err) + return + } + + defer func() { + ms.deleteTxHashs(sess.GetSessionId()) + ms.SessionList.CloseSession(sess) + if err := recover(); err != nil { + log.Error("websocket recover:", err) + } + }() + + conn.SetReadLimit(maxMessageSize) + conn.SetReadDeadline(time.Now().Add(pongTimeout)) + conn.SetPongHandler(func(string) error { + conn.SetReadDeadline(time.Now().Add(pongTimeout)) + sess.UpdateLastReadTime() + return nil + }) + + // client auth + err = ms.sendClientAuthChallenge(sess) + if err != nil { + log.Error("send client auth challenge: ", err) + return + } + + done := make(chan struct{}) + defer close(done) + go func() { + ticker := time.NewTicker(pingInterval) + defer ticker.Stop() + var err error + for { + select { + case <-ticker.C: + err = sess.Ping() + if err != nil { + return + } + case <-done: + return + } + } + }() + + for { + messageType, bysMsg, err := conn.ReadMessage() + if err != nil { + log.Errorf("websocket read message error: %v", err) + break + } + conn.SetReadDeadline(time.Now().Add(pongTimeout)) + sess.UpdateLastReadTime() + + err = ms.OnDataHandle(sess, messageType, bysMsg, r) + if err != nil { + log.Error(err) + } + } +} diff --git a/api/websocket/server/wsserver.go b/api/websocket/server/wsserver.go new file mode 100644 index 000000000..f91762165 --- /dev/null +++ b/api/websocket/server/wsserver.go @@ -0,0 +1,117 @@ +package server + +import ( + "context" + "crypto/tls" + "net" + "net/http" + "strconv" + "time" + + "github.com/gorilla/websocket" + api "github.com/nknorg/nkn/v2/api/common" + "github.com/nknorg/nkn/v2/api/ratelimiter" + "github.com/nknorg/nkn/v2/config" + "github.com/nknorg/nkn/v2/util/log" +) + +// type conn interface { +// Start(s *MsgServer, wssCertReady chan struct{}) error +// } + +type wsServer struct { + s *MsgServer + Upgrader websocket.Upgrader + listener net.Listener + tlsListener net.Listener + server *http.Server + tlsServer *http.Server +} + +func (ws *wsServer) Start(s *MsgServer, wssCertReady chan struct{}) error { + ws.s = s + if config.Parameters.HttpWsPort == 0 { + log.Error("Not configure HttpWsPort port ") + return nil + } + ws.Upgrader.CheckOrigin = func(r *http.Request) bool { + return true + } + + var err error + + ws.listener, err = net.Listen("tcp", ":"+strconv.Itoa(int(config.Parameters.HttpWsPort))) + if err != nil { + log.Error("net.Listen: ", err.Error()) + return err + } + + ws.server = &http.Server{Handler: http.HandlerFunc(ws.websocketHandler)} + go ws.server.Serve(ws.listener) + + go func(wssCertReady chan struct{}) { + if wssCertReady == nil { + return + } + for { + select { + case <-wssCertReady: + log.Info("wss cert received") + ws.tlsListener, err = ws.initTlsListen() + if err != nil { + log.Error("Https Cert: ", err.Error()) + } + err = ws.server.Serve(ws.tlsListener) + if err != nil { + log.Error(err) + } + return + case <-time.After(300 * time.Second): + log.Info("wss server is unavailable yet") + } + } + }(wssCertReady) + + return nil +} + +func (ws *wsServer) websocketHandler(w http.ResponseWriter, r *http.Request) { + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err == nil { + limiter := ratelimiter.GetLimiter("ws:"+host, config.Parameters.WsIPRateLimit, int(config.Parameters.WsIPRateBurst)) + if !limiter.Allow() { + log.Infof("Ws connection limit of %s reached", host) + w.WriteHeader(http.StatusTooManyRequests) + return + } + } + + wsServer, err := ws.Upgrader.Upgrade(w, r, nil) + if err != nil { + log.Error("websocket Upgrader: ", err) + return + } + defer wsServer.Close() + + ws.s.newConnection(wsServer, r) +} + +func (ws *wsServer) initTlsListen() (net.Listener, error) { + tlsConfig := &tls.Config{ + GetCertificate: api.GetWssCertificate, + } + + listener, err := tls.Listen("tcp", ":"+strconv.Itoa(int(config.Parameters.HttpWssPort)), tlsConfig) + if err != nil { + log.Error(err) + return nil, err + } + return listener, nil +} + +func (ws *wsServer) Stop() { + if ws.server != nil { + ws.server.Shutdown(context.Background()) + log.Error("Close websocket ") + } +} diff --git a/api/websocket/session/session.go b/api/websocket/session/session.go index 5435ccfe5..a9d152219 100644 --- a/api/websocket/session/session.go +++ b/api/websocket/session/session.go @@ -13,6 +13,17 @@ const ( writeTimeout = 10 * time.Second ) +type Conn interface { + SetReadLimit(int64) + SetReadDeadline(t time.Time) error + SetWriteDeadline(t time.Time) error + WriteMessage(messageType int, data []byte) (err error) + WriteJSON(v interface{}) error + ReadMessage() (messageType int, data []byte, err error) + SetPongHandler(func(string) error) + Close() error +} + type Session struct { sync.RWMutex sessionID string @@ -23,7 +34,7 @@ type Session struct { lastReadTime time.Time wsLock sync.Mutex - ws *websocket.Conn + ws Conn Challenge []byte // client auth, authorization challenge connectTime time.Time // The time which the session is established. @@ -33,7 +44,7 @@ func (s *Session) GetSessionId() string { return s.sessionID } -func newSession(wsConn *websocket.Conn) (session *Session, err error) { +func newSession(wsConn Conn) (session *Session, err error) { sessionID := uuid.NewUUID().String() session = &Session{ ws: wsConn, diff --git a/api/websocket/session/sessionlist.go b/api/websocket/session/sessionlist.go index a664671d4..51ac6f81b 100644 --- a/api/websocket/session/sessionlist.go +++ b/api/websocket/session/sessionlist.go @@ -3,8 +3,6 @@ package session import ( "errors" "sync" - - "github.com/gorilla/websocket" ) type SessionList struct { @@ -18,7 +16,7 @@ func NewSessionList() *SessionList { } } -func (sl *SessionList) NewSession(wsConn *websocket.Conn) (*Session, error) { +func (sl *SessionList) NewSession(wsConn Conn) (*Session, error) { session, err := newSession(wsConn) if err != nil { return nil, err diff --git a/api/websocket/websocket.go b/api/websocket/websocket.go index 891605410..f28745eb2 100644 --- a/api/websocket/websocket.go +++ b/api/websocket/websocket.go @@ -16,7 +16,7 @@ import ( "github.com/nknorg/nkn/v2/vault" ) -var ws *server.WsServer +var ws *server.MsgServer var ( pushBlockFlag bool = false @@ -24,10 +24,10 @@ var ( pushBlockTxsFlag bool = false ) -func NewServer(localNode node.ILocalNode, w *vault.Wallet) *server.WsServer { +func NewServer(localNode node.ILocalNode, w *vault.Wallet) *server.MsgServer { // common.SetNode(n) event.Queue.Subscribe(event.NewBlockProduced, SendBlock2WSclient) - ws = server.InitWsServer(localNode, w) + ws = server.InitMsgServer(localNode, w) return ws } @@ -124,6 +124,6 @@ func PushSigChainBlockHash(v interface{}) { } } -func GetServer() *server.WsServer { +func GetServer() *server.MsgServer { return ws } diff --git a/cmd/nknd/commands/root.go b/cmd/nknd/commands/root.go index 36ed5f1fc..1c6f945be 100644 --- a/cmd/nknd/commands/root.go +++ b/cmd/nknd/commands/root.go @@ -100,6 +100,7 @@ func init() { rootCmd.Flags().StringVar(&config.WebGuiListenAddress, "web-gui-listen-address", "", "web gui will listen this address (default: 127.0.0.1)") rootCmd.Flags().BoolVar(&config.WebGuiCreateWallet, "web-gui-create-wallet", false, "web gui create/open wallet") rootCmd.Flags().StringVar(&config.PasswordFile, "password-file", "", "read password from file, save password to file when --web-gui-create-wallet arguments be true and password file does not exist") + rootCmd.Flags().StringVar(&config.StunList, "stun", "", "Webrtc stun servers, multiple servers should be split by comma") rootCmd.Flags().MarkHidden("passwd") } diff --git a/config.local.json b/config.local.json index 562b3a41a..3500ac222 100644 --- a/config.local.json +++ b/config.local.json @@ -6,5 +6,10 @@ "SeedList": [ "http://127.0.0.1:30003" ], + "StunList": [ + "stun:stun.l.google.com:19302", + "stun:stun.cloudflare.com:3478", + "stun:stunserver.stunprotocol.org:3478" + ], "GenesisBlockProposer": "" } diff --git a/config.mainnet.json b/config.mainnet.json index 2f3e2bdf5..8468c5ca6 100644 --- a/config.mainnet.json +++ b/config.mainnet.json @@ -46,5 +46,10 @@ "http://mainnet-seed-0043.nkn.org:30003", "http://mainnet-seed-0044.nkn.org:30003" ], + "StunList": [ + "stun:stun.l.google.com:19302", + "stun:stun.cloudflare.com:3478", + "stun:stunserver.stunprotocol.org:3478" + ], "GenesisBlockProposer": "a0309f8280ca86687a30ca86556113a253762e40eb884fc6063cad2b1ebd7de5" } diff --git a/config.testnet.json b/config.testnet.json index 92302ba9e..c1d6fa9be 100644 --- a/config.testnet.json +++ b/config.testnet.json @@ -7,6 +7,11 @@ "http://devnet-seed-0003.nkn.org:30003", "http://devnet-seed-0004.nkn.org:30003" ], + "StunList": [ + "stun:stun.l.google.com:19302", + "stun:stun.cloudflare.com:3478", + "stun:stunserver.stunprotocol.org:3478" + ], "GenesisBlockProposer": "0149c42944eea91f094c16538eff0449d4d1e236f31c8c706b2e40e98402984c", "BeneficiaryAddr": "" } diff --git a/config/config.go b/config/config.go index 405398587..e184f21e2 100644 --- a/config/config.go +++ b/config/config.go @@ -249,6 +249,7 @@ var ( WalletFile string BeneficiaryAddr string SeedList string + StunList string GenesisBlockProposer string AllowEmptyBeneficiaryAddress bool WebGuiListenAddress string @@ -332,6 +333,7 @@ var ( type Configuration struct { Version int `json:"Version"` SeedList []string `json:"SeedList"` + StunList []string `json:"StunList"` HttpWssDomain string `json:"HttpWssDomain"` HttpWssCert string `json:"HttpWssCert"` HttpWssKey string `json:"HttpWssKey"` @@ -449,6 +451,10 @@ func Init() error { Parameters.SeedList = strings.Split(SeedList, ",") } + if len(StunList) > 0 { + Parameters.StunList = strings.Split(StunList, ",") + } + if len(GenesisBlockProposer) > 0 { Parameters.GenesisBlockProposer = GenesisBlockProposer } @@ -558,6 +564,10 @@ func (config *Configuration) verify() error { return errors.New("seed list in config file should not be blank") } + if len(config.StunList) == 0 { + return errors.New("stun list in config file should not be blank") + } + if config.NumTxnPerBlock > MaxNumTxnPerBlock { return fmt.Errorf("NumTxnPerBlock cannot be greater than %d", MaxNumTxnPerBlock) } diff --git a/go.mod b/go.mod index 168390e10..d46ffb89a 100644 --- a/go.mod +++ b/go.mod @@ -21,15 +21,17 @@ require ( github.com/rdegges/go-ipify v0.0.0-20150526035502-2d94a6a86c40 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.3 + github.com/stretchr/testify v1.9.0 github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 github.com/wk8/go-ordered-map v1.0.0 - golang.org/x/crypto v0.17.0 - golang.org/x/sys v0.15.0 // indirect + golang.org/x/crypto v0.21.0 + golang.org/x/sys v0.18.0 // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 google.golang.org/protobuf v1.30.0 ) +require github.com/pion/webrtc/v4 v4.0.0-beta.17 + require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/cenkalti/backoff/v4 v4.0.0 // indirect @@ -43,7 +45,7 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.1 // indirect - github.com/google/uuid v1.1.1 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/sessions v1.1.3 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect @@ -65,6 +67,22 @@ require ( github.com/nknorg/go-nat v1.0.1 // indirect github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pion/datachannel v1.5.6 // indirect + github.com/pion/dtls/v2 v2.2.10 // indirect + github.com/pion/ice/v3 v3.0.6 // indirect + github.com/pion/interceptor v0.1.29 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.14 // indirect + github.com/pion/rtp v1.8.5 // indirect + github.com/pion/sctp v1.8.16 // indirect + github.com/pion/sdp/v3 v3.0.9 // indirect + github.com/pion/srtp/v3 v3.0.1 // indirect + github.com/pion/stun/v2 v2.0.0 // indirect + github.com/pion/transport/v2 v2.2.4 // indirect + github.com/pion/transport/v3 v3.0.2 // indirect + github.com/pion/turn/v3 v3.0.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect @@ -77,8 +95,8 @@ require ( gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 // indirect gitlab.com/NebulousLabs/go-upnp v0.0.0-20181011194642-3a71999ed0d3 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/square/go-jose.v2 v2.3.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 9a97083b5..94208060a 100644 --- a/go.sum +++ b/go.sum @@ -173,8 +173,9 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= @@ -300,12 +301,12 @@ github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXW github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -320,6 +321,46 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg= +github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA= +github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/ice/v3 v3.0.6 h1:UC5vZCMhmve7yv+Y6E5eTnRTl+t9LLtmeBYQ9038Zm8= +github.com/pion/ice/v3 v3.0.6/go.mod h1:4eMTUKQEjC1fGQGB6qUzy2ux9Pc1v9EsO3hNaii+kXI= +github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M= +github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= +github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE= +github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= +github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.5 h1:uYzINfaK+9yWs7r537z/Rc1SvT8ILjBcmDOpJcTB+OU= +github.com/pion/rtp v1.8.5/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA= +github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY= +github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= +github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= +github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= +github.com/pion/srtp/v3 v3.0.1 h1:AkIQRIZ+3tAOJMQ7G301xtrD1vekQbNeRO7eY1K8ZHk= +github.com/pion/srtp/v3 v3.0.1/go.mod h1:3R3a1qIOIxBkVTLGFjafKK6/fJoTdQDhcC67HOyMbJ8= +github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= +github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= +github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= +github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= +github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= +github.com/pion/turn/v3 v3.0.2 h1:iBonAIIKRwkVUJBFiFd/kSjytP7FlX0HwCyBDJPRDdU= +github.com/pion/turn/v3 v3.0.2/go.mod h1:vw0Dz420q7VYAF3J4wJKzReLHIo2LGp4ev8nXQexYsc= +github.com/pion/webrtc/v4 v4.0.0-beta.17 h1:KdAbozM+lQ3Dz1NJ0JATRDQ4W02WUhWwIkvjyBRODL0= +github.com/pion/webrtc/v4 v4.0.0-beta.17/go.mod h1:I/Z0MFtc6Ok7mN7kZmA1xqU7KA9ycZZx/6eXz5+yD+4= github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -368,6 +409,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -378,8 +420,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= @@ -409,6 +453,7 @@ github.com/xtaci/smux v1.2.11 h1:QI4M2HgkkpsVU3Bfcmyx10qURBEeHfKi7xDhGEORfu0= github.com/xtaci/smux v1.2.11/go.mod h1:f+nYm6SpuHMy/SH0zpbvAFHT1QoMcgLOsWcFip5KfPw= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 h1:dizWJqTWjwyD8KGcMOwgrkqu1JIkofYgKkmDeNE7oAs= gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40/go.mod h1:rOnSnoRyxMI3fe/7KIbVcsHRGxe30OONv8dEgo+vCfA= gitlab.com/NebulousLabs/go-upnp v0.0.0-20181011194642-3a71999ed0d3 h1:qXqiXDgeQxspR3reot1pWme00CX1pXbxesdzND+EjbU= @@ -435,8 +480,13 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -467,6 +517,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -497,8 +549,16 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -510,8 +570,10 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -549,17 +611,39 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -603,6 +687,8 @@ golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -662,8 +748,9 @@ google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cn google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=