diff --git a/cmd/kvcli/main.go b/cmd/kvcli/main.go index 1754403f..48c0876b 100644 --- a/cmd/kvcli/main.go +++ b/cmd/kvcli/main.go @@ -9,7 +9,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/client/lcd" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" @@ -20,6 +19,7 @@ import ( //stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" "github.com/kava-labs/kava/internal/app" + "github.com/kava-labs/kava/internal/lcd" //"github.com/kava-labs/kava/internal/types" ) diff --git a/internal/lcd/decode.go b/internal/lcd/decode.go new file mode 100644 index 00000000..96769dd9 --- /dev/null +++ b/internal/lcd/decode.go @@ -0,0 +1,58 @@ +package lcd + +import ( + "net/http" + //"encoding/hex" + "encoding/base64" + "encoding/json" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/auth" +) + +type TxBody struct { + TxBase64 string `json:"txbase64"` +} + +// Decode a tx from base64 into json +func DecodeTxRequestHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // get the input base64 string + var m TxBody + decoder := json.NewDecoder(r.Body) + err := decoder.Decode(&m) + if err != nil { + w.WriteHeader(400) + w.Write([]byte(err.Error())) + return + } + + // convert from base64 string to bytes + txBytes, err := base64.StdEncoding.DecodeString(m.TxBase64) + if err != nil { + w.WriteHeader(400) + w.Write([]byte(err.Error())) + return + } + + // convert bytes to Tx struct + var tx auth.StdTx + err = cdc.UnmarshalBinary(txBytes, &tx) + if err != nil { + w.WriteHeader(400) + w.Write([]byte(err.Error())) + return + } + + // convert Tx struct to json (bytes) and return + output, err := cdc.MarshalJSON(tx) + if err != nil { + w.WriteHeader(500) + w.Write([]byte(err.Error())) + return + } + + w.Write(output) + } +} diff --git a/internal/lcd/root.go b/internal/lcd/root.go new file mode 100644 index 00000000..6bd82cff --- /dev/null +++ b/internal/lcd/root.go @@ -0,0 +1,85 @@ +package lcd + +import ( + "net/http" + "os" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tmlibs/log" + + tmserver "github.com/tendermint/tendermint/rpc/lib/server" + cmn "github.com/tendermint/tmlibs/common" + + client "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + keys "github.com/cosmos/cosmos-sdk/client/keys" + rpc "github.com/cosmos/cosmos-sdk/client/rpc" + tx "github.com/cosmos/cosmos-sdk/client/tx" + version "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/wire" + auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + //ibc "github.com/cosmos/cosmos-sdk/x/ibc/client/rest" + //stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" +) + +// ServeCommand will generate a long-running rest server +// (aka Light Client Daemon) that exposes functionality similar +// to the cli, but over rest +func ServeCommand(cdc *wire.Codec) *cobra.Command { + flagListenAddr := "laddr" + flagCORS := "cors" + + cmd := &cobra.Command{ + Use: "rest-server", + Short: "Start LCD (light-client daemon), a local REST server", + RunE: func(cmd *cobra.Command, args []string) error { + listenAddr := viper.GetString(flagListenAddr) + handler := createHandler(cdc) + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). + With("module", "rest-server") + listener, err := tmserver.StartHTTPServer(listenAddr, handler, logger) + if err != nil { + return err + } + logger.Info("REST server started") + + // Wait forever and cleanup + cmn.TrapSignal(func() { + err := listener.Close() + logger.Error("Error closing listener", "err", err) + }) + return nil + }, + } + cmd.Flags().StringP(flagListenAddr, "a", "tcp://localhost:1317", "Address for server to listen on") + cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)") + cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + return cmd +} + +func createHandler(cdc *wire.Codec) http.Handler { + r := mux.NewRouter() + r.HandleFunc("/version", version.RequestHandler).Methods("GET") + + kb, err := keys.GetKeyBase() //XXX + if err != nil { + panic(err) + } + + ctx := context.NewCoreContextFromViper() + + // TODO make more functional? aka r = keys.RegisterRoutes(r) + keys.RegisterRoutes(r) + rpc.RegisterRoutes(ctx, r) + tx.RegisterRoutes(ctx, r, cdc) + auth.RegisterRoutes(ctx, r, cdc, "acc") + bank.RegisterRoutes(ctx, r, cdc, kb) + //ibc.RegisterRoutes(ctx, r, cdc, kb) + //stake.RegisterRoutes(ctx, r, cdc, kb) + r.HandleFunc("/decode_tx", DecodeTxRequestHandlerFn(ctx, cdc)).Methods("POST") + return r +} diff --git a/internal/lcd/wire.go b/internal/lcd/wire.go new file mode 100644 index 00000000..f146e102 --- /dev/null +++ b/internal/lcd/wire.go @@ -0,0 +1,12 @@ +package lcd + +import ( + amino "github.com/tendermint/go-amino" + ctypes "github.com/tendermint/tendermint/rpc/core/types" +) + +var cdc = amino.NewCodec() + +func init() { + ctypes.RegisterAmino(cdc) +}