client/rest, modules/coin/rest: moved code around
After offline emails and a video call with @ethanfrey, a goal was decided to move things around i.e: - [X] Move /build/send and /query/account to modules/coin/rest Due to that move, there is a lot of overlap between needed code and utils so extracted common code to make https://github.com/tendermint/tmlibs/pull/33 so make sure to pull in that commit into your tmlibs tree. After code review feedback: client/rest, modules/coin/rest: FoutputProof, PrepareSendTx helper * Extract OutputProof to FoutputProof helper that can be used in modules/coin/rest/handlers.go as proofs.FoutputProof * Revert r.HandleFunc("/tx", doPostTx).Methods("POST") which was erraneously deleted * Use function signatures from "tendermint/tmblibs/common"
This commit is contained in:
parent
67f25f54ed
commit
1a45755027
|
@ -5,4 +5,4 @@ merkleeyes.db
|
|||
build
|
||||
shunit2
|
||||
docs/guide/*.sh
|
||||
|
||||
keys/
|
||||
|
|
|
@ -2,6 +2,8 @@ package proofs
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -111,15 +113,22 @@ type proof struct {
|
|||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// OutputProof prints the proof to stdout
|
||||
// reuse this for printing proofs and we should enhance this for text/json,
|
||||
// better presentation of height
|
||||
func OutputProof(info interface{}, height uint64) error {
|
||||
wrap := proof{height, info}
|
||||
res, err := data.ToJSON(wrap)
|
||||
// FoutputProof writes the output of wrapping height and info
|
||||
// in the form {"data": <the_data>, "height": <the_height>}
|
||||
// to the provider io.Writer
|
||||
func FoutputProof(w io.Writer, v interface{}, height uint64) error {
|
||||
wrap := &proof{height, v}
|
||||
blob, err := data.ToJSON(wrap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(res))
|
||||
return nil
|
||||
_, err = fmt.Fprintf(w, "%s\n", blob)
|
||||
return err
|
||||
}
|
||||
|
||||
// OutputProof prints the proof to stdout
|
||||
// reuse this for printing proofs and we should enhance this for text/json,
|
||||
// better presentation of height
|
||||
func OutputProof(data interface{}, height uint64) error {
|
||||
return FoutputProof(os.Stdout, data, height)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
coinrest "github.com/tendermint/basecoin/modules/coin/rest"
|
||||
)
|
||||
|
||||
var ServeCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Serve the light REST client for tendermint",
|
||||
Long: "Access basecoin via REST",
|
||||
RunE: serve,
|
||||
}
|
||||
|
||||
const envPortFlag = "port"
|
||||
|
||||
func init() {
|
||||
_ = ServeCmd.PersistentFlags().Int(envPortFlag, 8998, "the port to run the server on")
|
||||
}
|
||||
|
||||
const defaultAlgo = "ed25519"
|
||||
|
||||
func serve(cmd *cobra.Command, args []string) error {
|
||||
port := viper.GetInt(envPortFlag)
|
||||
keysManager := DefaultKeysManager()
|
||||
router := mux.NewRouter()
|
||||
ctx := Context{
|
||||
Keys: New(keysManager, defaultAlgo),
|
||||
}
|
||||
if err := ctx.RegisterHandlers(router); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := coinrest.RegisterHandlers(router); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
log.Printf("Serving on %q", addr)
|
||||
return http.ListenAndServe(addr, router)
|
||||
}
|
|
@ -1,25 +1,15 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
"github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/modules/auth"
|
||||
"github.com/tendermint/basecoin/modules/base"
|
||||
"github.com/tendermint/basecoin/modules/coin"
|
||||
"github.com/tendermint/basecoin/modules/fee"
|
||||
"github.com/tendermint/basecoin/modules/nonce"
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
keysutils "github.com/tendermint/go-crypto/cmd"
|
||||
keys "github.com/tendermint/go-crypto/keys"
|
||||
lightclient "github.com/tendermint/light-client"
|
||||
"github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
type Keys struct {
|
||||
|
@ -42,19 +32,19 @@ func (k *Keys) GenerateKey(w http.ResponseWriter, r *http.Request) {
|
|||
ckReq := &CreateKeyRequest{
|
||||
Algo: k.algo,
|
||||
}
|
||||
if err := parseRequestJSON(r, ckReq); err != nil {
|
||||
writeError(w, err)
|
||||
if err := common.ParseRequestAndValidateJSON(r, ckReq); err != nil {
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, seed, err := k.manager.Create(ckReq.Name, ckReq.Passphrase, ckReq.Algo)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := &CreateKeyResponse{Key: key, Seed: seed}
|
||||
writeSuccess(w, res)
|
||||
common.WriteSuccess(w, res)
|
||||
}
|
||||
|
||||
func (k *Keys) GetKey(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -62,19 +52,19 @@ func (k *Keys) GetKey(w http.ResponseWriter, r *http.Request) {
|
|||
name := query["name"]
|
||||
key, err := k.manager.Get(name)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
writeSuccess(w, &key)
|
||||
common.WriteSuccess(w, &key)
|
||||
}
|
||||
|
||||
func (k *Keys) ListKeys(w http.ResponseWriter, r *http.Request) {
|
||||
keys, err := k.manager.List()
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
writeSuccess(w, keys)
|
||||
common.WriteSuccess(w, keys)
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -83,52 +73,52 @@ var (
|
|||
|
||||
func (k *Keys) UpdateKey(w http.ResponseWriter, r *http.Request) {
|
||||
uReq := new(UpdateKeyRequest)
|
||||
if err := parseRequestJSON(r, uReq); err != nil {
|
||||
writeError(w, err)
|
||||
if err := common.ParseRequestAndValidateJSON(r, uReq); err != nil {
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
query := mux.Vars(r)
|
||||
name := query["name"]
|
||||
if name != uReq.Name {
|
||||
writeError(w, errNonMatchingPathAndJSONKeyNames)
|
||||
common.WriteError(w, errNonMatchingPathAndJSONKeyNames)
|
||||
return
|
||||
}
|
||||
|
||||
if err := k.manager.Update(uReq.Name, uReq.OldPass, uReq.NewPass); err != nil {
|
||||
writeError(w, err)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
key, err := k.manager.Get(uReq.Name)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
writeSuccess(w, &key)
|
||||
common.WriteSuccess(w, &key)
|
||||
}
|
||||
|
||||
func (k *Keys) DeleteKey(w http.ResponseWriter, r *http.Request) {
|
||||
dReq := new(DeleteKeyRequest)
|
||||
if err := parseRequestJSON(r, dReq); err != nil {
|
||||
writeError(w, err)
|
||||
if err := common.ParseRequestAndValidateJSON(r, dReq); err != nil {
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
query := mux.Vars(r)
|
||||
name := query["name"]
|
||||
if name != dReq.Name {
|
||||
writeError(w, errNonMatchingPathAndJSONKeyNames)
|
||||
common.WriteError(w, errNonMatchingPathAndJSONKeyNames)
|
||||
return
|
||||
}
|
||||
|
||||
if err := k.manager.Delete(dReq.Name, dReq.Passphrase); err != nil {
|
||||
writeError(w, err)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := &ErrorResponse{Success: true}
|
||||
writeSuccess(w, resp)
|
||||
resp := &common.ErrorResponse{Success: true}
|
||||
common.WriteSuccess(w, resp)
|
||||
}
|
||||
|
||||
func (k *Keys) Register(r *mux.Router) {
|
||||
|
@ -145,140 +135,38 @@ type Context struct {
|
|||
|
||||
func (ctx *Context) RegisterHandlers(r *mux.Router) error {
|
||||
ctx.Keys.Register(r)
|
||||
r.HandleFunc("/build/send", doSend).Methods("POST")
|
||||
r.HandleFunc("/sign", doSign).Methods("POST")
|
||||
r.HandleFunc("/tx", doPostTx).Methods("POST")
|
||||
r.HandleFunc("/query/account/{signature}", doAccountQuery).Methods("GET")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractAddress(signature string) (address string, err *ErrorResponse) {
|
||||
// Expecting the signature of the form:
|
||||
// sig:<ADDRESS>
|
||||
splits := strings.Split(signature, ":")
|
||||
if len(splits) < 2 {
|
||||
return "", &ErrorResponse{
|
||||
Error: `expecting the signature of the form "sig:<ADDRESS>"`,
|
||||
Code: 406,
|
||||
}
|
||||
}
|
||||
if splits[0] != "sigs" {
|
||||
return "", &ErrorResponse{
|
||||
Error: `expecting the signature of the form "sig:<ADDRESS>"`,
|
||||
Code: 406,
|
||||
}
|
||||
}
|
||||
return splits[1], nil
|
||||
}
|
||||
|
||||
func doAccountQuery(w http.ResponseWriter, r *http.Request) {
|
||||
query := mux.Vars(r)
|
||||
signature := query["signature"]
|
||||
address, errResp := extractAddress(signature)
|
||||
if errResp != nil {
|
||||
writeCode(w, errResp, errResp.Code)
|
||||
return
|
||||
}
|
||||
actor, err := commands.ParseActor(address)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
actor = coin.ChainAddr(actor)
|
||||
key := stack.PrefixedKey(coin.NameCoin, actor.Bytes())
|
||||
account := new(coin.Account)
|
||||
proof, err := proofs.GetAndParseAppProof(key, account)
|
||||
if lightclient.IsNoDataErr(err) {
|
||||
err := fmt.Errorf("account bytes are empty for address: %q", address)
|
||||
writeError(w, err)
|
||||
return
|
||||
} else if err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := proofs.OutputProof(account, proof.BlockHeight()); err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
writeSuccess(w, account)
|
||||
}
|
||||
|
||||
func doPostTx(w http.ResponseWriter, r *http.Request) {
|
||||
tx := new(basecoin.Tx)
|
||||
if err := parseRequestJSON(r, tx); err != nil {
|
||||
writeError(w, err)
|
||||
if err := common.ParseRequestAndValidateJSON(r, tx); err != nil {
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
commit, err := PostTx(*tx)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccess(w, commit)
|
||||
common.WriteSuccess(w, commit)
|
||||
}
|
||||
|
||||
func doSign(w http.ResponseWriter, r *http.Request) {
|
||||
sr := new(SignRequest)
|
||||
if err := parseRequestJSON(r, sr); err != nil {
|
||||
writeError(w, err)
|
||||
if err := common.ParseRequestAndValidateJSON(r, sr); err != nil {
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
tx := sr.Tx
|
||||
if err := SignTx(sr.Name, sr.Password, tx); err != nil {
|
||||
writeError(w, err)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
writeSuccess(w, tx)
|
||||
}
|
||||
|
||||
func doSend(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
si := new(SendInput)
|
||||
if err := parseRequestJSON(r, si); err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var errsList []string
|
||||
if si.From == nil {
|
||||
errsList = append(errsList, `"from" cannot be nil`)
|
||||
}
|
||||
if si.Sequence <= 0 {
|
||||
errsList = append(errsList, `"sequence" must be > 0`)
|
||||
}
|
||||
if si.To == nil {
|
||||
errsList = append(errsList, `"to" cannot be nil`)
|
||||
}
|
||||
if len(si.Amount) == 0 {
|
||||
errsList = append(errsList, `"amount" cannot be empty`)
|
||||
}
|
||||
if len(errsList) > 0 {
|
||||
err := &ErrorResponse{
|
||||
Error: strings.Join(errsList, ", "),
|
||||
Code: 406,
|
||||
}
|
||||
writeCode(w, err, 406)
|
||||
return
|
||||
}
|
||||
|
||||
tx := coin.NewSendOneTx(*si.From, *si.To, si.Amount)
|
||||
// fees are optional
|
||||
if si.Fees != nil && !si.Fees.IsZero() {
|
||||
tx = fee.NewFee(tx, *si.Fees, *si.From)
|
||||
}
|
||||
// only add the actual signer to the nonce
|
||||
signers := []basecoin.Actor{*si.From}
|
||||
tx = nonce.NewTx(si.Sequence, signers, tx)
|
||||
tx = base.NewChainTx(commands.GetChainID(), 0, tx)
|
||||
|
||||
if si.Multi {
|
||||
tx = auth.NewMulti(tx).Wrap()
|
||||
} else {
|
||||
tx = auth.NewSig(tx).Wrap()
|
||||
}
|
||||
writeSuccess(w, tx)
|
||||
common.WriteSuccess(w, tx)
|
||||
}
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/modules/coin"
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
data "github.com/tendermint/go-wire/data"
|
||||
)
|
||||
|
||||
type CreateKeyRequest struct {
|
||||
|
@ -40,16 +32,6 @@ type SignRequest struct {
|
|||
Tx basecoin.Tx `json:"tx" validate:"required"`
|
||||
}
|
||||
|
||||
type ErrorResponse struct {
|
||||
Success bool `json:"success,omitempty"`
|
||||
|
||||
// Error is the error message if Success is false
|
||||
Error string `json:"error,omitempty"`
|
||||
|
||||
// Code is set if Success is false
|
||||
Code int `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
type CreateKeyResponse struct {
|
||||
Key keys.Info `json:"key,omitempty"`
|
||||
Seed string `json:"seed_phrase,omitempty"`
|
||||
|
@ -68,48 +50,3 @@ type SendInput struct {
|
|||
From *basecoin.Actor `json:"from"`
|
||||
Amount coin.Coins `json:"amount"`
|
||||
}
|
||||
|
||||
// Validators
|
||||
|
||||
var theValidator = validator.New()
|
||||
|
||||
func validate(req interface{}) error {
|
||||
return errors.Wrap(theValidator.Struct(req), "Validate")
|
||||
}
|
||||
|
||||
// Helpers
|
||||
func parseRequestJSON(r *http.Request, save interface{}) error {
|
||||
defer r.Body.Close()
|
||||
|
||||
slurp, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Read Request")
|
||||
}
|
||||
if err := json.Unmarshal(slurp, save); err != nil {
|
||||
return errors.Wrap(err, "Parse")
|
||||
}
|
||||
return validate(save)
|
||||
}
|
||||
|
||||
func writeSuccess(w http.ResponseWriter, data interface{}) {
|
||||
writeCode(w, data, 200)
|
||||
}
|
||||
|
||||
func writeCode(w http.ResponseWriter, out interface{}, code int) {
|
||||
blob, err := data.ToJSON(out)
|
||||
if err != nil {
|
||||
writeError(w, err)
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
w.Write(blob)
|
||||
}
|
||||
}
|
||||
|
||||
func writeError(w http.ResponseWriter, err error) {
|
||||
resp := &ErrorResponse{
|
||||
Code: 406,
|
||||
Error: err.Error(),
|
||||
}
|
||||
writeCode(w, resp, 406)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
|
@ -20,24 +17,12 @@ var srvCli = &cobra.Command{
|
|||
Long: `Baseserver presents a nice (not raw hex) interface to the basecoin blockchain structure.`,
|
||||
}
|
||||
|
||||
var serveCmd = &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Serve the light REST client for tendermint",
|
||||
Long: "Access basecoin via REST",
|
||||
RunE: serve,
|
||||
}
|
||||
|
||||
var port int
|
||||
|
||||
func main() {
|
||||
commands.AddBasicFlags(srvCli)
|
||||
|
||||
flagset := serveCmd.Flags()
|
||||
flagset.IntVar(&port, "port", 8998, "the port to run the server on")
|
||||
|
||||
srvCli.AddCommand(
|
||||
commands.InitCmd,
|
||||
serveCmd,
|
||||
rest.ServeCmd,
|
||||
)
|
||||
|
||||
// TODO: Decide whether to use $HOME/.basecli for compatibility
|
||||
|
@ -47,20 +32,3 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
const defaultAlgo = "ed25519"
|
||||
|
||||
func serve(cmd *cobra.Command, args []string) error {
|
||||
keysManager := rest.DefaultKeysManager()
|
||||
router := mux.NewRouter()
|
||||
ctx := rest.Context{
|
||||
Keys: rest.New(keysManager, defaultAlgo),
|
||||
}
|
||||
if err := ctx.RegisterHandlers(router); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
log.Printf("Serving on %q", addr)
|
||||
return http.ListenAndServe(addr, router)
|
||||
}
|
||||
|
|
|
@ -173,7 +173,7 @@ imports:
|
|||
- types
|
||||
- version
|
||||
- name: github.com/tendermint/tmlibs
|
||||
version: 2f6f3e6aa70bb19b70a6e73210273fa127041070
|
||||
version: 75372988e737a9f672c0e7f6308042620bd3e151
|
||||
subpackages:
|
||||
- autofile
|
||||
- cli
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
package rest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/client/commands"
|
||||
"github.com/tendermint/basecoin/client/commands/proofs"
|
||||
"github.com/tendermint/basecoin/modules/auth"
|
||||
"github.com/tendermint/basecoin/modules/base"
|
||||
"github.com/tendermint/basecoin/modules/coin"
|
||||
"github.com/tendermint/basecoin/modules/fee"
|
||||
"github.com/tendermint/basecoin/modules/nonce"
|
||||
"github.com/tendermint/basecoin/stack"
|
||||
lightclient "github.com/tendermint/light-client"
|
||||
"github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
// SendInput is the request to send an amount from one actor to another.
|
||||
// Note: Not using the `validator:""` tags here because SendInput has
|
||||
// many fields so it would be nice to figure out all the invalid
|
||||
// inputs and report them back to the caller, in one shot.
|
||||
type SendInput struct {
|
||||
Fees *coin.Coin `json:"fees"`
|
||||
Multi bool `json:"multi,omitempty"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
|
||||
To *basecoin.Actor `json:"to"`
|
||||
From *basecoin.Actor `json:"from"`
|
||||
Amount coin.Coins `json:"amount"`
|
||||
}
|
||||
|
||||
func RegisterHandlers(r *mux.Router) error {
|
||||
r.HandleFunc("/build/send", doSend).Methods("POST")
|
||||
r.HandleFunc("/query/account/{signature}", doQueryAccount).Methods("GET")
|
||||
return nil
|
||||
}
|
||||
|
||||
// doQueryAccount is the HTTP handlerfunc to query an account
|
||||
// It expects a query string with
|
||||
func doQueryAccount(w http.ResponseWriter, r *http.Request) {
|
||||
query := mux.Vars(r)
|
||||
signature := query["signature"]
|
||||
actor, err := commands.ParseActor(signature)
|
||||
if err != nil {
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
actor = coin.ChainAddr(actor)
|
||||
key := stack.PrefixedKey(coin.NameCoin, actor.Bytes())
|
||||
account := new(coin.Account)
|
||||
proof, err := proofs.GetAndParseAppProof(key, account)
|
||||
if lightclient.IsNoDataErr(err) {
|
||||
err := fmt.Errorf("account bytes are empty for address: %q", signature)
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
} else if err != nil {
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := proofs.FoutputProof(w, account, proof.BlockHeight()); err != nil {
|
||||
common.WriteError(w, err)
|
||||
}
|
||||
}
|
||||
|
||||
func PrepareSendTx(si *SendInput) basecoin.Tx {
|
||||
tx := coin.NewSendOneTx(*si.From, *si.To, si.Amount)
|
||||
// fees are optional
|
||||
if si.Fees != nil && !si.Fees.IsZero() {
|
||||
tx = fee.NewFee(tx, *si.Fees, *si.From)
|
||||
}
|
||||
// only add the actual signer to the nonce
|
||||
signers := []basecoin.Actor{*si.From}
|
||||
tx = nonce.NewTx(si.Sequence, signers, tx)
|
||||
tx = base.NewChainTx(commands.GetChainID(), 0, tx)
|
||||
|
||||
if si.Multi {
|
||||
tx = auth.NewMulti(tx).Wrap()
|
||||
} else {
|
||||
tx = auth.NewSig(tx).Wrap()
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
func doSend(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
si := new(SendInput)
|
||||
if err := common.ParseRequestAndValidateJSON(r, si); err != nil {
|
||||
common.WriteError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var errsList []string
|
||||
if si.From == nil {
|
||||
errsList = append(errsList, `"from" cannot be nil`)
|
||||
}
|
||||
if si.Sequence <= 0 {
|
||||
errsList = append(errsList, `"sequence" must be > 0`)
|
||||
}
|
||||
if si.To == nil {
|
||||
errsList = append(errsList, `"to" cannot be nil`)
|
||||
}
|
||||
if len(si.Amount) == 0 {
|
||||
errsList = append(errsList, `"amount" cannot be empty`)
|
||||
}
|
||||
if len(errsList) > 0 {
|
||||
code := http.StatusBadRequest
|
||||
err := &common.ErrorResponse{
|
||||
Err: strings.Join(errsList, ", "),
|
||||
Code: code,
|
||||
}
|
||||
common.WriteCode(w, err, code)
|
||||
return
|
||||
}
|
||||
|
||||
tx := PrepareSendTx(si)
|
||||
common.WriteSuccess(w, tx)
|
||||
}
|
Loading…
Reference in New Issue