Browse Source

Merge branch 'develop' of https://github.com/tendermint/tendermint2 into develop

pull/43/merge
Ethan Buchman 10 years ago
parent
commit
8cc8757252
9 changed files with 189 additions and 159 deletions
  1. +47
    -20
      binary/reflect.go
  2. +20
    -0
      binary/reflect_test.go
  3. +1
    -0
      daemon/daemon.go
  4. +8
    -7
      rpc/handlers.go
  5. +2
    -2
      rpc/http_params.go
  6. +22
    -47
      rpc/http_server.go
  7. +16
    -19
      rpc/test/http_rpc_test.go
  8. +27
    -22
      rpc/test/json_rpc_test.go
  9. +46
    -42
      rpc/test/test.go

+ 47
- 20
binary/reflect.go View File

@ -9,7 +9,7 @@ import (
"sync"
"time"
. "github.com/tendermint/tendermint2/common"
. "github.com/tendermint/tendermint/common"
)
type TypeInfo struct {
@ -26,6 +26,19 @@ type TypeInfo struct {
// If Type is concrete
HasTypeByte bool
TypeByte byte
// If Type is kind reflect.Struct
Fields []StructFieldInfo
}
type StructFieldInfo struct {
Index int // Struct field index
JSONName string // Corresponding JSON field name. (override with `json=""`)
Type reflect.Type // Struct field type
}
func (info StructFieldInfo) unpack() (int, string, reflect.Type) {
return info.Index, info.JSONName, info.Type
}
// e.g. If o is struct{Foo}{}, return is the Foo interface type.
@ -155,6 +168,28 @@ func RegisterType(info *TypeInfo) *TypeInfo {
}
}
// If struct, register field name options
if rt.Kind() == reflect.Struct {
numFields := rt.NumField()
structFields := []StructFieldInfo{}
for i := 0; i < numFields; i++ {
field := rt.Field(i)
if field.PkgPath != "" {
continue
}
jsonName := field.Tag.Get("json")
if jsonName == "" {
jsonName = field.Name
}
structFields = append(structFields, StructFieldInfo{
Index: i,
JSONName: jsonName,
Type: field.Type,
})
}
info.Fields = structFields
}
return info
}
@ -532,19 +567,15 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *erro
return
}
// TODO: ensure that all fields are set?
for name, value := range oMap {
field, ok := rt.FieldByName(name)
// TODO: disallow unknown oMap fields?
for _, fieldInfo := range typeInfo.Fields {
i, jsonName, fieldType := fieldInfo.unpack()
value, ok := oMap[jsonName]
if !ok {
*err = errors.New(Fmt("Attempt to set unknown field %v", field.Name))
return
continue // Skip missing fields.
}
// JAE: I don't think golang reflect lets us set unexported fields, but just in case:
if field.PkgPath != "" {
*err = errors.New(Fmt("Attempt to set unexported field %v", field.Name))
return
}
fieldRv := rv.FieldByName(name)
readReflectJSON(fieldRv, field.Type, value, err)
fieldRv := rv.Field(i)
readReflectJSON(fieldRv, fieldType, value, err)
}
}
@ -643,21 +674,17 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64,
WriteTo(jsonBytes, w, n, err)
} else {
WriteTo([]byte("{"), w, n, err)
numFields := rt.NumField()
wroteField := false
for i := 0; i < numFields; i++ {
field := rt.Field(i)
if field.PkgPath != "" {
continue
}
for _, fieldInfo := range typeInfo.Fields {
i, jsonName, fieldType := fieldInfo.unpack()
fieldRv := rv.Field(i)
if wroteField {
WriteTo([]byte(","), w, n, err)
} else {
wroteField = true
}
WriteTo([]byte(Fmt("\"%v\":", field.Name)), w, n, err)
writeReflectJSON(fieldRv, field.Type, w, n, err)
WriteTo([]byte(Fmt("\"%v\":", jsonName)), w, n, err)
writeReflectJSON(fieldRv, fieldType, w, n, err)
}
WriteTo([]byte("}"), w, n, err)
}


+ 20
- 0
binary/reflect_test.go View File

@ -401,3 +401,23 @@ func TestJSON(t *testing.T) {
}
}
//------------------------------------------------------------------------------
type Foo struct {
FieldA string `json:"fieldA"` // json field name is "fieldA"
FieldB string // json field name is "FieldB"
fieldC string // not exported, not serialized.
}
func TestJSONFieldNames(t *testing.T) {
for i := 0; i < 20; i++ { // Try to ensure deterministic success.
foo := Foo{"a", "b", "c"}
stringified := string(JSONBytes(foo))
expected := `{"fieldA":"a","FieldB":"b"}`
if stringified != expected {
t.Fatalf("JSONFieldNames error: expected %v, got %v",
expected, stringified)
}
}
}

+ 1
- 0
daemon/daemon.go View File

@ -199,6 +199,7 @@ func deboraBroadcast(n *Node) func([]byte) {
func Daemon(deborable DeboraMode) {
// Add to debora
if deborable == DeboraPeerMode {
// TODO: support debora.logfile
if err := debora.Add(PublicKey, SrcPath, AppName, config.App().GetString("Debora.LogFile")); err != nil {
log.Info("Failed to add program to debora", "error", err)
}


+ 8
- 7
rpc/handlers.go View File

@ -97,22 +97,23 @@ func JSONRPCHandler(w http.ResponseWriter, r *http.Request) {
var jrpc JSONRPC
err := json.Unmarshal(b, &jrpc)
if err != nil {
// TODO
WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return
}
funcInfo := funcMap[jrpc.Method]
args, err := jsonParamsToArgs(funcInfo, jrpc.Params)
if err != nil {
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return
}
returns := funcInfo.f.Call(args)
response, err := returnsToResponse(returns)
if err != nil {
WriteAPIResponse(w, API_ERROR, nil, err.Error())
WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return
}
WriteAPIResponse(w, API_OK, response, "")
WriteRPCResponse(w, NewRPCResponse(response, ""))
}
// covert a list of interfaces to properly typed values
@ -149,16 +150,16 @@ func toHttpHandler(funcInfo *FuncWrapper) func(http.ResponseWriter, *http.Reques
return func(w http.ResponseWriter, r *http.Request) {
args, err := httpParamsToArgs(funcInfo, r)
if err != nil {
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error())
WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return
}
returns := funcInfo.f.Call(args)
response, err := returnsToResponse(returns)
if err != nil {
WriteAPIResponse(w, API_ERROR, nil, err.Error())
WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return
}
WriteAPIResponse(w, API_OK, response, "")
WriteRPCResponse(w, NewRPCResponse(response, ""))
}
}


+ 2
- 2
rpc/http_params.go View File

@ -23,8 +23,8 @@ var (
//RE_ID12 = regexp.MustCompile(`^[a-zA-Z0-9]{12}$`)
)
func panicAPI(err error) {
panic(APIResponse{API_INVALID_PARAM, nil, err.Error()})
func panicRPC(err error) {
panic(NewRPCResponse(nil, err.Error()))
}
func GetParam(r *http.Request, param string) string {


+ 22
- 47
rpc/http_server.go View File

@ -23,64 +23,39 @@ func StartHTTPServer() {
}()
}
//-----------------------------------------------------------------------------
type APIStatus string
const (
API_OK APIStatus = "OK"
API_ERROR APIStatus = "ERROR"
API_INVALID_PARAM APIStatus = "INVALID_PARAM"
API_UNAUTHORIZED APIStatus = "UNAUTHORIZED"
API_REDIRECT APIStatus = "REDIRECT"
)
type APIResponse struct {
Status APIStatus `json:"status"`
Data interface{} `json:"data"`
Error string `json:"error"`
type RPCResponse struct {
Result interface{} `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
func (res APIResponse) StatusError() string {
return fmt.Sprintf("Status(%v) %v", res.Status, res.Error)
}
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{}{}
func NewRPCResponse(res interface{}, err string) RPCResponse {
if res == nil {
res = struct{}{}
}
return RPCResponse{
Result: res,
Error: err,
Id: "",
JSONRPC: 2,
}
res.Data = data
res.Error = responseErr
}
func WriteRPCResponse(w http.ResponseWriter, res RPCResponse) {
buf, n, err := new(bytes.Buffer), new(int64), new(error)
binary.WriteJSON(res, buf, n, err)
if *err != nil {
log.Warn("Failed to write JSON APIResponse", "error", err)
log.Warn("Failed to write JSON RPCResponse", "error", err)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
/* Bad idea: (e.g. hard to use with jQuery)
switch res.Status {
case API_OK:
w.WriteHeader(200)
case API_ERROR:
w.WriteHeader(400)
case API_UNAUTHORIZED:
w.WriteHeader(401)
case API_INVALID_PARAM:
w.WriteHeader(420)
case API_REDIRECT:
w.WriteHeader(430)
default:
w.WriteHeader(440)
}*/
w.Write(buf.Bytes())
}
//-----------------------------------------------------------------------------
// Wraps an HTTP handler, adding error logging.
//
// If the inner function panics, the outer function recovers, logs, sends an
@ -113,9 +88,9 @@ func RecoverAndLogHandler(handler http.Handler) http.Handler {
// at least to my localhost.
if e := recover(); e != nil {
// If APIResponse,
if res, ok := e.(APIResponse); ok {
WriteAPIResponse(rww, res.Status, nil, res.Error)
// If RPCResponse
if res, ok := e.(RPCResponse); ok {
WriteRPCResponse(rww, res)
} else {
// For the rest,
rww.WriteHeader(http.StatusInternalServerError)


+ 16
- 19
rpc/test/http_rpc_test.go View File

@ -3,11 +3,9 @@ package rpc
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/tendermint/tendermint2/binary"
. "github.com/tendermint/tendermint2/common"
"github.com/tendermint/tendermint2/config"
"github.com/tendermint/tendermint2/merkle"
"github.com/tendermint/tendermint2/rpc/core"
"github.com/tendermint/tendermint2/state"
@ -28,19 +26,19 @@ func TestHTTPStatus(t *testing.T) {
if err != nil {
t.Fatal(err)
}
var status struct {
Status string
Data core.ResponseStatus
Error string
var response struct {
Result core.ResponseStatus `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
err = json.Unmarshal(body, &status)
binary.ReadJSON(&response, body, &err)
if err != nil {
t.Fatal(err)
}
data := status.Data
if data.Network != config.App().GetString("Network") {
t.Fatal(fmt.Errorf("Network mismatch: got %s expected %s", data.Network, config.App().Get("Network")))
}
result := response.Result
fmt.Println(">>>", result)
return
}
func TestHTTPGenPriv(t *testing.T) {
@ -56,18 +54,17 @@ func TestHTTPGenPriv(t *testing.T) {
if err != nil {
t.Fatal(err)
}
var status struct {
Status string
Data core.ResponseGenPrivAccount
Error string
var response struct {
Result core.ResponseGenPrivAccount `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
binary.ReadJSON(&status, body, &err)
binary.ReadJSON(&response, body, &err)
if err != nil {
t.Fatal(err)
}
if len(status.Data.PrivAccount.Address) == 0 {
t.Fatal("Failed to generate an address")
}
fmt.Println(">>>", response)
}
func TestHTTPGetAccount(t *testing.T) {


+ 27
- 22
rpc/test/json_rpc_test.go View File

@ -38,17 +38,20 @@ func TestJSONStatus(t *testing.T) {
if err != nil {
t.Fatal(err)
}
status := new(struct {
Status string
Data core.ResponseStatus
Error string
})
err = json.Unmarshal(body, status)
var response struct {
Result core.ResponseStatus `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
binary.ReadJSON(&response, body, &err)
if err != nil {
t.Fatal(err)
}
if status.Data.Network != config.App().GetString("Network") {
t.Fatal(fmt.Errorf("Network mismatch: got %s expected %s", status.Data.Network, config.App().Get("Network")))
if response.Result.Network != config.App().GetString("Network") {
t.Fatal(fmt.Errorf("Network mismatch: got %s expected %s",
response.Result.Network, config.App().Get("Network")))
}
}
@ -76,16 +79,17 @@ func TestJSONGenPriv(t *testing.T) {
if err != nil {
t.Fatal(err)
}
var status struct {
Status string
Data core.ResponseGenPrivAccount
Error string
var response struct {
Result core.ResponseGenPrivAccount `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
binary.ReadJSON(&status, body, &err)
binary.ReadJSON(&response, body, &err)
if err != nil {
t.Fatal(err)
}
if len(status.Data.PrivAccount.Address) == 0 {
if len(response.Result.PrivAccount.Address) == 0 {
t.Fatal("Failed to generate an address")
}
}
@ -138,16 +142,17 @@ func TestJSONBroadcastTx(t *testing.T) {
}
b := w.Bytes()
var status struct {
Status string
Data core.ResponseBroadcastTx
Error string
var response struct {
Result core.ResponseBroadcastTx `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &status)
if status.Status == "ERROR" {
t.Fatal(status.Error)
requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &response)
if response.Error != "" {
t.Fatal(response.Error)
}
receipt := status.Data.Receipt
receipt := response.Result.Receipt
if receipt.CreatesContract > 0 {
t.Fatal("This tx does not create a contract")
}


+ 46
- 42
rpc/test/test.go View File

@ -96,16 +96,17 @@ func getAccount(t *testing.T, typ string, addr []byte) *account.Account {
if err != nil {
t.Fatal(err)
}
var status struct {
Status string
Data core.ResponseGetAccount
Error string
var response struct {
Result core.ResponseGetAccount `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
binary.ReadJSON(&status, body, &err)
binary.ReadJSON(&response, body, &err)
if err != nil {
t.Fatal(err)
}
return status.Data.Account
return response.Result.Account
}
func makeSendTx(t *testing.T, typ string, from, to []byte, amt uint64) *types.SendTx {
@ -165,7 +166,7 @@ func makeCallTx(t *testing.T, typ string, from, to, data []byte, amt, gaslim, fe
return tx
}
func requestResponse(t *testing.T, method string, values url.Values, status interface{}) {
func requestResponse(t *testing.T, method string, values url.Values, response interface{}) {
resp, err := http.PostForm(requestAddr+method, values)
if err != nil {
t.Fatal(err)
@ -175,7 +176,7 @@ func requestResponse(t *testing.T, method string, values url.Values, status inte
if err != nil {
t.Fatal(err)
}
binary.ReadJSON(status, body, &err)
binary.ReadJSON(response, body, &err)
if err != nil {
t.Fatal(err)
}
@ -207,18 +208,18 @@ func signTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [64]byt
t.Fatal(err)
}
var status struct {
Status string
Data core.ResponseSignTx
Error string
var response struct {
Result core.ResponseSignTx `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
requestResponse(t, "unsafe/sign_tx", url.Values{"tx": {string(b)}, "privAccounts": {string(w.Bytes())}}, &status)
if status.Status == "ERROR" {
t.Fatal(status.Error)
requestResponse(t, "unsafe/sign_tx", url.Values{"tx": {string(b)}, "privAccounts": {string(w.Bytes())}}, &response)
if response.Error != "" {
t.Fatal(response.Error)
}
response := status.Data
//tx = response.Tx.(*types.SendTx)
return response.Tx, privAcc
result := response.Result
return result.Tx, privAcc
}
func broadcastTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [64]byte, amt, gaslim, fee uint64) (types.Tx, core.Receipt) {
@ -232,45 +233,48 @@ func broadcastTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [6
}
b := w.Bytes()
var status struct {
Status string
Data core.ResponseBroadcastTx
Error string
var response struct {
Result core.ResponseBroadcastTx `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &status)
if status.Status == "ERROR" {
t.Fatal(status.Error)
requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &response)
if response.Error != "" {
t.Fatal(response.Error)
}
return tx, status.Data.Receipt
return tx, response.Result.Receipt
}
func dumpStorage(t *testing.T, addr []byte) core.ResponseDumpStorage {
addrString := "\"" + hex.EncodeToString(addr) + "\""
var status struct {
Status string
Data core.ResponseDumpStorage
Error string
var response struct {
Result core.ResponseDumpStorage `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
requestResponse(t, "dump_storage", url.Values{"address": {addrString}}, &status)
if status.Status != "OK" {
t.Fatal(status.Error)
requestResponse(t, "dump_storage", url.Values{"address": {addrString}}, &response)
if response.Error != "" {
t.Fatal(response.Error)
}
return status.Data
return response.Result
}
func getStorage(t *testing.T, addr, slot []byte) []byte {
addrString := "\"" + hex.EncodeToString(addr) + "\""
slotString := "\"" + hex.EncodeToString(slot) + "\""
var status struct {
Status string
Data core.ResponseGetStorage
Error string
var response struct {
Result core.ResponseGetStorage `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
}
requestResponse(t, "get_storage", url.Values{"address": {addrString}, "storage": {slotString}}, &status)
if status.Status != "OK" {
t.Fatal(status.Error)
requestResponse(t, "get_storage", url.Values{"address": {addrString}, "storage": {slotString}}, &response)
if response.Error != "" {
t.Fatal(response.Error)
}
return status.Data.Value
return response.Result.Value
}
func checkTx(t *testing.T, fromAddr []byte, priv *account.PrivAccount, tx *types.SendTx) {


Loading…
Cancel
Save