rpc: generalized rpc using reflection on funcs and params

This commit is contained in:
Ethan Buchman 2015-03-27 02:25:41 -07:00
parent 1fb1163721
commit 9aeafffd9b
14 changed files with 436 additions and 405 deletions

View File

@ -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()
}

View File

@ -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})
}

View File

@ -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})
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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=...
*/

View File

@ -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})
}

61
rpc/responses.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -0,0 +1,24 @@
{
"Accounts": [
{
"Address": "d7dff9806078899c8da3fe3633cc0bf3c6c2b1bb",
"Amount": 200000000
},
{
"Address": "AC89A6DDF4C309A89A2C4078CE409A5A7B282270",
"Amount": 200000000
}
],
"Validators": [
{
"PubKey": [1, "2239c21c81ea7173a6c489145490c015e05d4b97448933b708a7ec5b7b4921e3"],
"Amount": 1000000,
"UnbondTo": [
{
"Address": "d7dff9806078899c8da3fe3633cc0bf3c6c2b1bb",
"Amount": 100000
}
]
}
]
}

View File

@ -0,0 +1 @@
{"Address":"D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB","PubKey":[1,"2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3"],"PrivKey":[1,"FDE3BD94CB327D19464027BA668194C5EFA46AE83E8419D7542CFF41F00C81972239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3"],"LastHeight":3,"LastRound":0,"LastStep":2}

View File

@ -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
*/

View File

@ -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})
}

View File

@ -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})
}