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