mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-25 12:11:22 +00:00
rpc: fixes for better type handlings, explicit error field in response, more tests
This commit is contained in:
parent
9aeafffd9b
commit
6e81e8a848
@ -25,6 +25,18 @@ func GenPrivAccount() *PrivAccount {
|
||||
}
|
||||
}
|
||||
|
||||
func GenPrivAccountFromKey(privKeyBytes [64]byte) *PrivAccount {
|
||||
pubKeyBytes := ed25519.MakePublicKey(&privKeyBytes)
|
||||
pubKey := PubKeyEd25519(pubKeyBytes[:])
|
||||
privKey := PrivKeyEd25519(privKeyBytes[:])
|
||||
return &PrivAccount{
|
||||
Address: pubKey.Address(),
|
||||
PubKey: pubKey,
|
||||
PrivKey: privKey,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (privAccount *PrivAccount) Sign(o Signable) Signature {
|
||||
return privAccount.PrivKey.Sign(SignBytes(o))
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ func SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (types.Tx, error)
|
||||
return nil, fmt.Errorf("Invalid (empty) privAccount @%v", i)
|
||||
}
|
||||
}
|
||||
|
||||
switch tx.(type) {
|
||||
case *types.SendTx:
|
||||
sendTx := tx.(*types.SendTx)
|
||||
|
@ -39,6 +39,7 @@ var funcMap = map[string]*FuncWrapper{
|
||||
"sign_tx": funcWrap("sign_tx", core.SignTx),
|
||||
}
|
||||
|
||||
// map each function to an empty struct which can hold its return values
|
||||
var responseMap = map[string]reflect.Value{
|
||||
"status": reflect.ValueOf(&ResponseStatus{}),
|
||||
"net_info": reflect.ValueOf(&ResponseNetInfo{}),
|
||||
@ -52,6 +53,7 @@ var responseMap = map[string]reflect.Value{
|
||||
"sign_tx": reflect.ValueOf(&ResponseSignTx{}),
|
||||
}
|
||||
|
||||
// holds all type information for each function
|
||||
type FuncWrapper struct {
|
||||
f reflect.Value // function from "rpc/core"
|
||||
args []reflect.Type // type of each function arg
|
||||
@ -76,39 +78,57 @@ func toHandler(funcName string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
values, err := queryToValues(funcInfo, r)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_INVALID_PARAM, err.Error())
|
||||
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
||||
return
|
||||
}
|
||||
returns := funcInfo.f.Call(values)
|
||||
response, err := returnsToResponse(funcInfo, returns)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_ERROR, err.Error())
|
||||
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
||||
return
|
||||
}
|
||||
WriteAPIResponse(w, API_OK, response)
|
||||
WriteAPIResponse(w, API_OK, response, "")
|
||||
}
|
||||
}
|
||||
|
||||
// covert an http query to a list of properly typed values
|
||||
// to be properly decoded the arg must be a concrete type from tendermint
|
||||
func queryToValues(funcInfo *FuncWrapper, r *http.Request) ([]reflect.Value, error) {
|
||||
argTypes := funcInfo.args
|
||||
argNames := funcInfo.argNames
|
||||
|
||||
var err error
|
||||
values := make([]reflect.Value, len(argNames))
|
||||
fmt.Println("names:", argNames)
|
||||
for i, name := range argNames {
|
||||
ty := argTypes[i]
|
||||
v := reflect.New(ty).Elem()
|
||||
kind := v.Kind()
|
||||
arg := GetParam(r, name)
|
||||
switch kind {
|
||||
case reflect.Interface:
|
||||
v = reflect.New(ty)
|
||||
binary.ReadJSON(v.Interface(), []byte(arg), &err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v = v.Elem()
|
||||
case reflect.Struct:
|
||||
binary.ReadJSON(v.Interface(), []byte(arg), &err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case reflect.Slice:
|
||||
rt := ty.Elem()
|
||||
if rt.Kind() == reflect.Uint8 {
|
||||
v = reflect.ValueOf([]byte(arg))
|
||||
} else {
|
||||
v = reflect.New(ty)
|
||||
binary.ReadJSON(v.Interface(), []byte(arg), &err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
case reflect.Int64:
|
||||
u, err := strconv.ParseInt(arg, 10, 64)
|
||||
if err != nil {
|
||||
@ -142,6 +162,7 @@ func queryToValues(funcInfo *FuncWrapper, r *http.Request) ([]reflect.Value, err
|
||||
}
|
||||
|
||||
// covert a list of interfaces to properly typed values
|
||||
// TODO!
|
||||
func paramsToValues(funcInfo *FuncWrapper, params []interface{}) ([]reflect.Value, error) {
|
||||
values := make([]reflect.Value, len(params))
|
||||
for i, p := range params {
|
||||
@ -156,7 +177,9 @@ func returnsToResponse(funcInfo *FuncWrapper, returns []reflect.Value) (interfac
|
||||
finalType := returnTypes[len(returnTypes)-1]
|
||||
if finalType.Implements(reflect.TypeOf((*error)(nil)).Elem()) {
|
||||
errV := returns[len(returnTypes)-1]
|
||||
return nil, errV.Interface().(error)
|
||||
if errV.Interface() != nil {
|
||||
return nil, fmt.Errorf("%v", errV.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
v := funcInfo.response.Elem()
|
||||
@ -181,15 +204,16 @@ func JsonRpcHandler(w http.ResponseWriter, r *http.Request) {
|
||||
funcInfo := funcMap[jrpc.Method]
|
||||
values, err := paramsToValues(funcInfo, jrpc.Params)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_INVALID_PARAM, err.Error())
|
||||
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
|
||||
return
|
||||
}
|
||||
returns := funcInfo.f.Call(values)
|
||||
response, err := returnsToResponse(funcInfo, returns)
|
||||
if err != nil {
|
||||
WriteAPIResponse(w, API_ERROR, err.Error())
|
||||
WriteAPIResponse(w, API_ERROR, nil, err.Error())
|
||||
return
|
||||
}
|
||||
WriteAPIResponse(w, API_OK, response)
|
||||
WriteAPIResponse(w, API_OK, response, "")
|
||||
}
|
||||
|
||||
func initHandlers() {
|
||||
|
@ -24,7 +24,7 @@ var (
|
||||
)
|
||||
|
||||
func panicAPI(err error) {
|
||||
panic(APIResponse{API_INVALID_PARAM, err.Error()})
|
||||
panic(APIResponse{API_INVALID_PARAM, nil, err.Error()})
|
||||
}
|
||||
|
||||
func GetParam(r *http.Request, param string) string {
|
||||
|
@ -38,16 +38,22 @@ const (
|
||||
type APIResponse struct {
|
||||
Status APIStatus `json:"status"`
|
||||
Data interface{} `json:"data"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func (res APIResponse) Error() string {
|
||||
return fmt.Sprintf("Status(%v) %v", res.Status, res.Data)
|
||||
func (res APIResponse) StatusError() string {
|
||||
return fmt.Sprintf("Status(%v) %v", res.Status, res.Error)
|
||||
}
|
||||
|
||||
func WriteAPIResponse(w http.ResponseWriter, status APIStatus, data interface{}) {
|
||||
func WriteAPIResponse(w http.ResponseWriter, status APIStatus, data interface{}, responseErr string) {
|
||||
res := APIResponse{}
|
||||
res.Status = status
|
||||
if data == nil {
|
||||
// so json doesn't vommit
|
||||
data = struct{}{}
|
||||
}
|
||||
res.Data = data
|
||||
res.Error = responseErr
|
||||
|
||||
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||
binary.WriteJSON(res, buf, n, err)
|
||||
@ -109,7 +115,7 @@ func RecoverAndLogHandler(handler http.Handler) http.Handler {
|
||||
|
||||
// If APIResponse,
|
||||
if res, ok := e.(APIResponse); ok {
|
||||
WriteAPIResponse(rww, res.Status, res.Data)
|
||||
WriteAPIResponse(rww, res.Status, nil, res.Error)
|
||||
} else {
|
||||
// For the rest,
|
||||
rww.WriteHeader(http.StatusInternalServerError)
|
||||
|
@ -5,11 +5,14 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/tendermint/tendermint/account"
|
||||
"github.com/tendermint/tendermint/binary"
|
||||
"github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/daemon"
|
||||
"github.com/tendermint/tendermint/logger"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/rpc"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -22,6 +25,9 @@ var (
|
||||
chainId string
|
||||
node *daemon.Node
|
||||
userAddr = "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB"
|
||||
userPriv = "FDE3BD94CB327D19464027BA668194C5EFA46AE83E8419D7542CFF41F00C81972239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3"
|
||||
|
||||
userPub = "2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3"
|
||||
)
|
||||
|
||||
func newNode(ready chan struct{}) {
|
||||
@ -49,7 +55,9 @@ func init() {
|
||||
app.Set("RPC.HTTP.ListenAddr", rpcAddr)
|
||||
app.Set("GenesisFile", rootDir+"/genesis.json")
|
||||
app.Set("PrivValidatorFile", rootDir+"/priv_validator.json")
|
||||
app.Set("Log.Stdout.Level", "debug")
|
||||
config.SetApp(app)
|
||||
logger.InitLog()
|
||||
// start a node
|
||||
ready := make(chan struct{})
|
||||
go newNode(ready)
|
||||
@ -105,10 +113,9 @@ func TestGenPriv(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccount(t *testing.T) {
|
||||
byteAddr, _ := hex.DecodeString(userAddr)
|
||||
func getAccount(t *testing.T, addr []byte) *account.Account {
|
||||
resp, err := http.PostForm(requestAddr+"get_account",
|
||||
url.Values{"address": {string(byteAddr)}})
|
||||
url.Values{"address": {string(addr)}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -120,54 +127,159 @@ func TestGetAccount(t *testing.T) {
|
||||
var status struct {
|
||||
Status string
|
||||
Data rpc.ResponseGetAccount
|
||||
Error string
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
binary.ReadJSON(&status, body, &err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bytes.Compare(status.Data.Account.Address, byteAddr) != 0 {
|
||||
t.Fatalf("Failed to get correct account. Got %x, expected %x", status.Data.Account.Address, byteAddr)
|
||||
return status.Data.Account
|
||||
}
|
||||
|
||||
func TestGetAccount(t *testing.T) {
|
||||
byteAddr, _ := hex.DecodeString(userAddr)
|
||||
acc := getAccount(t, byteAddr)
|
||||
if bytes.Compare(acc.Address, byteAddr) != 0 {
|
||||
t.Fatalf("Failed to get correct account. Got %x, expected %x", acc.Address, byteAddr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSignedTx(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
acc := mint.MempoolReactor.Mempool.GetState().GetAccount(mint.priv.Address)
|
||||
func makeTx(t *testing.T, from, to []byte, amt uint64) *types.SendTx {
|
||||
acc := getAccount(t, from)
|
||||
nonce := 0
|
||||
if acc != nil {
|
||||
nonce = int(acc.Sequence) + 1
|
||||
}
|
||||
|
||||
amtInt, err := strconv.Atoi(amt)
|
||||
bytePub, err := hex.DecodeString(userPub)
|
||||
if err != nil {
|
||||
return "", err
|
||||
t.Fatal(err)
|
||||
}
|
||||
amtUint64 := uint64(amtInt)
|
||||
|
||||
tx := &blk.SendTx{
|
||||
Inputs: []*blk.TxInput{
|
||||
&blk.TxInput{
|
||||
Address: mint.priv.Address,
|
||||
Amount: amtUint64,
|
||||
tx := &types.SendTx{
|
||||
Inputs: []*types.TxInput{
|
||||
&types.TxInput{
|
||||
Address: from,
|
||||
Amount: amt,
|
||||
Sequence: uint(nonce),
|
||||
Signature: account.SignatureEd25519{},
|
||||
PubKey: mint.priv.PubKey,
|
||||
PubKey: account.PubKeyEd25519(bytePub),
|
||||
},
|
||||
},
|
||||
Outputs: []*blk.TxOutput{
|
||||
&blk.TxOutput{
|
||||
Address: addrB,
|
||||
Amount: amtUint64,
|
||||
Outputs: []*types.TxOutput{
|
||||
&types.TxOutput{
|
||||
Address: to,
|
||||
Amount: amt,
|
||||
},
|
||||
},
|
||||
}
|
||||
tx.Inputs[0].Signature = mint.priv.PrivKey.Sign(account.SignBytes(tx))
|
||||
err = mint.MempoolReactor.BroadcastTx(tx)
|
||||
return hex.EncodeToString(merkle.HashFromBinary(tx)), err
|
||||
return tx
|
||||
}
|
||||
|
||||
*/
|
||||
func requestResponse(t *testing.T, method string, values url.Values, status interface{}) {
|
||||
resp, err := http.PostForm(requestAddr+method, values)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
binary.ReadJSON(status, body, &err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignedTx(t *testing.T) {
|
||||
byteAddr, _ := hex.DecodeString(userAddr)
|
||||
var byteKey [64]byte
|
||||
oh, _ := hex.DecodeString(userPriv)
|
||||
copy(byteKey[:], oh)
|
||||
|
||||
amt := uint64(100)
|
||||
toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54}
|
||||
tx := makeTx(t, byteAddr, toAddr, amt)
|
||||
|
||||
/*b, err := json.Marshal(rpc.InterfaceType{types.TxTypeSend, tx})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}*/
|
||||
|
||||
n := new(int64)
|
||||
var err error
|
||||
w := new(bytes.Buffer)
|
||||
binary.WriteJSON(tx, w, n, &err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := w.Bytes()
|
||||
|
||||
privAcc := account.GenPrivAccountFromKey(byteKey)
|
||||
if bytes.Compare(privAcc.PubKey.Address(), byteAddr) != 0 {
|
||||
t.Fatal("Faield to generate correct priv acc")
|
||||
}
|
||||
w = new(bytes.Buffer)
|
||||
binary.WriteJSON([]*account.PrivAccount{privAcc}, w, n, &err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var status struct {
|
||||
Status string
|
||||
Data rpc.ResponseSignTx
|
||||
Error string
|
||||
}
|
||||
requestResponse(t, "unsafe/sign_tx", url.Values{"tx": {string(b)}, "privAccounts": {string(w.Bytes())}}, &status)
|
||||
|
||||
if status.Status == "ERROR" {
|
||||
t.Fatal(status.Error)
|
||||
}
|
||||
response := status.Data
|
||||
//tx = status.Data.(rpc.ResponseSignTx).Tx.(*types.SendTx)
|
||||
tx = response.Tx.(*types.SendTx)
|
||||
if bytes.Compare(tx.Inputs[0].Address, byteAddr) != 0 {
|
||||
t.Fatal("Tx input addresses don't match!")
|
||||
}
|
||||
|
||||
signBytes := account.SignBytes(tx)
|
||||
in := tx.Inputs[0] //(*types.SendTx).Inputs[0]
|
||||
|
||||
if err := in.ValidateBasic(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(privAcc.PubKey, in.PubKey)
|
||||
// Check signatures
|
||||
// acc := getAccount(t, byteAddr)
|
||||
// NOTE: using the acc here instead of the in fails; its PubKeyNil ... ?
|
||||
if !in.PubKey.VerifyBytes(signBytes, in.Signature) {
|
||||
t.Fatal(types.ErrTxInvalidSignature)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBroadcastTx(t *testing.T) {
|
||||
/*
|
||||
byteAddr, _ := hex.DecodeString(userAddr)
|
||||
|
||||
amt := uint64(100)
|
||||
toAddr := []byte{20, 143, 25, 63, 16, 177, 83, 29, 91, 91, 54, 23, 233, 46, 190, 121, 122, 34, 86, 54}
|
||||
tx := makeTx(t, byteAddr, toAddr, amt)
|
||||
|
||||
b, err := json.Marshal([]interface{}{types.TxTypeSend, tx})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var status struct {
|
||||
Status string
|
||||
Data rpc.ResponseSignTx
|
||||
}
|
||||
// TODO ...
|
||||
*/
|
||||
}
|
||||
|
||||
/*tx.Inputs[0].Signature = mint.priv.PrivKey.Sign(account.SignBytes(tx))
|
||||
err = mint.MempoolReactor.BroadcastTx(tx)
|
||||
return hex.EncodeToString(merkle.HashFromBinary(tx)), err*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user