diff --git a/rpc/core/README.md b/rpc/core/README.md new file mode 100644 index 000000000..1ed2f8494 --- /dev/null +++ b/rpc/core/README.md @@ -0,0 +1,15 @@ +# Tendermint RPC + +## Generate markdown for [Slate](https://github.com/tendermint/slate) + +We are using [Slate](https://github.com/tendermint/slate) to power our RPC +documentation. If you are changing a comment, make sure to copy the resulting +changes to the slate repo and make a PR +[there](https://github.com/tendermint/slate) as well. For generating markdown +use: + +```shell +go get github.com/melekes/godoc2md + +godoc2md -template rpc/core/doc_template.txt github.com/tendermint/tendermint/rpc/core | grep -v -e "pipe.go" -e "routes.go" -e "dev.go" | sed 's$/src/target$https://github.com/tendermint/tendermint/tree/master/rpc/core$' +``` diff --git a/rpc/core/abci.go b/rpc/core/abci.go index 0cb29f479..21508caf7 100644 --- a/rpc/core/abci.go +++ b/rpc/core/abci.go @@ -6,8 +6,45 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" ) -//----------------------------------------------------------------------------- - +// Query the application for some information. +// +// ```shell +// curl 'localhost:46657/abci_query?path=""&data="abcd"&prove=true' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.ABCIQuery("", "abcd", true) +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "response": { +// "log": "exists", +// "height": 0, +// "proof": "010114FED0DAD959F36091AD761C922ABA3CBF1D8349990101020103011406AA2262E2F448242DF2C2607C3CDC705313EE3B0001149D16177BC71E445476174622EA559715C293740C", +// "value": "61626364", +// "key": "61626364", +// "index": -1, +// "code": 0 +// } +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` +// +// ### Query Parameters +// +// | Parameter | Type | Default | Required | Description | +// |-----------+--------+---------+----------+---------------------------------------| +// | path | string | false | false | Path to the data ("/a/b/c") | +// | data | []byte | false | true | Data | +// | prove | bool | false | false | Include a proof of the data inclusion | func ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) { resQuery, err := proxyAppQuery.QuerySync(abci.RequestQuery{ Path: path, @@ -23,6 +60,31 @@ func ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuer }, nil } +// Get some info about the application. +// +// ```shell +// curl 'localhost:46657/abci_info' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// info, err := client.ABCIInfo() +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "response": { +// "data": "{\"size\":3}" +// } +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func ABCIInfo() (*ctypes.ResultABCIInfo, error) { resInfo, err := proxyAppQuery.InfoSync() if err != nil { diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index 873b711d8..1f9e85422 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -8,9 +8,59 @@ import ( . "github.com/tendermint/tmlibs/common" ) -//----------------------------------------------------------------------------- - -// Returns at most 20 blocks +// Get block headers for minHeight <= height <= maxHeight. +// +// ```shell +// curl 'localhost:46657/blockchain?minHeight=10&maxHeight=10' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// info, err := client.BlockchainInfo(10, 10) +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "block_metas": [ +// { +// "header": { +// "app_hash": "", +// "chain_id": "test-chain-6UTNIN", +// "height": 10, +// "time": "2017-05-29T15:05:53.877Z", +// "num_txs": 0, +// "last_block_id": { +// "parts": { +// "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", +// "total": 1 +// }, +// "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" +// }, +// "last_commit_hash": "F31CC4282E50B3F2A58D763D233D76F26D26CABE", +// "data_hash": "", +// "validators_hash": "9365FC80F234C967BD233F5A3E2AB2F1E4B0E5AA" +// }, +// "block_id": { +// "parts": { +// "hash": "277A4DBEF91483A18B85F2F5677ABF9694DFA40F", +// "total": 1 +// }, +// "hash": "96B1D2F2D201BA4BC383EB8224139DB1294944E5" +// } +// } +// ], +// "last_height": 5493 +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` +// +// func BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { if maxHeight == 0 { maxHeight = blockStore.Height() @@ -34,8 +84,105 @@ func BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, err return &ctypes.ResultBlockchainInfo{blockStore.Height(), blockMetas}, nil } -//----------------------------------------------------------------------------- - +// Get block at a given height. +// +// ```shell +// curl 'localhost:46657/block?height=10' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// info, err := client.Block(10) +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "block": { +// "last_commit": { +// "precommits": [ +// { +// "signature": { +// "data": "12C0D8893B8A38224488DC1DE6270DF76BB1A5E9DB1C68577706A6A97C6EC34FFD12339183D5CA8BC2F46148773823DE905B7F6F5862FD564038BB7AE03BF50D", +// "type": "ed25519" +// }, +// "block_id": { +// "parts": { +// "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", +// "total": 1 +// }, +// "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" +// }, +// "type": 2, +// "round": 0, +// "height": 9, +// "validator_index": 0, +// "validator_address": "E89A51D60F68385E09E716D353373B11F8FACD62" +// } +// ], +// "blockID": { +// "parts": { +// "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", +// "total": 1 +// }, +// "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" +// } +// }, +// "data": { +// "txs": [] +// }, +// "header": { +// "app_hash": "", +// "chain_id": "test-chain-6UTNIN", +// "height": 10, +// "time": "2017-05-29T15:05:53.877Z", +// "num_txs": 0, +// "last_block_id": { +// "parts": { +// "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", +// "total": 1 +// }, +// "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" +// }, +// "last_commit_hash": "F31CC4282E50B3F2A58D763D233D76F26D26CABE", +// "data_hash": "", +// "validators_hash": "9365FC80F234C967BD233F5A3E2AB2F1E4B0E5AA" +// } +// }, +// "block_meta": { +// "header": { +// "app_hash": "", +// "chain_id": "test-chain-6UTNIN", +// "height": 10, +// "time": "2017-05-29T15:05:53.877Z", +// "num_txs": 0, +// "last_block_id": { +// "parts": { +// "hash": "3C78F00658E06744A88F24FF97A0A5011139F34A", +// "total": 1 +// }, +// "hash": "F70588DAB36BDA5A953D548A16F7D48C6C2DFD78" +// }, +// "last_commit_hash": "F31CC4282E50B3F2A58D763D233D76F26D26CABE", +// "data_hash": "", +// "validators_hash": "9365FC80F234C967BD233F5A3E2AB2F1E4B0E5AA" +// }, +// "block_id": { +// "parts": { +// "hash": "277A4DBEF91483A18B85F2F5677ABF9694DFA40F", +// "total": 1 +// }, +// "hash": "96B1D2F2D201BA4BC383EB8224139DB1294944E5" +// } +// } +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func Block(height int) (*ctypes.ResultBlock, error) { if height == 0 { return nil, fmt.Errorf("Height must be greater than 0") @@ -49,8 +196,75 @@ func Block(height int) (*ctypes.ResultBlock, error) { return &ctypes.ResultBlock{blockMeta, block}, nil } -//----------------------------------------------------------------------------- - +// Get block commit at a given height. +// +// ```shell +// curl 'localhost:46657/commit?height=11' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// info, err := client.Commit(11) +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "canonical": true, +// "commit": { +// "precommits": [ +// { +// "signature": { +// "data": "00970429FEC652E9E21D106A90AE8C5413759A7488775CEF4A3F44DC46C7F9D941070E4FBE9ED54DF247FA3983359A0C3A238D61DE55C75C9116D72ABC9CF50F", +// "type": "ed25519" +// }, +// "block_id": { +// "parts": { +// "hash": "9E37CBF266BC044A779E09D81C456E653B89E006", +// "total": 1 +// }, +// "hash": "CC6E861E31CA4334E9888381B4A9137D1458AB6A" +// }, +// "type": 2, +// "round": 0, +// "height": 11, +// "validator_index": 0, +// "validator_address": "E89A51D60F68385E09E716D353373B11F8FACD62" +// } +// ], +// "blockID": { +// "parts": { +// "hash": "9E37CBF266BC044A779E09D81C456E653B89E006", +// "total": 1 +// }, +// "hash": "CC6E861E31CA4334E9888381B4A9137D1458AB6A" +// } +// }, +// "header": { +// "app_hash": "", +// "chain_id": "test-chain-6UTNIN", +// "height": 11, +// "time": "2017-05-29T15:05:54.893Z", +// "num_txs": 0, +// "last_block_id": { +// "parts": { +// "hash": "277A4DBEF91483A18B85F2F5677ABF9694DFA40F", +// "total": 1 +// }, +// "hash": "96B1D2F2D201BA4BC383EB8224139DB1294944E5" +// }, +// "last_commit_hash": "3CE0C9727CE524BA9CB7C91E28F08E2B94001087", +// "data_hash": "", +// "validators_hash": "9365FC80F234C967BD233F5A3E2AB2F1E4B0E5AA" +// } +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func Commit(height int) (*ctypes.ResultCommit, error) { if height == 0 { return nil, fmt.Errorf("Height must be greater than 0") diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index e2ccce442..fdce29e48 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -1,17 +1,75 @@ package core import ( - "github.com/tendermint/go-wire" + wire "github.com/tendermint/go-wire" cm "github.com/tendermint/tendermint/consensus" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" ) +// Get current validators set along with a block height. +// +// ```shell +// curl 'localhost:46657/validators' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// state, err := client.Validators() +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "validators": [ +// { +// "accum": 0, +// "voting_power": 10, +// "pub_key": { +// "data": "68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D", +// "type": "ed25519" +// }, +// "address": "E89A51D60F68385E09E716D353373B11F8FACD62" +// } +// ], +// "block_height": 5241 +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func Validators() (*ctypes.ResultValidators, error) { blockHeight, validators := consensusState.GetValidators() return &ctypes.ResultValidators{blockHeight, validators}, nil } +// Dump consensus state. +// +// ```shell +// curl 'localhost:46657/dump_consensus_state' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// state, err := client.DumpConsensusState() +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "peer_round_states": [], +// "round_state": "RoundState{\n H:3537 R:0 S:RoundStepNewHeight\n StartTime: 2017-05-31 12:32:31.178653883 +0000 UTC\n CommitTime: 2017-05-31 12:32:30.178653883 +0000 UTC\n Validators: ValidatorSet{\n Proposer: Validator{E89A51D60F68385E09E716D353373B11F8FACD62 {PubKeyEd25519{68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D}} VP:10 A:0}\n Validators:\n Validator{E89A51D60F68385E09E716D353373B11F8FACD62 {PubKeyEd25519{68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D}} VP:10 A:0}\n }\n Proposal: \n ProposalBlock: nil-PartSet nil-Block\n LockedRound: 0\n LockedBlock: nil-PartSet nil-Block\n Votes: HeightVoteSet{H:3537 R:0~0\n VoteSet{H:3537 R:0 T:1 +2/3: BA{1:_} map[]}\n VoteSet{H:3537 R:0 T:2 +2/3: BA{1:_} map[]}\n }\n LastCommit: VoteSet{H:3536 R:0 T:2 +2/3:B7F988FBCDC68F9320E346EECAA76E32F6054654:1:673BE7C01F74 BA{1:X} map[]}\n LastValidators: ValidatorSet{\n Proposer: Validator{E89A51D60F68385E09E716D353373B11F8FACD62 {PubKeyEd25519{68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D}} VP:10 A:0}\n Validators:\n Validator{E89A51D60F68385E09E716D353373B11F8FACD62 {PubKeyEd25519{68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D}} VP:10 A:0}\n }\n}" +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { roundState := consensusState.GetRoundState() peerRoundStates := []string{} diff --git a/rpc/core/doc.go b/rpc/core/doc.go new file mode 100644 index 000000000..a72cec020 --- /dev/null +++ b/rpc/core/doc.go @@ -0,0 +1,107 @@ +/* +# Introduction + +Tendermint supports the following RPC protocols: + +* URI over HTTP +* JSONRPC over HTTP +* JSONRPC over websockets + +Tendermint RPC is built using [our own RPC library](https://github.com/tendermint/tendermint/tree/master/rpc/lib). Documentation and tests for that library could be found at `tendermint/rpc/lib` directory. + +## Configuration + +Set the `laddr` config parameter under `[rpc]` table in the `$TMHOME/config.toml` file or the `--rpc.laddr` command-line flag to the desired protocol://host:port setting. Default: `tcp://0.0.0.0:46657`. + +## Arguments + +Arguments which expect strings or byte arrays may be passed as quoted strings, like `"abc"` or as `0x`-prefixed strings, like `0x616263`. + +## URI/HTTP + +```bash +curl 'localhost:46657/broadcast_tx_sync?tx="abc"' +``` + +> Response: + +```json +{ + "error": "", + "result": { + "hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF", + "log": "", + "data": "", + "code": 0 + }, + "id": "", + "jsonrpc": "2.0" +} +``` + +The first entry in the result-array (`96`) is the method this response correlates with. `96` refers to "ResultTypeBroadcastTx", see [responses.go](https://github.com/tendermint/tendermint/blob/master/rpc/core/types/responses.go) for a complete overview. + +## JSONRPC/HTTP + +JSONRPC requests can be POST'd to the root RPC endpoint via HTTP (e.g. `http://localhost:46657/`). + +```json +{ + "method": "broadcast_tx_sync", + "jsonrpc": "2.0", + "params": [ "abc" ], + "id": "dontcare" +} +``` + +## JSONRPC/websockets + +JSONRPC requests can be made via websocket. The websocket endpoint is at `/websocket`, e.g. `localhost:46657/websocket`. Asynchronous RPC functions like event `subscribe` and `unsubscribe` are only available via websockets. + + +## More Examples + +See the various bash tests using curl in `test/`, and examples using the `Go` API in `rpc/client/`. + +## Get the list + +An HTTP Get request to the root RPC endpoint shows a list of available endpoints. + +```bash +curl 'localhost:46657' +``` + +> Response: + +```plain +Available endpoints: +/abci_info +/dump_consensus_state +/genesis +/net_info +/num_unconfirmed_txs +/status +/unconfirmed_txs +/unsafe_flush_mempool +/unsafe_stop_cpu_profiler +/validators + +Endpoints that require arguments: +/abci_query?path=_&data=_&prove=_ +/block?height=_ +/blockchain?minHeight=_&maxHeight=_ +/broadcast_tx_async?tx=_ +/broadcast_tx_commit?tx=_ +/broadcast_tx_sync?tx=_ +/commit?height=_ +/dial_seeds?seeds=_ +/subscribe?event=_ +/tx?hash=_&prove=_ +/unsafe_start_cpu_profiler?filename=_ +/unsafe_write_heap_profile?filename=_ +/unsubscribe?event=_ +``` + +# Endpoints +*/ +package core diff --git a/rpc/core/doc_template.txt b/rpc/core/doc_template.txt new file mode 100644 index 000000000..896d0c271 --- /dev/null +++ b/rpc/core/doc_template.txt @@ -0,0 +1,8 @@ +{{with .PDoc}} +{{comment_md .Doc}} +{{example_html $ ""}} + +{{range .Funcs}}{{$name_html := html .Name}}## [{{$name_html}}]({{posLink_url $ .Decl}}) +{{comment_md .Doc}}{{end}} +{{end}} +--- diff --git a/rpc/core/events.go b/rpc/core/events.go index d7cd75612..4671d3417 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -6,6 +6,33 @@ import ( "github.com/tendermint/tendermint/types" ) +// Subscribe for events via WebSocket. +// +// ```go +// import "github.com/tendermint/tendermint/types" +// +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.AddListenerForEvent(types.EventStringNewBlock()) +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": {}, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` +// +// ### Query Parameters +// +// | Parameter | Type | Default | Required | Description | +// |-----------+--------+---------+----------+-------------| +// | event | string | "" | true | Event name | +// +// func Subscribe(wsCtx rpctypes.WSRPCContext, event string) (*ctypes.ResultSubscribe, error) { logger.Info("Subscribe to event", "remote", wsCtx.GetRemoteAddr(), "event", event) types.AddListenerForEvent(wsCtx.GetEventSwitch(), wsCtx.GetRemoteAddr(), event, func(msg types.TMEventData) { @@ -17,6 +44,33 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, event string) (*ctypes.ResultSubscri return &ctypes.ResultSubscribe{}, nil } +// Unsubscribe from events via WebSocket. +// +// ```go +// import 'github.com/tendermint/tendermint/types' +// +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.RemoveListenerForEvent(types.EventStringNewBlock()) +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": {}, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` +// +// ### Query Parameters +// +// | Parameter | Type | Default | Required | Description | +// |-----------+--------+---------+----------+-------------| +// | event | string | "" | true | Event name | +// +// func Unsubscribe(wsCtx rpctypes.WSRPCContext, event string) (*ctypes.ResultUnsubscribe, error) { logger.Info("Unsubscribe to event", "remote", wsCtx.GetRemoteAddr(), "event", event) wsCtx.GetEventSwitch().RemoveListenerForEvent(event, wsCtx.GetRemoteAddr()) diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 5b794db79..94fc0efce 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -14,6 +14,37 @@ import ( // NOTE: tx should be signed, but this is only checked at the app level (not by Tendermint!) // Returns right away, with no response +// +// ```shell +// curl 'localhost:46657/broadcast_tx_async?tx="123"' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.BroadcastTxAsync("123") +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "hash": "E39AAB7A537ABAA237831742DCE1117F187C3C52", +// "log": "", +// "data": "", +// "code": 0 +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` +// +// ### Query Parameters +// +// | Parameter | Type | Default | Required | Description | +// |-----------+------+---------+----------+-----------------| +// | tx | Tx | nil | true | The transaction | func BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { err := mempool.CheckTx(tx, nil) if err != nil { @@ -22,7 +53,38 @@ func BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { return &ctypes.ResultBroadcastTx{Hash: tx.Hash()}, nil } -// Returns with the response from CheckTx +// Returns with the response from CheckTx. +// +// ```shell +// curl 'localhost:46657/broadcast_tx_sync?tx="456"' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.BroadcastTxSync("456") +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "jsonrpc": "2.0", +// "id": "", +// "result": { +// "code": 0, +// "data": "", +// "log": "", +// "hash": "0D33F2F03A5234F38706E43004489E061AC40A2E" +// }, +// "error": "" +// } +// ``` +// +// ### Query Parameters +// +// | Parameter | Type | Default | Required | Description | +// |-----------+------+---------+----------+-----------------| +// | tx | Tx | nil | true | The transaction | func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { resCh := make(chan *abci.Response, 1) err := mempool.CheckTx(tx, func(res *abci.Response) { @@ -45,6 +107,45 @@ func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { // or if we timeout waiting for tx to commit. // If CheckTx or DeliverTx fail, no error will be returned, but the returned result // will contain a non-OK ABCI code. +// +// ```shell +// curl 'localhost:46657/broadcast_tx_commit?tx="789"' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.BroadcastTxCommit("789") +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "height": 26682, +// "hash": "75CA0F856A4DA078FC4911580360E70CEFB2EBEE", +// "deliver_tx": { +// "log": "", +// "data": "", +// "code": 0 +// }, +// "check_tx": { +// "log": "", +// "data": "", +// "code": 0 +// } +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` +// +// ### Query Parameters +// +// | Parameter | Type | Default | Required | Description | +// |-----------+------+---------+----------+-----------------| +// | tx | Tx | nil | true | The transaction | func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { // subscribe to tx being committed in block @@ -104,11 +205,59 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { panic("Should never happen!") } +// Get unconfirmed transactions including their number. +// +// ```shell +// curl 'localhost:46657/unconfirmed_txs' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.UnconfirmedTxs() +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "txs": [], +// "n_txs": 0 +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func UnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { txs := mempool.Reap(-1) return &ctypes.ResultUnconfirmedTxs{len(txs), txs}, nil } +// Get number of unconfirmed transactions. +// +// ```shell +// curl 'localhost:46657/num_unconfirmed_txs' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.UnconfirmedTxs() +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "txs": null, +// "n_txs": 0 +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { return &ctypes.ResultUnconfirmedTxs{N: mempool.Size()}, nil } diff --git a/rpc/core/net.go b/rpc/core/net.go index b56216ca7..6c2dc587e 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -6,8 +6,33 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" ) -//----------------------------------------------------------------------------- - +// Get network info. +// +// ```shell +// curl 'localhost:46657/net_info' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// info, err := client.NetInfo() +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "peers": [], +// "listeners": [ +// "Listener(@10.0.2.15:46656)" +// ], +// "listening": true +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func NetInfo() (*ctypes.ResultNetInfo, error) { listening := p2pSwitch.IsListening() listeners := []string{} @@ -29,9 +54,6 @@ func NetInfo() (*ctypes.ResultNetInfo, error) { }, nil } -//----------------------------------------------------------------------------- - -// Dial given list of seeds func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { if len(seeds) == 0 { @@ -46,8 +68,43 @@ func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { return &ctypes.ResultDialSeeds{"Dialing seeds in progress. See /net_info for details"}, nil } -//----------------------------------------------------------------------------- - +// Get genesis file. +// +// ```shell +// curl 'localhost:46657/genesis' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// genesis, err := client.Genesis() +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "genesis": { +// "app_hash": "", +// "validators": [ +// { +// "name": "", +// "amount": 10, +// "pub_key": { +// "data": "68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D", +// "type": "ed25519" +// } +// } +// ], +// "chain_id": "test-chain-6UTNIN", +// "genesis_time": "2017-05-29T15:05:41.671Z" +// } +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func Genesis() (*ctypes.ResultGenesis, error) { return &ctypes.ResultGenesis{genDoc}, nil } diff --git a/rpc/core/status.go b/rpc/core/status.go index ba19c0e9b..4a8d84ece 100644 --- a/rpc/core/status.go +++ b/rpc/core/status.go @@ -6,6 +6,53 @@ import ( "github.com/tendermint/tendermint/types" ) +// Get Tendermint status including node info, pubkey, latest block +// hash, app hash, block height and time. +// +// ```shell +// curl 'localhost:46657/status' +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// result, err := client.Status() +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "latest_block_time": 1.49631773695e+18, +// "latest_block_height": 22924, +// "latest_app_hash": "9D16177BC71E445476174622EA559715C293740C", +// "latest_block_hash": "75B36EEF96C277A592D8B14867098C58F68BB180", +// "pub_key": { +// "data": "68DFDA7E50F82946E7E8546BED37944A422CD1B831E70DF66BA3B8430593944D", +// "type": "ed25519" +// }, +// "node_info": { +// "other": [ +// "wire_version=0.6.2", +// "p2p_version=0.5.0", +// "consensus_version=v1/0.2.2", +// "rpc_version=0.7.0/3", +// "tx_index=on", +// "rpc_addr=tcp://0.0.0.0:46657" +// ], +// "version": "0.10.0-rc1-aa22bd84", +// "listen_addr": "10.0.2.15:46656", +// "remote_addr": "", +// "network": "test-chain-6UTNIN", +// "moniker": "anonymous", +// "pub_key": "659B9E54DD6EF9FEF28FAD40629AF0E4BD3C2563BB037132B054A176E00F1D94" +// } +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` func Status() (*ctypes.ResultStatus, error) { latestHeight := blockStore.Height() var ( diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 5bd6e1806..03a911e2c 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -8,9 +8,64 @@ import ( "github.com/tendermint/tendermint/types" ) -// Tx allow user to query the transaction results. `nil` could mean the -// transaction is in the mempool, invalidated, or was not send in the first +// Tx allows you to query the transaction results. `nil` could mean the +// transaction is in the mempool, invalidated, or was not sent in the first // place. +// +// ```shell +// curl "localhost:46657/tx?hash=0x2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF" +// ``` +// +// ```go +// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") +// tx, err := client.Tx([]byte("2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF"), true) +// ``` +// +// > The above command returns JSON structured like this: +// +// ```json +// { +// "error": "", +// "result": { +// "proof": { +// "Proof": { +// "aunts": [] +// }, +// "Data": "YWJjZA==", +// "RootHash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF", +// "Total": 1, +// "Index": 0 +// }, +// "tx": "YWJjZA==", +// "tx_result": { +// "log": "", +// "data": "", +// "code": 0 +// }, +// "index": 0, +// "height": 52 +// }, +// "id": "", +// "jsonrpc": "2.0" +// } +// ``` +// +// Returns a transaction matching the given transaction hash. +// +// ### Query Parameters +// +// | Parameter | Type | Default | Required | Description | +// |-----------+--------+---------+----------+-----------------------------------------------------------| +// | hash | []byte | nil | true | The transaction hash | +// | prove | bool | false | false | Include a proof of the transaction inclusion in the block | +// +// ### Returns +// +// - `proof`: the `types.TxProof` object +// - `tx`: `[]byte` - the transaction +// - `tx_result`: the `abci.Result` object +// - `index`: `int` - index of the transaction +// - `height`: `int` - height of the block where this transaction was in func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { // if index is disabled, return error