From 9aeafffd9bc220cb0b8edee0a9f518b9eef6621c Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 27 Mar 2015 02:25:41 -0700 Subject: [PATCH] rpc: generalized rpc using reflection on funcs and params --- daemon/daemon.go | 9 +- rpc/accounts.go | 75 ------- rpc/blocks.go | 65 ------ rpc/core/blocks.go | 4 +- rpc/http_handlers.go | 244 ++++++++++++++++++++++- rpc/mempool.go | 57 ------ rpc/net.go | 55 ----- rpc/responses.go | 61 ++++++ rpc/rpc.go | 29 --- rpc/test/.tendermint/genesis.json | 24 +++ rpc/test/.tendermint/priv_validator.json | 1 + rpc/test/rpc_test.go | 115 ++++++++++- rpc/txs.go | 66 ------ rpc/validators.go | 36 ---- 14 files changed, 436 insertions(+), 405 deletions(-) delete mode 100644 rpc/accounts.go delete mode 100644 rpc/blocks.go delete mode 100644 rpc/mempool.go delete mode 100644 rpc/net.go create mode 100644 rpc/responses.go delete mode 100644 rpc/rpc.go create mode 100644 rpc/test/.tendermint/genesis.json create mode 100755 rpc/test/.tendermint/priv_validator.json delete mode 100644 rpc/txs.go delete mode 100644 rpc/validators.go diff --git a/daemon/daemon.go b/daemon/daemon.go index b50506188..2cba7dc20 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -11,6 +11,7 @@ import ( mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/rpc" + "github.com/tendermint/tendermint/rpc/core" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -137,10 +138,10 @@ func (n *Node) DialSeed() { } func (n *Node) StartRpc() { - rpc.SetRPCBlockStore(n.blockStore) - rpc.SetRPCConsensusState(n.consensusState) - rpc.SetRPCMempoolReactor(n.mempoolReactor) - rpc.SetRPCSwitch(n.sw) + core.SetPipeBlockStore(n.blockStore) + core.SetPipeConsensusState(n.consensusState) + core.SetPipeMempoolReactor(n.mempoolReactor) + core.SetPipeSwitch(n.sw) rpc.StartHTTPServer() } diff --git a/rpc/accounts.go b/rpc/accounts.go deleted file mode 100644 index 8a44d03b0..000000000 --- a/rpc/accounts.go +++ /dev/null @@ -1,75 +0,0 @@ -package rpc - -import ( - "net/http" - - "github.com/tendermint/tendermint/account" - "github.com/tendermint/tendermint/binary" - . "github.com/tendermint/tendermint/common" -) - -//----------------------------------------------------------------------------- - -// Request: {} - -type ResponseGenPrivAccount struct { - PrivAccount *account.PrivAccount -} - -func GenPrivAccountHandler(w http.ResponseWriter, r *http.Request) { - privAccount := account.GenPrivAccount() - - WriteAPIResponse(w, API_OK, ResponseGenPrivAccount{privAccount}) -} - -//----------------------------------------------------------------------------- - -// Request: {"address": string} - -type ResponseGetAccount struct { - Account *account.Account -} - -func GetAccountHandler(w http.ResponseWriter, r *http.Request) { - addressStr := GetParam(r, "address") - - var address []byte - var err error - binary.ReadJSON(&address, []byte(addressStr), &err) - if err != nil { - WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid address: %v", err)) - return - } - - state := consensusState.GetState() - account_ := state.GetAccount(address) - - if account_ == nil { - WriteAPIResponse(w, API_OK, struct{}{}) - return - } - - WriteAPIResponse(w, API_OK, ResponseGetAccount{account_}) -} - -//----------------------------------------------------------------------------- - -// Request: {} - -type ResponseListAccounts struct { - BlockHeight uint - Accounts []*account.Account -} - -func ListAccountsHandler(w http.ResponseWriter, r *http.Request) { - var blockHeight uint - var accounts []*account.Account - state := consensusState.GetState() - blockHeight = state.LastBlockHeight - state.GetAccounts().Iterate(func(key interface{}, value interface{}) bool { - accounts = append(accounts, value.(*account.Account)) - return false - }) - - WriteAPIResponse(w, API_OK, ResponseListAccounts{blockHeight, accounts}) -} diff --git a/rpc/blocks.go b/rpc/blocks.go deleted file mode 100644 index 5a9e37e54..000000000 --- a/rpc/blocks.go +++ /dev/null @@ -1,65 +0,0 @@ -package rpc - -import ( - "net/http" - - . "github.com/tendermint/tendermint/common" - "github.com/tendermint/tendermint/types" -) - -//----------------------------------------------------------------------------- - -// Request: {} - -type ResponseBlockchainInfo struct { - LastHeight uint - BlockMetas []*types.BlockMeta -} - -func BlockchainInfoHandler(w http.ResponseWriter, r *http.Request) { - minHeight, _ := GetParamUint(r, "min_height") - maxHeight, _ := GetParamUint(r, "max_height") - if maxHeight == 0 { - maxHeight = blockStore.Height() - } else { - maxHeight = MinUint(blockStore.Height(), maxHeight) - } - if minHeight == 0 { - minHeight = uint(MaxInt(1, int(maxHeight)-20)) - } - log.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight) - - blockMetas := []*types.BlockMeta{} - for height := maxHeight; height >= minHeight; height-- { - blockMeta := blockStore.LoadBlockMeta(height) - blockMetas = append(blockMetas, blockMeta) - } - - WriteAPIResponse(w, API_OK, ResponseBlockchainInfo{blockStore.Height(), blockMetas}) -} - -//----------------------------------------------------------------------------- - -// Request: {"height": uint} - -type ResponseGetBlock struct { - BlockMeta *types.BlockMeta - Block *types.Block -} - -func GetBlockHandler(w http.ResponseWriter, r *http.Request) { - height, _ := GetParamUint(r, "height") - if height == 0 { - WriteAPIResponse(w, API_INVALID_PARAM, "height must be greater than 1") - return - } - if height > blockStore.Height() { - WriteAPIResponse(w, API_INVALID_PARAM, "height must be less than the current blockchain height") - return - } - - blockMeta := blockStore.LoadBlockMeta(height) - block := blockStore.LoadBlock(height) - - WriteAPIResponse(w, API_OK, ResponseGetBlock{blockMeta, block}) -} diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index 0a92d2044..84e7b53b0 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -8,7 +8,7 @@ import ( //----------------------------------------------------------------------------- -func BlockchainInfoHandler(minHeight, maxHeight uint) (uint, []*types.BlockMeta) { +func BlockchainInfo(minHeight, maxHeight uint) (uint, []*types.BlockMeta) { if maxHeight == 0 { maxHeight = blockStore.Height() } else { @@ -30,7 +30,7 @@ func BlockchainInfoHandler(minHeight, maxHeight uint) (uint, []*types.BlockMeta) //----------------------------------------------------------------------------- -func GetBlockHandler(height uint) (*types.BlockMeta, *types.Block, error) { +func GetBlock(height uint) (*types.BlockMeta, *types.Block, error) { if height == 0 { return nil, nil, fmt.Errorf("height must be greater than 1") } diff --git a/rpc/http_handlers.go b/rpc/http_handlers.go index 14a5a6813..f1a278c0d 100644 --- a/rpc/http_handlers.go +++ b/rpc/http_handlers.go @@ -1,21 +1,245 @@ package rpc import ( + "encoding/json" + "fmt" + "github.com/tendermint/tendermint/binary" + "github.com/tendermint/tendermint/rpc/core" + "io/ioutil" "net/http" + "reflect" + "strconv" ) +// map each function to the argument names +var argsMap = map[string][]string{ + "status": []string{}, + "net_info": []string{}, + "blockchain": []string{"min_height", "max_height"}, + "get_block": []string{"height"}, + "get_account": []string{"address"}, + "list_validators": []string{}, + "broadcast_tx": []string{"tx"}, + "list_accounts": []string{}, + "gen_priv_account": []string{}, + "sign_tx": []string{"tx", "privAccounts"}, +} + +// cache all type information about each function up front +var funcMap = map[string]*FuncWrapper{ + "status": funcWrap("status", core.Status), + "net_info": funcWrap("net_info", core.NetInfo), + "blockchain": funcWrap("blockchain", core.BlockchainInfo), + "get_block": funcWrap("get_block", core.GetBlock), + "get_account": funcWrap("get_account", core.GetAccount), + "list_validators": funcWrap("list_validators", core.ListValidators), + "broadcast_tx": funcWrap("broadcast_tx", core.BroadcastTx), + "list_accounts": funcWrap("list_accounts", core.ListAccounts), + "gen_priv_account": funcWrap("gen_priv_account", core.GenPrivAccount), + "sign_tx": funcWrap("sign_tx", core.SignTx), +} + +var responseMap = map[string]reflect.Value{ + "status": reflect.ValueOf(&ResponseStatus{}), + "net_info": reflect.ValueOf(&ResponseNetInfo{}), + "blockchain": reflect.ValueOf(&ResponseBlockchainInfo{}), + "get_block": reflect.ValueOf(&ResponseGetBlock{}), + "get_account": reflect.ValueOf(&ResponseGetAccount{}), + "list_validators": reflect.ValueOf(&ResponseListValidators{}), + "broadcast_tx": reflect.ValueOf(&ResponseBroadcastTx{}), + "list_accounts": reflect.ValueOf(&ResponseListAccounts{}), + "gen_priv_account": reflect.ValueOf(&ResponseGenPrivAccount{}), + "sign_tx": reflect.ValueOf(&ResponseSignTx{}), +} + +type FuncWrapper struct { + f reflect.Value // function from "rpc/core" + args []reflect.Type // type of each function arg + returns []reflect.Type // type of each return arg + argNames []string // name of each argument + response reflect.Value // response struct (to be filled with "returns") +} + +func funcWrap(name string, f interface{}) *FuncWrapper { + return &FuncWrapper{ + f: reflect.ValueOf(f), + args: funcArgTypes(f), + returns: funcReturnTypes(f), + argNames: argsMap[name], + response: responseMap[name], + } +} + +// convert from a function name to the http handler +func toHandler(funcName string) func(http.ResponseWriter, *http.Request) { + funcInfo := funcMap[funcName] + return func(w http.ResponseWriter, r *http.Request) { + values, err := queryToValues(funcInfo, r) + if err != nil { + WriteAPIResponse(w, API_INVALID_PARAM, 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_OK, response) + } +} + +// covert an http query to a list of properly typed values +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.Struct: + binary.ReadJSON(v.Interface(), []byte(arg), &err) + if err != nil { + return nil, err + } + case reflect.Slice: + v = reflect.ValueOf([]byte(arg)) + case reflect.Int64: + u, err := strconv.ParseInt(arg, 10, 64) + if err != nil { + return nil, err + } + v = reflect.ValueOf(u) + case reflect.Int32: + u, err := strconv.ParseInt(arg, 10, 32) + if err != nil { + return nil, err + } + v = reflect.ValueOf(u) + case reflect.Uint64: + u, err := strconv.ParseUint(arg, 10, 64) + if err != nil { + return nil, err + } + v = reflect.ValueOf(u) + case reflect.Uint: + u, err := strconv.ParseUint(arg, 10, 32) + if err != nil { + return nil, err + } + v = reflect.ValueOf(u) + default: + v = reflect.ValueOf(arg) + } + values[i] = v + } + return values, nil +} + +// covert a list of interfaces to properly typed values +func paramsToValues(funcInfo *FuncWrapper, params []interface{}) ([]reflect.Value, error) { + values := make([]reflect.Value, len(params)) + for i, p := range params { + values[i] = reflect.ValueOf(p) + } + return values, nil +} + +// convert a list of values to a populated struct with the correct types +func returnsToResponse(funcInfo *FuncWrapper, returns []reflect.Value) (interface{}, error) { + returnTypes := funcInfo.returns + finalType := returnTypes[len(returnTypes)-1] + if finalType.Implements(reflect.TypeOf((*error)(nil)).Elem()) { + errV := returns[len(returnTypes)-1] + return nil, errV.Interface().(error) + } + + v := funcInfo.response.Elem() + nFields := v.NumField() + for i := 0; i < nFields; i++ { + field := v.FieldByIndex([]int{i}) + field.Set(returns[i]) + } + + return v.Interface(), nil +} + +// jsonrpc calls grab the given method's function info and runs reflect.Call +func JsonRpcHandler(w http.ResponseWriter, r *http.Request) { + b, _ := ioutil.ReadAll(r.Body) + var jrpc JsonRpc + err := json.Unmarshal(b, &jrpc) + if err != nil { + // TODO + } + + funcInfo := funcMap[jrpc.Method] + values, err := paramsToValues(funcInfo, jrpc.Params) + if err != nil { + WriteAPIResponse(w, API_INVALID_PARAM, 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_OK, response) +} + func initHandlers() { - http.HandleFunc("/status", StatusHandler) - http.HandleFunc("/net_info", NetInfoHandler) - http.HandleFunc("/blockchain", BlockchainInfoHandler) - http.HandleFunc("/get_block", GetBlockHandler) - http.HandleFunc("/get_account", GetAccountHandler) - http.HandleFunc("/list_validators", ListValidatorsHandler) - http.HandleFunc("/broadcast_tx", BroadcastTxHandler) + // HTTP endpoints + // toHandler runs once for each function and caches + // all reflection data + http.HandleFunc("/status", toHandler("status")) + http.HandleFunc("/net_info", toHandler("net_info")) + http.HandleFunc("/blockchain", toHandler("blockchain")) + http.HandleFunc("/get_block", toHandler("get_block")) + http.HandleFunc("/get_account", toHandler("get_account")) + http.HandleFunc("/list_validators", toHandler("list_validators")) + http.HandleFunc("/broadcast_tx", toHandler("broadcast_tx")) + http.HandleFunc("/list_accounts", toHandler("list_accounts")) + http.HandleFunc("/unsafe/gen_priv_account", toHandler("gen_priv_account")) + http.HandleFunc("/unsafe/sign_tx", toHandler("sign_tx")) //http.HandleFunc("/call", CallHandler) //http.HandleFunc("/get_storage", GetStorageHandler) - http.HandleFunc("/develop/gen_priv_account", GenPrivAccountHandler) - http.HandleFunc("/develop/list_accounts", ListAccountsHandler) - http.HandleFunc("/develop/sign_tx", SignTxHandler) + // JsonRPC endpoints + http.HandleFunc("/", JsonRpcHandler) + // unsafe JsonRPC endpoints + //http.HandleFunc("/unsafe", UnsafeJsonRpcHandler) + +} + +type JsonRpc struct { + JsonRpc string `json:"jsonrpc"` + Method string `json:"method"` + Params []interface{} `json:"params"` + Id int `json:"id"` +} + +// this will panic if not passed a function +func funcArgTypes(f interface{}) []reflect.Type { + t := reflect.TypeOf(f) + n := t.NumIn() + types := make([]reflect.Type, n) + for i := 0; i < n; i++ { + types[i] = t.In(i) + } + return types +} + +func funcReturnTypes(f interface{}) []reflect.Type { + t := reflect.TypeOf(f) + n := t.NumOut() + types := make([]reflect.Type, n) + for i := 0; i < n; i++ { + types[i] = t.Out(i) + } + return types } diff --git a/rpc/mempool.go b/rpc/mempool.go deleted file mode 100644 index 5be8997b9..000000000 --- a/rpc/mempool.go +++ /dev/null @@ -1,57 +0,0 @@ -package rpc - -import ( - "net/http" - - "github.com/tendermint/tendermint/binary" - . "github.com/tendermint/tendermint/common" - "github.com/tendermint/tendermint/merkle" - "github.com/tendermint/tendermint/state" - "github.com/tendermint/tendermint/types" -) - -//----------------------------------------------------------------------------- - -// Request: {"tx": string} -// Note: "tx" should be json encoded signed transaction - -type ResponseBroadcastTx struct { - TxHash []byte - CreatesContract bool - ContractAddr []byte -} - -func BroadcastTxHandler(w http.ResponseWriter, r *http.Request) { - txJSON := GetParam(r, "tx") - var err error - var tx types.Tx - binary.ReadJSON(&tx, []byte(txJSON), &err) - if err != nil { - WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid tx: %v", err)) - return - } - - err = mempoolReactor.BroadcastTx(tx) - if err != nil { - WriteAPIResponse(w, API_ERROR, Fmt("Error broadcasting transaction: %v", err)) - return - } - - txHash := merkle.HashFromBinary(tx) - var createsContract bool - var contractAddr []byte - - if callTx, ok := tx.(*types.CallTx); ok { - if callTx.Address == nil { - createsContract = true - contractAddr = state.NewContractAddress(callTx.Input.Address, uint64(callTx.Input.Sequence)) - } - } - - WriteAPIResponse(w, API_OK, ResponseBroadcastTx{txHash, createsContract, contractAddr}) - return -} - -/* -curl -H 'content-type: text/plain;' http://127.0.0.1:8888/submit_tx?tx=... -*/ diff --git a/rpc/net.go b/rpc/net.go deleted file mode 100644 index 6caeaec44..000000000 --- a/rpc/net.go +++ /dev/null @@ -1,55 +0,0 @@ -package rpc - -import ( - "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/types" - "net/http" -) - -//----------------------------------------------------------------------------- - -// Request: {} - -type ResponseStatus struct { - ChainId string - LatestBlockHash []byte - LatestBlockHeight uint - LatestBlockTime int64 // nano - Network string -} - -func StatusHandler(w http.ResponseWriter, r *http.Request) { - genesisHash := p2pSwitch.GetChainId() - latestHeight := blockStore.Height() - var ( - latestBlockMeta *types.BlockMeta - latestBlockHash []byte - latestBlockTime int64 - ) - if latestHeight != 0 { - latestBlockMeta = blockStore.LoadBlockMeta(latestHeight) - latestBlockHash = latestBlockMeta.Hash - latestBlockTime = latestBlockMeta.Header.Time.UnixNano() - } - - WriteAPIResponse(w, API_OK, ResponseStatus{genesisHash, latestBlockHash, latestHeight, latestBlockTime, config.App().GetString("Network")}) -} - -//----------------------------------------------------------------------------- - -// Request: {} - -type ResponseNetInfo struct { - NumPeers int - Listening bool - Network string -} - -func NetInfoHandler(w http.ResponseWriter, r *http.Request) { - o, i, _ := p2pSwitch.NumPeers() - numPeers := o + i - listening := p2pSwitch.IsListening() - network := config.App().GetString("Network") - WriteAPIResponse(w, API_OK, - ResponseNetInfo{numPeers, listening, network}) -} diff --git a/rpc/responses.go b/rpc/responses.go new file mode 100644 index 000000000..cc77ff794 --- /dev/null +++ b/rpc/responses.go @@ -0,0 +1,61 @@ +package rpc + +import ( + "github.com/tendermint/tendermint/account" + sm "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/types" +) + +type ResponseGenPrivAccount struct { + PrivAccount *account.PrivAccount +} + +type ResponseGetAccount struct { + Account *account.Account +} + +type ResponseListAccounts struct { + BlockHeight uint + Accounts []*account.Account +} + +type ResponseBlockchainInfo struct { + LastHeight uint + BlockMetas []*types.BlockMeta +} + +type ResponseGetBlock struct { + BlockMeta *types.BlockMeta + Block *types.Block +} + +// curl -H 'content-type: text/plain;' http://127.0.0.1:8888/submit_tx?tx=... +type ResponseBroadcastTx struct { + TxHash []byte + CreatesContract bool + ContractAddr []byte +} + +type ResponseStatus struct { + GenesisHash []byte + Network string + LatestBlockHash []byte + LatestBlockHeight uint + LatestBlockTime int64 // nano +} + +type ResponseNetInfo struct { + NumPeers int + Listening bool + Network string +} + +type ResponseSignTx struct { + Tx types.Tx +} + +type ResponseListValidators struct { + BlockHeight uint + BondedValidators []*sm.Validator + UnbondingValidators []*sm.Validator +} diff --git a/rpc/rpc.go b/rpc/rpc.go deleted file mode 100644 index 94631249c..000000000 --- a/rpc/rpc.go +++ /dev/null @@ -1,29 +0,0 @@ -package rpc - -import ( - "github.com/tendermint/tendermint/consensus" - mempl "github.com/tendermint/tendermint/mempool" - "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/types" -) - -var blockStore *types.BlockStore -var consensusState *consensus.ConsensusState -var mempoolReactor *mempl.MempoolReactor -var p2pSwitch *p2p.Switch - -func SetRPCBlockStore(bs *types.BlockStore) { - blockStore = bs -} - -func SetRPCConsensusState(cs *consensus.ConsensusState) { - consensusState = cs -} - -func SetRPCMempoolReactor(mr *mempl.MempoolReactor) { - mempoolReactor = mr -} - -func SetRPCSwitch(sw *p2p.Switch) { - p2pSwitch = sw -} diff --git a/rpc/test/.tendermint/genesis.json b/rpc/test/.tendermint/genesis.json new file mode 100644 index 000000000..c324aa760 --- /dev/null +++ b/rpc/test/.tendermint/genesis.json @@ -0,0 +1,24 @@ +{ + "Accounts": [ + { + "Address": "d7dff9806078899c8da3fe3633cc0bf3c6c2b1bb", + "Amount": 200000000 + }, + { + "Address": "AC89A6DDF4C309A89A2C4078CE409A5A7B282270", + "Amount": 200000000 + } + ], + "Validators": [ + { + "PubKey": [1, "2239c21c81ea7173a6c489145490c015e05d4b97448933b708a7ec5b7b4921e3"], + "Amount": 1000000, + "UnbondTo": [ + { + "Address": "d7dff9806078899c8da3fe3633cc0bf3c6c2b1bb", + "Amount": 100000 + } + ] + } + ] +} diff --git a/rpc/test/.tendermint/priv_validator.json b/rpc/test/.tendermint/priv_validator.json new file mode 100755 index 000000000..e3bcca94d --- /dev/null +++ b/rpc/test/.tendermint/priv_validator.json @@ -0,0 +1 @@ +{"Address":"D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB","PubKey":[1,"2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3"],"PrivKey":[1,"FDE3BD94CB327D19464027BA668194C5EFA46AE83E8419D7542CFF41F00C81972239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3"],"LastHeight":3,"LastRound":0,"LastStep":2} \ No newline at end of file diff --git a/rpc/test/rpc_test.go b/rpc/test/rpc_test.go index b480e310a..f63e457fc 100644 --- a/rpc/test/rpc_test.go +++ b/rpc/test/rpc_test.go @@ -1,16 +1,19 @@ package rpc import ( + "bytes" + "encoding/hex" "encoding/json" "fmt" + "github.com/tendermint/tendermint/binary" "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/daemon" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/rpc" "io/ioutil" "net/http" + "net/url" "testing" - "time" ) var ( @@ -18,9 +21,10 @@ var ( requestAddr = "http://" + rpcAddr + "/" chainId string node *daemon.Node + userAddr = "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB" ) -func newNode() { +func newNode(ready chan struct{}) { // Create & start node node = daemon.NewNode() l := p2p.NewDefaultListener("tcp", config.App().GetString("ListenAddr"), false) @@ -29,6 +33,7 @@ func newNode() { // Run the RPC server. node.StartRpc() + ready <- struct{}{} // Sleep forever ch := make(chan struct{}) @@ -36,14 +41,19 @@ func newNode() { } func init() { + rootDir := ".tendermint" + config.Init(rootDir) app := config.App() app.Set("SeedNode", "") app.Set("DB.Backend", "memdb") app.Set("RPC.HTTP.ListenAddr", rpcAddr) + app.Set("GenesisFile", rootDir+"/genesis.json") + app.Set("PrivValidatorFile", rootDir+"/priv_validator.json") config.SetApp(app) // start a node - go newNode() - time.Sleep(2 * time.Second) + ready := make(chan struct{}) + go newNode(ready) + <-ready } func TestSayHello(t *testing.T) { @@ -64,7 +74,100 @@ func TestSayHello(t *testing.T) { if err != nil { t.Fatal(err) } - if status.Data.ChainId != node.Switch().GetChainId() { - t.Fatal(fmt.Errorf("ChainId mismatch: got %s expected %s", status.Data.ChainId, node.Switch().GetChainId())) + 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"))) } } + +func TestGenPriv(t *testing.T) { + resp, err := http.Get(requestAddr + "unsafe/gen_priv_account") + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != 200 { + t.Fatal(resp) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + var status struct { + Status string + Data rpc.ResponseGenPrivAccount + } + binary.ReadJSON(&status, body, &err) + if err != nil { + t.Fatal(err) + } + if len(status.Data.PrivAccount.Address) == 0 { + t.Fatal("Failed to generate an address") + } +} + +func TestGetAccount(t *testing.T) { + byteAddr, _ := hex.DecodeString(userAddr) + resp, err := http.PostForm(requestAddr+"get_account", + url.Values{"address": {string(byteAddr)}}) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + var status struct { + Status string + Data rpc.ResponseGetAccount + } + 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) + } + +} + +func TestSignedTx(t *testing.T) { + +} + +/* + acc := mint.MempoolReactor.Mempool.GetState().GetAccount(mint.priv.Address) + nonce := 0 + if acc != nil { + nonce = int(acc.Sequence) + 1 + } + + amtInt, err := strconv.Atoi(amt) + if err != nil { + return "", err + } + amtUint64 := uint64(amtInt) + + tx := &blk.SendTx{ + Inputs: []*blk.TxInput{ + &blk.TxInput{ + Address: mint.priv.Address, + Amount: amtUint64, + Sequence: uint(nonce), + Signature: account.SignatureEd25519{}, + PubKey: mint.priv.PubKey, + }, + }, + Outputs: []*blk.TxOutput{ + &blk.TxOutput{ + Address: addrB, + Amount: amtUint64, + }, + }, + } + tx.Inputs[0].Signature = mint.priv.PrivKey.Sign(account.SignBytes(tx)) + err = mint.MempoolReactor.BroadcastTx(tx) + return hex.EncodeToString(merkle.HashFromBinary(tx)), err + +*/ diff --git a/rpc/txs.go b/rpc/txs.go deleted file mode 100644 index 6ef8c3140..000000000 --- a/rpc/txs.go +++ /dev/null @@ -1,66 +0,0 @@ -package rpc - -import ( - "net/http" - - "github.com/tendermint/tendermint/account" - "github.com/tendermint/tendermint/binary" - . "github.com/tendermint/tendermint/common" - "github.com/tendermint/tendermint/types" -) - -//----------------------------------------------------------------------------- - -// Request: {"tx": string} -// Note: "tx" should be json encoded unsigned transaction - -type ResponseSignTx struct { - types.Tx -} - -func SignTxHandler(w http.ResponseWriter, r *http.Request) { - txStr := GetParam(r, "tx") - privAccountsStr := GetParam(r, "privAccounts") - - var err error - var tx types.Tx - binary.ReadJSON(&tx, []byte(txStr), &err) - if err != nil { - WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid tx: %v", err)) - return - } - privAccounts := binary.ReadJSON([]*account.PrivAccount{}, []byte(privAccountsStr), &err).([]*account.PrivAccount) - if err != nil { - WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid privAccounts: %v", err)) - return - } - for i, privAccount := range privAccounts { - if privAccount == nil || privAccount.PrivKey == nil { - WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid (empty) privAccount @%v", i)) - return - } - } - - switch tx.(type) { - case *types.SendTx: - sendTx := tx.(*types.SendTx) - for i, input := range sendTx.Inputs { - input.PubKey = privAccounts[i].PubKey - input.Signature = privAccounts[i].Sign(sendTx) - } - case *types.BondTx: - bondTx := tx.(*types.BondTx) - for i, input := range bondTx.Inputs { - input.PubKey = privAccounts[i].PubKey - input.Signature = privAccounts[i].Sign(bondTx) - } - case *types.UnbondTx: - unbondTx := tx.(*types.UnbondTx) - unbondTx.Signature = privAccounts[0].Sign(unbondTx).(account.SignatureEd25519) - case *types.RebondTx: - rebondTx := tx.(*types.RebondTx) - rebondTx.Signature = privAccounts[0].Sign(rebondTx).(account.SignatureEd25519) - } - - WriteAPIResponse(w, API_OK, ResponseSignTx{tx}) -} diff --git a/rpc/validators.go b/rpc/validators.go deleted file mode 100644 index dfd4104a6..000000000 --- a/rpc/validators.go +++ /dev/null @@ -1,36 +0,0 @@ -package rpc - -import ( - "net/http" - - sm "github.com/tendermint/tendermint/state" -) - -//----------------------------------------------------------------------------- - -// Request: {} - -type ResponseListValidators struct { - BlockHeight uint - BondedValidators []*sm.Validator - UnbondingValidators []*sm.Validator -} - -func ListValidatorsHandler(w http.ResponseWriter, r *http.Request) { - var blockHeight uint - var bondedValidators []*sm.Validator - var unbondingValidators []*sm.Validator - - state := consensusState.GetState() - blockHeight = state.LastBlockHeight - state.BondedValidators.Iterate(func(index uint, val *sm.Validator) bool { - bondedValidators = append(bondedValidators, val) - return false - }) - state.UnbondingValidators.Iterate(func(index uint, val *sm.Validator) bool { - unbondingValidators = append(unbondingValidators, val) - return false - }) - - WriteAPIResponse(w, API_OK, ResponseListValidators{blockHeight, bondedValidators, unbondingValidators}) -}