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 }