diff --git a/plugins/api/handlers.go b/plugins/api/handlers.go index f458ac478..4380fab3d 100644 --- a/plugins/api/handlers.go +++ b/plugins/api/handlers.go @@ -84,6 +84,10 @@ func (s *server) handleNodeVersionReq() http.HandlerFunc { return hnd.NodeVersionReqHandler(s.ctx) } +func (s *server) handleAccountReq(cdc *wire.Codec, ctx context.CoreContext, tokens tkstore.Mapper, accStoreName string) http.HandlerFunc { + return hnd.AccountReqHandler(cdc, ctx, tokens, accStoreName) +} + func (s *server) handleSimulateReq(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc { h := hnd.SimulateReqHandler(cdc, ctx) return s.withTextPlainForm(s.limitReqSize(h)) diff --git a/plugins/api/handlers/account.go b/plugins/api/handlers/account.go new file mode 100644 index 000000000..6eb332016 --- /dev/null +++ b/plugins/api/handlers/account.go @@ -0,0 +1,83 @@ +package handlers + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + + "github.com/BiJie/BinanceChain/common/types" + tkclient "github.com/BiJie/BinanceChain/plugins/tokens/client/rest" + tkstore "github.com/BiJie/BinanceChain/plugins/tokens/store" + "github.com/BiJie/BinanceChain/wire" +) + +// AccountReqHandler queries for an account and returns its information. +func AccountReqHandler( + cdc *wire.Codec, ctx context.CoreContext, tokens tkstore.Mapper, accStoreName string, +) http.HandlerFunc { + type response struct { + auth.BaseAccount + Balances []tkclient.TokenBalance `json:"balances"` + Coins *struct{} `json:"coins,omitempty"` // omit `coins` + } + + responseType := "application/json" + + accDecoder := authcmd.GetAccountDecoder(cdc) + + throw := func(w http.ResponseWriter, status int, message string) { + w.WriteHeader(status) + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte(message)) + return + } + + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + bech32addr := vars["address"] + + addr, err := sdk.AccAddressFromBech32(bech32addr) + if err != nil { + throw(w, http.StatusBadRequest, err.Error()) + return + } + + res, err := ctx.QueryStore(auth.AddressStoreKey(addr), accStoreName) + if err != nil { + errMsg := fmt.Sprintf("couldn't query account. Error: %s", err.Error()) + throw(w, http.StatusInternalServerError, errMsg) + return + } + + // the query will return empty if there is no data for this account + if len(res) == 0 { + w.WriteHeader(http.StatusNoContent) + return + } + + // decode the value + account, err := accDecoder(res) + if err != nil { + errMsg := fmt.Sprintf("couldn't parse query result. Result: %s. Error: %s", res, err.Error()) + throw(w, http.StatusInternalServerError, errMsg) + return + } + + bals, err := tkclient.GetBalances(cdc, ctx, tokens, account.GetAddress()) + resp := response{ + BaseAccount: account.(*types.AppAccount).BaseAccount, + Balances: bals, + } + + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", responseType) + json.NewEncoder(w).Encode(resp) + } +} diff --git a/plugins/api/handlers/simulate.go b/plugins/api/handlers/simulate.go index 706580780..02a882d51 100644 --- a/plugins/api/handlers/simulate.go +++ b/plugins/api/handlers/simulate.go @@ -66,6 +66,7 @@ func SimulateReqHandler(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFu return } + w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", responseType) w.Write(output) } diff --git a/plugins/api/routes.go b/plugins/api/routes.go index af6bf5003..2b6bb9168 100644 --- a/plugins/api/routes.go +++ b/plugins/api/routes.go @@ -20,6 +20,10 @@ func (s *server) bindRoutes() *server { r.HandleFunc("/node_version", s.handleNodeVersionReq()). Methods("GET") + // auth routes + r.HandleFunc(prefix+"/account/{address}", s.handleAccountReq(s.cdc, s.ctx, s.tokens, s.accStoreName)). + Methods("GET") + // tx routes r.HandleFunc(prefix+"/simulate", s.handleSimulateReq(s.cdc, s.ctx)). Methods("POST") diff --git a/plugins/dex/client/rest/getdepth.go b/plugins/dex/client/rest/getdepth.go index d7e8afdc2..a9c4ba441 100644 --- a/plugins/dex/client/rest/getdepth.go +++ b/plugins/dex/client/rest/getdepth.go @@ -76,6 +76,7 @@ func DepthReqHandler(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc return } + w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", responseType) err = rutils.StreamDepthResponse(w, ob, limit) diff --git a/plugins/dex/client/rest/getpairs.go b/plugins/dex/client/rest/getpairs.go index bc74b8d4b..168f78e5a 100644 --- a/plugins/dex/client/rest/getpairs.go +++ b/plugins/dex/client/rest/getpairs.go @@ -91,6 +91,7 @@ func GetPairsReqHandler(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFu return } + w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", responseType) w.Write(output) } diff --git a/plugins/dex/client/rest/putorder.go b/plugins/dex/client/rest/putorder.go index 1825f9cfb..f51598d71 100644 --- a/plugins/dex/client/rest/putorder.go +++ b/plugins/dex/client/rest/putorder.go @@ -166,6 +166,7 @@ func PutOrderReqHandler(cdc *wire.Codec, ctx context.CoreContext, accStoreName s return } + w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", responseType) w.Write(output) } diff --git a/plugins/tokens/client/rest/getbalance.go b/plugins/tokens/client/rest/getbalance.go index 80e72f300..4cbb4c357 100644 --- a/plugins/tokens/client/rest/getbalance.go +++ b/plugins/tokens/client/rest/getbalance.go @@ -25,7 +25,7 @@ func BalanceReqHandler(cdc *wire.Codec, ctx context.CoreContext, tokens tokens.M } type response struct { Address string `json:"address"` - Balance tokenBalance `json:"balance"` + Balance TokenBalance `json:"balance"` } throw := func(w http.ResponseWriter, status int, err error) { w.WriteHeader(status) @@ -78,7 +78,7 @@ func BalanceReqHandler(cdc *wire.Codec, ctx context.CoreContext, tokens tokens.M resp := response{ Address: vars["address"], - Balance: tokenBalance{ + Balance: TokenBalance{ Symbol: params.symbol, Free: utils.Fixed8(coins.AmountOf(params.symbol).Int64()), Locked: utils.Fixed8(locked.Int64()), diff --git a/plugins/tokens/client/rest/getbalances.go b/plugins/tokens/client/rest/getbalances.go index 7605d3f8d..2247956a2 100644 --- a/plugins/tokens/client/rest/getbalances.go +++ b/plugins/tokens/client/rest/getbalances.go @@ -1,7 +1,6 @@ package rest import ( - "fmt" "net/http" "github.com/gorilla/mux" @@ -9,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/BiJie/BinanceChain/common/utils" "github.com/BiJie/BinanceChain/plugins/tokens" "github.com/BiJie/BinanceChain/wire" ) @@ -18,13 +16,9 @@ import ( func BalancesReqHandler( cdc *wire.Codec, ctx context.CoreContext, tokens tokens.Mapper, ) http.HandlerFunc { - type params struct { - address sdk.AccAddress - } - type response struct { Address string `json:"address"` - Balances []tokenBalance `json:"balances"` + Balances []TokenBalance `json:"balances"` } responseType := "application/json" @@ -46,57 +40,12 @@ func BalancesReqHandler( return } - params := params{ - address: addr, - } - - coins, err := getCoinsCC(cdc, ctx, params.address) + bals, err := GetBalances(cdc, ctx, tokens, addr) if err != nil { - throw(w, http.StatusNotFound, err) + throw(w, http.StatusInternalServerError, err) return } - // must do it this way because GetTokenList relies on store.Iterator - // which we can't use from a CoreContext - var denoms map[string]bool - denoms = map[string]bool{} - for _, coin := range coins { - denom := coin.Denom - exists := tokens.ExistsCC(ctx, denom) - // TODO: we probably actually want to show zero balances. - // if exists && !sdk.Int.IsZero(coins.AmountOf(denom)) { - if exists { - denoms[denom] = true - } - } - - symbs := make([]string, 0, len(denoms)) - bals := make([]tokenBalance, 0, len(denoms)) - for symb := range denoms { - symbs = append(symbs, symb) - // count locked and frozen coins - locked := sdk.NewInt(0) - frozen := sdk.NewInt(0) - lockedc, err := getLockedCC(cdc, ctx, params.address) - if err != nil { - fmt.Println("getLockedCC error ignored, will use `0`") - } else { - locked = lockedc.AmountOf(symb) - } - frozenc, err := getFrozenCC(cdc, ctx, params.address) - if err != nil { - fmt.Println("getFrozenCC error ignored, will use `0`") - } else { - frozen = frozenc.AmountOf(symb) - } - bals = append(bals, tokenBalance{ - Symbol: symb, - Free: utils.Fixed8(coins.AmountOf(symb).Int64()), - Locked: utils.Fixed8(locked.Int64()), - Frozen: utils.Fixed8(frozen.Int64()), - }) - } - resp := response{ Address: vars["address"], Balances: bals, @@ -108,6 +57,7 @@ func BalancesReqHandler( return } + w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", responseType) w.Write(output) } diff --git a/plugins/tokens/client/rest/gettoken.go b/plugins/tokens/client/rest/gettoken.go index 22a3ff95e..9d8cea2c5 100644 --- a/plugins/tokens/client/rest/gettoken.go +++ b/plugins/tokens/client/rest/gettoken.go @@ -69,6 +69,7 @@ func GetTokenReqHandler(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFu return } + w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", responseType) w.Write(output) } diff --git a/plugins/tokens/client/rest/gettokens.go b/plugins/tokens/client/rest/gettokens.go index b7398e77a..bbe27a6a5 100644 --- a/plugins/tokens/client/rest/gettokens.go +++ b/plugins/tokens/client/rest/gettokens.go @@ -91,6 +91,7 @@ func GetTokensReqHandler(cdc *wire.Codec, ctx context.CoreContext) http.HandlerF return } + w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", responseType) w.Write(output) } diff --git a/plugins/tokens/client/rest/helpers.go b/plugins/tokens/client/rest/helpers.go index 41f2ae79a..01b0ecd4c 100644 --- a/plugins/tokens/client/rest/helpers.go +++ b/plugins/tokens/client/rest/helpers.go @@ -1,6 +1,8 @@ package rest import ( + "fmt" + "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -8,16 +10,69 @@ import ( "github.com/BiJie/BinanceChain/common" "github.com/BiJie/BinanceChain/common/types" "github.com/BiJie/BinanceChain/common/utils" + "github.com/BiJie/BinanceChain/plugins/tokens" "github.com/BiJie/BinanceChain/wire" ) -type tokenBalance struct { +type TokenBalance struct { Symbol string `json:"symbol"` Free utils.Fixed8 `json:"free"` Locked utils.Fixed8 `json:"locked"` Frozen utils.Fixed8 `json:"frozen"` } +func GetBalances( + cdc *wire.Codec, ctx context.CoreContext, tokens tokens.Mapper, addr sdk.AccAddress, +) ([]TokenBalance, error) { + coins, err := getCoinsCC(cdc, ctx, addr) + if err != nil { + return nil, err + } + + // must do it this way because GetTokenList relies on store.Iterator + // which we can't use from a CoreContext + var denoms map[string]bool + denoms = map[string]bool{} + for _, coin := range coins { + denom := coin.Denom + exists := tokens.ExistsCC(ctx, denom) + // TODO: we probably actually want to show zero balances. + // if exists && !sdk.Int.IsZero(coins.AmountOf(denom)) { + if exists { + denoms[denom] = true + } + } + + symbs := make([]string, 0, len(denoms)) + bals := make([]TokenBalance, 0, len(denoms)) + for symb := range denoms { + symbs = append(symbs, symb) + // count locked and frozen coins + locked := sdk.NewInt(0) + frozen := sdk.NewInt(0) + lockedc, err := getLockedCC(cdc, ctx, addr) + if err != nil { + fmt.Println("getLockedCC error ignored, will use `0`") + } else { + locked = lockedc.AmountOf(symb) + } + frozenc, err := getFrozenCC(cdc, ctx, addr) + if err != nil { + fmt.Println("getFrozenCC error ignored, will use `0`") + } else { + frozen = frozenc.AmountOf(symb) + } + bals = append(bals, TokenBalance{ + Symbol: symb, + Free: utils.Fixed8(coins.AmountOf(symb).Int64()), + Locked: utils.Fixed8(locked.Int64()), + Frozen: utils.Fixed8(frozen.Int64()), + }) + } + + return bals, nil +} + func decodeAccount(cdc *wire.Codec, bz *[]byte) (acc auth.Account, err error) { err = cdc.UnmarshalBinaryBare(*bz, &acc) if err != nil {