github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/api/api.go (about)

     1  package api
     2  
     3  import (
     4  	"crypto/tls"
     5  	"net"
     6  	"net/http"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/kr/secureheader"
    11  	log "github.com/sirupsen/logrus"
    12  	cmn "github.com/tendermint/tmlibs/common"
    13  
    14  	"github.com/bytom/bytom/accesstoken"
    15  	"github.com/bytom/bytom/blockchain/txfeed"
    16  	cfg "github.com/bytom/bytom/config"
    17  	"github.com/bytom/bytom/dashboard/dashboard"
    18  	"github.com/bytom/bytom/dashboard/equity"
    19  	"github.com/bytom/bytom/errors"
    20  	"github.com/bytom/bytom/event"
    21  	"github.com/bytom/bytom/mining/cpuminer"
    22  	"github.com/bytom/bytom/mining/miningpool"
    23  	"github.com/bytom/bytom/net/http/authn"
    24  	"github.com/bytom/bytom/net/http/gzip"
    25  	"github.com/bytom/bytom/net/http/httpjson"
    26  	"github.com/bytom/bytom/net/http/static"
    27  	"github.com/bytom/bytom/net/websocket"
    28  	"github.com/bytom/bytom/netsync"
    29  	"github.com/bytom/bytom/p2p"
    30  	"github.com/bytom/bytom/protocol"
    31  	"github.com/bytom/bytom/wallet"
    32  )
    33  
    34  var (
    35  	errNotAuthenticated = errors.New("not authenticated")
    36  	httpReadTimeout     = 2 * time.Minute
    37  	httpWriteTimeout    = time.Hour
    38  )
    39  
    40  const (
    41  	// SUCCESS indicates the rpc calling is successful.
    42  	SUCCESS = "success"
    43  	// FAIL indicated the rpc calling is failed.
    44  	FAIL      = "fail"
    45  	logModule = "api"
    46  )
    47  
    48  // Response describes the response standard.
    49  type Response struct {
    50  	Status      string      `json:"status,omitempty"`
    51  	Code        string      `json:"code,omitempty"`
    52  	Msg         string      `json:"msg,omitempty"`
    53  	ErrorDetail string      `json:"error_detail,omitempty"`
    54  	Data        interface{} `json:"data,omitempty"`
    55  }
    56  
    57  //NewSuccessResponse success response
    58  func NewSuccessResponse(data interface{}) Response {
    59  	return Response{Status: SUCCESS, Data: data}
    60  }
    61  
    62  //FormatErrResp format error response
    63  func FormatErrResp(err error) (response Response) {
    64  	response = Response{Status: FAIL}
    65  	root := errors.Root(err)
    66  	// Some types cannot be used as map keys, for example slices.
    67  	// If an error's underlying type is one of these, don't panic.
    68  	// Just treat it like any other missing entry.
    69  	defer func() {
    70  		if err := recover(); err != nil {
    71  			response.ErrorDetail = ""
    72  		}
    73  	}()
    74  
    75  	if info, ok := respErrFormatter[root]; ok {
    76  		response.Code = info.ChainCode
    77  		response.Msg = info.Message
    78  		response.ErrorDetail = err.Error()
    79  	} else {
    80  		response.Code = respErrFormatter[ErrDefault].ChainCode
    81  		response.Msg = respErrFormatter[ErrDefault].Message
    82  		response.ErrorDetail = err.Error()
    83  	}
    84  	return response
    85  }
    86  
    87  //NewErrorResponse error response
    88  func NewErrorResponse(err error) Response {
    89  	response := FormatErrResp(err)
    90  	return response
    91  }
    92  
    93  type waitHandler struct {
    94  	h  http.Handler
    95  	wg sync.WaitGroup
    96  }
    97  
    98  func (wh *waitHandler) Set(h http.Handler) {
    99  	wh.h = h
   100  	wh.wg.Done()
   101  }
   102  
   103  func (wh *waitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   104  	wh.wg.Wait()
   105  	wh.h.ServeHTTP(w, req)
   106  }
   107  
   108  // API is the scheduling center for server
   109  type API struct {
   110  	sync            NetSync
   111  	wallet          *wallet.Wallet
   112  	accessTokens    *accesstoken.CredentialStore
   113  	chain           *protocol.Chain
   114  	server          *http.Server
   115  	handler         http.Handler
   116  	txFeedTracker   *txfeed.Tracker
   117  	cpuMiner        *cpuminer.CPUMiner
   118  	miningPool      *miningpool.MiningPool
   119  	notificationMgr *websocket.WSNotificationManager
   120  	eventDispatcher *event.Dispatcher
   121  }
   122  
   123  func (a *API) initServer(config *cfg.Config) {
   124  	// The waitHandler accepts incoming requests, but blocks until its underlying
   125  	// handler is set, when the second phase is complete.
   126  	var coreHandler waitHandler
   127  	var handler http.Handler
   128  
   129  	coreHandler.wg.Add(1)
   130  	mux := http.NewServeMux()
   131  	mux.Handle("/", &coreHandler)
   132  
   133  	handler = AuthHandler(mux, a.accessTokens, config.Auth.Disable)
   134  	handler = RedirectHandler(handler)
   135  
   136  	secureheader.DefaultConfig.PermitClearLoopback = true
   137  	secureheader.DefaultConfig.HTTPSRedirect = false
   138  	secureheader.DefaultConfig.Next = handler
   139  
   140  	a.server = &http.Server{
   141  		// Note: we should not set TLSConfig here;
   142  		// we took care of TLS with the listener in maybeUseTLS.
   143  		Handler:      secureheader.DefaultConfig,
   144  		ReadTimeout:  httpReadTimeout,
   145  		WriteTimeout: httpWriteTimeout,
   146  		// Disable HTTP/2 for now until the Go implementation is more stable.
   147  		// https://github.com/golang/go/issues/16450
   148  		// https://github.com/golang/go/issues/17071
   149  		TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
   150  	}
   151  
   152  	coreHandler.Set(a)
   153  }
   154  
   155  // StartServer start the server
   156  func (a *API) StartServer(address string) {
   157  	log.WithFields(log.Fields{"module": logModule, "api address:": address}).Info("Rpc listen")
   158  	listener, err := net.Listen("tcp", address)
   159  	if err != nil {
   160  		cmn.Exit(cmn.Fmt("Failed to register tcp port: %v", err))
   161  	}
   162  
   163  	// The `Serve` call has to happen in its own goroutine because
   164  	// it's blocking and we need to proceed to the rest of the core setup after
   165  	// we call it.
   166  	go func() {
   167  		if err := a.server.Serve(listener); err != nil {
   168  			log.WithFields(log.Fields{"module": logModule, "error": errors.Wrap(err, "Serve")}).Error("Rpc server")
   169  		}
   170  	}()
   171  }
   172  
   173  type NetSync interface {
   174  	IsListening() bool
   175  	IsCaughtUp() bool
   176  	PeerCount() int
   177  	GetNetwork() string
   178  	BestPeer() *netsync.PeerInfo
   179  	DialPeerWithAddress(addr *p2p.NetAddress) error
   180  	GetPeerInfos() []*netsync.PeerInfo
   181  	StopPeer(peerID string) error
   182  }
   183  
   184  // NewAPI create and initialize the API
   185  func NewAPI(sync NetSync, wallet *wallet.Wallet, txfeeds *txfeed.Tracker, cpuMiner *cpuminer.CPUMiner, miningPool *miningpool.MiningPool, chain *protocol.Chain, config *cfg.Config, token *accesstoken.CredentialStore, dispatcher *event.Dispatcher, notificationMgr *websocket.WSNotificationManager) *API {
   186  	api := &API{
   187  		sync:          sync,
   188  		wallet:        wallet,
   189  		chain:         chain,
   190  		accessTokens:  token,
   191  		txFeedTracker: txfeeds,
   192  		cpuMiner:      cpuMiner,
   193  		miningPool:    miningPool,
   194  
   195  		eventDispatcher: dispatcher,
   196  		notificationMgr: notificationMgr,
   197  	}
   198  	api.buildHandler()
   199  	api.initServer(config)
   200  
   201  	return api
   202  }
   203  
   204  func (a *API) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
   205  	a.handler.ServeHTTP(rw, req)
   206  }
   207  
   208  // buildHandler is in charge of all the rpc handling.
   209  func (a *API) buildHandler() {
   210  	walletEnable := false
   211  	m := http.NewServeMux()
   212  	if a.wallet != nil {
   213  		walletEnable = true
   214  		m.Handle("/create-account", jsonHandler(a.createAccount))
   215  		m.Handle("/update-account-alias", jsonHandler(a.updateAccountAlias))
   216  		m.Handle("/list-accounts", jsonHandler(a.listAccounts))
   217  		m.Handle("/delete-account", jsonHandler(a.deleteAccount))
   218  
   219  		m.Handle("/create-account-receiver", jsonHandler(a.createAccountReceiver))
   220  		m.Handle("/list-addresses", jsonHandler(a.listAddresses))
   221  		m.Handle("/validate-address", jsonHandler(a.validateAddress))
   222  		m.Handle("/list-pubkeys", jsonHandler(a.listPubKeys))
   223  
   224  		m.Handle("/get-mining-address", jsonHandler(a.getMiningAddress))
   225  		m.Handle("/set-mining-address", jsonHandler(a.setMiningAddress))
   226  
   227  		m.Handle("/get-coinbase-arbitrary", jsonHandler(a.getCoinbaseArbitrary))
   228  		m.Handle("/set-coinbase-arbitrary", jsonHandler(a.setCoinbaseArbitrary))
   229  
   230  		m.Handle("/create-asset", jsonHandler(a.createAsset))
   231  		m.Handle("/update-asset-alias", jsonHandler(a.updateAssetAlias))
   232  		m.Handle("/get-asset", jsonHandler(a.getAsset))
   233  		m.Handle("/list-assets", jsonHandler(a.listAssets))
   234  
   235  		m.Handle("/create-key", jsonHandler(a.pseudohsmCreateKey))
   236  		m.Handle("/update-key-alias", jsonHandler(a.pseudohsmUpdateKeyAlias))
   237  		m.Handle("/list-keys", jsonHandler(a.pseudohsmListKeys))
   238  		m.Handle("/delete-key", jsonHandler(a.pseudohsmDeleteKey))
   239  		m.Handle("/reset-key-password", jsonHandler(a.pseudohsmResetPassword))
   240  		m.Handle("/check-key-password", jsonHandler(a.pseudohsmCheckPassword))
   241  		m.Handle("/sign-message", jsonHandler(a.signMessage))
   242  
   243  		m.Handle("/build-transaction", jsonHandler(a.build))
   244  		m.Handle("/build-chain-transactions", jsonHandler(a.buildChainTxs))
   245  		m.Handle("/sign-transaction", jsonHandler(a.signTemplate))
   246  		m.Handle("/sign-transactions", jsonHandler(a.signTemplates))
   247  
   248  		m.Handle("/get-transaction", jsonHandler(a.getTransaction))
   249  		m.Handle("/list-transactions", jsonHandler(a.listTransactions))
   250  
   251  		m.Handle("/list-balances", jsonHandler(a.listBalances))
   252  		m.Handle("/list-unspent-outputs", jsonHandler(a.listUnspentOutputs))
   253  
   254  		m.Handle("/decode-program", jsonHandler(a.decodeProgram))
   255  
   256  		m.Handle("/backup-wallet", jsonHandler(a.backupWalletImage))
   257  		m.Handle("/restore-wallet", jsonHandler(a.restoreWalletImage))
   258  		m.Handle("/rescan-wallet", jsonHandler(a.rescanWallet))
   259  		m.Handle("/wallet-info", jsonHandler(a.getWalletInfo))
   260  		m.Handle("/recovery-wallet", jsonHandler(a.recoveryFromRootXPubs))
   261  	} else {
   262  		log.Warn("Please enable wallet")
   263  	}
   264  
   265  	m.Handle("/", alwaysError(errors.New("not Found")))
   266  	m.Handle("/error", jsonHandler(a.walletError))
   267  
   268  	m.Handle("/create-access-token", jsonHandler(a.createAccessToken))
   269  	m.Handle("/list-access-tokens", jsonHandler(a.listAccessTokens))
   270  	m.Handle("/delete-access-token", jsonHandler(a.deleteAccessToken))
   271  	m.Handle("/check-access-token", jsonHandler(a.checkAccessToken))
   272  
   273  	m.Handle("/create-transaction-feed", jsonHandler(a.createTxFeed))
   274  	m.Handle("/get-transaction-feed", jsonHandler(a.getTxFeed))
   275  	m.Handle("/update-transaction-feed", jsonHandler(a.updateTxFeed))
   276  	m.Handle("/delete-transaction-feed", jsonHandler(a.deleteTxFeed))
   277  	m.Handle("/list-transaction-feeds", jsonHandler(a.listTxFeeds))
   278  
   279  	m.Handle("/submit-transaction", jsonHandler(a.submit))
   280  	m.Handle("/submit-transactions", jsonHandler(a.submitTxs))
   281  	m.Handle("/estimate-transaction-gas", jsonHandler(a.estimateTxGas))
   282  	m.Handle("/estimate-chain-transaction-gas", jsonHandler(a.estimateChainTxGas))
   283  
   284  	m.Handle("/get-unconfirmed-transaction", jsonHandler(a.getUnconfirmedTx))
   285  	m.Handle("/list-unconfirmed-transactions", jsonHandler(a.listUnconfirmedTxs))
   286  	m.Handle("/decode-raw-transaction", jsonHandler(a.decodeRawTransaction))
   287  
   288  	m.Handle("/get-block", jsonHandler(a.getBlock))
   289  	m.Handle("/get-raw-block", jsonHandler(a.getRawBlock))
   290  	m.Handle("/get-block-hash", jsonHandler(a.getBestBlockHash))
   291  	m.Handle("/get-block-header", jsonHandler(a.getBlockHeader))
   292  	m.Handle("/get-block-count", jsonHandler(a.getBlockCount))
   293  	m.Handle("/get-difficulty", jsonHandler(a.getDifficulty))
   294  	m.Handle("/get-hash-rate", jsonHandler(a.getHashRate))
   295  
   296  	m.Handle("/is-mining", jsonHandler(a.isMining))
   297  	m.Handle("/set-mining", jsonHandler(a.setMining))
   298  
   299  	m.Handle("/get-work", jsonHandler(a.getWork))
   300  	m.Handle("/get-work-json", jsonHandler(a.getWorkJSON))
   301  	m.Handle("/submit-block", jsonHandler(a.submitBlock))
   302  	m.Handle("/submit-work", jsonHandler(a.submitWork))
   303  	m.Handle("/submit-work-json", jsonHandler(a.submitWorkJSON))
   304  
   305  	m.Handle("/verify-message", jsonHandler(a.verifyMessage))
   306  	m.Handle("/compile", jsonHandler(a.compileEquity))
   307  
   308  	m.Handle("/gas-rate", jsonHandler(a.gasRate))
   309  	m.Handle("/net-info", jsonHandler(a.getNetInfo))
   310  
   311  	m.Handle("/list-peers", jsonHandler(a.listPeers))
   312  	m.Handle("/disconnect-peer", jsonHandler(a.disconnectPeer))
   313  	m.Handle("/connect-peer", jsonHandler(a.connectPeer))
   314  
   315  	m.Handle("/get-merkle-proof", jsonHandler(a.getMerkleProof))
   316  
   317  	m.HandleFunc("/websocket-subscribe", a.websocketHandler)
   318  
   319  	handler := walletHandler(m, walletEnable)
   320  	handler = webAssetsHandler(handler)
   321  	handler = gzip.Handler{Handler: handler}
   322  	a.handler = handler
   323  }
   324  
   325  // json Handler
   326  func jsonHandler(f interface{}) http.Handler {
   327  	h, err := httpjson.Handler(f, errorFormatter.Write)
   328  	if err != nil {
   329  		panic(err)
   330  	}
   331  	return h
   332  }
   333  
   334  // error Handler
   335  func alwaysError(err error) http.Handler {
   336  	return jsonHandler(func() error { return err })
   337  }
   338  
   339  func webAssetsHandler(next http.Handler) http.Handler {
   340  	mux := http.NewServeMux()
   341  	mux.Handle("/dashboard/", http.StripPrefix("/dashboard/", static.Handler{
   342  		Assets:  dashboard.Files,
   343  		Default: "index.html",
   344  	}))
   345  	mux.Handle("/equity/", http.StripPrefix("/equity/", static.Handler{
   346  		Assets:  equity.Files,
   347  		Default: "index.html",
   348  	}))
   349  	mux.Handle("/", next)
   350  
   351  	return mux
   352  }
   353  
   354  // AuthHandler access token auth Handler
   355  func AuthHandler(handler http.Handler, accessTokens *accesstoken.CredentialStore, authDisable bool) http.Handler {
   356  	authenticator := authn.NewAPI(accessTokens, authDisable)
   357  
   358  	return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   359  		// TODO(tessr): check that this path exists; return early if this path isn't legit
   360  		req, err := authenticator.Authenticate(req)
   361  		if err != nil {
   362  			log.WithFields(log.Fields{"module": logModule, "error": errors.Wrap(err, "Serve")}).Error("Authenticate fail")
   363  			err = errors.WithDetail(errNotAuthenticated, err.Error())
   364  			errorFormatter.Write(req.Context(), rw, err)
   365  			return
   366  		}
   367  		handler.ServeHTTP(rw, req)
   368  	})
   369  }
   370  
   371  // RedirectHandler redirect to dashboard handler
   372  func RedirectHandler(next http.Handler) http.Handler {
   373  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   374  		if req.URL.Path == "/" {
   375  			http.Redirect(w, req, "/dashboard/", http.StatusFound)
   376  			return
   377  		}
   378  		next.ServeHTTP(w, req)
   379  	})
   380  }
   381  
   382  func walletHandler(m *http.ServeMux, walletEnable bool) http.Handler {
   383  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   384  		// when the wallet is not been opened and the url path is not been found, modify url path to error,
   385  		// and redirect handler to error
   386  		if _, pattern := m.Handler(req); pattern != req.URL.Path && !walletEnable {
   387  			req.URL.Path = "/error"
   388  			walletRedirectHandler(w, req)
   389  			return
   390  		}
   391  
   392  		m.ServeHTTP(w, req)
   393  	})
   394  }
   395  
   396  // walletRedirectHandler redirect to error when the wallet is closed
   397  func walletRedirectHandler(w http.ResponseWriter, req *http.Request) {
   398  	h := http.RedirectHandler(req.URL.String(), http.StatusMovedPermanently)
   399  	h.ServeHTTP(w, req)
   400  }