Merge pull request #662 from cosmos/matt/ibc-rest

REST IBC transfer endpoint
This commit is contained in:
Ethan Buchman 2018-03-29 08:45:46 -04:00 committed by GitHub
commit 66a1130f9d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 171 additions and 0 deletions

View File

@ -252,6 +252,32 @@ func TestCoinSend(t *testing.T) {
assert.Equal(t, int64(1), mycoins.Amount)
}
func TestIBCTransfer(t *testing.T) {
// create TX
resultTx := doIBCTransfer(t, port, seed)
time.Sleep(time.Second * 2) // T
// check if tx was commited
assert.Equal(t, uint32(0), resultTx.CheckTx.Code)
assert.Equal(t, uint32(0), resultTx.DeliverTx.Code)
// query sender
res, body := request(t, port, "GET", "/accounts/"+sendAddr, nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var m auth.BaseAccount
err := json.Unmarshal([]byte(body), &m)
require.Nil(t, err)
coins := m.Coins
mycoins := coins[0]
assert.Equal(t, coinDenom, mycoins.Denom)
assert.Equal(t, coinAmount-2, mycoins.Amount)
// TODO: query ibc egress packet state
}
func TestTxs(t *testing.T) {
// TODO: re-enable once we can get txs by tag
@ -444,3 +470,32 @@ func doSend(t *testing.T, port, seed string) (receiveAddr string, resultTx ctype
return receiveAddr, resultTx
}
func doIBCTransfer(t *testing.T, port, seed string) (resultTx ctypes.ResultBroadcastTxCommit) {
// create receive address
kb := client.MockKeyBase()
receiveInfo, _, err := kb.Create("receive_address", "1234567890", cryptoKeys.CryptoAlgo("ed25519"))
require.Nil(t, err)
receiveAddr := receiveInfo.PubKey.Address().String()
// get the account to get the sequence
res, body := request(t, port, "GET", "/accounts/"+sendAddr, nil)
// require.Equal(t, http.StatusOK, res.StatusCode, body)
acc := auth.BaseAccount{}
err = json.Unmarshal([]byte(body), &acc)
require.Nil(t, err)
fmt.Println("BODY", body)
fmt.Println("ACC", acc)
sequence := acc.Sequence
// send
jsonStr := []byte(fmt.Sprintf(`{ "name":"%s", "password":"%s", "sequence":%d, "amount":[{ "denom": "%s", "amount": 1 }] }`, name, password, sequence, coinDenom))
res, body = request(t, port, "POST", "/ibc/testchain/"+receiveAddr+"/send", jsonStr)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = json.Unmarshal([]byte(body), &resultTx)
require.Nil(t, err)
return resultTx
}

View File

@ -20,6 +20,7 @@ import (
"github.com/cosmos/cosmos-sdk/wire"
auth "github.com/cosmos/cosmos-sdk/x/auth/rest"
bank "github.com/cosmos/cosmos-sdk/x/bank/rest"
ibc "github.com/cosmos/cosmos-sdk/x/ibc/rest"
)
const (
@ -78,5 +79,6 @@ func createHandler(cdc *wire.Codec) http.Handler {
tx.RegisterRoutes(r, cdc)
auth.RegisterRoutes(r, cdc, "main")
bank.RegisterRoutes(r, cdc, kb)
ibc.RegisterRoutes(r, cdc, kb)
return r
}

14
x/ibc/rest/root.go Normal file
View File

@ -0,0 +1,14 @@
package rest
import (
"github.com/gorilla/mux"
keys "github.com/tendermint/go-crypto/keys"
"github.com/cosmos/cosmos-sdk/wire"
)
// RegisterRoutes - Central function to define routes that get registered by the main application
func RegisterRoutes(r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
r.HandleFunc("/ibc/{destchain}/{address}/send", TransferRequestHandler(cdc, kb)).Methods("POST")
}

100
x/ibc/rest/transfer.go Normal file
View File

@ -0,0 +1,100 @@
package rest
import (
"encoding/hex"
"encoding/json"
"io/ioutil"
"net/http"
"github.com/gorilla/mux"
"github.com/spf13/viper"
"github.com/tendermint/go-crypto/keys"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/bank/commands"
"github.com/cosmos/cosmos-sdk/x/ibc"
)
type transferBody struct {
// Fees sdk.Coin `json="fees"`
Amount sdk.Coins `json:"amount"`
LocalAccountName string `json:"name"`
Password string `json:"password"`
SrcChainID string `json:"src_chain_id"`
Sequence int64 `json:"sequence"`
}
// TransferRequestHandler - http request handler to transfer coins to a address
// on a different chain via IBC
func TransferRequestHandler(cdc *wire.Codec, kb keys.Keybase) func(http.ResponseWriter, *http.Request) {
c := commands.Commander{cdc}
return func(w http.ResponseWriter, r *http.Request) {
// collect data
vars := mux.Vars(r)
destChainID := vars["destchain"]
address := vars["address"]
var m transferBody
body, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
err = json.Unmarshal(body, &m)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
info, err := kb.Get(m.LocalAccountName)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(err.Error()))
return
}
bz, err := hex.DecodeString(address)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
to := sdk.Address(bz)
// build message
packet := ibc.NewIBCPacket(info.PubKey.Address(), to, m.Amount, m.SrcChainID, destChainID)
msg := ibc.IBCTransferMsg{packet}
// sign
// XXX: OMG
viper.Set(client.FlagSequence, m.Sequence)
txBytes, err := builder.SignAndBuild(m.LocalAccountName, m.Password, msg, c.Cdc)
if err != nil {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(err.Error()))
return
}
// send
res, err := builder.BroadcastTx(txBytes)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
output, err := json.MarshalIndent(res, "", " ")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Write(output)
}
}