diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 7e776ab0..7a213b0f 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -9,6 +9,7 @@ import ( "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/common" + "github.com/tendermint/tendermint/events" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -19,7 +20,6 @@ const ( defaultChannelCapacity = 100 defaultSleepIntervalMS = 500 trySyncIntervalMS = 100 - // stop syncing when last block's time is // within this much of the system time. stopSyncingDurationMinutes = 10 @@ -41,6 +41,8 @@ type BlockchainReactor struct { lastBlock *types.Block quit chan struct{} running uint32 + + evsw *events.EventSwitch } func NewBlockchainReactor(state *sm.State, store *BlockStore, sync bool) *BlockchainReactor { @@ -239,6 +241,11 @@ func (bcR *BlockchainReactor) BroadcastStatus() error { return nil } +// implements events.Eventable +func (bcR *BlockchainReactor) SetEventSwitch(evsw *events.EventSwitch) { + bcR.evsw = evsw +} + //----------------------------------------------------------------------------- // Messages diff --git a/consensus/reactor.go b/consensus/reactor.go index a3028b6d..0e264714 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -12,6 +12,7 @@ import ( bc "github.com/tendermint/tendermint/blockchain" . "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/consensus/types" + "github.com/tendermint/tendermint/events" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -38,6 +39,8 @@ type ConsensusReactor struct { blockStore *bc.BlockStore conS *ConsensusState + + evsw *events.EventSwitch } func NewConsensusReactor(consensusState *ConsensusState, blockStore *bc.BlockStore) *ConsensusReactor { @@ -230,6 +233,11 @@ func (conR *ConsensusReactor) ResetToState(state *sm.State) { conR.conS.updateToState(state, false) } +// implements events.Eventable +func (conR *ConsensusReactor) SetEventSwitch(evsw *events.EventSwitch) { + conR.evsw = evsw +} + //-------------------------------------- func makeRoundStepMessages(rs *RoundState) (nrsMsg *NewRoundStepMessage, csMsg *CommitStepMessage) { diff --git a/events/events.go b/events/events.go index 05c6f663..46f01cb5 100644 --- a/events/events.go +++ b/events/events.go @@ -5,6 +5,12 @@ import ( "sync/atomic" ) +// reactors and other modules should export +// this interface to become eventable +type Eventable interface { + SetEventSwitch(*EventSwitch) +} + type EventSwitch struct { mtx sync.RWMutex eventCells map[string]*eventCell diff --git a/mempool/reactor.go b/mempool/reactor.go index e16cf933..ae469da4 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -6,6 +6,7 @@ import ( "sync/atomic" "github.com/tendermint/tendermint/binary" + "github.com/tendermint/tendermint/events" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) @@ -22,6 +23,8 @@ type MempoolReactor struct { stopped uint32 Mempool *Mempool + + evsw *events.EventSwitch } func NewMempoolReactor(mempool *Mempool) *MempoolReactor { @@ -110,6 +113,11 @@ func (memR *MempoolReactor) BroadcastTx(tx types.Tx) error { return nil } +// implements events.Eventable +func (memR *MempoolReactor) SetEventSwitch(evsw *events.EventSwitch) { + memR.evsw = evsw +} + //----------------------------------------------------------------------------- // Messages diff --git a/node/node.go b/node/node.go index 198f2d13..53141340 100644 --- a/node/node.go +++ b/node/node.go @@ -8,6 +8,7 @@ import ( "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/consensus" dbm "github.com/tendermint/tendermint/db" + "github.com/tendermint/tendermint/events" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/rpc" @@ -17,6 +18,7 @@ import ( type Node struct { sw *p2p.Switch + evsw *events.EventSwitch book *p2p.AddrBook blockStore *bc.BlockStore pexReactor *p2p.PEXReactor @@ -49,6 +51,9 @@ func NewNode() *Node { log.Info("No PrivValidator found", "file", config.App().GetString("PrivValidatorFile")) } + eventSwitch := new(events.EventSwitch) + eventSwitch.Start() + // Get PEXReactor book := p2p.NewAddrBook(config.App().GetString("AddrBookFile")) pexReactor := p2p.NewPEXReactor(book) @@ -74,8 +79,13 @@ func NewNode() *Node { sw.AddReactor("BLOCKCHAIN", bcReactor) sw.AddReactor("CONSENSUS", consensusReactor) + // add the event switch to all services + // they should all satisfy events.Eventable + SetEventSwitch(eventSwitch, pexReactor, bcReactor, mempoolReactor, consensusReactor) + return &Node{ sw: sw, + evsw: eventSwitch, book: book, blockStore: blockStore, pexReactor: pexReactor, @@ -104,6 +114,13 @@ func (n *Node) Stop() { n.book.Stop() } +// Add the event switch to reactors, mempool, etc. +func SetEventSwitch(evsw *events.EventSwitch, eventables ...events.Eventable) { + for _, e := range eventables { + e.SetEventSwitch(evsw) + } +} + // Add a Listener to accept inbound peer connections. func (n *Node) AddListener(l p2p.Listener) { log.Info(Fmt("Added %v", l)) @@ -129,8 +146,7 @@ func (n *Node) StartRPC() { core.SetConsensusState(n.consensusState) core.SetMempoolReactor(n.mempoolReactor) core.SetSwitch(n.sw) - - rpc.StartHTTPServer(config.App().GetString("RPC.HTTP.ListenAddr"), core.Routes) + rpc.StartHTTPServer(config.App().GetString("RPC.HTTP.ListenAddr"), core.Routes, n.evsw) } func (n *Node) Switch() *p2p.Switch { diff --git a/p2p/pex_reactor.go b/p2p/pex_reactor.go index 6af07058..20e175fc 100644 --- a/p2p/pex_reactor.go +++ b/p2p/pex_reactor.go @@ -9,6 +9,7 @@ import ( "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/common" + "github.com/tendermint/tendermint/events" ) var pexErrInvalidMessage = errors.New("Invalid PEX message") @@ -31,6 +32,8 @@ type PEXReactor struct { stopped uint32 book *AddrBook + + evsw *events.EventSwitch } func NewPEXReactor(book *AddrBook) *PEXReactor { @@ -207,6 +210,11 @@ func (pexR *PEXReactor) ensurePeers() { } } +// implements events.Eventable +func (pexR *PEXReactor) SetEventSwitch(evsw *events.EventSwitch) { + pexR.evsw = evsw +} + //----------------------------------------------------------------------------- // Messages diff --git a/rpc/core/accounts.go b/rpc/core/accounts.go index 813bfd43..98b0bdd7 100644 --- a/rpc/core/accounts.go +++ b/rpc/core/accounts.go @@ -16,20 +16,20 @@ func GetAccount(address []byte) (*ctypes.ResponseGetAccount, error) { return &ctypes.ResponseGetAccount{cache.GetAccount(address)}, nil } -func GetStorage(address, slot []byte) (*ctypes.ResponseGetStorage, error) { +func GetStorage(address, key []byte) (*ctypes.ResponseGetStorage, error) { state := consensusState.GetState() account := state.GetAccount(address) if account == nil { return nil, fmt.Errorf("Unknown address: %X", address) } storageRoot := account.StorageRoot - storage := state.LoadStorage(storageRoot) + storageTree := state.LoadStorage(storageRoot) - _, value := storage.Get(RightPadWord256(slot).Bytes()) + _, value := storageTree.Get(RightPadWord256(key).Bytes()) if value == nil { - return &ctypes.ResponseGetStorage{slot, nil}, nil + return &ctypes.ResponseGetStorage{key, nil}, nil } - return &ctypes.ResponseGetStorage{slot, value.([]byte)}, nil + return &ctypes.ResponseGetStorage{key, value.([]byte)}, nil } func ListAccounts() (*ctypes.ResponseListAccounts, error) { @@ -51,9 +51,9 @@ func DumpStorage(addr []byte) (*ctypes.ResponseDumpStorage, error) { return nil, fmt.Errorf("Unknown address: %X", addr) } storageRoot := account.StorageRoot - storage := state.LoadStorage(storageRoot) + storageTree := state.LoadStorage(storageRoot) storageItems := []ctypes.StorageItem{} - storage.Iterate(func(key interface{}, value interface{}) bool { + storageTree.Iterate(func(key interface{}, value interface{}) bool { storageItems = append(storageItems, ctypes.StorageItem{ key.([]byte), value.([]byte)}) return false diff --git a/rpc/core/routes.go b/rpc/core/routes.go index b9d7d211..62630fb5 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -11,11 +11,12 @@ TODO: support Call && GetStorage. var Routes = map[string]*rpc.RPCFunc{ "status": rpc.NewRPCFunc(Status, []string{}), "net_info": rpc.NewRPCFunc(NetInfo, []string{}), - "blockchain": rpc.NewRPCFunc(BlockchainInfo, []string{"min_height", "max_height"}), + "blockchain": rpc.NewRPCFunc(BlockchainInfo, []string{"minHeight", "maxHeight"}), "get_block": rpc.NewRPCFunc(GetBlock, []string{"height"}), "get_account": rpc.NewRPCFunc(GetAccount, []string{"address"}), - "get_storage": rpc.NewRPCFunc(GetStorage, []string{"address", "storage"}), + "get_storage": rpc.NewRPCFunc(GetStorage, []string{"address", "key"}), "call": rpc.NewRPCFunc(Call, []string{"address", "data"}), + "call_code": rpc.NewRPCFunc(CallCode, []string{"code", "data"}), "list_validators": rpc.NewRPCFunc(ListValidators, []string{}), "dump_storage": rpc.NewRPCFunc(DumpStorage, []string{"address"}), "broadcast_tx": rpc.NewRPCFunc(BroadcastTx, []string{"tx"}), diff --git a/rpc/core/txs.go b/rpc/core/txs.go index 2b4999f9..f16a3ae7 100644 --- a/rpc/core/txs.go +++ b/rpc/core/txs.go @@ -52,6 +52,31 @@ func Call(address, data []byte) (*ctypes.ResponseCall, error) { return &ctypes.ResponseCall{Return: ret}, nil } +// Run the given code on an isolated and unpersisted state +// Cannot be used to create new contracts +func CallCode(code, data []byte) (*ctypes.ResponseCall, error) { + + st := consensusState.GetState() // performs a copy + cache := mempoolReactor.Mempool.GetCache() + callee := &vm.Account{Address: Zero256} + caller := &vm.Account{Address: Zero256} + txCache := state.NewTxCache(cache) + params := vm.Params{ + BlockHeight: uint64(st.LastBlockHeight), + BlockHash: RightPadWord256(st.LastBlockHash), + BlockTime: st.LastBlockTime.Unix(), + GasLimit: 10000000, + } + + vmach := vm.NewVM(txCache, params, caller.Address) + gas := uint64(1000000000) + ret, err := vmach.Call(caller, callee, code, data, 0, &gas) + if err != nil { + return nil, err + } + return &ctypes.ResponseCall{Return: ret}, nil +} + //----------------------------------------------------------------------------- func SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*ctypes.ResponseSignTx, error) { diff --git a/rpc/core_client/client.go b/rpc/core_client/client.go index 0b99f25e..dc717dcb 100644 --- a/rpc/core_client/client.go +++ b/rpc/core_client/client.go @@ -5,14 +5,51 @@ import ( "fmt" "github.com/tendermint/tendermint/binary" "github.com/tendermint/tendermint/rpc" + // NOTE: do not import rpc/core. + // What kind of client imports all of core logic? :P "io/ioutil" "net/http" "net/url" - "reflect" + + //"reflect" // Uncomment to use go:generate // _ "github.com/tendermint/go-rpc-gen" ) +// maps camel-case function names to lower case rpc version +var reverseFuncMap = map[string]string{ + "Status": "status", + "NetInfo": "net_info", + "BlockchainInfo": "blockchain", + "GetBlock": "get_block", + "GetAccount": "get_account", + "GetStorage": "get_storage", + "Call": "call", + "CallCode": "call_code", + "ListValidators": "list_validators", + "DumpStorage": "dump_storage", + "BroadcastTx": "broadcast_tx", + "ListAccounts": "list_accounts", + "GenPrivAccount": "unsafe/gen_priv_account", + "SignTx": "unsafe/sign_tx", +} + +/* +// fill the map from camelcase to lowercase +func fillReverseFuncMap() map[string]string { + fMap := make(map[string]string) + for name, f := range core.Routes { + camelName := runtime.FuncForPC(f.f.Pointer()).Name() + spl := strings.Split(camelName, ".") + if len(spl) > 1 { + camelName = spl[len(spl)-1] + } + fMap[camelName] = name + } + return fMap +} +*/ + type Response struct { Status string Data interface{} @@ -39,67 +76,21 @@ func NewClient(addr, typ string) Client { return nil } -func argsToJson(args ...interface{}) ([][]string, error) { +func argsToJson(args ...interface{}) ([]string, error) { l := len(args) - jsons := make([][]string, l) + jsons := make([]string, l) n, err := new(int64), new(error) for i, a := range args { - //if its a slice, we serliaze separately and pack into a slice of strings - // otherwise its a slice of length 1 - if v := reflect.ValueOf(a); v.Kind() == reflect.Slice { - ty := v.Type() - rt := ty.Elem() - if rt.Kind() == reflect.Uint8 { - buf := new(bytes.Buffer) - binary.WriteJSON(a, buf, n, err) - if *err != nil { - return nil, *err - } - jsons[i] = []string{string(buf.Bytes())} - } else { - slice := make([]string, v.Len()) - for j := 0; j < v.Len(); j++ { - buf := new(bytes.Buffer) - binary.WriteJSON(v.Index(j).Interface(), buf, n, err) - if *err != nil { - return nil, *err - } - slice[j] = string(buf.Bytes()) - } - jsons[i] = slice - } - } else { - buf := new(bytes.Buffer) - binary.WriteJSON(a, buf, n, err) - if *err != nil { - return nil, *err - } - jsons[i] = []string{string(buf.Bytes())} + buf := new(bytes.Buffer) + binary.WriteJSON(a, buf, n, err) + if *err != nil { + return nil, *err } + jsons[i] = string(buf.Bytes()) } return jsons, nil } -func (c *ClientHTTP) RequestResponse(method string, values url.Values) (*Response, error) { - resp, err := http.PostForm(c.addr+method, values) - if err != nil { - return nil, err - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - response := new(Response) - fmt.Println(string(body)) - binary.ReadJSON(response, body, &err) - if err != nil { - return nil, err - } - fmt.Println(response.Data) - return response, nil -} - func (c *ClientJSON) RequestResponse(s rpc.RPCRequest) (b []byte, err error) { b = binary.JSONBytes(s) buf := bytes.NewBuffer(b) @@ -154,10 +145,10 @@ func argsToURLValues(argNames []string, args ...interface{}) (url.Values, error) } for i, name := range argNames { s := slice[i] - values.Set(name, s[0]) - for i := 1; i < len(s); i++ { - values.Add(name, s[i]) - } + values.Set(name, s) // s[0] + /*for j := 1; j < len(s); j++ { + values.Add(name, s[j]) + }*/ } return values, nil } @@ -177,7 +168,7 @@ fmt /*rpc-gen:template:*ClientJSON func (c *ClientJSON) {{name}}({{args.def}}) ({{response}}) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: {{lowername}}, + Method: reverseFuncMap["{{name}}"], Params: []interface{}{ {{args.ident}} }, Id: 0, } @@ -206,7 +197,7 @@ fmt if err != nil{ return nil, err } - resp, err := http.PostForm(c.addr+{{lowername}}, values) + resp, err := http.PostForm(c.addr+reverseFuncMap["{{name}}"], values) if err != nil { return nil, err } diff --git a/rpc/core_client/client_methods.go b/rpc/core_client/client_methods.go index 938fad01..c62d7b38 100644 --- a/rpc/core_client/client_methods.go +++ b/rpc/core_client/client_methods.go @@ -14,14 +14,15 @@ import ( ) type Client interface { - BlockchainInfo(minHeight uint) (*ctypes.ResponseBlockchainInfo, error) + BlockchainInfo(minHeight uint, maxHeight uint) (*ctypes.ResponseBlockchainInfo, error) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error) - Call(address []byte) (*ctypes.ResponseCall, error) + Call(address []byte, data []byte) (*ctypes.ResponseCall, error) + CallCode(code []byte, data []byte) (*ctypes.ResponseCall, error) DumpStorage(addr []byte) (*ctypes.ResponseDumpStorage, error) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) GetAccount(address []byte) (*ctypes.ResponseGetAccount, error) GetBlock(height uint) (*ctypes.ResponseGetBlock, error) - GetStorage(address []byte) (*ctypes.ResponseGetStorage, error) + GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error) ListAccounts() (*ctypes.ResponseListAccounts, error) ListValidators() (*ctypes.ResponseListValidators, error) NetInfo() (*ctypes.ResponseNetInfo, error) @@ -29,12 +30,12 @@ type Client interface { Status() (*ctypes.ResponseStatus, error) } -func (c *ClientHTTP) BlockchainInfo(minHeight uint) (*ctypes.ResponseBlockchainInfo, error) { - values, err := argsToURLValues([]string{"minHeight"}, minHeight) +func (c *ClientHTTP) BlockchainInfo(minHeight uint, maxHeight uint) (*ctypes.ResponseBlockchainInfo, error) { + values, err := argsToURLValues([]string{"minHeight", "maxHeight"}, minHeight, maxHeight) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"blockchain_info", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["BlockchainInfo"], values) if err != nil { return nil, err } @@ -64,7 +65,7 @@ func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, erro if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"broadcast_tx", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["BroadcastTx"], values) if err != nil { return nil, err } @@ -89,12 +90,42 @@ func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, erro return response.Result, nil } -func (c *ClientHTTP) Call(address []byte) (*ctypes.ResponseCall, error) { - values, err := argsToURLValues([]string{"address"}, address) +func (c *ClientHTTP) Call(address []byte, data []byte) (*ctypes.ResponseCall, error) { + values, err := argsToURLValues([]string{"address", "data"}, address, data) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"call", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["Call"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var response struct { + Result *ctypes.ResponseCall `json:"result"` + Error string `json:"error"` + Id string `json:"id"` + JSONRPC string `json:"jsonrpc"` + } + binary.ReadJSON(&response, body, &err) + if err != nil { + return nil, err + } + if response.Error != "" { + return nil, fmt.Errorf(response.Error) + } + return response.Result, nil +} + +func (c *ClientHTTP) CallCode(code []byte, data []byte) (*ctypes.ResponseCall, error) { + values, err := argsToURLValues([]string{"code", "data"}, code, data) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["CallCode"], values) if err != nil { return nil, err } @@ -124,7 +155,7 @@ func (c *ClientHTTP) DumpStorage(addr []byte) (*ctypes.ResponseDumpStorage, erro if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"dump_storage", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["DumpStorage"], values) if err != nil { return nil, err } @@ -150,11 +181,11 @@ func (c *ClientHTTP) DumpStorage(addr []byte) (*ctypes.ResponseDumpStorage, erro } func (c *ClientHTTP) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"gen_priv_account", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["GenPrivAccount"], values) if err != nil { return nil, err } @@ -184,7 +215,7 @@ func (c *ClientHTTP) GetAccount(address []byte) (*ctypes.ResponseGetAccount, err if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"get_account", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["GetAccount"], values) if err != nil { return nil, err } @@ -214,7 +245,7 @@ func (c *ClientHTTP) GetBlock(height uint) (*ctypes.ResponseGetBlock, error) { if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"get_block", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["GetBlock"], values) if err != nil { return nil, err } @@ -239,12 +270,12 @@ func (c *ClientHTTP) GetBlock(height uint) (*ctypes.ResponseGetBlock, error) { return response.Result, nil } -func (c *ClientHTTP) GetStorage(address []byte) (*ctypes.ResponseGetStorage, error) { - values, err := argsToURLValues([]string{"address"}, address) +func (c *ClientHTTP) GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error) { + values, err := argsToURLValues([]string{"address", "key"}, address, key) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"get_storage", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["GetStorage"], values) if err != nil { return nil, err } @@ -270,11 +301,11 @@ func (c *ClientHTTP) GetStorage(address []byte) (*ctypes.ResponseGetStorage, err } func (c *ClientHTTP) ListAccounts() (*ctypes.ResponseListAccounts, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"list_accounts", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["ListAccounts"], values) if err != nil { return nil, err } @@ -300,11 +331,11 @@ func (c *ClientHTTP) ListAccounts() (*ctypes.ResponseListAccounts, error) { } func (c *ClientHTTP) ListValidators() (*ctypes.ResponseListValidators, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"list_validators", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["ListValidators"], values) if err != nil { return nil, err } @@ -330,11 +361,11 @@ func (c *ClientHTTP) ListValidators() (*ctypes.ResponseListValidators, error) { } func (c *ClientHTTP) NetInfo() (*ctypes.ResponseNetInfo, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"net_info", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["NetInfo"], values) if err != nil { return nil, err } @@ -364,7 +395,7 @@ func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (* if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"sign_tx", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["SignTx"], values) if err != nil { return nil, err } @@ -390,11 +421,11 @@ func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (* } func (c *ClientHTTP) Status() (*ctypes.ResponseStatus, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"status", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["Status"], values) if err != nil { return nil, err } @@ -419,11 +450,11 @@ func (c *ClientHTTP) Status() (*ctypes.ResponseStatus, error) { return response.Result, nil } -func (c *ClientJSON) BlockchainInfo(minHeight uint) (*ctypes.ResponseBlockchainInfo, error) { +func (c *ClientJSON) BlockchainInfo(minHeight uint, maxHeight uint) (*ctypes.ResponseBlockchainInfo, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "blockchain_info", - Params: []interface{}{minHeight}, + Method: reverseFuncMap["BlockchainInfo"], + Params: []interface{}{minHeight, maxHeight}, Id: 0, } body, err := c.RequestResponse(request) @@ -449,7 +480,7 @@ func (c *ClientJSON) BlockchainInfo(minHeight uint) (*ctypes.ResponseBlockchainI func (c *ClientJSON) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "broadcast_tx", + Method: reverseFuncMap["BroadcastTx"], Params: []interface{}{tx}, Id: 0, } @@ -473,11 +504,38 @@ func (c *ClientJSON) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, erro return response.Result, nil } -func (c *ClientJSON) Call(address []byte) (*ctypes.ResponseCall, error) { +func (c *ClientJSON) Call(address []byte, data []byte) (*ctypes.ResponseCall, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "call", - Params: []interface{}{address}, + Method: reverseFuncMap["Call"], + Params: []interface{}{address, data}, + Id: 0, + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + var response struct { + Result *ctypes.ResponseCall `json:"result"` + Error string `json:"error"` + Id string `json:"id"` + JSONRPC string `json:"jsonrpc"` + } + binary.ReadJSON(&response, body, &err) + if err != nil { + return nil, err + } + if response.Error != "" { + return nil, fmt.Errorf(response.Error) + } + return response.Result, nil +} + +func (c *ClientJSON) CallCode(code []byte, data []byte) (*ctypes.ResponseCall, error) { + request := rpc.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["CallCode"], + Params: []interface{}{code, data}, Id: 0, } body, err := c.RequestResponse(request) @@ -503,7 +561,7 @@ func (c *ClientJSON) Call(address []byte) (*ctypes.ResponseCall, error) { func (c *ClientJSON) DumpStorage(addr []byte) (*ctypes.ResponseDumpStorage, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "dump_storage", + Method: reverseFuncMap["DumpStorage"], Params: []interface{}{addr}, Id: 0, } @@ -530,8 +588,8 @@ func (c *ClientJSON) DumpStorage(addr []byte) (*ctypes.ResponseDumpStorage, erro func (c *ClientJSON) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "gen_priv_account", - Params: []interface{}{nil}, + Method: reverseFuncMap["GenPrivAccount"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) @@ -557,7 +615,7 @@ func (c *ClientJSON) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) { func (c *ClientJSON) GetAccount(address []byte) (*ctypes.ResponseGetAccount, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "get_account", + Method: reverseFuncMap["GetAccount"], Params: []interface{}{address}, Id: 0, } @@ -584,7 +642,7 @@ func (c *ClientJSON) GetAccount(address []byte) (*ctypes.ResponseGetAccount, err func (c *ClientJSON) GetBlock(height uint) (*ctypes.ResponseGetBlock, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "get_block", + Method: reverseFuncMap["GetBlock"], Params: []interface{}{height}, Id: 0, } @@ -608,11 +666,11 @@ func (c *ClientJSON) GetBlock(height uint) (*ctypes.ResponseGetBlock, error) { return response.Result, nil } -func (c *ClientJSON) GetStorage(address []byte) (*ctypes.ResponseGetStorage, error) { +func (c *ClientJSON) GetStorage(address []byte, key []byte) (*ctypes.ResponseGetStorage, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "get_storage", - Params: []interface{}{address}, + Method: reverseFuncMap["GetStorage"], + Params: []interface{}{address, key}, Id: 0, } body, err := c.RequestResponse(request) @@ -638,8 +696,8 @@ func (c *ClientJSON) GetStorage(address []byte) (*ctypes.ResponseGetStorage, err func (c *ClientJSON) ListAccounts() (*ctypes.ResponseListAccounts, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "list_accounts", - Params: []interface{}{nil}, + Method: reverseFuncMap["ListAccounts"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) @@ -665,8 +723,8 @@ func (c *ClientJSON) ListAccounts() (*ctypes.ResponseListAccounts, error) { func (c *ClientJSON) ListValidators() (*ctypes.ResponseListValidators, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "list_validators", - Params: []interface{}{nil}, + Method: reverseFuncMap["ListValidators"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) @@ -692,8 +750,8 @@ func (c *ClientJSON) ListValidators() (*ctypes.ResponseListValidators, error) { func (c *ClientJSON) NetInfo() (*ctypes.ResponseNetInfo, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "net_info", - Params: []interface{}{nil}, + Method: reverseFuncMap["NetInfo"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) @@ -719,7 +777,7 @@ func (c *ClientJSON) NetInfo() (*ctypes.ResponseNetInfo, error) { func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*ctypes.ResponseSignTx, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "sign_tx", + Method: reverseFuncMap["SignTx"], Params: []interface{}{tx, privAccounts}, Id: 0, } @@ -746,8 +804,8 @@ func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (* func (c *ClientJSON) Status() (*ctypes.ResponseStatus, error) { request := rpc.RPCRequest{ JSONRPC: "2.0", - Method: "status", - Params: []interface{}{nil}, + Method: reverseFuncMap["Status"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) diff --git a/rpc/core_client/client_methods.go.bak b/rpc/core_client/client_methods.go.bak index 1bf5badc..b9af5ff1 100644 --- a/rpc/core_client/client_methods.go.bak +++ b/rpc/core_client/client_methods.go.bak @@ -1,3 +1,5 @@ +// File generated by github.com/ebuchman/rpc-gen + package rpc import ( @@ -11,14 +13,15 @@ import ( ) type Client interface { - BlockchainInfo(minHeight uint) (*core.ResponseBlockchainInfo, error) + BlockchainInfo(minHeight uint, maxHeight uint) (*core.ResponseBlockchainInfo, error) BroadcastTx(tx types.Tx) (*core.ResponseBroadcastTx, error) - Call(address []byte) (*core.ResponseCall, error) + Call(address []byte, data []byte) (*core.ResponseCall, error) + CallCode(code []byte, data []byte) (*core.ResponseCall, error) DumpStorage(addr []byte) (*core.ResponseDumpStorage, error) GenPrivAccount() (*core.ResponseGenPrivAccount, error) GetAccount(address []byte) (*core.ResponseGetAccount, error) GetBlock(height uint) (*core.ResponseGetBlock, error) - GetStorage(address []byte) (*core.ResponseGetStorage, error) + GetStorage(address []byte, key []byte) (*core.ResponseGetStorage, error) ListAccounts() (*core.ResponseListAccounts, error) ListValidators() (*core.ResponseListValidators, error) NetInfo() (*core.ResponseNetInfo, error) @@ -26,12 +29,12 @@ type Client interface { Status() (*core.ResponseStatus, error) } -func (c *ClientHTTP) BlockchainInfo(minHeight uint) (*core.ResponseBlockchainInfo, error) { - values, err := argsToURLValues([]string{"minHeight"}, minHeight) +func (c *ClientHTTP) BlockchainInfo(minHeight uint, maxHeight uint) (*core.ResponseBlockchainInfo, error) { + values, err := argsToURLValues([]string{"minHeight", "maxHeight"}, minHeight, maxHeight) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"blockchain_info", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["BlockchainInfo"], values) if err != nil { return nil, err } @@ -61,7 +64,7 @@ func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*core.ResponseBroadcastTx, error) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"broadcast_tx", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["BroadcastTx"], values) if err != nil { return nil, err } @@ -86,12 +89,42 @@ func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*core.ResponseBroadcastTx, error) return response.Result, nil } -func (c *ClientHTTP) Call(address []byte) (*core.ResponseCall, error) { - values, err := argsToURLValues([]string{"address"}, address) +func (c *ClientHTTP) Call(address []byte, data []byte) (*core.ResponseCall, error) { + values, err := argsToURLValues([]string{"address", "data"}, address, data) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"call", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["Call"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var response struct { + Result *core.ResponseCall `json:"result"` + Error string `json:"error"` + Id string `json:"id"` + JSONRPC string `json:"jsonrpc"` + } + binary.ReadJSON(&response, body, &err) + if err != nil { + return nil, err + } + if response.Error != "" { + return nil, fmt.Errorf(response.Error) + } + return response.Result, nil +} + +func (c *ClientHTTP) CallCode(code []byte, data []byte) (*core.ResponseCall, error) { + values, err := argsToURLValues([]string{"code", "data"}, code, data) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["CallCode"], values) if err != nil { return nil, err } @@ -121,7 +154,7 @@ func (c *ClientHTTP) DumpStorage(addr []byte) (*core.ResponseDumpStorage, error) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"dump_storage", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["DumpStorage"], values) if err != nil { return nil, err } @@ -147,11 +180,11 @@ func (c *ClientHTTP) DumpStorage(addr []byte) (*core.ResponseDumpStorage, error) } func (c *ClientHTTP) GenPrivAccount() (*core.ResponseGenPrivAccount, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"gen_priv_account", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["GenPrivAccount"], values) if err != nil { return nil, err } @@ -181,7 +214,7 @@ func (c *ClientHTTP) GetAccount(address []byte) (*core.ResponseGetAccount, error if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"get_account", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["GetAccount"], values) if err != nil { return nil, err } @@ -211,7 +244,7 @@ func (c *ClientHTTP) GetBlock(height uint) (*core.ResponseGetBlock, error) { if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"get_block", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["GetBlock"], values) if err != nil { return nil, err } @@ -236,12 +269,12 @@ func (c *ClientHTTP) GetBlock(height uint) (*core.ResponseGetBlock, error) { return response.Result, nil } -func (c *ClientHTTP) GetStorage(address []byte) (*core.ResponseGetStorage, error) { - values, err := argsToURLValues([]string{"address"}, address) +func (c *ClientHTTP) GetStorage(address []byte, key []byte) (*core.ResponseGetStorage, error) { + values, err := argsToURLValues([]string{"address", "key"}, address, key) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"get_storage", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["GetStorage"], values) if err != nil { return nil, err } @@ -267,11 +300,11 @@ func (c *ClientHTTP) GetStorage(address []byte) (*core.ResponseGetStorage, error } func (c *ClientHTTP) ListAccounts() (*core.ResponseListAccounts, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"list_accounts", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["ListAccounts"], values) if err != nil { return nil, err } @@ -297,11 +330,11 @@ func (c *ClientHTTP) ListAccounts() (*core.ResponseListAccounts, error) { } func (c *ClientHTTP) ListValidators() (*core.ResponseListValidators, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"list_validators", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["ListValidators"], values) if err != nil { return nil, err } @@ -327,11 +360,11 @@ func (c *ClientHTTP) ListValidators() (*core.ResponseListValidators, error) { } func (c *ClientHTTP) NetInfo() (*core.ResponseNetInfo, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"net_info", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["NetInfo"], values) if err != nil { return nil, err } @@ -357,11 +390,11 @@ func (c *ClientHTTP) NetInfo() (*core.ResponseNetInfo, error) { } func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*core.ResponseSignTx, error) { - values, err := argsToURLValues([]string{"tx", "privAccounts"}, tx, privAccounts) + values, err := argsToURLValues([]string{"tx", "priv_accounts"}, tx, privAccounts) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"sign_tx", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["SignTx"], values) if err != nil { return nil, err } @@ -387,11 +420,11 @@ func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (* } func (c *ClientHTTP) Status() (*core.ResponseStatus, error) { - values, err := argsToURLValues(nil, nil) + values, err := argsToURLValues(nil) if err != nil { return nil, err } - resp, err := http.PostForm(c.addr+"status", values) + resp, err := http.PostForm(c.addr+reverseFuncMap["Status"], values) if err != nil { return nil, err } @@ -416,11 +449,11 @@ func (c *ClientHTTP) Status() (*core.ResponseStatus, error) { return response.Result, nil } -func (c *ClientJSON) BlockchainInfo(minHeight uint) (*core.ResponseBlockchainInfo, error) { +func (c *ClientJSON) BlockchainInfo(minHeight uint, maxHeight uint) (*core.ResponseBlockchainInfo, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "blockchain_info", - Params: []interface{}{minHeight}, + Method: reverseFuncMap["BlockchainInfo"], + Params: []interface{}{minHeight, maxHeight}, Id: 0, } body, err := c.RequestResponse(request) @@ -446,7 +479,7 @@ func (c *ClientJSON) BlockchainInfo(minHeight uint) (*core.ResponseBlockchainInf func (c *ClientJSON) BroadcastTx(tx types.Tx) (*core.ResponseBroadcastTx, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "broadcast_tx", + Method: reverseFuncMap["BroadcastTx"], Params: []interface{}{tx}, Id: 0, } @@ -470,11 +503,38 @@ func (c *ClientJSON) BroadcastTx(tx types.Tx) (*core.ResponseBroadcastTx, error) return response.Result, nil } -func (c *ClientJSON) Call(address []byte) (*core.ResponseCall, error) { +func (c *ClientJSON) Call(address []byte, data []byte) (*core.ResponseCall, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "call", - Params: []interface{}{address}, + Method: reverseFuncMap["Call"], + Params: []interface{}{address, data}, + Id: 0, + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + var response struct { + Result *core.ResponseCall `json:"result"` + Error string `json:"error"` + Id string `json:"id"` + JSONRPC string `json:"jsonrpc"` + } + binary.ReadJSON(&response, body, &err) + if err != nil { + return nil, err + } + if response.Error != "" { + return nil, fmt.Errorf(response.Error) + } + return response.Result, nil +} + +func (c *ClientJSON) CallCode(code []byte, data []byte) (*core.ResponseCall, error) { + request := RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["CallCode"], + Params: []interface{}{code, data}, Id: 0, } body, err := c.RequestResponse(request) @@ -500,7 +560,7 @@ func (c *ClientJSON) Call(address []byte) (*core.ResponseCall, error) { func (c *ClientJSON) DumpStorage(addr []byte) (*core.ResponseDumpStorage, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "dump_storage", + Method: reverseFuncMap["DumpStorage"], Params: []interface{}{addr}, Id: 0, } @@ -527,8 +587,8 @@ func (c *ClientJSON) DumpStorage(addr []byte) (*core.ResponseDumpStorage, error) func (c *ClientJSON) GenPrivAccount() (*core.ResponseGenPrivAccount, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "gen_priv_account", - Params: []interface{}{nil}, + Method: reverseFuncMap["GenPrivAccount"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) @@ -554,7 +614,7 @@ func (c *ClientJSON) GenPrivAccount() (*core.ResponseGenPrivAccount, error) { func (c *ClientJSON) GetAccount(address []byte) (*core.ResponseGetAccount, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "get_account", + Method: reverseFuncMap["GetAccount"], Params: []interface{}{address}, Id: 0, } @@ -581,7 +641,7 @@ func (c *ClientJSON) GetAccount(address []byte) (*core.ResponseGetAccount, error func (c *ClientJSON) GetBlock(height uint) (*core.ResponseGetBlock, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "get_block", + Method: reverseFuncMap["GetBlock"], Params: []interface{}{height}, Id: 0, } @@ -605,11 +665,11 @@ func (c *ClientJSON) GetBlock(height uint) (*core.ResponseGetBlock, error) { return response.Result, nil } -func (c *ClientJSON) GetStorage(address []byte) (*core.ResponseGetStorage, error) { +func (c *ClientJSON) GetStorage(address []byte, key []byte) (*core.ResponseGetStorage, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "get_storage", - Params: []interface{}{address}, + Method: reverseFuncMap["GetStorage"], + Params: []interface{}{address, key}, Id: 0, } body, err := c.RequestResponse(request) @@ -635,8 +695,8 @@ func (c *ClientJSON) GetStorage(address []byte) (*core.ResponseGetStorage, error func (c *ClientJSON) ListAccounts() (*core.ResponseListAccounts, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "list_accounts", - Params: []interface{}{nil}, + Method: reverseFuncMap["ListAccounts"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) @@ -662,8 +722,8 @@ func (c *ClientJSON) ListAccounts() (*core.ResponseListAccounts, error) { func (c *ClientJSON) ListValidators() (*core.ResponseListValidators, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "list_validators", - Params: []interface{}{nil}, + Method: reverseFuncMap["ListValidators"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) @@ -689,8 +749,8 @@ func (c *ClientJSON) ListValidators() (*core.ResponseListValidators, error) { func (c *ClientJSON) NetInfo() (*core.ResponseNetInfo, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "net_info", - Params: []interface{}{nil}, + Method: reverseFuncMap["NetInfo"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) @@ -716,7 +776,7 @@ func (c *ClientJSON) NetInfo() (*core.ResponseNetInfo, error) { func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (*core.ResponseSignTx, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "sign_tx", + Method: reverseFuncMap["SignTx"], Params: []interface{}{tx, privAccounts}, Id: 0, } @@ -743,8 +803,8 @@ func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (* func (c *ClientJSON) Status() (*core.ResponseStatus, error) { request := RPCRequest{ JSONRPC: "2.0", - Method: "status", - Params: []interface{}{nil}, + Method: reverseFuncMap["Status"], + Params: []interface{}{}, Id: 0, } body, err := c.RequestResponse(request) diff --git a/rpc/handlers.go b/rpc/handlers.go index a13ab98b..93332916 100644 --- a/rpc/handlers.go +++ b/rpc/handlers.go @@ -1,12 +1,16 @@ package rpc import ( + "bytes" "encoding/json" "fmt" "github.com/tendermint/tendermint/binary" + "github.com/tendermint/tendermint/events" + "golang.org/x/net/websocket" "io/ioutil" "net/http" "reflect" + "time" ) func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc) { @@ -19,6 +23,15 @@ func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc) { mux.HandleFunc("/", makeJSONRPCHandler(funcMap)) } +func RegisterEventsHandler(mux *http.ServeMux, evsw *events.EventSwitch) { + // websocket endpoint + w := NewWebsocketManager(evsw) + mux.Handle("/events", websocket.Handler(w.eventsHandler)) +} + +//------------------------------------- +// function introspection + // holds all type information for each function type RPCFunc struct { f reflect.Value // underlying rpc function @@ -27,6 +40,7 @@ type RPCFunc struct { argNames []string // name of each argument } +// wraps a function for quicker introspection func NewRPCFunc(f interface{}, args []string) *RPCFunc { return &RPCFunc{ f: reflect.ValueOf(f), @@ -36,6 +50,7 @@ func NewRPCFunc(f interface{}, args []string) *RPCFunc { } } +// return a function's argument types func funcArgTypes(f interface{}) []reflect.Type { t := reflect.TypeOf(f) n := t.NumIn() @@ -46,6 +61,7 @@ func funcArgTypes(f interface{}) []reflect.Type { return types } +// return a function's return types func funcReturnTypes(f interface{}) []reflect.Type { t := reflect.TypeOf(f) n := t.NumOut() @@ -56,12 +72,17 @@ func funcReturnTypes(f interface{}) []reflect.Type { return types } +// function introspection //----------------------------------------------------------------------------- // rpc.json // jsonrpc calls grab the given method's function info and runs reflect.Call func makeJSONRPCHandler(funcMap map[string]*RPCFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + if len(r.URL.Path) > 1 { + WriteRPCResponse(w, NewRPCResponse(nil, fmt.Sprintf("Invalid JSONRPC endpoint %s", r.URL.Path))) + return + } b, _ := ioutil.ReadAll(r.Body) var request RPCRequest err := json.Unmarshal(b, &request) @@ -69,7 +90,6 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc) http.HandlerFunc { WriteRPCResponse(w, NewRPCResponse(nil, err.Error())) return } - rpcFunc := funcMap[request.Method] if rpcFunc == nil { WriteRPCResponse(w, NewRPCResponse(nil, "RPC method unknown: "+request.Method)) @@ -169,6 +189,162 @@ func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) { // rpc.http //----------------------------------------------------------------------------- +// rpc.websocket + +// for requests coming in +type WsRequest struct { + Type string // subscribe or unsubscribe + Event string +} + +// for responses going out +type WsResponse struct { + Event string + Data interface{} + Error string +} + +// a single websocket connection +// contains the listeners id +type Connection struct { + id string + wsCon *websocket.Conn + writeChan chan WsResponse + quitChan chan struct{} + failedSends uint +} + +// new websocket connection wrapper +func NewConnection(con *websocket.Conn) *Connection { + return &Connection{ + id: con.RemoteAddr().String(), + wsCon: con, + writeChan: make(chan WsResponse, WriteChanBuffer), // buffered. we keep track when its full + } +} + +// close the connection +func (c *Connection) Close() { + c.wsCon.Close() + close(c.writeChan) + close(c.quitChan) +} + +// main manager for all websocket connections +// holds the event switch +type WebsocketManager struct { + ew *events.EventSwitch + cons map[string]*Connection +} + +func NewWebsocketManager(ew *events.EventSwitch) *WebsocketManager { + return &WebsocketManager{ + ew: ew, + cons: make(map[string]*Connection), + } +} + +func (w *WebsocketManager) eventsHandler(con *websocket.Conn) { + // register connection + c := NewConnection(con) + w.cons[con.RemoteAddr().String()] = c + + // read subscriptions/unsubscriptions to events + go w.read(c) + // write responses + go w.write(c) +} + +const ( + WsConnectionReaperSeconds = 5 + MaxFailedSendsSeconds = 10 + WriteChanBuffer = 10 +) + +// read from the socket and subscribe to or unsubscribe from events +func (w *WebsocketManager) read(con *Connection) { + reaper := time.Tick(time.Second * WsConnectionReaperSeconds) + for { + select { + case <-reaper: + if con.failedSends > MaxFailedSendsSeconds { + // sending has failed too many times. + // kill the connection + con.quitChan <- struct{}{} + } + default: + var in []byte + if err := websocket.Message.Receive(con.wsCon, &in); err != nil { + // an error reading the connection, + // so kill the connection + con.quitChan <- struct{}{} + } + var req WsRequest + err := json.Unmarshal(in, &req) + if err != nil { + errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error()) + con.writeChan <- WsResponse{Error: errStr} + } + switch req.Type { + case "subscribe": + w.ew.AddListenerForEvent(con.id, req.Event, func(msg interface{}) { + resp := WsResponse{ + Event: req.Event, + Data: msg, + } + select { + case con.writeChan <- resp: + // yay + con.failedSends = 0 + default: + // channel is full + // if this happens too many times, + // close connection + con.failedSends += 1 + } + }) + case "unsubscribe": + if req.Event != "" { + w.ew.RemoveListenerForEvent(req.Event, con.id) + } else { + w.ew.RemoveListener(con.id) + } + default: + con.writeChan <- WsResponse{Error: "Unknown request type: " + req.Type} + } + + } + } +} + +// receives on a write channel and writes out to the socket +func (w *WebsocketManager) write(con *Connection) { + n, err := new(int64), new(error) + for { + select { + case msg := <-con.writeChan: + buf := new(bytes.Buffer) + binary.WriteJSON(msg, buf, n, err) + if *err != nil { + log.Error("Failed to write JSON WsResponse", "error", err) + } else { + websocket.Message.Send(con.wsCon, buf.Bytes()) + } + case <-con.quitChan: + w.closeConn(con) + return + } + } +} + +// close a connection and delete from manager +func (w *WebsocketManager) closeConn(con *Connection) { + con.Close() + delete(w.cons, con.id) +} + +// rpc.websocket +//----------------------------------------------------------------------------- // returns is Response struct and error. If error is not nil, return it func unreflectResponse(returns []reflect.Value) (interface{}, error) { diff --git a/rpc/http_server.go b/rpc/http_server.go index 1aa5888a..2defdf5d 100644 --- a/rpc/http_server.go +++ b/rpc/http_server.go @@ -10,12 +10,16 @@ import ( "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/common" + "github.com/tendermint/tendermint/events" ) -func StartHTTPServer(listenAddr string, funcMap map[string]*RPCFunc) { +func StartHTTPServer(listenAddr string, funcMap map[string]*RPCFunc, evsw *events.EventSwitch) { log.Info(Fmt("Starting RPC HTTP server on %s", listenAddr)) mux := http.NewServeMux() RegisterRPCFuncs(mux, funcMap) + if evsw != nil { + RegisterEventsHandler(mux, evsw) + } go func() { res := http.ListenAndServe( listenAddr, diff --git a/rpc/test/client_rpc_test.go b/rpc/test/client_rpc_test.go new file mode 100644 index 00000000..0d455c3d --- /dev/null +++ b/rpc/test/client_rpc_test.go @@ -0,0 +1,75 @@ +package rpc + +import ( + "testing" +) + +//-------------------------------------------------------------------------------- +// Test the HTTP client + +func TestHTTPStatus(t *testing.T) { + testStatus(t, "HTTP") +} + +func TestHTTPGenPriv(t *testing.T) { + testGenPriv(t, "HTTP") +} + +func TestHTTPGetAccount(t *testing.T) { + testGetAccount(t, "HTTP") +} + +func TestHTTPSignedTx(t *testing.T) { + testSignedTx(t, "HTTP") +} + +func TestHTTPBroadcastTx(t *testing.T) { + testBroadcastTx(t, "HTTP") +} + +func TestHTTPGetStorage(t *testing.T) { + testGetStorage(t, "HTTP") +} + +func TestHTTPCallCode(t *testing.T) { + testCallCode(t, "HTTP") +} + +func TestHTTPCallContract(t *testing.T) { + testCall(t, "HTTP") +} + +//-------------------------------------------------------------------------------- +// Test the JSONRPC client + +func TestJSONStatus(t *testing.T) { + testStatus(t, "JSONRPC") +} + +func TestJSONGenPriv(t *testing.T) { + testGenPriv(t, "JSONRPC") +} + +func TestJSONGetAccount(t *testing.T) { + testGetAccount(t, "JSONRPC") +} + +func TestJSONSignedTx(t *testing.T) { + testSignedTx(t, "JSONRPC") +} + +func TestJSONBroadcastTx(t *testing.T) { + testBroadcastTx(t, "JSONRPC") +} + +func TestJSONGetStorage(t *testing.T) { + testGetStorage(t, "JSONRPC") +} + +func TestJSONCallCode(t *testing.T) { + testCallCode(t, "JSONRPC") +} + +func TestJSONCallContract(t *testing.T) { + testCall(t, "JSONRPC") +} diff --git a/rpc/test/test.go b/rpc/test/helpers.go similarity index 57% rename from rpc/test/test.go rename to rpc/test/helpers.go index ab776cf0..8fb594da 100644 --- a/rpc/test/test.go +++ b/rpc/test/helpers.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/hex" "github.com/tendermint/tendermint/account" - "github.com/tendermint/tendermint/binary" + . "github.com/tendermint/tendermint/common" "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/logger" nm "github.com/tendermint/tendermint/node" @@ -13,12 +13,10 @@ import ( cclient "github.com/tendermint/tendermint/rpc/core_client" "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - "io/ioutil" - "net/http" - "net/url" "testing" ) +// global variables for use across all tests var ( rpcAddr = "127.0.0.1:8089" requestAddr = "http://" + rpcAddr + "/" @@ -30,6 +28,11 @@ var ( userAddr = "D7DFF9806078899C8DA3FE3633CC0BF3C6C2B1BB" userPriv = "FDE3BD94CB327D19464027BA668194C5EFA46AE83E8419D7542CFF41F00C81972239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" userPub = "2239C21C81EA7173A6C489145490C015E05D4B97448933B708A7EC5B7B4921E3" + + clients = map[string]cclient.Client{ + "JSONRPC": cclient.NewClient(requestAddr, "JSONRPC"), + "HTTP": cclient.NewClient(requestAddr, "HTTP"), + } ) func decodeHex(hexStr string) []byte { @@ -40,6 +43,7 @@ func decodeHex(hexStr string) []byte { return bytes } +// create a new node and sleep forever func newNode(ready chan struct{}) { // Create & start node node = nm.NewNode() @@ -56,6 +60,7 @@ func newNode(ready chan struct{}) { <-ch } +// initialize config and create new node func init() { rootDir := ".tendermint" config.Init(rootDir) @@ -84,64 +89,10 @@ func init() { <-ready } -func getAccount(t *testing.T, typ string, addr []byte) *account.Account { - var client cclient.Client - switch typ { - case "JSONRPC": - client = cclient.NewClient(requestAddr, "JSONRPC") - case "HTTP": - client = cclient.NewClient(requestAddr, "HTTP") - } - ac, err := client.GetAccount(addr) - if err != nil { - t.Fatal(err) - } - return ac.Account -} - -/* -func getAccount(t *testing.T, typ string, addr []byte) *account.Account { - var resp *http.Response - var err error - switch typ { - case "JSONRPC": - s := rpc.JSONRPC{ - JSONRPC: "2.0", - Method: "get_account", - Params: []interface{}{hex.EncodeToString(addr)}, - Id: 0, - } - b, err := json.Marshal(s) - if err != nil { - t.Fatal(err) - } - buf := bytes.NewBuffer(b) - resp, err = http.Post(requestAddr, "text/json", buf) - case "HTTP": - resp, err = http.PostForm(requestAddr+"get_account", - url.Values{"address": {"\"" + (hex.EncodeToString(addr)) + "\""}}) - } - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - var response struct { - Result ctypes.ResponseGetAccount `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` - } - binary.ReadJSON(&response, body, &err) - if err != nil { - t.Fatal(err) - } - return response.Result.Account -}*/ +//------------------------------------------------------------------------------- +// make transactions +// make a send tx (uses get account to figure out the nonce) func makeSendTx(t *testing.T, typ string, from, to []byte, amt uint64) *types.SendTx { acc := getAccount(t, typ, from) nonce := 0 @@ -172,6 +123,7 @@ func makeSendTx(t *testing.T, typ string, from, to []byte, amt uint64) *types.Se return tx } +// make a call tx (uses get account to figure out the nonce) func makeCallTx(t *testing.T, typ string, from, to, data []byte, amt, gaslim, fee uint64) *types.CallTx { acc := getAccount(t, typ, from) nonce := 0 @@ -199,22 +151,21 @@ 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, response 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) - } - binary.ReadJSON(response, body, &err) +// make transactions +//------------------------------------------------------------------------------- +// rpc call wrappers + +// get the account +func getAccount(t *testing.T, typ string, addr []byte) *account.Account { + client := clients[typ] + ac, err := client.GetAccount(addr) if err != nil { t.Fatal(err) } + return ac.Account } +// make and sign transaction func signTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [64]byte, amt, gaslim, fee uint64) (types.Tx, *account.PrivAccount) { var tx types.Tx if data == nil { @@ -223,93 +174,77 @@ func signTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [64]byt tx = makeCallTx(t, typ, fromAddr, toAddr, data, amt, gaslim, fee) } - n, w := new(int64), new(bytes.Buffer) - var err error - binary.WriteJSON(tx, w, n, &err) - if err != nil { - t.Fatal(err) - } - b := w.Bytes() - privAcc := account.GenPrivAccountFromKey(key) if bytes.Compare(privAcc.PubKey.Address(), fromAddr) != 0 { t.Fatal("Faield to generate correct priv acc") } - w = new(bytes.Buffer) - binary.WriteJSON([]*account.PrivAccount{privAcc}, w, n, &err) + + client := clients[typ] + resp, err := client.SignTx(tx, []*account.PrivAccount{privAcc}) if err != nil { t.Fatal(err) } - - var response struct { - Result ctypes.ResponseSignTx `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` - } - requestResponse(t, "unsafe/sign_tx", url.Values{"tx": {string(b)}, "privAccounts": {string(w.Bytes())}}, &response) - if response.Error != "" { - t.Fatal(response.Error) - } - result := response.Result - return result.Tx, privAcc + return resp.Tx, privAcc } +// create, sign, and broadcast a transaction func broadcastTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [64]byte, amt, gaslim, fee uint64) (types.Tx, ctypes.Receipt) { tx, _ := signTx(t, typ, fromAddr, toAddr, data, key, amt, gaslim, fee) - - n, w := new(int64), new(bytes.Buffer) - var err error - binary.WriteJSON(tx, w, n, &err) + client := clients[typ] + resp, err := client.BroadcastTx(tx) if err != nil { t.Fatal(err) } - b := w.Bytes() - - var response struct { - Result ctypes.ResponseBroadcastTx `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` - } - requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &response) - if response.Error != "" { - t.Fatal(response.Error) - } - return tx, response.Result.Receipt + mempoolCount += 1 + return tx, resp.Receipt } +// dump all storage for an account. currently unused func dumpStorage(t *testing.T, addr []byte) ctypes.ResponseDumpStorage { - addrString := "\"" + hex.EncodeToString(addr) + "\"" - var response struct { - Result ctypes.ResponseDumpStorage `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` + client := clients["HTTP"] + resp, err := client.DumpStorage(addr) + if err != nil { + t.Fatal(err) } - requestResponse(t, "dump_storage", url.Values{"address": {addrString}}, &response) - if response.Error != "" { - t.Fatal(response.Error) - } - return response.Result + return *resp } -func getStorage(t *testing.T, addr, slot []byte) []byte { - addrString := "\"" + hex.EncodeToString(addr) + "\"" - slotString := "\"" + hex.EncodeToString(slot) + "\"" - var response struct { - Result ctypes.ResponseGetStorage `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` +func getStorage(t *testing.T, typ string, addr, key []byte) []byte { + client := clients[typ] + resp, err := client.GetStorage(addr, key) + if err != nil { + t.Fatal(err) } - requestResponse(t, "get_storage", url.Values{"address": {addrString}, "storage": {slotString}}, &response) - if response.Error != "" { - t.Fatal(response.Error) - } - return response.Result.Value + return resp.Value } +func callCode(t *testing.T, client cclient.Client, code, data, expected []byte) { + resp, err := client.CallCode(code, data) + if err != nil { + t.Fatal(err) + } + ret := resp.Return + // NOTE: we don't flip memory when it comes out of RETURN (?!) + if bytes.Compare(ret, LeftPadWord256(expected).Bytes()) != 0 { + t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected) + } +} + +func callContract(t *testing.T, client cclient.Client, address, data, expected []byte) { + resp, err := client.Call(address, data) + if err != nil { + t.Fatal(err) + } + ret := resp.Return + // NOTE: we don't flip memory when it comes out of RETURN (?!) + if bytes.Compare(ret, LeftPadWord256(expected).Bytes()) != 0 { + t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected) + } +} + +//-------------------------------------------------------------------------------- +// utility verification function + func checkTx(t *testing.T, fromAddr []byte, priv *account.PrivAccount, tx *types.SendTx) { if bytes.Compare(tx.Inputs[0].Address, fromAddr) != 0 { t.Fatal("Tx input addresses don't match!") diff --git a/rpc/test/http_rpc_test.go b/rpc/test/http_rpc_test.go deleted file mode 100644 index 3eb057e4..00000000 --- a/rpc/test/http_rpc_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/hex" - "fmt" - "github.com/tendermint/tendermint/binary" - . "github.com/tendermint/tendermint/common" - "github.com/tendermint/tendermint/merkle" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - "github.com/tendermint/tendermint/state" - "github.com/tendermint/tendermint/types" - "io/ioutil" - "net/http" - "testing" - "time" -) - -func TestHTTPStatus(t *testing.T) { - resp, err := http.Get(requestAddr + "status") - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - var response struct { - Result ctypes.ResponseStatus `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` - } - binary.ReadJSON(&response, body, &err) - if err != nil { - t.Fatal(err) - } - result := response.Result - fmt.Println(">>>", result) - return -} - -func TestHTTPGenPriv(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 response struct { - Result ctypes.ResponseGenPrivAccount `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` - } - binary.ReadJSON(&response, body, &err) - if err != nil { - t.Fatal(err) - } - fmt.Println(">>>", response) -} - -func TestHTTPGetAccount(t *testing.T) { - byteAddr, _ := hex.DecodeString(userAddr) - acc := getAccount(t, "HTTP", byteAddr) - if acc == nil { - t.Fatalf("Account was nil") - } - if bytes.Compare(acc.Address, byteAddr) != 0 { - t.Fatalf("Failed to get correct account. Got %x, expected %x", acc.Address, byteAddr) - } - -} - -func TestHTTPSignedTx(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, priv := signTx(t, "HTTP", byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) - - toAddr = []byte{20, 143, 24, 63, 16, 17, 83, 29, 90, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54} - tx, priv = signTx(t, "HTTP", byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) - - toAddr = []byte{0, 0, 4, 0, 0, 4, 0, 0, 4, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54} - tx, priv = signTx(t, "HTTP", byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) -} - -func TestHTTPBroadcastTx(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, receipt := broadcastTx(t, "HTTP", byteAddr, toAddr, nil, byteKey, amt, 0, 0) - if receipt.CreatesContract > 0 { - t.Fatal("This tx does not create a contract") - } - if len(receipt.TxHash) == 0 { - t.Fatal("Failed to compute tx hash") - } - pool := node.MempoolReactor().Mempool - txs := pool.GetProposalTxs() - if len(txs) != mempoolCount+1 { - t.Fatalf("The mem pool has %d txs. Expected %d", len(txs), mempoolCount+1) - } - tx2 := txs[mempoolCount].(*types.SendTx) - mempoolCount += 1 - if bytes.Compare(merkle.HashFromBinary(tx), merkle.HashFromBinary(tx2)) != 0 { - t.Fatal("inconsistent hashes for mempool tx and sent tx") - } - -} - -func TestHTTPGetStorage(t *testing.T) { - priv := state.LoadPrivValidator(".tendermint/priv_validator.json") - _ = priv - //ctypes.SetPrivValidator(priv) - - byteAddr, _ := hex.DecodeString(userAddr) - var byteKey [64]byte - oh, _ := hex.DecodeString(userPriv) - copy(byteKey[:], oh) - - amt := uint64(1100) - code := []byte{0x60, 0x5, 0x60, 0x1, 0x55} - _, receipt := broadcastTx(t, "HTTP", byteAddr, nil, code, byteKey, amt, 1000, 1000) - if receipt.CreatesContract == 0 { - t.Fatal("This tx creates a contract") - } - if len(receipt.TxHash) == 0 { - t.Fatal("Failed to compute tx hash") - } - contractAddr := receipt.ContractAddr - if len(contractAddr) == 0 { - t.Fatal("Creates contract but resulting address is empty") - } - time.Sleep(time.Second * 20) - mempoolCount -= 1 - - v := getStorage(t, contractAddr, []byte{0x1}) - got := RightPadWord256(v) - expected := RightPadWord256([]byte{0x5}) - if got.Compare(expected) != 0 { - t.Fatalf("Wrong storage value. Got %x, expected %x", got.Bytes(), expected.Bytes()) - } -} - -/*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/test/json_rpc_test.go b/rpc/test/json_rpc_test.go deleted file mode 100644 index 5b8221ad..00000000 --- a/rpc/test/json_rpc_test.go +++ /dev/null @@ -1,177 +0,0 @@ -package rpc - -import ( - "bytes" - "encoding/hex" - "encoding/json" - "fmt" - "github.com/tendermint/tendermint/binary" - . "github.com/tendermint/tendermint/common" - "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/rpc" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - "github.com/tendermint/tendermint/types" - "io/ioutil" - "net/http" - "net/url" - "testing" -) - -func TestJSONStatus(t *testing.T) { - s := rpc.RPCRequest{ - JSONRPC: "2.0", - Method: "status", - Params: []interface{}{}, - Id: 0, - } - b, err := json.Marshal(s) - if err != nil { - t.Fatal(err) - } - buf := bytes.NewBuffer(b) - resp, err := http.Post(requestAddr, "text/json", buf) - if err != nil { - t.Fatal(err) - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatal(err) - } - - var response struct { - Result ctypes.ResponseStatus `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` - } - binary.ReadJSON(&response, body, &err) - if err != nil { - t.Fatal(err) - } - 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"))) - } -} - -func TestJSONGenPriv(t *testing.T) { - s := rpc.RPCRequest{ - JSONRPC: "2.0", - Method: "unsafe/gen_priv_account", - Params: []interface{}{}, - Id: 0, - } - b, err := json.Marshal(s) - if err != nil { - t.Fatal(err) - } - buf := bytes.NewBuffer(b) - resp, err := http.Post(requestAddr, "text/json", buf) - 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 response struct { - Result ctypes.ResponseGenPrivAccount `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` - } - binary.ReadJSON(&response, body, &err) - if err != nil { - t.Fatal(err) - } - if len(response.Result.PrivAccount.Address) == 0 { - t.Fatal("Failed to generate an address") - } -} - -func TestJSONGetAccount(t *testing.T) { - byteAddr, _ := hex.DecodeString(userAddr) - acc := getAccount(t, "JSONRPC", byteAddr) - if bytes.Compare(acc.Address, byteAddr) != 0 { - t.Fatalf("Failed to get correct account. Got %x, expected %x", acc.Address, byteAddr) - } - -} - -func TestJSONSignedTx(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, priv := signTx(t, "JSONRPC", byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) - - toAddr = []byte{20, 143, 24, 63, 16, 17, 83, 29, 90, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54} - tx, priv = signTx(t, "JSONRPC", byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) - - toAddr = []byte{0, 0, 4, 0, 0, 4, 0, 0, 4, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54} - tx, priv = signTx(t, "JSONRPC", byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) -} - -func TestJSONBroadcastTx(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, priv := signTx(t, "JSONRPC", byteAddr, toAddr, nil, byteKey, amt, 0, 0) - checkTx(t, byteAddr, priv, tx.(*types.SendTx)) - - n, w := new(int64), new(bytes.Buffer) - var err error - binary.WriteJSON(tx, w, n, &err) - if err != nil { - t.Fatal(err) - } - b := w.Bytes() - - var response struct { - Result ctypes.ResponseBroadcastTx `json:"result"` - Error string `json:"error"` - Id string `json:"id"` - JSONRPC string `json:"jsonrpc"` - } - requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &response) - if response.Error != "" { - t.Fatal(response.Error) - } - receipt := response.Result.Receipt - if receipt.CreatesContract > 0 { - t.Fatal("This tx does not create a contract") - } - if len(receipt.TxHash) == 0 { - t.Fatal("Failed to compute tx hash") - } - pool := node.MempoolReactor().Mempool - txs := pool.GetProposalTxs() - if len(txs) != mempoolCount+1 { - t.Fatalf("The mem pool has %d txs. Expected %d", len(txs), mempoolCount+1) - } - tx2 := txs[mempoolCount].(*types.SendTx) - mempoolCount += 1 - if bytes.Compare(types.TxId(tx), types.TxId(tx2)) != 0 { - t.Fatal(Fmt("inconsistent hashes for mempool tx and sent tx: %v vs %v", tx, tx2)) - } - -} - -/*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/test/tests.go b/rpc/test/tests.go new file mode 100644 index 00000000..8fbd81e3 --- /dev/null +++ b/rpc/test/tests.go @@ -0,0 +1,200 @@ +package rpc + +import ( + "bytes" + "encoding/hex" + "fmt" + . "github.com/tendermint/tendermint/common" + "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/types" + "testing" + "time" +) + +func testStatus(t *testing.T, typ string) { + client := clients[typ] + resp, err := client.Status() + if err != nil { + t.Fatal(err) + } + fmt.Println(">>>", resp) + if resp.Network != config.App().GetString("Network") { + t.Fatal(fmt.Errorf("Network mismatch: got %s expected %s", + resp.Network, config.App().Get("Network"))) + } +} + +func testGenPriv(t *testing.T, typ string) { + client := clients[typ] + resp, err := client.GenPrivAccount() + if err != nil { + t.Fatal(err) + } + fmt.Println(">>>", resp) + if len(resp.PrivAccount.Address) == 0 { + t.Fatal("Failed to generate an address") + } +} + +func testGetAccount(t *testing.T, typ string) { + byteAddr, _ := hex.DecodeString(userAddr) + acc := getAccount(t, typ, byteAddr) + if acc == nil { + t.Fatalf("Account was nil") + } + 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, typ string) { + 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, priv := signTx(t, typ, byteAddr, toAddr, nil, byteKey, amt, 0, 0) + checkTx(t, byteAddr, priv, tx.(*types.SendTx)) + + toAddr = []byte{20, 143, 24, 63, 16, 17, 83, 29, 90, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54} + tx, priv = signTx(t, typ, byteAddr, toAddr, nil, byteKey, amt, 0, 0) + checkTx(t, byteAddr, priv, tx.(*types.SendTx)) + + toAddr = []byte{0, 0, 4, 0, 0, 4, 0, 0, 4, 91, 52, 2, 0, 41, 190, 121, 122, 34, 86, 54} + tx, priv = signTx(t, typ, byteAddr, toAddr, nil, byteKey, amt, 0, 0) + checkTx(t, byteAddr, priv, tx.(*types.SendTx)) +} + +func testBroadcastTx(t *testing.T, typ string) { + 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, receipt := broadcastTx(t, typ, byteAddr, toAddr, nil, byteKey, amt, 0, 0) + if receipt.CreatesContract > 0 { + t.Fatal("This tx does not create a contract") + } + if len(receipt.TxHash) == 0 { + t.Fatal("Failed to compute tx hash") + } + pool := node.MempoolReactor().Mempool + txs := pool.GetProposalTxs() + if len(txs) != mempoolCount { + t.Fatalf("The mem pool has %d txs. Expected %d", len(txs), mempoolCount) + } + tx2 := txs[mempoolCount-1].(*types.SendTx) + n, err := new(int64), new(error) + buf1, buf2 := new(bytes.Buffer), new(bytes.Buffer) + tx.WriteSignBytes(buf1, n, err) + tx2.WriteSignBytes(buf2, n, err) + if bytes.Compare(buf1.Bytes(), buf2.Bytes()) != 0 { + t.Fatal("inconsistent hashes for mempool tx and sent tx") + } +} + +func testGetStorage(t *testing.T, typ string) { + priv := state.LoadPrivValidator(".tendermint/priv_validator.json") + _ = priv + //core.SetPrivValidator(priv) + + byteAddr, _ := hex.DecodeString(userAddr) + var byteKey [64]byte + oh, _ := hex.DecodeString(userPriv) + copy(byteKey[:], oh) + + amt := uint64(1100) + code := []byte{0x60, 0x5, 0x60, 0x1, 0x55} + _, receipt := broadcastTx(t, typ, byteAddr, nil, code, byteKey, amt, 1000, 1000) + if receipt.CreatesContract == 0 { + t.Fatal("This tx creates a contract") + } + if len(receipt.TxHash) == 0 { + t.Fatal("Failed to compute tx hash") + } + contractAddr := receipt.ContractAddr + if len(contractAddr) == 0 { + t.Fatal("Creates contract but resulting address is empty") + } + + // allow it to get mined + time.Sleep(time.Second * 20) + mempoolCount = 0 + + v := getStorage(t, typ, contractAddr, []byte{0x1}) + got := RightPadWord256(v) + expected := RightPadWord256([]byte{0x5}) + if got.Compare(expected) != 0 { + t.Fatalf("Wrong storage value. Got %x, expected %x", got.Bytes(), expected.Bytes()) + } +} + +func testCallCode(t *testing.T, typ string) { + client := clients[typ] + + // add two integers and return the result + code := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} + data := []byte{} + expected := []byte{0xb} + callCode(t, client, code, data, expected) + + // pass two ints as calldata, add, and return the result + code = []byte{0x60, 0x0, 0x35, 0x60, 0x20, 0x35, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} + data = append(LeftPadWord256([]byte{0x5}).Bytes(), LeftPadWord256([]byte{0x6}).Bytes()...) + expected = []byte{0xb} + callCode(t, client, code, data, expected) +} + +func testCall(t *testing.T, typ string) { + client := clients[typ] + + priv := state.LoadPrivValidator(".tendermint/priv_validator.json") + _ = priv + //core.SetPrivValidator(priv) + + byteAddr, _ := hex.DecodeString(userAddr) + var byteKey [64]byte + oh, _ := hex.DecodeString(userPriv) + copy(byteKey[:], oh) + + // create the contract + amt := uint64(6969) + // this is the code we want to run when the contract is called + contractCode := []byte{0x60, 0x5, 0x60, 0x6, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} + // the is the code we need to return the contractCode when the contract is initialized + lenCode := len(contractCode) + // push code to the stack + //code := append([]byte{byte(0x60 + lenCode - 1)}, LeftPadWord256(contractCode).Bytes()...) + code := append([]byte{0x7f}, RightPadWord256(contractCode).Bytes()...) + // store it in memory + code = append(code, []byte{0x60, 0x0, 0x52}...) + // return whats in memory + //code = append(code, []byte{0x60, byte(32 - lenCode), 0x60, byte(lenCode), 0xf3}...) + code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...) + + _, receipt := broadcastTx(t, typ, byteAddr, nil, code, byteKey, amt, 1000, 1000) + if receipt.CreatesContract == 0 { + t.Fatal("This tx creates a contract") + } + if len(receipt.TxHash) == 0 { + t.Fatal("Failed to compute tx hash") + } + contractAddr := receipt.ContractAddr + if len(contractAddr) == 0 { + t.Fatal("Creates contract but resulting address is empty") + } + + // allow it to get mined + time.Sleep(time.Second * 20) + mempoolCount = 0 + + // run a call through the contract + data := []byte{} + expected := []byte{0xb} + callContract(t, client, contractAddr, data, expected) +} diff --git a/vm/stack.go b/vm/stack.go index 6b74643a..41d50c70 100644 --- a/vm/stack.go +++ b/vm/stack.go @@ -32,7 +32,7 @@ func (st *Stack) useGas(gasToUse uint64) { } func (st *Stack) setErr(err error) { - if *st.err != nil { + if *st.err == nil { *st.err = err } }