github.com/cryptohub-digital/blockbook-fork@v0.0.0-20230713133354-673c927af7f1/server/websocket.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/json"
     5  	"math/big"
     6  	"net/http"
     7  	"runtime/debug"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/cryptohub-digital/blockbook-fork/api"
    15  	"github.com/cryptohub-digital/blockbook-fork/bchain"
    16  	"github.com/cryptohub-digital/blockbook-fork/common"
    17  	"github.com/cryptohub-digital/blockbook-fork/db"
    18  	"github.com/cryptohub-digital/blockbook-fork/fiat"
    19  	"github.com/golang/glog"
    20  	"github.com/gorilla/websocket"
    21  	"github.com/juju/errors"
    22  )
    23  
    24  const upgradeFailed = "Upgrade failed: "
    25  const outChannelSize = 500
    26  const defaultTimeout = 60 * time.Second
    27  
    28  // allRates is a special "currency" parameter that means all available currencies
    29  const allFiatRates = "!ALL!"
    30  
    31  var (
    32  	// ErrorMethodNotAllowed is returned when client tries to upgrade method other than GET
    33  	ErrorMethodNotAllowed = errors.New("Method not allowed")
    34  
    35  	connectionCounter uint64
    36  )
    37  
    38  type websocketChannel struct {
    39  	id            uint64
    40  	conn          *websocket.Conn
    41  	out           chan *WsRes
    42  	ip            string
    43  	requestHeader http.Header
    44  	alive         bool
    45  	aliveLock     sync.Mutex
    46  	addrDescs     []string // subscribed address descriptors as strings
    47  }
    48  
    49  // WebsocketServer is a handle to websocket server
    50  type WebsocketServer struct {
    51  	upgrader                        *websocket.Upgrader
    52  	db                              *db.RocksDB
    53  	txCache                         *db.TxCache
    54  	chain                           bchain.BlockChain
    55  	chainParser                     bchain.BlockChainParser
    56  	mempool                         bchain.Mempool
    57  	metrics                         *common.Metrics
    58  	is                              *common.InternalState
    59  	api                             *api.Worker
    60  	block0hash                      string
    61  	newBlockSubscriptions           map[*websocketChannel]string
    62  	newBlockSubscriptionsLock       sync.Mutex
    63  	newTransactionEnabled           bool
    64  	newTransactionSubscriptions     map[*websocketChannel]string
    65  	newTransactionSubscriptionsLock sync.Mutex
    66  	addressSubscriptions            map[string]map[*websocketChannel]string
    67  	addressSubscriptionsLock        sync.Mutex
    68  	fiatRatesSubscriptions          map[string]map[*websocketChannel]string
    69  	fiatRatesTokenSubscriptions     map[*websocketChannel][]string
    70  	fiatRatesSubscriptionsLock      sync.Mutex
    71  }
    72  
    73  // NewWebsocketServer creates new websocket interface to blockbook and returns its handle
    74  func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*WebsocketServer, error) {
    75  	api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	b0, err := db.GetBlockHash(0)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	s := &WebsocketServer{
    84  		upgrader: &websocket.Upgrader{
    85  			ReadBufferSize:  1024 * 32,
    86  			WriteBufferSize: 1024 * 32,
    87  			CheckOrigin:     checkOrigin,
    88  		},
    89  		db:                          db,
    90  		txCache:                     txCache,
    91  		chain:                       chain,
    92  		chainParser:                 chain.GetChainParser(),
    93  		mempool:                     mempool,
    94  		metrics:                     metrics,
    95  		is:                          is,
    96  		api:                         api,
    97  		block0hash:                  b0,
    98  		newBlockSubscriptions:       make(map[*websocketChannel]string),
    99  		newTransactionEnabled:       is.EnableSubNewTx,
   100  		newTransactionSubscriptions: make(map[*websocketChannel]string),
   101  		addressSubscriptions:        make(map[string]map[*websocketChannel]string),
   102  		fiatRatesSubscriptions:      make(map[string]map[*websocketChannel]string),
   103  		fiatRatesTokenSubscriptions: make(map[*websocketChannel][]string),
   104  	}
   105  	return s, nil
   106  }
   107  
   108  // allow all origins
   109  func checkOrigin(r *http.Request) bool {
   110  	return true
   111  }
   112  
   113  func getIP(r *http.Request) string {
   114  	ip := r.Header.Get("X-Real-Ip")
   115  	if ip != "" {
   116  		return ip
   117  	}
   118  	return r.RemoteAddr
   119  }
   120  
   121  // ServeHTTP sets up handler of websocket channel
   122  func (s *WebsocketServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   123  	if r.Method != "GET" {
   124  		http.Error(w, upgradeFailed+ErrorMethodNotAllowed.Error(), 503)
   125  		return
   126  	}
   127  	conn, err := s.upgrader.Upgrade(w, r, nil)
   128  	if err != nil {
   129  		http.Error(w, upgradeFailed+err.Error(), 503)
   130  		return
   131  	}
   132  	c := &websocketChannel{
   133  		id:            atomic.AddUint64(&connectionCounter, 1),
   134  		conn:          conn,
   135  		out:           make(chan *WsRes, outChannelSize),
   136  		ip:            getIP(r),
   137  		requestHeader: r.Header,
   138  		alive:         true,
   139  	}
   140  	go s.inputLoop(c)
   141  	go s.outputLoop(c)
   142  	s.onConnect(c)
   143  }
   144  
   145  // GetHandler returns http handler
   146  func (s *WebsocketServer) GetHandler() http.Handler {
   147  	return s
   148  }
   149  
   150  func (s *WebsocketServer) closeChannel(c *websocketChannel) {
   151  	if c.CloseOut() {
   152  		c.conn.Close()
   153  		s.onDisconnect(c)
   154  	}
   155  }
   156  
   157  func (c *websocketChannel) CloseOut() bool {
   158  	c.aliveLock.Lock()
   159  	defer c.aliveLock.Unlock()
   160  	if c.alive {
   161  		c.alive = false
   162  		//clean out
   163  		close(c.out)
   164  		for len(c.out) > 0 {
   165  			<-c.out
   166  		}
   167  		return true
   168  	}
   169  	return false
   170  }
   171  
   172  func (c *websocketChannel) DataOut(data *WsRes) {
   173  	c.aliveLock.Lock()
   174  	defer c.aliveLock.Unlock()
   175  	if c.alive {
   176  		if len(c.out) < outChannelSize-1 {
   177  			c.out <- data
   178  		} else {
   179  			glog.Warning("Channel ", c.id, " overflow, closing")
   180  			// close the connection but do not call CloseOut - would call duplicate c.aliveLock.Lock
   181  			// CloseOut will be called because the closed connection will cause break in the inputLoop
   182  			c.conn.Close()
   183  		}
   184  	}
   185  }
   186  
   187  func (s *WebsocketServer) inputLoop(c *websocketChannel) {
   188  	defer func() {
   189  		if r := recover(); r != nil {
   190  			glog.Error("recovered from panic: ", r, ", ", c.id)
   191  			debug.PrintStack()
   192  			s.closeChannel(c)
   193  		}
   194  	}()
   195  	for {
   196  		t, d, err := c.conn.ReadMessage()
   197  		if err != nil {
   198  			s.closeChannel(c)
   199  			return
   200  		}
   201  		switch t {
   202  		case websocket.TextMessage:
   203  			var req WsReq
   204  			err := json.Unmarshal(d, &req)
   205  			if err != nil {
   206  				glog.Error("Error parsing message from ", c.id, ", ", string(d), ", ", err)
   207  				s.closeChannel(c)
   208  				return
   209  			}
   210  			go s.onRequest(c, &req)
   211  		case websocket.BinaryMessage:
   212  			glog.Error("Binary message received from ", c.id, ", ", c.ip)
   213  			s.closeChannel(c)
   214  			return
   215  		case websocket.PingMessage:
   216  			c.conn.WriteControl(websocket.PongMessage, nil, time.Now().Add(defaultTimeout))
   217  		case websocket.CloseMessage:
   218  			s.closeChannel(c)
   219  			return
   220  		case websocket.PongMessage:
   221  			// do nothing
   222  		}
   223  	}
   224  }
   225  
   226  func (s *WebsocketServer) outputLoop(c *websocketChannel) {
   227  	defer func() {
   228  		if r := recover(); r != nil {
   229  			glog.Error("recovered from panic: ", r, ", ", c.id)
   230  			s.closeChannel(c)
   231  		}
   232  	}()
   233  	for m := range c.out {
   234  		err := c.conn.WriteJSON(m)
   235  		if err != nil {
   236  			glog.Error("Error sending message to ", c.id, ", ", err)
   237  			s.closeChannel(c)
   238  			return
   239  		}
   240  	}
   241  }
   242  
   243  func (s *WebsocketServer) onConnect(c *websocketChannel) {
   244  	glog.Info("Client connected ", c.id, ", ", c.ip)
   245  	s.metrics.WebsocketClients.Inc()
   246  }
   247  
   248  func (s *WebsocketServer) onDisconnect(c *websocketChannel) {
   249  	s.unsubscribeNewBlock(c)
   250  	s.unsubscribeNewTransaction(c)
   251  	s.unsubscribeAddresses(c)
   252  	s.unsubscribeFiatRates(c)
   253  	glog.Info("Client disconnected ", c.id, ", ", c.ip)
   254  	s.metrics.WebsocketClients.Dec()
   255  }
   256  
   257  var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *WsReq) (interface{}, error){
   258  	"getAccountInfo": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   259  		r, err := unmarshalGetAccountInfoRequest(req.Params)
   260  		if err == nil {
   261  			rv, err = s.getAccountInfo(r)
   262  		}
   263  		return
   264  	},
   265  	"getInfo": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   266  		return s.getInfo()
   267  	},
   268  	"getBlockHash": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   269  		r := WsBlockHashReq{}
   270  		err = json.Unmarshal(req.Params, &r)
   271  		if err == nil {
   272  			rv, err = s.getBlockHash(r.Height)
   273  		}
   274  		return
   275  	},
   276  	"getBlock": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   277  		if !s.is.ExtendedIndex {
   278  			return nil, errors.New("Not supported")
   279  		}
   280  		r := WsBlockReq{}
   281  		err = json.Unmarshal(req.Params, &r)
   282  		if r.PageSize == 0 {
   283  			r.PageSize = 1000000
   284  		}
   285  		if err == nil {
   286  			rv, err = s.getBlock(r.Id, r.Page, r.PageSize)
   287  		}
   288  		return
   289  	},
   290  	"getAccountUtxo": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   291  		r := WsAccountUtxoReq{}
   292  		err = json.Unmarshal(req.Params, &r)
   293  		if err == nil {
   294  			rv, err = s.getAccountUtxo(r.Descriptor)
   295  		}
   296  		return
   297  	},
   298  	"getBalanceHistory": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   299  		r := WsBalanceHistoryReq{}
   300  		err = json.Unmarshal(req.Params, &r)
   301  		if err == nil {
   302  			if r.From <= 0 {
   303  				r.From = 0
   304  			}
   305  			if r.To <= 0 {
   306  				r.To = 0
   307  			}
   308  			if r.GroupBy <= 0 {
   309  				r.GroupBy = 3600
   310  			}
   311  			rv, err = s.api.GetXpubBalanceHistory(r.Descriptor, r.From, r.To, r.Currencies, r.Gap, r.GroupBy)
   312  			if err != nil {
   313  				rv, err = s.api.GetBalanceHistory(r.Descriptor, r.From, r.To, r.Currencies, r.GroupBy)
   314  			}
   315  		}
   316  		return
   317  	},
   318  	"getTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   319  		r := WsTransactionReq{}
   320  		err = json.Unmarshal(req.Params, &r)
   321  		if err == nil {
   322  			rv, err = s.getTransaction(r.Txid)
   323  		}
   324  		return
   325  	},
   326  	"getTransactionSpecific": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   327  		r := WsTransactionSpecificReq{}
   328  		err = json.Unmarshal(req.Params, &r)
   329  		if err == nil {
   330  			rv, err = s.getTransactionSpecific(r.Txid)
   331  		}
   332  		return
   333  	},
   334  	"estimateFee": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   335  		return s.estimateFee(c, req.Params)
   336  	},
   337  	"sendTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   338  		r := WsSendTransactionReq{}
   339  		err = json.Unmarshal(req.Params, &r)
   340  		if err == nil {
   341  			rv, err = s.sendTransaction(r.Hex)
   342  		}
   343  		return
   344  	},
   345  	"getMempoolFilters": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   346  		r := WsMempoolFiltersReq{}
   347  		err = json.Unmarshal(req.Params, &r)
   348  		if err == nil {
   349  			rv, err = s.getMempoolFilters(&r)
   350  		}
   351  		return
   352  	},
   353  	"subscribeNewBlock": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   354  		return s.subscribeNewBlock(c, req)
   355  	},
   356  	"unsubscribeNewBlock": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   357  		return s.unsubscribeNewBlock(c)
   358  	},
   359  	"subscribeNewTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   360  		return s.subscribeNewTransaction(c, req)
   361  	},
   362  	"unsubscribeNewTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   363  		return s.unsubscribeNewTransaction(c)
   364  	},
   365  	"subscribeAddresses": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   366  		ad, err := s.unmarshalAddresses(req.Params)
   367  		if err == nil {
   368  			rv, err = s.subscribeAddresses(c, ad, req)
   369  		}
   370  		return
   371  	},
   372  	"unsubscribeAddresses": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   373  		return s.unsubscribeAddresses(c)
   374  	},
   375  	"subscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   376  		var r WsSubscribeFiatRatesReq
   377  		err = json.Unmarshal(req.Params, &r)
   378  		if err != nil {
   379  			return nil, err
   380  		}
   381  		r.Currency = strings.ToLower(r.Currency)
   382  		for i := range r.Tokens {
   383  			r.Tokens[i] = strings.ToLower(r.Tokens[i])
   384  		}
   385  		return s.subscribeFiatRates(c, &r, req)
   386  	},
   387  	"unsubscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   388  		return s.unsubscribeFiatRates(c)
   389  	},
   390  	"ping": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   391  		r := struct{}{}
   392  		return r, nil
   393  	},
   394  	"getCurrentFiatRates": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   395  		r := WsCurrentFiatRatesReq{}
   396  		err = json.Unmarshal(req.Params, &r)
   397  		if err == nil {
   398  			rv, err = s.getCurrentFiatRates(r.Currencies, r.Token)
   399  		}
   400  		return
   401  	},
   402  	"getFiatRatesForTimestamps": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   403  		r := WsFiatRatesForTimestampsReq{}
   404  		err = json.Unmarshal(req.Params, &r)
   405  		if err == nil {
   406  			rv, err = s.getFiatRatesForTimestamps(r.Timestamps, r.Currencies, r.Token)
   407  		}
   408  		return
   409  	},
   410  	"getFiatRatesTickersList": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
   411  		r := WsFiatRatesTickersListReq{}
   412  		err = json.Unmarshal(req.Params, &r)
   413  		if err == nil {
   414  			rv, err = s.getAvailableVsCurrencies(r.Timestamp, r.Token)
   415  		}
   416  		return
   417  	},
   418  }
   419  
   420  func (s *WebsocketServer) onRequest(c *websocketChannel, req *WsReq) {
   421  	var err error
   422  	var data interface{}
   423  	defer func() {
   424  		if r := recover(); r != nil {
   425  			glog.Error("Client ", c.id, ", onRequest ", req.Method, " recovered from panic: ", r)
   426  			debug.PrintStack()
   427  			e := resultError{}
   428  			e.Error.Message = "Internal error"
   429  			data = e
   430  		}
   431  		// nil data means no response
   432  		if data != nil {
   433  			c.DataOut(&WsRes{
   434  				ID:   req.ID,
   435  				Data: data,
   436  			})
   437  		}
   438  		s.metrics.WebsocketPendingRequests.With((common.Labels{"method": req.Method})).Dec()
   439  	}()
   440  	t := time.Now()
   441  	s.metrics.WebsocketPendingRequests.With((common.Labels{"method": req.Method})).Inc()
   442  	defer s.metrics.WebsocketReqDuration.With(common.Labels{"method": req.Method}).Observe(float64(time.Since(t)) / 1e3) // in microseconds
   443  	f, ok := requestHandlers[req.Method]
   444  	if ok {
   445  		data, err = f(s, c, req)
   446  		if err == nil {
   447  			glog.V(1).Info("Client ", c.id, " onRequest ", req.Method, " success")
   448  			s.metrics.WebsocketRequests.With(common.Labels{"method": req.Method, "status": "success"}).Inc()
   449  		} else {
   450  			if apiErr, ok := err.(*api.APIError); !ok || !apiErr.Public {
   451  				glog.Error("Client ", c.id, " onMessage ", req.Method, ": ", errors.ErrorStack(err), ", data ", string(req.Params))
   452  			}
   453  			s.metrics.WebsocketRequests.With(common.Labels{"method": req.Method, "status": "failure"}).Inc()
   454  			e := resultError{}
   455  			e.Error.Message = err.Error()
   456  			data = e
   457  		}
   458  	} else {
   459  		glog.V(1).Info("Client ", c.id, " onMessage ", req.Method, ": unknown method, data ", string(req.Params))
   460  	}
   461  }
   462  
   463  func unmarshalGetAccountInfoRequest(params []byte) (*WsAccountInfoReq, error) {
   464  	var r WsAccountInfoReq
   465  	err := json.Unmarshal(params, &r)
   466  	if err != nil {
   467  		return nil, err
   468  	}
   469  	return &r, nil
   470  }
   471  
   472  func (s *WebsocketServer) getAccountInfo(req *WsAccountInfoReq) (res *api.Address, err error) {
   473  	var opt api.AccountDetails
   474  	switch req.Details {
   475  	case "tokens":
   476  		opt = api.AccountDetailsTokens
   477  	case "tokenBalances":
   478  		opt = api.AccountDetailsTokenBalances
   479  	case "txids":
   480  		opt = api.AccountDetailsTxidHistory
   481  	case "txslight":
   482  		opt = api.AccountDetailsTxHistoryLight
   483  	case "txs":
   484  		opt = api.AccountDetailsTxHistory
   485  	default:
   486  		opt = api.AccountDetailsBasic
   487  	}
   488  	var tokensToReturn api.TokensToReturn
   489  	switch req.Tokens {
   490  	case "used":
   491  		tokensToReturn = api.TokensToReturnUsed
   492  	case "nonzero":
   493  		tokensToReturn = api.TokensToReturnNonzeroBalance
   494  	default:
   495  		tokensToReturn = api.TokensToReturnDerived
   496  	}
   497  	filter := api.AddressFilter{
   498  		FromHeight:     uint32(req.FromHeight),
   499  		ToHeight:       uint32(req.ToHeight),
   500  		Contract:       req.ContractFilter,
   501  		Vout:           api.AddressFilterVoutOff,
   502  		TokensToReturn: tokensToReturn,
   503  	}
   504  	if req.PageSize == 0 {
   505  		req.PageSize = txsOnPage
   506  	}
   507  	a, err := s.api.GetXpubAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, req.Gap, strings.ToLower(req.SecondaryCurrency))
   508  	if err != nil {
   509  		return s.api.GetAddress(req.Descriptor, req.Page, req.PageSize, opt, &filter, strings.ToLower(req.SecondaryCurrency))
   510  	}
   511  	return a, nil
   512  }
   513  
   514  func (s *WebsocketServer) getAccountUtxo(descriptor string) (api.Utxos, error) {
   515  	utxo, err := s.api.GetXpubUtxo(descriptor, false, 0)
   516  	if err != nil {
   517  		return s.api.GetAddressUtxo(descriptor, false)
   518  	}
   519  	return utxo, nil
   520  }
   521  
   522  func (s *WebsocketServer) getTransaction(txid string) (*api.Tx, error) {
   523  	return s.api.GetTransaction(txid, false, false)
   524  }
   525  
   526  func (s *WebsocketServer) getTransactionSpecific(txid string) (interface{}, error) {
   527  	return s.chain.GetTransactionSpecific(&bchain.Tx{Txid: txid})
   528  }
   529  
   530  func (s *WebsocketServer) getInfo() (*WsInfoRes, error) {
   531  	vi := common.GetVersionInfo()
   532  	bi := s.is.GetBackendInfo()
   533  	height, hash, err := s.db.GetBestBlock()
   534  	if err != nil {
   535  		return nil, err
   536  	}
   537  	return &WsInfoRes{
   538  		Name:       s.is.Coin,
   539  		Shortcut:   s.is.CoinShortcut,
   540  		Decimals:   s.chainParser.AmountDecimals(),
   541  		BestHeight: int(height),
   542  		BestHash:   hash,
   543  		Version:    vi.Version,
   544  		Block0Hash: s.block0hash,
   545  		Testnet:    s.chain.IsTestnet(),
   546  		Backend: WsBackendInfo{
   547  			Version:          bi.Version,
   548  			Subversion:       bi.Subversion,
   549  			ConsensusVersion: bi.ConsensusVersion,
   550  			Consensus:        bi.Consensus,
   551  		},
   552  	}, nil
   553  }
   554  
   555  func (s *WebsocketServer) getBlockHash(height int) (*WsBlockHashRes, error) {
   556  	h, err := s.db.GetBlockHash(uint32(height))
   557  	if err != nil {
   558  		return nil, err
   559  	}
   560  	return &WsBlockHashRes{
   561  		Hash: h,
   562  	}, nil
   563  }
   564  
   565  func (s *WebsocketServer) getBlock(id string, page, pageSize int) (interface{}, error) {
   566  	block, err := s.api.GetBlock(id, page, pageSize)
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  	return block, nil
   571  }
   572  
   573  func (s *WebsocketServer) estimateFee(c *websocketChannel, params []byte) (interface{}, error) {
   574  	var r WsEstimateFeeReq
   575  	err := json.Unmarshal(params, &r)
   576  	if err != nil {
   577  		return nil, err
   578  	}
   579  	res := make([]WsEstimateFeeRes, len(r.Blocks))
   580  	if s.chainParser.GetChainType() == bchain.ChainEthereumType {
   581  		gas, err := s.chain.EthereumTypeEstimateGas(r.Specific)
   582  		if err != nil {
   583  			return nil, err
   584  		}
   585  		sg := strconv.FormatUint(gas, 10)
   586  		b := 1
   587  		if len(r.Blocks) > 0 {
   588  			b = r.Blocks[0]
   589  		}
   590  		fee, err := s.api.EstimateFee(b, true)
   591  		if err != nil {
   592  			return nil, err
   593  		}
   594  		for i := range r.Blocks {
   595  			res[i].FeePerUnit = fee.String()
   596  			res[i].FeeLimit = sg
   597  			fee.Mul(&fee, new(big.Int).SetUint64(gas))
   598  			res[i].FeePerTx = fee.String()
   599  		}
   600  	} else if s.chainParser.GetChainType() == bchain.ChainCoreCoinType {
   601  		energy, err := s.chain.CoreCoinTypeEstimateEnergy(r.Specific)
   602  		if err != nil {
   603  			return nil, err
   604  		}
   605  		sg := strconv.FormatUint(energy, 10)
   606  		b := 1
   607  		if len(r.Blocks) > 0 {
   608  			b = r.Blocks[0]
   609  		}
   610  		fee, err := s.api.EstimateFee(b, true)
   611  		if err != nil {
   612  			return nil, err
   613  		}
   614  		for i := range r.Blocks {
   615  			res[i].FeePerUnit = fee.String()
   616  			res[i].FeeLimit = sg
   617  			fee.Mul(&fee, new(big.Int).SetUint64(energy))
   618  			res[i].FeePerTx = fee.String()
   619  		}
   620  	} else {
   621  		conservative := true
   622  		v, ok := r.Specific["conservative"]
   623  		if ok {
   624  			vc, ok := v.(bool)
   625  			if ok {
   626  				conservative = vc
   627  			}
   628  		}
   629  		txSize := 0
   630  		v, ok = r.Specific["txsize"]
   631  		if ok {
   632  			f, ok := v.(float64)
   633  			if ok {
   634  				txSize = int(f)
   635  			}
   636  		}
   637  		for i, b := range r.Blocks {
   638  			fee, err := s.api.EstimateFee(b, conservative)
   639  			if err != nil {
   640  				return nil, err
   641  			}
   642  			res[i].FeePerUnit = fee.String()
   643  			if txSize > 0 {
   644  				fee.Mul(&fee, big.NewInt(int64(txSize)))
   645  				fee.Add(&fee, big.NewInt(500))
   646  				fee.Div(&fee, big.NewInt(1000))
   647  				res[i].FeePerTx = fee.String()
   648  			}
   649  		}
   650  	}
   651  	return res, nil
   652  }
   653  
   654  func (s *WebsocketServer) sendTransaction(tx string) (res resultSendTransaction, err error) {
   655  	txid, err := s.chain.SendRawTransaction(tx)
   656  	if err != nil {
   657  		return res, err
   658  	}
   659  	res.Result = txid
   660  	return
   661  }
   662  
   663  func (s *WebsocketServer) getMempoolFilters(r *WsMempoolFiltersReq) (res bchain.MempoolTxidFilterEntries, err error) {
   664  	res, err = s.mempool.GetTxidFilterEntries(r.ScriptType, r.FromTimestamp)
   665  	return
   666  }
   667  
   668  type subscriptionResponse struct {
   669  	Subscribed bool `json:"subscribed"`
   670  }
   671  type subscriptionResponseMessage struct {
   672  	Subscribed bool   `json:"subscribed"`
   673  	Message    string `json:"message"`
   674  }
   675  
   676  func (s *WebsocketServer) subscribeNewBlock(c *websocketChannel, req *WsReq) (res interface{}, err error) {
   677  	s.newBlockSubscriptionsLock.Lock()
   678  	defer s.newBlockSubscriptionsLock.Unlock()
   679  	s.newBlockSubscriptions[c] = req.ID
   680  	s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeNewBlock"})).Set(float64(len(s.newBlockSubscriptions)))
   681  	return &subscriptionResponse{true}, nil
   682  }
   683  
   684  func (s *WebsocketServer) unsubscribeNewBlock(c *websocketChannel) (res interface{}, err error) {
   685  	s.newBlockSubscriptionsLock.Lock()
   686  	defer s.newBlockSubscriptionsLock.Unlock()
   687  	delete(s.newBlockSubscriptions, c)
   688  	s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeNewBlock"})).Set(float64(len(s.newBlockSubscriptions)))
   689  	return &subscriptionResponse{false}, nil
   690  }
   691  
   692  func (s *WebsocketServer) subscribeNewTransaction(c *websocketChannel, req *WsReq) (res interface{}, err error) {
   693  	s.newTransactionSubscriptionsLock.Lock()
   694  	defer s.newTransactionSubscriptionsLock.Unlock()
   695  	if !s.newTransactionEnabled {
   696  		return &subscriptionResponseMessage{false, "subscribeNewTransaction not enabled, use -enablesubnewtx flag to enable."}, nil
   697  	}
   698  	s.newTransactionSubscriptions[c] = req.ID
   699  	s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeNewTransaction"})).Set(float64(len(s.newTransactionSubscriptions)))
   700  	return &subscriptionResponse{true}, nil
   701  }
   702  
   703  func (s *WebsocketServer) unsubscribeNewTransaction(c *websocketChannel) (res interface{}, err error) {
   704  	s.newTransactionSubscriptionsLock.Lock()
   705  	defer s.newTransactionSubscriptionsLock.Unlock()
   706  	if !s.newTransactionEnabled {
   707  		return &subscriptionResponseMessage{false, "unsubscribeNewTransaction not enabled, use -enablesubnewtx flag to enable."}, nil
   708  	}
   709  	delete(s.newTransactionSubscriptions, c)
   710  	s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeNewTransaction"})).Set(float64(len(s.newTransactionSubscriptions)))
   711  	return &subscriptionResponse{false}, nil
   712  }
   713  
   714  func (s *WebsocketServer) unmarshalAddresses(params []byte) ([]string, error) {
   715  	r := WsSubscribeAddressesReq{}
   716  	err := json.Unmarshal(params, &r)
   717  	if err != nil {
   718  		return nil, err
   719  	}
   720  	rv := make([]string, len(r.Addresses))
   721  	for i, a := range r.Addresses {
   722  		ad, err := s.chainParser.GetAddrDescFromAddress(a)
   723  		if err != nil {
   724  			return nil, err
   725  		}
   726  		rv[i] = string(ad)
   727  	}
   728  	return rv, nil
   729  }
   730  
   731  // unsubscribe addresses without addressSubscriptionsLock - can be called only from subscribeAddresses and unsubscribeAddresses
   732  func (s *WebsocketServer) doUnsubscribeAddresses(c *websocketChannel) {
   733  	for _, ads := range c.addrDescs {
   734  		sa, e := s.addressSubscriptions[ads]
   735  		if e {
   736  			for sc := range sa {
   737  				if sc == c {
   738  					delete(sa, c)
   739  				}
   740  			}
   741  			if len(sa) == 0 {
   742  				delete(s.addressSubscriptions, ads)
   743  			}
   744  		}
   745  	}
   746  	c.addrDescs = nil
   747  }
   748  
   749  func (s *WebsocketServer) subscribeAddresses(c *websocketChannel, addrDesc []string, req *WsReq) (res interface{}, err error) {
   750  	s.addressSubscriptionsLock.Lock()
   751  	defer s.addressSubscriptionsLock.Unlock()
   752  	// unsubscribe all previous subscriptions
   753  	s.doUnsubscribeAddresses(c)
   754  	for _, ads := range addrDesc {
   755  		as, ok := s.addressSubscriptions[ads]
   756  		if !ok {
   757  			as = make(map[*websocketChannel]string)
   758  			s.addressSubscriptions[ads] = as
   759  		}
   760  		as[c] = req.ID
   761  	}
   762  	c.addrDescs = addrDesc
   763  	s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeAddresses"})).Set(float64(len(s.addressSubscriptions)))
   764  	return &subscriptionResponse{true}, nil
   765  }
   766  
   767  // unsubscribeAddresses unsubscribes all address subscriptions by this channel
   768  func (s *WebsocketServer) unsubscribeAddresses(c *websocketChannel) (res interface{}, err error) {
   769  	s.addressSubscriptionsLock.Lock()
   770  	defer s.addressSubscriptionsLock.Unlock()
   771  	s.doUnsubscribeAddresses(c)
   772  	s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeAddresses"})).Set(float64(len(s.addressSubscriptions)))
   773  	return &subscriptionResponse{false}, nil
   774  }
   775  
   776  // unsubscribe fiat rates without fiatRatesSubscriptionsLock - can be called only from subscribeFiatRates and unsubscribeFiatRates
   777  func (s *WebsocketServer) doUnsubscribeFiatRates(c *websocketChannel) {
   778  	for fr, sa := range s.fiatRatesSubscriptions {
   779  		for sc := range sa {
   780  			if sc == c {
   781  				delete(sa, c)
   782  			}
   783  		}
   784  		if len(sa) == 0 {
   785  			delete(s.fiatRatesSubscriptions, fr)
   786  		}
   787  	}
   788  	delete(s.fiatRatesTokenSubscriptions, c)
   789  }
   790  
   791  // subscribeFiatRates subscribes all FiatRates subscriptions by this channel
   792  func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, d *WsSubscribeFiatRatesReq, req *WsReq) (res interface{}, err error) {
   793  	s.fiatRatesSubscriptionsLock.Lock()
   794  	defer s.fiatRatesSubscriptionsLock.Unlock()
   795  	// unsubscribe all previous subscriptions
   796  	s.doUnsubscribeFiatRates(c)
   797  	currency := d.Currency
   798  	if currency == "" {
   799  		currency = allFiatRates
   800  	} else {
   801  		currency = strings.ToLower(currency)
   802  	}
   803  	as, ok := s.fiatRatesSubscriptions[currency]
   804  	if !ok {
   805  		as = make(map[*websocketChannel]string)
   806  		s.fiatRatesSubscriptions[currency] = as
   807  	}
   808  	as[c] = req.ID
   809  	if len(d.Tokens) != 0 {
   810  		s.fiatRatesTokenSubscriptions[c] = d.Tokens
   811  	}
   812  	s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeFiatRates"})).Set(float64(len(s.fiatRatesSubscriptions)))
   813  	return &subscriptionResponse{true}, nil
   814  }
   815  
   816  // unsubscribeFiatRates unsubscribes all FiatRates subscriptions by this channel
   817  func (s *WebsocketServer) unsubscribeFiatRates(c *websocketChannel) (res interface{}, err error) {
   818  	s.fiatRatesSubscriptionsLock.Lock()
   819  	defer s.fiatRatesSubscriptionsLock.Unlock()
   820  	s.doUnsubscribeFiatRates(c)
   821  	s.metrics.WebsocketSubscribes.With((common.Labels{"method": "subscribeFiatRates"})).Set(float64(len(s.fiatRatesSubscriptions)))
   822  	return &subscriptionResponse{false}, nil
   823  }
   824  
   825  func (s *WebsocketServer) onNewBlockAsync(hash string, height uint32) {
   826  	s.newBlockSubscriptionsLock.Lock()
   827  	defer s.newBlockSubscriptionsLock.Unlock()
   828  	data := struct {
   829  		Height uint32 `json:"height"`
   830  		Hash   string `json:"hash"`
   831  	}{
   832  		Height: height,
   833  		Hash:   hash,
   834  	}
   835  	for c, id := range s.newBlockSubscriptions {
   836  		c.DataOut(&WsRes{
   837  			ID:   id,
   838  			Data: &data,
   839  		})
   840  	}
   841  	glog.Info("broadcasting new block ", height, " ", hash, " to ", len(s.newBlockSubscriptions), " channels")
   842  }
   843  
   844  // OnNewBlock is a callback that broadcasts info about new block to subscribed clients
   845  func (s *WebsocketServer) OnNewBlock(hash string, height uint32) {
   846  	go s.onNewBlockAsync(hash, height)
   847  }
   848  
   849  func (s *WebsocketServer) sendOnNewTx(tx *api.Tx) {
   850  	s.newTransactionSubscriptionsLock.Lock()
   851  	defer s.newTransactionSubscriptionsLock.Unlock()
   852  	for c, id := range s.newTransactionSubscriptions {
   853  		c.DataOut(&WsRes{
   854  			ID:   id,
   855  			Data: &tx,
   856  		})
   857  	}
   858  	glog.Info("broadcasting new tx ", tx.Txid, " to ", len(s.newTransactionSubscriptions), " channels")
   859  }
   860  
   861  func (s *WebsocketServer) sendOnNewTxAddr(stringAddressDescriptor string, tx *api.Tx) {
   862  	addrDesc := bchain.AddressDescriptor(stringAddressDescriptor)
   863  	addr, _, err := s.chainParser.GetAddressesFromAddrDesc(addrDesc)
   864  	if err != nil {
   865  		glog.Error("GetAddressesFromAddrDesc error ", err, " for ", addrDesc)
   866  		return
   867  	}
   868  	if len(addr) == 1 {
   869  		data := struct {
   870  			Address string  `json:"address"`
   871  			Tx      *api.Tx `json:"tx"`
   872  		}{
   873  			Address: addr[0],
   874  			Tx:      tx,
   875  		}
   876  		s.addressSubscriptionsLock.Lock()
   877  		defer s.addressSubscriptionsLock.Unlock()
   878  		as, ok := s.addressSubscriptions[stringAddressDescriptor]
   879  		if ok {
   880  			for c, id := range as {
   881  				c.DataOut(&WsRes{
   882  					ID:   id,
   883  					Data: &data,
   884  				})
   885  			}
   886  			glog.Info("broadcasting new tx ", tx.Txid, ", addr ", addr[0], " to ", len(as), " channels")
   887  		}
   888  	}
   889  }
   890  
   891  func (s *WebsocketServer) getNewTxSubscriptions(tx *bchain.MempoolTx) map[string]struct{} {
   892  	// check if there is any subscription in inputs, outputs and token transfers
   893  	s.addressSubscriptionsLock.Lock()
   894  	defer s.addressSubscriptionsLock.Unlock()
   895  	subscribed := make(map[string]struct{})
   896  	for i := range tx.Vin {
   897  		sad := string(tx.Vin[i].AddrDesc)
   898  		if len(sad) > 0 {
   899  			as, ok := s.addressSubscriptions[sad]
   900  			if ok && len(as) > 0 {
   901  				subscribed[sad] = struct{}{}
   902  			}
   903  		}
   904  	}
   905  	for i := range tx.Vout {
   906  		addrDesc, err := s.chainParser.GetAddrDescFromVout(&tx.Vout[i])
   907  		if err == nil && len(addrDesc) > 0 {
   908  			sad := string(addrDesc)
   909  			as, ok := s.addressSubscriptions[sad]
   910  			if ok && len(as) > 0 {
   911  				subscribed[sad] = struct{}{}
   912  			}
   913  		}
   914  	}
   915  	for i := range tx.TokenTransfers {
   916  		addrDesc, err := s.chainParser.GetAddrDescFromAddress(tx.TokenTransfers[i].From)
   917  		if err == nil && len(addrDesc) > 0 {
   918  			sad := string(addrDesc)
   919  			as, ok := s.addressSubscriptions[sad]
   920  			if ok && len(as) > 0 {
   921  				subscribed[sad] = struct{}{}
   922  			}
   923  		}
   924  		addrDesc, err = s.chainParser.GetAddrDescFromAddress(tx.TokenTransfers[i].To)
   925  		if err == nil && len(addrDesc) > 0 {
   926  			sad := string(addrDesc)
   927  			as, ok := s.addressSubscriptions[sad]
   928  			if ok && len(as) > 0 {
   929  				subscribed[sad] = struct{}{}
   930  			}
   931  		}
   932  	}
   933  	return subscribed
   934  }
   935  
   936  func (s *WebsocketServer) onNewTxAsync(tx *bchain.MempoolTx, subscribed map[string]struct{}) {
   937  	atx, err := s.api.GetTransactionFromMempoolTx(tx)
   938  	if err != nil {
   939  		glog.Error("GetTransactionFromMempoolTx error ", err, " for ", tx.Txid)
   940  		return
   941  	}
   942  	s.sendOnNewTx(atx)
   943  	for stringAddressDescriptor := range subscribed {
   944  		s.sendOnNewTxAddr(stringAddressDescriptor, atx)
   945  	}
   946  }
   947  
   948  // OnNewTx is a callback that broadcasts info about a tx affecting subscribed address
   949  func (s *WebsocketServer) OnNewTx(tx *bchain.MempoolTx) {
   950  	subscribed := s.getNewTxSubscriptions(tx)
   951  	if len(s.newTransactionSubscriptions) > 0 || len(subscribed) > 0 {
   952  		go s.onNewTxAsync(tx, subscribed)
   953  	}
   954  }
   955  
   956  func (s *WebsocketServer) broadcastTicker(currency string, rates map[string]float32, ticker *common.CurrencyRatesTicker) {
   957  	as, ok := s.fiatRatesSubscriptions[currency]
   958  	if ok && len(as) > 0 {
   959  		data := struct {
   960  			Rates interface{} `json:"rates"`
   961  		}{
   962  			Rates: rates,
   963  		}
   964  		for c, id := range as {
   965  			var tokens []string
   966  			if ticker != nil {
   967  				tokens = s.fiatRatesTokenSubscriptions[c]
   968  			}
   969  			if len(tokens) > 0 {
   970  				dataWithTokens := struct {
   971  					Rates      interface{}        `json:"rates"`
   972  					TokenRates map[string]float32 `json:"tokenRates,omitempty"`
   973  				}{
   974  					Rates:      rates,
   975  					TokenRates: map[string]float32{},
   976  				}
   977  				for _, token := range tokens {
   978  					rate := ticker.TokenRateInCurrency(token, currency)
   979  					if rate > 0 {
   980  						dataWithTokens.TokenRates[token] = rate
   981  					}
   982  				}
   983  				c.DataOut(&WsRes{
   984  					ID:   id,
   985  					Data: &dataWithTokens,
   986  				})
   987  			} else {
   988  				c.DataOut(&WsRes{
   989  					ID:   id,
   990  					Data: &data,
   991  				})
   992  			}
   993  		}
   994  		glog.Info("broadcasting new rates for currency ", currency, " to ", len(as), " channels")
   995  	}
   996  }
   997  
   998  // OnNewFiatRatesTicker is a callback that broadcasts info about fiat rates affecting subscribed currency
   999  func (s *WebsocketServer) OnNewFiatRatesTicker(ticker *common.CurrencyRatesTicker) {
  1000  	s.fiatRatesSubscriptionsLock.Lock()
  1001  	defer s.fiatRatesSubscriptionsLock.Unlock()
  1002  	for currency, rate := range ticker.Rates {
  1003  		s.broadcastTicker(currency, map[string]float32{currency: rate}, ticker)
  1004  	}
  1005  	s.broadcastTicker(allFiatRates, ticker.Rates, nil)
  1006  }
  1007  
  1008  func (s *WebsocketServer) getCurrentFiatRates(currencies []string, token string) (*api.FiatTicker, error) {
  1009  	ret, err := s.api.GetCurrentFiatRates(currencies, strings.ToLower(token))
  1010  	return ret, err
  1011  }
  1012  
  1013  func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []int64, currencies []string, token string) (*api.FiatTickers, error) {
  1014  	ret, err := s.api.GetFiatRatesForTimestamps(timestamps, currencies, strings.ToLower(token))
  1015  	return ret, err
  1016  }
  1017  
  1018  func (s *WebsocketServer) getAvailableVsCurrencies(timestamp int64, token string) (*api.AvailableVsCurrencies, error) {
  1019  	ret, err := s.api.GetAvailableVsCurrencies(timestamp, strings.ToLower(token))
  1020  	return ret, err
  1021  }