mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-19 14:34:17 +00:00
Merge pull request #521 from tendermint/json-rpc-patch
updated json response to match spec by @davebryson
This commit is contained in:
+28
-27
@@ -10,34 +10,35 @@ BREAKING CHANGES:
|
|||||||
|
|
||||||
FEATURES:
|
FEATURES:
|
||||||
- Peer reputation management
|
- Peer reputation management
|
||||||
- Use the chain as its own CA for nodes and validators
|
- Use the chain as its own CA for nodes and validators
|
||||||
- Tooling to run multiple blockchains/apps, possibly in a single process
|
- Tooling to run multiple blockchains/apps, possibly in a single process
|
||||||
- State syncing (without transaction replay)
|
- State syncing (without transaction replay)
|
||||||
- Improved support for querying history and state
|
- Improved support for querying history and state
|
||||||
- Add authentication and rate-limitting to the RPC
|
- Add authentication and rate-limitting to the RPC
|
||||||
|
|
||||||
IMPROVEMENTS:
|
IMPROVEMENTS:
|
||||||
- Improve subtleties around mempool caching and logic
|
- Improve subtleties around mempool caching and logic
|
||||||
- Consensus optimizations:
|
- Consensus optimizations:
|
||||||
- cache block parts for faster agreement after round changes
|
- cache block parts for faster agreement after round changes
|
||||||
- propagate block parts rarest first
|
- propagate block parts rarest first
|
||||||
- Better testing of the consensus state machine (ie. use a DSL)
|
- Better testing of the consensus state machine (ie. use a DSL)
|
||||||
- Auto compiled serialization/deserialization code instead of go-wire reflection
|
- Auto compiled serialization/deserialization code instead of go-wire reflection
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
- Graceful handling/recovery for apps that have non-determinism or fail to halt
|
- Graceful handling/recovery for apps that have non-determinism or fail to halt
|
||||||
- Graceful handling/recovery for violations of safety, or liveness
|
- Graceful handling/recovery for violations of safety, or liveness
|
||||||
|
|
||||||
## 0.11.0 (Date)
|
## 0.11.0 (Date)
|
||||||
|
|
||||||
IMPROVEMENTS:
|
IMPROVEMENTS:
|
||||||
- docs: Added documentation from the tools repo to Read The Docs pipeline
|
- docs: Added documentation from the tools repo to Read The Docs pipeline
|
||||||
|
- rpc: updated json response to match http://www.jsonrpc.org/specification spec
|
||||||
|
|
||||||
## 0.10.4 (September 5, 2017)
|
## 0.10.4 (September 5, 2017)
|
||||||
|
|
||||||
IMPROVEMENTS:
|
IMPROVEMENTS:
|
||||||
- docs: Added Slate docs to each rpc function (see rpc/core)
|
- docs: Added Slate docs to each rpc function (see rpc/core)
|
||||||
- docs: Ported all website docs to Read The Docs
|
- docs: Ported all website docs to Read The Docs
|
||||||
- config: expose some p2p params to tweak performance: RecvRate, SendRate, and MaxMsgPacketPayloadSize
|
- config: expose some p2p params to tweak performance: RecvRate, SendRate, and MaxMsgPacketPayloadSize
|
||||||
- rpc: Upgrade the websocket client and server, including improved auto reconnect, and proper ping/pong
|
- rpc: Upgrade the websocket client and server, including improved auto reconnect, and proper ping/pong
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@ IMPROVEMENTS:
|
|||||||
|
|
||||||
FEATURES:
|
FEATURES:
|
||||||
- Use `--trace` to get stack traces for logged errors
|
- Use `--trace` to get stack traces for logged errors
|
||||||
- types: GenesisDoc.ValidatorHash returns the hash of the genesis validator set
|
- types: GenesisDoc.ValidatorHash returns the hash of the genesis validator set
|
||||||
- types: GenesisDocFromFile parses a GenesiDoc from a JSON file
|
- types: GenesisDocFromFile parses a GenesiDoc from a JSON file
|
||||||
|
|
||||||
IMPROVEMENTS:
|
IMPROVEMENTS:
|
||||||
@@ -101,7 +102,7 @@ Also includes the Grand Repo-Merge of 2017.
|
|||||||
BREAKING CHANGES:
|
BREAKING CHANGES:
|
||||||
|
|
||||||
- Config and Flags:
|
- Config and Flags:
|
||||||
- The `config` map is replaced with a [`Config` struct](https://github.com/tendermint/tendermint/blob/master/config/config.go#L11),
|
- The `config` map is replaced with a [`Config` struct](https://github.com/tendermint/tendermint/blob/master/config/config.go#L11),
|
||||||
containing substructs: `BaseConfig`, `P2PConfig`, `MempoolConfig`, `ConsensusConfig`, `RPCConfig`
|
containing substructs: `BaseConfig`, `P2PConfig`, `MempoolConfig`, `ConsensusConfig`, `RPCConfig`
|
||||||
- This affects the following flags:
|
- This affects the following flags:
|
||||||
- `--seeds` is now `--p2p.seeds`
|
- `--seeds` is now `--p2p.seeds`
|
||||||
@@ -114,16 +115,16 @@ containing substructs: `BaseConfig`, `P2PConfig`, `MempoolConfig`, `ConsensusCon
|
|||||||
```
|
```
|
||||||
[p2p]
|
[p2p]
|
||||||
laddr="tcp://1.2.3.4:46656"
|
laddr="tcp://1.2.3.4:46656"
|
||||||
|
|
||||||
[consensus]
|
[consensus]
|
||||||
timeout_propose=1000
|
timeout_propose=1000
|
||||||
```
|
```
|
||||||
- Use viper and `DefaultConfig() / TestConfig()` functions to handle defaults, and remove `config/tendermint` and `config/tendermint_test`
|
- Use viper and `DefaultConfig() / TestConfig()` functions to handle defaults, and remove `config/tendermint` and `config/tendermint_test`
|
||||||
- Change some function and method signatures to
|
- Change some function and method signatures to
|
||||||
- Change some [function and method signatures](https://gist.github.com/ebuchman/640d5fc6c2605f73497992fe107ebe0b) accomodate new config
|
- Change some [function and method signatures](https://gist.github.com/ebuchman/640d5fc6c2605f73497992fe107ebe0b) accomodate new config
|
||||||
|
|
||||||
- Logger
|
- Logger
|
||||||
- Replace static `log15` logger with a simple interface, and provide a new implementation using `go-kit`.
|
- Replace static `log15` logger with a simple interface, and provide a new implementation using `go-kit`.
|
||||||
See our new [logging library](https://github.com/tendermint/tmlibs/log) and [blog post](https://tendermint.com/blog/abstracting-the-logger-interface-in-go) for more details
|
See our new [logging library](https://github.com/tendermint/tmlibs/log) and [blog post](https://tendermint.com/blog/abstracting-the-logger-interface-in-go) for more details
|
||||||
- Levels `warn` and `notice` are removed (you may need to change them in your `config.toml`!)
|
- Levels `warn` and `notice` are removed (you may need to change them in your `config.toml`!)
|
||||||
- Change some [function and method signatures](https://gist.github.com/ebuchman/640d5fc6c2605f73497992fe107ebe0b) to accept a logger
|
- Change some [function and method signatures](https://gist.github.com/ebuchman/640d5fc6c2605f73497992fe107ebe0b) to accept a logger
|
||||||
@@ -166,7 +167,7 @@ IMPROVEMENTS:
|
|||||||
- Limit `/blockchain_info` call to return a maximum of 20 blocks
|
- Limit `/blockchain_info` call to return a maximum of 20 blocks
|
||||||
- Use `.Wrap()` and `.Unwrap()` instead of eg. `PubKeyS` for `go-crypto` types
|
- Use `.Wrap()` and `.Unwrap()` instead of eg. `PubKeyS` for `go-crypto` types
|
||||||
- RPC JSON responses use pretty printing (via `json.MarshalIndent`)
|
- RPC JSON responses use pretty printing (via `json.MarshalIndent`)
|
||||||
- Color code different instances of the consensus for tests
|
- Color code different instances of the consensus for tests
|
||||||
- Isolate viper to `cmd/tendermint/commands` and do not read config from file for tests
|
- Isolate viper to `cmd/tendermint/commands` and do not read config from file for tests
|
||||||
|
|
||||||
|
|
||||||
@@ -194,7 +195,7 @@ IMPROVEMENTS:
|
|||||||
- WAL uses #ENDHEIGHT instead of #HEIGHT (#HEIGHT will stop working in 0.10.0)
|
- WAL uses #ENDHEIGHT instead of #HEIGHT (#HEIGHT will stop working in 0.10.0)
|
||||||
- Peers included via `--seeds`, under `seeds` in the config, or in `/dial_seeds` are now persistent, and will be reconnected to if the connection breaks
|
- Peers included via `--seeds`, under `seeds` in the config, or in `/dial_seeds` are now persistent, and will be reconnected to if the connection breaks
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
|
|
||||||
- Fix bug in fast-sync where we stop syncing after a peer is removed, even if they're re-added later
|
- Fix bug in fast-sync where we stop syncing after a peer is removed, even if they're re-added later
|
||||||
- Fix handshake replay to handle validator set changes and results of DeliverTx when we crash after app.Commit but before state.Save()
|
- Fix handshake replay to handle validator set changes and results of DeliverTx when we crash after app.Commit but before state.Save()
|
||||||
@@ -210,7 +211,7 @@ message RequestQuery{
|
|||||||
bytes data = 1;
|
bytes data = 1;
|
||||||
string path = 2;
|
string path = 2;
|
||||||
uint64 height = 3;
|
uint64 height = 3;
|
||||||
bool prove = 4;
|
bool prove = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ResponseQuery{
|
message ResponseQuery{
|
||||||
@@ -234,7 +235,7 @@ type BlockMeta struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `ValidatorSet.Proposer` is exposed as a field and persisted with the `State`. Use `GetProposer()` to initialize or update after validator-set changes.
|
- `ValidatorSet.Proposer` is exposed as a field and persisted with the `State`. Use `GetProposer()` to initialize or update after validator-set changes.
|
||||||
|
|
||||||
- `tendermint gen_validator` command output is now pure JSON
|
- `tendermint gen_validator` command output is now pure JSON
|
||||||
|
|
||||||
@@ -277,7 +278,7 @@ type BlockID struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `Vote` data type now includes validator address and index:
|
- `Vote` data type now includes validator address and index:
|
||||||
|
|
||||||
```
|
```
|
||||||
type Vote struct {
|
type Vote struct {
|
||||||
@@ -297,7 +298,7 @@ type Vote struct {
|
|||||||
|
|
||||||
FEATURES:
|
FEATURES:
|
||||||
|
|
||||||
- New message type on the ConsensusReactor, `Maj23Msg`, for peers to alert others they've seen a Maj23,
|
- New message type on the ConsensusReactor, `Maj23Msg`, for peers to alert others they've seen a Maj23,
|
||||||
in order to track and handle conflicting votes intelligently to prevent Byzantine faults from causing halts:
|
in order to track and handle conflicting votes intelligently to prevent Byzantine faults from causing halts:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -320,7 +321,7 @@ IMPROVEMENTS:
|
|||||||
- Less verbose logging
|
- Less verbose logging
|
||||||
- Better test coverage (37% -> 49%)
|
- Better test coverage (37% -> 49%)
|
||||||
- Canonical SignBytes for signable types
|
- Canonical SignBytes for signable types
|
||||||
- Write-Ahead Log for Mempool and Consensus via tmlibs/autofile
|
- Write-Ahead Log for Mempool and Consensus via tmlibs/autofile
|
||||||
- Better in-process testing for the consensus reactor and byzantine faults
|
- Better in-process testing for the consensus reactor and byzantine faults
|
||||||
- Better crash/restart testing for individual nodes at preset failure points, and of networks at arbitrary points
|
- Better crash/restart testing for individual nodes at preset failure points, and of networks at arbitrary points
|
||||||
- Better abstraction over timeout mechanics
|
- Better abstraction over timeout mechanics
|
||||||
@@ -400,7 +401,7 @@ FEATURES:
|
|||||||
- TMSP and RPC support TCP and UNIX sockets
|
- TMSP and RPC support TCP and UNIX sockets
|
||||||
- Addition config options including block size and consensus parameters
|
- Addition config options including block size and consensus parameters
|
||||||
- New WAL mode `cswal_light`; logs only the validator's own votes
|
- New WAL mode `cswal_light`; logs only the validator's own votes
|
||||||
- New RPC endpoints:
|
- New RPC endpoints:
|
||||||
- for starting/stopping profilers, and for updating config
|
- for starting/stopping profilers, and for updating config
|
||||||
- `/broadcast_tx_commit`, returns when tx is included in a block, else an error
|
- `/broadcast_tx_commit`, returns when tx is included in a block, else an error
|
||||||
- `/unsafe_flush_mempool`, empties the mempool
|
- `/unsafe_flush_mempool`, empties the mempool
|
||||||
@@ -421,14 +422,14 @@ BUG FIXES:
|
|||||||
|
|
||||||
Strict versioning only began with the release of v0.7.0, in late summer 2016.
|
Strict versioning only began with the release of v0.7.0, in late summer 2016.
|
||||||
The project itself began in early summer 2014 and was workable decentralized cryptocurrency software by the end of that year.
|
The project itself began in early summer 2014 and was workable decentralized cryptocurrency software by the end of that year.
|
||||||
Through the course of 2015, in collaboration with Eris Industries (now Monax Indsutries),
|
Through the course of 2015, in collaboration with Eris Industries (now Monax Indsutries),
|
||||||
many additional features were integrated, including an implementation from scratch of the Ethereum Virtual Machine.
|
many additional features were integrated, including an implementation from scratch of the Ethereum Virtual Machine.
|
||||||
That implementation now forms the heart of [Burrow](https://github.com/hyperledger/burrow).
|
That implementation now forms the heart of [Burrow](https://github.com/hyperledger/burrow).
|
||||||
In the later half of 2015, the consensus algorithm was upgraded with a more asynchronous design and a more deterministic and robust implementation.
|
In the later half of 2015, the consensus algorithm was upgraded with a more asynchronous design and a more deterministic and robust implementation.
|
||||||
|
|
||||||
By late 2015, frustration with the difficulty of forking a large monolithic stack to create alternative cryptocurrency designs led to the
|
By late 2015, frustration with the difficulty of forking a large monolithic stack to create alternative cryptocurrency designs led to the
|
||||||
invention of the Application Blockchain Interface (ABCI), then called the Tendermint Socket Protocol (TMSP).
|
invention of the Application Blockchain Interface (ABCI), then called the Tendermint Socket Protocol (TMSP).
|
||||||
The Ethereum Virtual Machine and various other transaction features were removed, and Tendermint was whittled down to a core consensus engine
|
The Ethereum Virtual Machine and various other transaction features were removed, and Tendermint was whittled down to a core consensus engine
|
||||||
driving an application running in another process.
|
driving an application running in another process.
|
||||||
The ABCI interface and implementation were iterated on and improved over the course of 2016,
|
The ABCI interface and implementation were iterated on and improved over the course of 2016,
|
||||||
until versioned history kicked in with v0.7.0.
|
until versioned history kicked in with v0.7.0.
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
"github.com/tendermint/tendermint/rpc/lib/types"
|
rpctypes "github.com/tendermint/tendermint/rpc/lib/types"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, event string) (*ctypes.ResultSubscri
|
|||||||
// NOTE: EventSwitch callbacks must be nonblocking
|
// NOTE: EventSwitch callbacks must be nonblocking
|
||||||
// NOTE: RPCResponses of subscribed events have id suffix "#event"
|
// NOTE: RPCResponses of subscribed events have id suffix "#event"
|
||||||
tmResult := &ctypes.ResultEvent{event, msg}
|
tmResult := &ctypes.ResultEvent{event, msg}
|
||||||
wsCtx.TryWriteRPCResponse(rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", tmResult, ""))
|
wsCtx.TryWriteRPCResponse(rpctypes.NewRPCSuccessResponse(wsCtx.Request.ID+"#event", tmResult))
|
||||||
})
|
})
|
||||||
return &ctypes.ResultSubscribe{}, nil
|
return &ctypes.ResultSubscribe{}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ func NewJSONRPCClient(remote string) *JSONRPCClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *JSONRPCClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
|
func (c *JSONRPCClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
|
||||||
request, err := types.MapToRequest("", method, params)
|
request, err := types.MapToRequest("jsonrpc-client", method, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -146,9 +146,8 @@ func unmarshalResponseBytes(responseBytes []byte, result interface{}) (interface
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Errorf("Error unmarshalling rpc response: %v", err)
|
return nil, errors.Errorf("Error unmarshalling rpc response: %v", err)
|
||||||
}
|
}
|
||||||
errorStr := response.Error
|
if response.Error != nil {
|
||||||
if errorStr != "" {
|
return nil, errors.Errorf("Response error: %v", response.Error.Message)
|
||||||
return nil, errors.Errorf("Response error: %v", errorStr)
|
|
||||||
}
|
}
|
||||||
// unmarshal the RawMessage into the result
|
// unmarshal the RawMessage into the result
|
||||||
err = json.Unmarshal(*response.Result, result)
|
err = json.Unmarshal(*response.Result, result)
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ func (c *WSClient) Send(ctx context.Context, request types.RPCRequest) error {
|
|||||||
|
|
||||||
// Call the given method. See Send description.
|
// Call the given method. See Send description.
|
||||||
func (c *WSClient) Call(ctx context.Context, method string, params map[string]interface{}) error {
|
func (c *WSClient) Call(ctx context.Context, method string, params map[string]interface{}) error {
|
||||||
request, err := types.MapToRequest("", method, params)
|
request, err := types.MapToRequest("ws-client", method, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ func (c *WSClient) Call(ctx context.Context, method string, params map[string]in
|
|||||||
// CallWithArrayParams the given method with params in a form of array. See
|
// CallWithArrayParams the given method with params in a form of array. See
|
||||||
// Send description.
|
// Send description.
|
||||||
func (c *WSClient) CallWithArrayParams(ctx context.Context, method string, params []interface{}) error {
|
func (c *WSClient) CallWithArrayParams(ctx context.Context, method string, params []interface{}) error {
|
||||||
request, err := types.ArrayToRequest("", method, params)
|
request, err := types.ArrayToRequest("ws-client", method, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -422,8 +422,8 @@ func (c *WSClient) readRoutine() {
|
|||||||
c.ErrorsCh <- err
|
c.ErrorsCh <- err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if response.Error != "" {
|
if response.Error != nil {
|
||||||
c.ErrorsCh <- errors.Errorf(response.Error)
|
c.ErrorsCh <- errors.New(response.Error.Message)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Logger.Info("got response", "resp", response.Result)
|
c.Logger.Info("got response", "resp", response.Result)
|
||||||
|
|||||||
+27
-21
@@ -110,35 +110,36 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
|
|||||||
var request types.RPCRequest
|
var request types.RPCRequest
|
||||||
err := json.Unmarshal(b, &request)
|
err := json.Unmarshal(b, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse("", nil, fmt.Sprintf("Error unmarshalling request: %v", err.Error())))
|
WriteRPCResponseHTTP(w, types.RPCParseError("", errors.Wrap(err, "Error unmarshalling request")))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// A Notification is a Request object without an "id" member.
|
||||||
|
// The Server MUST NOT reply to a Notification, including those that are within a batch request.
|
||||||
|
if request.ID == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(r.URL.Path) > 1 {
|
if len(r.URL.Path) > 1 {
|
||||||
WriteRPCResponseHTTPError(w, http.StatusNotFound, types.NewRPCResponse(request.ID, nil, fmt.Sprintf("Invalid JSONRPC endpoint %s", r.URL.Path)))
|
WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(request.ID, errors.Errorf("Path %s is invalid", r.URL.Path)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rpcFunc := funcMap[request.Method]
|
rpcFunc := funcMap[request.Method]
|
||||||
if rpcFunc == nil {
|
if rpcFunc == nil || rpcFunc.ws {
|
||||||
WriteRPCResponseHTTPError(w, http.StatusNotFound, types.NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
|
WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(request.ID))
|
||||||
return
|
|
||||||
}
|
|
||||||
if rpcFunc.ws {
|
|
||||||
WriteRPCResponseHTTPError(w, http.StatusMethodNotAllowed, types.NewRPCResponse(request.ID, nil, "RPC method is only for websockets: "+request.Method))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
args, err := jsonParamsToArgsRPC(rpcFunc, request.Params)
|
args, err := jsonParamsToArgsRPC(rpcFunc, request.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse(request.ID, nil, fmt.Sprintf("Error converting json params to arguments: %v", err.Error())))
|
WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments")))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
returns := rpcFunc.f.Call(args)
|
returns := rpcFunc.f.Call(args)
|
||||||
logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns)
|
logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns)
|
||||||
result, err := unreflectResult(returns)
|
result, err := unreflectResult(returns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteRPCResponseHTTPError(w, http.StatusInternalServerError, types.NewRPCResponse(request.ID, result, err.Error()))
|
WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, result, ""))
|
WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(request.ID, result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +230,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
|
|||||||
// Exception for websocket endpoints
|
// Exception for websocket endpoints
|
||||||
if rpcFunc.ws {
|
if rpcFunc.ws {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
WriteRPCResponseHTTPError(w, http.StatusMethodNotAllowed, types.NewRPCResponse("", nil, "This RPC method is only for websockets"))
|
WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// All other endpoints
|
// All other endpoints
|
||||||
@@ -237,17 +238,17 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
|
|||||||
logger.Debug("HTTP HANDLER", "req", r)
|
logger.Debug("HTTP HANDLER", "req", r)
|
||||||
args, err := httpParamsToArgs(rpcFunc, r)
|
args, err := httpParamsToArgs(rpcFunc, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse("", nil, fmt.Sprintf("Error converting http params to args: %v", err.Error())))
|
WriteRPCResponseHTTP(w, types.RPCInvalidParamsError("", errors.Wrap(err, "Error converting http params to arguments")))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
returns := rpcFunc.f.Call(args)
|
returns := rpcFunc.f.Call(args)
|
||||||
logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
|
logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
|
||||||
result, err := unreflectResult(returns)
|
result, err := unreflectResult(returns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteRPCResponseHTTPError(w, http.StatusInternalServerError, types.NewRPCResponse("", nil, err.Error()))
|
WriteRPCResponseHTTP(w, types.RPCInternalError("", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
WriteRPCResponseHTTP(w, types.NewRPCResponse("", result, ""))
|
WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse("", result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,8 +510,13 @@ func (wsc *wsConnection) readRoutine() {
|
|||||||
var request types.RPCRequest
|
var request types.RPCRequest
|
||||||
err = json.Unmarshal(in, &request)
|
err = json.Unmarshal(in, &request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error())
|
wsc.WriteRPCResponse(types.RPCParseError("", errors.Wrap(err, "Error unmarshaling request")))
|
||||||
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, errStr))
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Notification is a Request object without an "id" member.
|
||||||
|
// The Server MUST NOT reply to a Notification, including those that are within a batch request.
|
||||||
|
if request.ID == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -518,7 +524,7 @@ func (wsc *wsConnection) readRoutine() {
|
|||||||
|
|
||||||
rpcFunc := wsc.funcMap[request.Method]
|
rpcFunc := wsc.funcMap[request.Method]
|
||||||
if rpcFunc == nil {
|
if rpcFunc == nil {
|
||||||
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
|
wsc.WriteRPCResponse(types.RPCMethodNotFoundError(request.ID))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var args []reflect.Value
|
var args []reflect.Value
|
||||||
@@ -529,7 +535,7 @@ func (wsc *wsConnection) readRoutine() {
|
|||||||
args, err = jsonParamsToArgsRPC(rpcFunc, request.Params)
|
args, err = jsonParamsToArgsRPC(rpcFunc, request.Params)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error()))
|
wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "Error converting json params to arguments")))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
returns := rpcFunc.f.Call(args)
|
returns := rpcFunc.f.Call(args)
|
||||||
@@ -539,10 +545,10 @@ func (wsc *wsConnection) readRoutine() {
|
|||||||
|
|
||||||
result, err := unreflectResult(returns)
|
result, err := unreflectResult(returns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error()))
|
wsc.WriteRPCResponse(types.RPCInternalError(request.ID, err))
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, result, ""))
|
wsc.WriteRPCResponse(types.NewRPCSuccessResponse(request.ID, result))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
types "github.com/tendermint/tendermint/rpc/lib/types"
|
types "github.com/tendermint/tendermint/rpc/lib/types"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
@@ -99,7 +100,7 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler
|
|||||||
// For the rest,
|
// For the rest,
|
||||||
logger.Error("Panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack()))
|
logger.Error("Panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack()))
|
||||||
rww.WriteHeader(http.StatusInternalServerError)
|
rww.WriteHeader(http.StatusInternalServerError)
|
||||||
WriteRPCResponseHTTP(rww, types.NewRPCResponse("", nil, fmt.Sprintf("Internal Server Error: %v", e)))
|
WriteRPCResponseHTTP(rww, types.RPCInternalError("", e.(error)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+53
-13
@@ -5,9 +5,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
events "github.com/tendermint/tmlibs/events"
|
events "github.com/tendermint/tmlibs/events"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
// REQUEST
|
||||||
|
|
||||||
type RPCRequest struct {
|
type RPCRequest struct {
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
@@ -47,42 +52,77 @@ func ArrayToRequest(id string, method string, params []interface{}) (RPCRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
// RESPONSE
|
||||||
|
|
||||||
|
type RPCError struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data string `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type RPCResponse struct {
|
type RPCResponse struct {
|
||||||
JSONRPC string `json:"jsonrpc"`
|
JSONRPC string `json:"jsonrpc"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Result *json.RawMessage `json:"result"`
|
Result *json.RawMessage `json:"result,omitempty"`
|
||||||
Error string `json:"error"`
|
Error *RPCError `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRPCResponse(id string, res interface{}, err string) RPCResponse {
|
func NewRPCSuccessResponse(id string, res interface{}) RPCResponse {
|
||||||
var raw *json.RawMessage
|
var raw *json.RawMessage
|
||||||
|
|
||||||
if res != nil {
|
if res != nil {
|
||||||
var js []byte
|
var js []byte
|
||||||
js, err2 := json.Marshal(res)
|
js, err := json.Marshal(res)
|
||||||
if err2 == nil {
|
if err != nil {
|
||||||
rawMsg := json.RawMessage(js)
|
return RPCInternalError(id, errors.Wrap(err, "Error marshalling response"))
|
||||||
raw = &rawMsg
|
|
||||||
} else {
|
|
||||||
err = err2.Error()
|
|
||||||
}
|
}
|
||||||
|
rawMsg := json.RawMessage(js)
|
||||||
|
raw = &rawMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return RPCResponse{JSONRPC: "2.0", ID: id, Result: raw}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRPCErrorResponse(id string, code int, msg string, data string) RPCResponse {
|
||||||
return RPCResponse{
|
return RPCResponse{
|
||||||
JSONRPC: "2.0",
|
JSONRPC: "2.0",
|
||||||
ID: id,
|
ID: id,
|
||||||
Result: raw,
|
Error: &RPCError{Code: code, Message: msg, Data: data},
|
||||||
Error: err,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (resp RPCResponse) String() string {
|
func (resp RPCResponse) String() string {
|
||||||
if resp.Error == "" {
|
if resp.Error == nil {
|
||||||
return fmt.Sprintf("[%s %v]", resp.ID, resp.Result)
|
return fmt.Sprintf("[%s %v]", resp.ID, resp.Result)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("[%s %s]", resp.ID, resp.Error)
|
return fmt.Sprintf("[%s %s]", resp.ID, resp.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RPCParseError(id string, err error) RPCResponse {
|
||||||
|
return NewRPCErrorResponse(id, -32700, "Parse error. Invalid JSON", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func RPCInvalidRequestError(id string, err error) RPCResponse {
|
||||||
|
return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func RPCMethodNotFoundError(id string) RPCResponse {
|
||||||
|
return NewRPCErrorResponse(id, -32601, "Method not found", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func RPCInvalidParamsError(id string, err error) RPCResponse {
|
||||||
|
return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func RPCInternalError(id string, err error) RPCResponse {
|
||||||
|
return NewRPCErrorResponse(id, -32603, "Internal error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func RPCServerError(id string, err error) RPCResponse {
|
||||||
|
return NewRPCErrorResponse(id, -32000, "Server error", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
||||||
// *wsConnection implements this interface.
|
// *wsConnection implements this interface.
|
||||||
@@ -100,7 +140,7 @@ type WSRPCContext struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
// sockets
|
// SOCKETS
|
||||||
//
|
//
|
||||||
// Determine if its a unix or tcp socket.
|
// Determine if its a unix or tcp socket.
|
||||||
// If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port
|
// If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package rpctypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SampleResult struct {
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponses(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
a := NewRPCSuccessResponse("1", &SampleResult{"hello"})
|
||||||
|
b, _ := json.Marshal(a)
|
||||||
|
s := `{"jsonrpc":"2.0","id":"1","result":{"Value":"hello"}}`
|
||||||
|
assert.Equal(string(s), string(b))
|
||||||
|
|
||||||
|
d := RPCParseError("1", errors.New("Hello world"))
|
||||||
|
e, _ := json.Marshal(d)
|
||||||
|
f := `{"jsonrpc":"2.0","id":"1","error":{"code":-32700,"message":"Parse error. Invalid JSON","data":"Hello world"}}`
|
||||||
|
assert.Equal(string(f), string(e))
|
||||||
|
|
||||||
|
g := RPCMethodNotFoundError("2")
|
||||||
|
h, _ := json.Marshal(g)
|
||||||
|
i := `{"jsonrpc":"2.0","id":"2","error":{"code":-32601,"message":"Method not found"}}`
|
||||||
|
assert.Equal(string(h), string(i))
|
||||||
|
}
|
||||||
+16
-13
@@ -23,39 +23,42 @@ function getCode() {
|
|||||||
else
|
else
|
||||||
# this wont actually work if theres an error ...
|
# this wont actually work if theres an error ...
|
||||||
echo "$R" | jq .code
|
echo "$R" | jq .code
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendTx() {
|
function sendTx() {
|
||||||
TX=$1
|
TX=$1
|
||||||
if [[ "$GRPC_BROADCAST_TX" == "" ]]; then
|
if [[ "$GRPC_BROADCAST_TX" == "" ]]; then
|
||||||
RESPONSE=`curl -s localhost:46657/broadcast_tx_commit?tx=0x$TX`
|
RESPONSE=$(curl -s localhost:46657/broadcast_tx_commit?tx=0x"$TX")
|
||||||
ERROR=`echo $RESPONSE | jq .error`
|
IS_ERR=$(echo "$RESPONSE" | jq 'has("error")')
|
||||||
|
ERROR=$(echo "$RESPONSE" | jq '.error')
|
||||||
ERROR=$(echo "$ERROR" | tr -d '"') # remove surrounding quotes
|
ERROR=$(echo "$ERROR" | tr -d '"') # remove surrounding quotes
|
||||||
|
|
||||||
RESPONSE=`echo $RESPONSE | jq .result`
|
RESPONSE=$(echo "$RESPONSE" | jq '.result')
|
||||||
else
|
else
|
||||||
if [ -f grpc_client ]; then
|
if [ -f grpc_client ]; then
|
||||||
rm grpc_client
|
rm grpc_client
|
||||||
fi
|
fi
|
||||||
echo "... building grpc_client"
|
echo "... building grpc_client"
|
||||||
go build -o grpc_client grpc_client.go
|
go build -o grpc_client grpc_client.go
|
||||||
RESPONSE=`./grpc_client $TX`
|
RESPONSE=$(./grpc_client "$TX")
|
||||||
|
IS_ERR=false
|
||||||
ERROR=""
|
ERROR=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "RESPONSE"
|
echo "RESPONSE"
|
||||||
echo $RESPONSE
|
echo "$RESPONSE"
|
||||||
|
|
||||||
echo $RESPONSE | jq . &> /dev/null
|
echo "$RESPONSE" | jq . &> /dev/null
|
||||||
IS_JSON=$?
|
IS_JSON=$?
|
||||||
if [[ "$IS_JSON" != "0" ]]; then
|
if [[ "$IS_JSON" != "0" ]]; then
|
||||||
|
IS_ERR=true
|
||||||
ERROR="$RESPONSE"
|
ERROR="$RESPONSE"
|
||||||
fi
|
fi
|
||||||
APPEND_TX_RESPONSE=`echo $RESPONSE | jq .deliver_tx`
|
APPEND_TX_RESPONSE=$(echo "$RESPONSE" | jq '.deliver_tx')
|
||||||
APPEND_TX_CODE=`getCode "$APPEND_TX_RESPONSE"`
|
APPEND_TX_CODE=$(getCode "$APPEND_TX_RESPONSE")
|
||||||
CHECK_TX_RESPONSE=`echo $RESPONSE | jq .check_tx`
|
CHECK_TX_RESPONSE=$(echo "$RESPONSE" | jq '.check_tx')
|
||||||
CHECK_TX_CODE=`getCode "$CHECK_TX_RESPONSE"`
|
CHECK_TX_CODE=$(getCode "$CHECK_TX_RESPONSE")
|
||||||
|
|
||||||
echo "-------"
|
echo "-------"
|
||||||
echo "TX $TX"
|
echo "TX $TX"
|
||||||
@@ -63,7 +66,7 @@ function sendTx() {
|
|||||||
echo "ERROR $ERROR"
|
echo "ERROR $ERROR"
|
||||||
echo "----"
|
echo "----"
|
||||||
|
|
||||||
if [[ "$ERROR" != "" ]]; then
|
if $IS_ERR; then
|
||||||
echo "Unexpected error sending tx ($TX): $ERROR"
|
echo "Unexpected error sending tx ($TX): $ERROR"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user