diff --git a/.changelog/unreleased/breaking-changes/1412-rpc-hardcode-websocket.md b/.changelog/unreleased/breaking-changes/1412-rpc-hardcode-websocket.md new file mode 100644 index 00000000000..038f750ede9 --- /dev/null +++ b/.changelog/unreleased/breaking-changes/1412-rpc-hardcode-websocket.md @@ -0,0 +1,3 @@ +- `[rpc/client]` Hard-code the `/websocket` endpoint path such that it is + no longer configurable, removing the related client constructor parameter + ([\#1412](https://github.com/cometbft/cometbft/pull/1412)) \ No newline at end of file diff --git a/.changelog/unreleased/improvements/1412-rpc-versioning.md b/.changelog/unreleased/improvements/1412-rpc-versioning.md new file mode 100644 index 00000000000..e809d20ea45 --- /dev/null +++ b/.changelog/unreleased/improvements/1412-rpc-versioning.md @@ -0,0 +1,3 @@ +- `[rpc]` The RPC API is now versioned, with all existing endpoints accessible + via `/v1/*` as well as `/*` + ([\#1412](https://github.com/cometbft/cometbft/pull/1412)) diff --git a/UPGRADING.md b/UPGRADING.md index 0260a5354dd..9e17b7d0f3e 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -13,18 +13,20 @@ The minimum Go version has been bumped to [v1.21][go121]. * The `Mempool` interface was modified on the following methods. Note that this interface is meant for internal use only, so you should be aware of these changes only if you happen to call these methods directly. - - `CheckTx`'s signature changed from + * `CheckTx`'s signature changed from `CheckTx(tx types.Tx, cb func(*abci.ResponseCheckTx), txInfo TxInfo) error` to `CheckTx(tx types.Tx) (abcicli.ReqRes, error)`. - - The method used to take a callback function `cb` to be applied to the ABCI + * The method used to take a callback function `cb` to be applied to the ABCI `CheckTx` response. Now `CheckTx` returns the ABCI response of type `abcicli.ReqRes`, on which the callback must be applied manually. For example: + ```golang reqRes, err := CheckTx(tx) cb(reqRes.Response.GetCheckTx()) ``` - - The second parameter was `txInfo`, which essentially contained information + + * The second parameter was `txInfo`, which essentially contained information about the sender of the transaction. Now that information is stored in the mempool reactor instead of the data structure, so it is no longer needed in this method. @@ -41,6 +43,24 @@ The minimum Go version has been bumped to [v1.21][go121]. * Removed the `replay` and `replay-console` subcommands ([\#1170](https://github.com/cometbft/cometbft/pull/1170)) +### RPC API + +* The RPC API is now versioned. + Although invoking methods without specifying the version is still supported for now, + support will be dropped in future releases and users are urged to use the versioned + approach. + For example, instead of `curl localhost:26657/block?height=5`, use + `curl localhost:26657/v1/block?height=5`. + +* The `/websocket` endpoint path is no longer configurable in the client or + server. Creating an RPC client now takes the form: + + ```golang + // The WebSocket endpoint in the following example is assumed to be available + // at http://localhost:26657/v1/websocket + rpcClient, err := client.New("http://localhost:26657/v1") + ``` + ## v0.38.0 This release introduces state machine-breaking changes, as well as substantial changes diff --git a/cmd/cometbft/commands/debug/debug.go b/cmd/cometbft/commands/debug/debug.go index 0d9430e89ec..2476968048b 100644 --- a/cmd/cometbft/commands/debug/debug.go +++ b/cmd/cometbft/commands/debug/debug.go @@ -32,8 +32,8 @@ func init() { DebugCmd.PersistentFlags().StringVar( &nodeRPCAddr, flagNodeRPCAddr, - "tcp://localhost:26657", - "the CometBFT node's RPC address (:)", + "tcp://localhost:26657/v1", + "the CometBFT node's RPC address (:) and version", ) DebugCmd.AddCommand(killCmd) diff --git a/cmd/cometbft/commands/debug/dump.go b/cmd/cometbft/commands/debug/dump.go index 8db59080ea8..e38462933dd 100644 --- a/cmd/cometbft/commands/debug/dump.go +++ b/cmd/cometbft/commands/debug/dump.go @@ -58,7 +58,7 @@ func dumpCmdHandler(_ *cobra.Command, args []string) error { } } - rpc, err := rpchttp.New(nodeRPCAddr, "/websocket") + rpc, err := rpchttp.New(nodeRPCAddr) if err != nil { return fmt.Errorf("failed to create new http client: %w", err) } diff --git a/cmd/cometbft/commands/debug/kill.go b/cmd/cometbft/commands/debug/kill.go index 90d84cf9eb7..da1e62836e2 100644 --- a/cmd/cometbft/commands/debug/kill.go +++ b/cmd/cometbft/commands/debug/kill.go @@ -43,7 +43,7 @@ func killCmdHandler(_ *cobra.Command, args []string) error { return errors.New("invalid output file") } - rpc, err := rpchttp.New(nodeRPCAddr, "/websocket") + rpc, err := rpchttp.New(nodeRPCAddr) if err != nil { return fmt.Errorf("failed to create new http client: %w", err) } diff --git a/inspect/inspect_test.go b/inspect/inspect_test.go index a5991d77081..12b774f5797 100644 --- a/inspect/inspect_test.go +++ b/inspect/inspect_test.go @@ -91,7 +91,7 @@ func TestBlock(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) resultBlock, err := cli.Block(context.Background(), &testHeight) require.NoError(t, err) @@ -143,7 +143,7 @@ func TestTxSearch(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) page := 1 @@ -191,7 +191,7 @@ func TestTx(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) res, err := cli.Tx(context.Background(), testHash, false) @@ -240,7 +240,7 @@ func TestConsensusParams(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) params, err := cli.ConsensusParams(context.Background(), &testHeight) require.NoError(t, err) @@ -290,7 +290,7 @@ func TestBlockResults(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) res, err := cli.BlockResults(context.Background(), &testHeight) require.NoError(t, err) @@ -337,7 +337,7 @@ func TestCommit(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) res, err := cli.Commit(context.Background(), &testHeight) require.NoError(t, err) @@ -390,7 +390,7 @@ func TestBlockByHash(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) res, err := cli.BlockByHash(context.Background(), testHash) require.NoError(t, err) @@ -442,7 +442,7 @@ func TestBlockchain(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) res, err := cli.BlockchainInfo(context.Background(), 0, 100) require.NoError(t, err) @@ -494,7 +494,7 @@ func TestValidators(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) testPage := 1 @@ -554,7 +554,7 @@ func TestBlockSearch(t *testing.T) { // Determine more deterministic method for prompting a context switch startedWG.Wait() requireConnect(t, rpcConfig.ListenAddress, 20) - cli, err := httpclient.New(rpcConfig.ListenAddress, "/websocket") + cli, err := httpclient.New(rpcConfig.ListenAddress + "/v1") require.NoError(t, err) testPage := 1 diff --git a/inspect/rpc/rpc.go b/inspect/rpc/rpc.go index 4367ade2d59..fec30ca57d6 100644 --- a/inspect/rpc/rpc.go +++ b/inspect/rpc/rpc.go @@ -61,6 +61,7 @@ func Handler(rpcConfig *config.RPCConfig, routes core.RoutesMap, logger log.Logg server.ReadLimit(rpcConfig.MaxBodyBytes)) wm.SetLogger(wmLogger) mux.HandleFunc("/websocket", wm.WebsocketHandler) + mux.HandleFunc("/v1/websocket", wm.WebsocketHandler) server.RegisterRPCFuncs(mux, routes, logger) var rootHandler http.Handler = mux diff --git a/light/provider/http/http.go b/light/provider/http/http.go index b73f7bd4524..ca3ad52039e 100644 --- a/light/provider/http/http.go +++ b/light/provider/http/http.go @@ -39,7 +39,7 @@ func New(chainID, remote string) (provider.Provider, error) { remote = "http://" + remote } - httpClient, err := rpchttp.NewWithTimeout(remote, "/websocket", timeout) + httpClient, err := rpchttp.NewWithTimeout(remote, timeout) if err != nil { return nil, err } diff --git a/light/provider/http/http_test.go b/light/provider/http/http_test.go index 1c499383151..ef0e588e865 100644 --- a/light/provider/http/http_test.go +++ b/light/provider/http/http_test.go @@ -34,60 +34,69 @@ func TestNewProvider(t *testing.T) { } func TestProvider(t *testing.T) { - app := kvstore.NewInMemoryApplication() - app.RetainBlocks = 10 - node := rpctest.StartCometBFT(app) - - cfg := rpctest.GetConfig() - defer os.RemoveAll(cfg.RootDir) - rpcAddr := cfg.RPC.ListenAddress - genDoc, err := types.GenesisDocFromFile(cfg.GenesisFile()) - require.NoError(t, err) - chainID := genDoc.ChainID - - c, err := rpchttp.New(rpcAddr, "/websocket") - require.Nil(t, err) - - p := lighthttp.NewWithClient(chainID, c) - require.NoError(t, err) - require.NotNil(t, p) - - // let it produce some blocks - err = rpcclient.WaitForHeight(c, 10, nil) - require.NoError(t, err) - - // let's get the highest block - lb, err := p.LightBlock(context.Background(), 0) - require.NoError(t, err) - require.NotNil(t, lb) - assert.True(t, lb.Height < 1000) - - // let's check this is valid somehow - assert.Nil(t, lb.ValidateBasic(chainID)) - - // historical queries now work :) - lower := lb.Height - 3 - lb, err = p.LightBlock(context.Background(), lower) - require.NoError(t, err) - assert.Equal(t, lower, lb.Height) - - // fetching missing heights (both future and pruned) should return appropriate errors - lb, err = p.LightBlock(context.Background(), 1000) - require.Error(t, err) - require.Nil(t, lb) - assert.Equal(t, provider.ErrHeightTooHigh, err) - - _, err = p.LightBlock(context.Background(), 1) - require.Error(t, err) - require.Nil(t, lb) - assert.Equal(t, provider.ErrLightBlockNotFound, err) - - // stop the full node and check that a no response error is returned - rpctest.StopCometBFT(node) - time.Sleep(10 * time.Second) - lb, err = p.LightBlock(context.Background(), lower+2) - // we should see a connection refused - require.Error(t, err) - require.Contains(t, err.Error(), "connection refused") - require.Nil(t, lb) + for _, path := range []string{"", "/", "/v1", "/v1/"} { + app := kvstore.NewInMemoryApplication() + app.RetainBlocks = 10 + node := rpctest.StartCometBFT(app, rpctest.RecreateConfig) + + cfg := rpctest.GetConfig() + defer os.RemoveAll(cfg.RootDir) + rpcAddr := cfg.RPC.ListenAddress + genDoc, err := types.GenesisDocFromFile(cfg.GenesisFile()) + require.NoError(t, err) + chainID := genDoc.ChainID + + c, err := rpchttp.New(rpcAddr + path) + require.Nil(t, err) + + p := lighthttp.NewWithClient(chainID, c) + require.NoError(t, err) + require.NotNil(t, p) + + // let it produce some blocks + err = rpcclient.WaitForHeight(c, 10, nil) + require.NoError(t, err) + + // let's get the highest block + lb, err := p.LightBlock(context.Background(), 0) + require.NoError(t, err) + require.NotNil(t, lb) + assert.True(t, lb.Height < 1000) + assert.True(t, lb.Height >= 10) + + // let's check this is valid somehow + assert.Nil(t, lb.ValidateBasic(chainID)) + + // historical queries now work :) + lb, err = p.LightBlock(context.Background(), 0) + require.NoError(t, err) + require.NotNil(t, lb) + lower := lb.Height - 3 + lb, err = p.LightBlock(context.Background(), lower) + require.NoError(t, err) + assert.Equal(t, lower, lb.Height) + + // fetching missing heights (both future and pruned) should return appropriate errors + lb, err = p.LightBlock(context.Background(), 0) + require.NoError(t, err) + require.NotNil(t, lb) + lb, err = p.LightBlock(context.Background(), lb.Height+1000) + require.Error(t, err) + require.Nil(t, lb) + assert.Equal(t, provider.ErrHeightTooHigh, err) + + _, err = p.LightBlock(context.Background(), 1) + require.Error(t, err) + require.Nil(t, lb) + assert.Equal(t, provider.ErrLightBlockNotFound, err) + + // stop the full node and check that a no response error is returned + rpctest.StopCometBFT(node) + time.Sleep(10 * time.Second) + lb, err = p.LightBlock(context.Background(), lower+2) + // we should see a connection refused + require.Error(t, err) + require.Contains(t, err.Error(), "connection refused") + require.Nil(t, lb) + } } diff --git a/light/proxy/proxy.go b/light/proxy/proxy.go index 450169a56b5..0380b2d14dd 100644 --- a/light/proxy/proxy.go +++ b/light/proxy/proxy.go @@ -32,7 +32,7 @@ func NewProxy( logger log.Logger, opts ...lrpc.Option, ) (*Proxy, error) { - rpcClient, err := rpchttp.NewWithTimeout(providerAddr, "/websocket", uint(config.WriteTimeout.Seconds())) + rpcClient, err := rpchttp.NewWithTimeout(providerAddr, uint(config.WriteTimeout.Seconds())) if err != nil { return nil, fmt.Errorf("failed to create http client for %s: %w", providerAddr, err) } @@ -104,6 +104,7 @@ func (p *Proxy) listen() (net.Listener, *http.ServeMux, error) { ) wm.SetLogger(wmLogger) mux.HandleFunc("/websocket", wm.WebsocketHandler) + mux.HandleFunc("/v1/websocket", wm.WebsocketHandler) // 3) Start a client. if !p.Client.IsRunning() { diff --git a/node/node.go b/node/node.go index d0286534cce..96b87605445 100644 --- a/node/node.go +++ b/node/node.go @@ -747,6 +747,7 @@ func (n *Node) startRPC() ([]net.Listener, error) { ) wm.SetLogger(wmLogger) mux.HandleFunc("/websocket", wm.WebsocketHandler) + mux.HandleFunc("/v1/websocket", wm.WebsocketHandler) rpcserver.RegisterRPCFuncs(mux, routes, rpcLogger) listener, err := rpcserver.Listen( listenAddr, diff --git a/rpc/client/examples_test.go b/rpc/client/examples_test.go index 831c3049da1..8f2f0628807 100644 --- a/rpc/client/examples_test.go +++ b/rpc/client/examples_test.go @@ -20,7 +20,7 @@ func ExampleHTTP_simple() { // Create our RPC client rpcAddr := rpctest.GetConfig().RPC.ListenAddress - c, err := rpchttp.New(rpcAddr, "/websocket") + c, err := rpchttp.New(rpcAddr) if err != nil { log.Fatal(err) //nolint:gocritic } @@ -72,7 +72,7 @@ func ExampleHTTP_batching() { // Create our RPC client rpcAddr := rpctest.GetConfig().RPC.ListenAddress - c, err := rpchttp.New(rpcAddr, "/websocket") + c, err := rpchttp.New(rpcAddr) if err != nil { log.Fatal(err) } diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index f9ccaeb5a4c..e43b493bbe5 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -39,7 +39,7 @@ the example for more details. Example: - c, err := New("http://192.168.1.10:26657", "/websocket") + c, err := New("http://192.168.1.10:26657/v1") if err != nil { // handle error } @@ -107,30 +107,29 @@ var ( //----------------------------------------------------------------------------- // HTTP -// New takes a remote endpoint in the form ://: and -// the websocket path (which always seems to be "/websocket") -// An error is returned on invalid remote. The function panics when remote is nil. -func New(remote, wsEndpoint string) (*HTTP, error) { +// New takes a remote endpoint in the form ://:. An error +// is returned on invalid remote. The function panics when remote is nil. +func New(remote string) (*HTTP, error) { httpClient, err := jsonrpcclient.DefaultHTTPClient(remote) if err != nil { return nil, err } - return NewWithClient(remote, wsEndpoint, httpClient) + return NewWithClient(remote, httpClient) } // Create timeout enabled http client -func NewWithTimeout(remote, wsEndpoint string, timeout uint) (*HTTP, error) { +func NewWithTimeout(remote string, timeout uint) (*HTTP, error) { httpClient, err := jsonrpcclient.DefaultHTTPClient(remote) if err != nil { return nil, err } httpClient.Timeout = time.Duration(timeout) * time.Second - return NewWithClient(remote, wsEndpoint, httpClient) + return NewWithClient(remote, httpClient) } -// NewWithClient allows for setting a custom http client (See New). -// An error is returned on invalid remote. The function panics when remote is nil. -func NewWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, error) { +// NewWithClient allows for setting a custom http client (See New). An error is +// returned on invalid remote. The function panics when remote is nil. +func NewWithClient(remote string, client *http.Client) (*HTTP, error) { if client == nil { panic("nil http.Client provided") } @@ -140,7 +139,7 @@ func NewWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, error return nil, err } - wsEvents, err := newWSEvents(remote, wsEndpoint) + wsEvents, err := newWSEvents(remote, "/websocket") if err != nil { return nil, err } diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index c3825361f1c..9c35f546880 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -27,13 +27,11 @@ import ( "github.com/cometbft/cometbft/types" ) -var ( - ctx = context.Background() -) +var ctx = context.Background() func getHTTPClient() *rpchttp.HTTP { - rpcAddr := rpctest.GetConfig().RPC.ListenAddress - c, err := rpchttp.New(rpcAddr, "/websocket") + rpcAddr := strings.Replace(rpctest.GetConfig().RPC.ListenAddress, "tcp://", "http://", 1) + c, err := rpchttp.New(rpcAddr + "/v1") if err != nil { panic(err) } @@ -43,7 +41,7 @@ func getHTTPClient() *rpchttp.HTTP { func getHTTPClientWithTimeout(timeout uint) *rpchttp.HTTP { rpcAddr := rpctest.GetConfig().RPC.ListenAddress - c, err := rpchttp.NewWithTimeout(rpcAddr, "/websocket", timeout) + c, err := rpchttp.NewWithTimeout(rpcAddr, timeout) if err != nil { panic(err) } @@ -65,7 +63,7 @@ func GetClients() []client.Client { func TestNilCustomHTTPClient(t *testing.T) { require.Panics(t, func() { - _, _ = rpchttp.NewWithClient("http://example.com", "/websocket", nil) + _, _ = rpchttp.NewWithClient("http://example.com", nil) }) require.Panics(t, func() { _, _ = rpcclient.NewWithHTTPClient("http://example.com", nil) @@ -74,7 +72,7 @@ func TestNilCustomHTTPClient(t *testing.T) { func TestCustomHTTPClient(t *testing.T) { remote := rpctest.GetConfig().RPC.ListenAddress - c, err := rpchttp.NewWithClient(remote, "/websocket", http.DefaultClient) + c, err := rpchttp.NewWithClient(remote, http.DefaultClient) require.Nil(t, err) status, err := c.Status(context.Background()) require.NoError(t, err) @@ -540,8 +538,8 @@ func TestBlockSearch(t *testing.T) { // otherwise it is 0 require.Equal(t, blockCount, 0) - } + func TestTxSearch(t *testing.T) { c := getHTTPClient() diff --git a/rpc/core/doc.go b/rpc/core/doc.go index dbd76ac69ca..6427a7d32ed 100644 --- a/rpc/core/doc.go +++ b/rpc/core/doc.go @@ -9,7 +9,7 @@ https://github.com/cometbft/cometbft/tree/main/rpc/jsonrpc. An HTTP Get request to the root RPC endpoint shows a list of available endpoints. ```bash -curl 'localhost:26657' +curl "http://localhost:26657" | textutil -stdin -convert txt -stdout | sed 's/\/\/localhost:26657//g' ``` > Response: @@ -19,27 +19,37 @@ Available endpoints: /abci_info /dump_consensus_state /genesis +/health /net_info /num_unconfirmed_txs /status -/health -/unconfirmed_txs /unsafe_flush_mempool -/validators +/unsubscribe_all? Endpoints that require arguments: -/abci_query?path=_&data=_&prove=_ +/abci_query?path=_&data=_&height=_&prove=_ /block?height=_ +/block_by_hash?hash=_ +/block_results?height=_ +/block_search?query=_&page=_&per_page=_&order_by=_ /blockchain?minHeight=_&maxHeight=_ +/broadcast_evidence?evidence=_ /broadcast_tx_async?tx=_ /broadcast_tx_commit?tx=_ /broadcast_tx_sync?tx=_ +/check_tx?tx=_ /commit?height=_ -/dial_seeds?seeds=_ -/dial_persistent_peers?persistent_peers=_ -/subscribe?event=_ +/consensus_params?height=_ +/consensus_state? +/genesis_chunked?chunk=_ +/header?height=_ +/header_by_hash?hash=_ +/subscribe?query=_ /tx?hash=_&prove=_ -/unsubscribe?event=_ +/tx_search?query=_&prove=_&page=_&per_page=_&order_by=_ +/unconfirmed_txs?limit=_ +/unsubscribe?query=_ +/validators?height=_&page=_&per_page=_ ``` */ package core diff --git a/rpc/jsonrpc/client/http_json_client_test.go b/rpc/jsonrpc/client/http_json_client_test.go index 03134dff583..4932f4096b6 100644 --- a/rpc/jsonrpc/client/http_json_client_test.go +++ b/rpc/jsonrpc/client/http_json_client_test.go @@ -71,6 +71,12 @@ func Test_parsedURL(t *testing.T) { expectedHostWithPath: "example.com:8080/rpc", expectedDialAddress: "example.com:8080", }, + "http path routed endpoint with version": { + url: "https://example.com:8080/rpc/v1", + expectedURL: "https://example.com:8080/rpc/v1", + expectedHostWithPath: "example.com:8080/rpc/v1", + expectedDialAddress: "example.com:8080", + }, } for name, tt := range tests { diff --git a/rpc/jsonrpc/jsonrpc_test.go b/rpc/jsonrpc/jsonrpc_test.go index 1f12c817a0d..f9fb92cc53a 100644 --- a/rpc/jsonrpc/jsonrpc_test.go +++ b/rpc/jsonrpc/jsonrpc_test.go @@ -137,6 +137,7 @@ func setup() { wm := server.NewWebsocketManager(Routes, server.ReadWait(5*time.Second), server.PingPeriod(1*time.Second)) wm.SetLogger(tcpLogger) mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler) + mux.HandleFunc("/v1"+websocketEndpoint, wm.WebsocketHandler) config := server.DefaultConfig() listener1, err := server.Listen(tcpAddr, config.MaxOpenConnections) if err != nil { @@ -154,6 +155,7 @@ func setup() { wm = server.NewWebsocketManager(Routes) wm.SetLogger(unixLogger) mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler) + mux2.HandleFunc("/v1"+websocketEndpoint, wm.WebsocketHandler) listener2, err := server.Listen(unixAddr, config.MaxOpenConnections) if err != nil { panic(err) @@ -336,6 +338,31 @@ func TestServersAndClientsBasic(t *testing.T) { } } +func TestServersAndClientsBasicV1(t *testing.T) { + serverAddrs := [...]string{tcpAddr, unixAddr} + for _, addr := range serverAddrs { + cl1, err := client.NewURI(addr) + require.Nil(t, err) + fmt.Printf("=== testing server on %s using URI client", addr) + testWithHTTPClient(t, cl1) + + cl2, err := client.New(addr) + require.Nil(t, err) + fmt.Printf("=== testing server on %s using JSONRPC client", addr) + testWithHTTPClient(t, cl2) + + cl3, err := client.NewWS(addr, "/v1"+websocketEndpoint) + require.Nil(t, err) + cl3.SetLogger(log.TestingLogger()) + err = cl3.Start() + require.Nil(t, err) + fmt.Printf("=== testing server on %s using WS client", addr) + testWithWSClient(t, cl3) + err = cl3.Stop() + require.NoError(t, err) + } +} + func TestHexStringArg(t *testing.T) { cl, err := client.NewURI(tcpAddr) require.Nil(t, err) @@ -386,6 +413,36 @@ func TestWSNewWSRPCFunc(t *testing.T) { assert.Equal(t, got, val) } +func TestWSNewWSRPCFuncV1(t *testing.T) { + cl, err := client.NewWS(tcpAddr, "/v1"+websocketEndpoint) + require.Nil(t, err) + cl.SetLogger(log.TestingLogger()) + err = cl.Start() + require.Nil(t, err) + t.Cleanup(func() { + if err := cl.Stop(); err != nil { + t.Error(err) + } + }) + + val := testVal + params := map[string]interface{}{ + "arg": val, + } + err = cl.Call(context.Background(), "echo_ws", params) + require.Nil(t, err) + + msg := <-cl.ResponsesCh + if msg.Error != nil { + t.Fatal(err) + } + result := new(ResultEcho) + err = json.Unmarshal(msg.Result, result) + require.Nil(t, err) + got := result.Value + assert.Equal(t, got, val) +} + func TestWSHandlesArrayParams(t *testing.T) { cl, err := client.NewWS(tcpAddr, websocketEndpoint) require.Nil(t, err) @@ -414,6 +471,34 @@ func TestWSHandlesArrayParams(t *testing.T) { assert.Equal(t, got, val) } +func TestWSHandlesArrayParamsV1(t *testing.T) { + cl, err := client.NewWS(tcpAddr, "/v1"+websocketEndpoint) + require.Nil(t, err) + cl.SetLogger(log.TestingLogger()) + err = cl.Start() + require.Nil(t, err) + t.Cleanup(func() { + if err := cl.Stop(); err != nil { + t.Error(err) + } + }) + + val := testVal + params := []interface{}{val} + err = cl.CallWithArrayParams(context.Background(), "echo_ws", params) + require.Nil(t, err) + + msg := <-cl.ResponsesCh + if msg.Error != nil { + t.Fatalf("%+v", err) + } + result := new(ResultEcho) + err = json.Unmarshal(msg.Result, result) + require.Nil(t, err) + got := result.Value + assert.Equal(t, got, val) +} + // TestWSClientPingPong checks that a client & server exchange pings // & pongs so connection stays alive. func TestWSClientPingPong(t *testing.T) { @@ -431,6 +516,21 @@ func TestWSClientPingPong(t *testing.T) { time.Sleep(6 * time.Second) } +func TestWSClientPingPongV1(t *testing.T) { + cl, err := client.NewWS(tcpAddr, "/v1"+websocketEndpoint) + require.Nil(t, err) + cl.SetLogger(log.TestingLogger()) + err = cl.Start() + require.Nil(t, err) + t.Cleanup(func() { + if err := cl.Stop(); err != nil { + t.Error(err) + } + }) + + time.Sleep(6 * time.Second) +} + func TestJSONRPCCaching(t *testing.T) { httpAddr := strings.Replace(tcpAddr, "tcp://", "http://", 1) cl, err := client.DefaultHTTPClient(httpAddr) diff --git a/rpc/jsonrpc/server/http_json_handler.go b/rpc/jsonrpc/server/http_json_handler.go index 8e5b363f151..8f56076a79b 100644 --- a/rpc/jsonrpc/server/http_json_handler.go +++ b/rpc/jsonrpc/server/http_json_handler.go @@ -8,6 +8,7 @@ import ( "net/http" "reflect" "sort" + "strings" cmtjson "github.com/cometbft/cometbft/libs/json" "github.com/cometbft/cometbft/libs/log" @@ -72,7 +73,8 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han ) continue } - if len(r.URL.Path) > 1 { + trimmedPath := strings.Trim(r.URL.Path, "/") + if trimmedPath != "" && trimmedPath != "v1" { responses = append( responses, types.RPCInvalidRequestError(request.ID, fmt.Errorf("path %s is invalid", r.URL.Path)), @@ -130,9 +132,8 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han func handleInvalidJSONRPCPaths(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - // Since the pattern "/" matches all paths not matched by other registered patterns, - // we check whether the path is indeed "/", otherwise return a 404 error - if r.URL.Path != "/" { + trimmedPath := strings.Trim(r.URL.Path, "/") + if trimmedPath != "" && trimmedPath != "v1" { http.NotFound(w, r) return } @@ -235,8 +236,9 @@ func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[st buf.WriteString("
Available endpoints:
") for _, name := range noArgNames { + // FIXME: The link should have the version as well. Where can we get it from the request? link := fmt.Sprintf("//%s/%s", r.Host, name) - buf.WriteString(fmt.Sprintf("%s
", link, link)) + buf.WriteString(fmt.Sprintf("%s
", link, name)) } buf.WriteString("
Endpoints that require arguments:
") diff --git a/rpc/jsonrpc/server/rpc_func.go b/rpc/jsonrpc/server/rpc_func.go index c1984e51fa0..cd7e5273e14 100644 --- a/rpc/jsonrpc/server/rpc_func.go +++ b/rpc/jsonrpc/server/rpc_func.go @@ -17,10 +17,13 @@ func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger lo // HTTP endpoints for funcName, rpcFunc := range funcMap { mux.HandleFunc("/"+funcName, makeHTTPHandler(rpcFunc, logger)) + mux.HandleFunc("/v1/"+funcName, makeHTTPHandler(rpcFunc, logger)) } // JSONRPC endpoints mux.HandleFunc("/", handleInvalidJSONRPCPaths(makeJSONRPCHandler(funcMap, logger))) + mux.HandleFunc("/v1", handleInvalidJSONRPCPaths(makeJSONRPCHandler(funcMap, logger))) + mux.HandleFunc("/v1/", handleInvalidJSONRPCPaths(makeJSONRPCHandler(funcMap, logger))) } type Option func(*RPCFunc) diff --git a/rpc/jsonrpc/server/ws_handler_test.go b/rpc/jsonrpc/server/ws_handler_test.go index da370088ca6..b403151950c 100644 --- a/rpc/jsonrpc/server/ws_handler_test.go +++ b/rpc/jsonrpc/server/ws_handler_test.go @@ -16,30 +16,32 @@ func TestWebsocketManagerHandler(t *testing.T) { s := newWSServer() defer s.Close() - // check upgrader works - d := websocket.Dialer{} - c, dialResp, err := d.Dial("ws://"+s.Listener.Addr().String()+"/websocket", nil) - require.NoError(t, err) - - if got, want := dialResp.StatusCode, http.StatusSwitchingProtocols; got != want { - t.Errorf("dialResp.StatusCode = %q, want %q", got, want) + for _, ep := range []string{"/websocket", "/v1/websocket"} { + // check upgrader works + d := websocket.Dialer{} + c, dialResp, err := d.Dial("ws://"+s.Listener.Addr().String()+ep, nil) + require.NoError(t, err) + + if got, want := dialResp.StatusCode, http.StatusSwitchingProtocols; got != want { + t.Errorf("dialResp.StatusCode = %q, want %q", got, want) + } + + // check basic functionality works + req, err := types.MapToRequest( + types.JSONRPCStringID("TestWebsocketManager"), + "c", + map[string]interface{}{"s": "a", "i": 10}, + ) + require.NoError(t, err) + err = c.WriteJSON(req) + require.NoError(t, err) + + var resp types.RPCResponse + err = c.ReadJSON(&resp) + require.NoError(t, err) + require.Nil(t, resp.Error) + dialResp.Body.Close() } - - // check basic functionality works - req, err := types.MapToRequest( - types.JSONRPCStringID("TestWebsocketManager"), - "c", - map[string]interface{}{"s": "a", "i": 10}, - ) - require.NoError(t, err) - err = c.WriteJSON(req) - require.NoError(t, err) - - var resp types.RPCResponse - err = c.ReadJSON(&resp) - require.NoError(t, err) - require.Nil(t, resp.Error) - dialResp.Body.Close() } func newWSServer() *httptest.Server { @@ -51,6 +53,7 @@ func newWSServer() *httptest.Server { mux := http.NewServeMux() mux.HandleFunc("/websocket", wm.WebsocketHandler) + mux.HandleFunc("/v1/websocket", wm.WebsocketHandler) return httptest.NewServer(mux) } diff --git a/rpc/openapi/openapi.yaml b/rpc/openapi/openapi.yaml index 6d389f777c2..3086b8ffa83 100644 --- a/rpc/openapi/openapi.yaml +++ b/rpc/openapi/openapi.yaml @@ -1,4 +1,4 @@ -openapi: 3.0.0 +openapi: 3.0.3 info: title: CometBFT RPC contact: @@ -9,7 +9,7 @@ info: * URI over HTTP * JSONRPC over HTTP - * JSONRPC over websockets + * JSONRPC over WebSockets ## Configuration @@ -27,7 +27,7 @@ info: section change the `cors_allowed_origins` property, please add the URL of the site where this OpenAPI document is running, for example: - `cors_allowed_origins = ["http://localhost:8088"]` + `cors_allowed_origins = ["http://localhost:26657"]` or if testing from the official documentation site: @@ -38,17 +38,33 @@ info: Arguments which expect strings or byte arrays may be passed as quoted strings, like `"abc"` or as `0x`-prefixed strings, like `0x616263`. + ## Versions + + Up to CometBFT v0.38.x the RPC API version followed CometBFT's version, even if there was + no actual change in the API. + In CometBFT v0.39.0, the RPC API version has changed to v1 and from then on its version + will be independent CometBFT's. + + Also, from CometBFT v0.39.0, the URL used will be suffixed with the RPC version. + For example, to invoke the `block` method, one should use `localhost:26657/v1/block` + as endpoint. + + For backwards compatibility, CometBFT v0.39.x will also supports invoking the methods + without the `v1` suffix, for example, `localhost:26657/block`, returning the exact same response. + However, the support for unsuffixed methods will be dropped in future releases and all users are + encouraged to migrate as soon as possible. + ## URI/HTTP A REST like interface. - curl localhost:26657/block?height=5 + curl localhost:26657/v1/block?height=5 ## JSONRPC/HTTP JSONRPC requests can be POST'd to the root RPC endpoint via HTTP. - curl --header "Content-Type: application/json" --request POST --data '{"method": "block", "params": ["5"], "id": 1}' localhost:26657 + curl --header "Content-Type: application/json" --request POST --data '{"method": "block", "params": ["5"], "id": 1}' localhost:26657/v1 ## JSONRPC/websockets @@ -60,20 +76,20 @@ info: For example using the [websocat](https://github.com/vi/websocat) tool, you can subscribe for 'NewBlock` events with the following command: - echo '{ "jsonrpc": "2.0","method": "subscribe","id": 0,"params": {"query": "tm.event='"'NewBlock'"'"} }' | websocat -n -t ws://127.0.0.1:26657/websocket + echo '{ "jsonrpc": "2.0","method": "subscribe","id": 0,"params": {"query": "tm.event='"'NewBlock'"'"} }' | websocat -n -t ws://127.0.0.1:26657/v1/websocket - version: "main" + version: "v1" license: name: Apache 2.0 url: https://github.com/cometbft/cometbft/blob/main/LICENSE servers: - url: https://rpc.cosmos.directory/cosmoshub - description: Interact with the CometBFT RPC from a public node in the Cosmos registry + description: CometBFT running on a public node in the Cosmos registry - url: http://localhost:26657 - description: Interact with CometBFT RPC node running locally + description: CometBFT running locally (`cors_allowed_origins` must be set; see above) tags: - name: Info - description: Informations about the node APIs + description: Information about the node APIs - name: Tx description: Transactions broadcast APIs - name: ABCI @@ -83,13 +99,15 @@ tags: - name: Unsafe description: Unsafe APIs paths: - /broadcast_tx_sync: + /v1/broadcast_tx_sync: get: - summary: Returns with the response from CheckTx. Does not wait for DeliverTx result. + summary: Submits a transaction to the blockchain and returns with the response from CheckTx. tags: - Tx operationId: broadcast_tx_sync description: | + Submits a transaction to be included in the blockchain and returns the response from CheckTx. + Does not wait for DeliverTx result. If you want to be sure that the transaction is included in a block, you can subscribe for the result using JSONRPC via a websocket. See https://docs.cometbft.com/main/core/subscription.html @@ -112,8 +130,8 @@ paths: required: true schema: type: string - example: "456" - description: The transaction + example: '"456"' + description: The transaction hash responses: "200": description: Empty @@ -127,13 +145,15 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /broadcast_tx_async: + /v1/broadcast_tx_async: get: - summary: Returns right away, with no response. Does not wait for CheckTx nor DeliverTx results. + summary: Submits a transaction to the blockchain and returns right away, with no response. tags: - Tx operationId: broadcast_tx_async description: | + Submits a transaction to be included in the blockchain and returns immediately. + Does not wait for CheckTx or DeliverTx results. If you want to be sure that the transaction is included in a block, you can subscribe for the result using JSONRPC via a websocket. See https://docs.cometbft.com/main/core/subscription.html @@ -156,7 +176,7 @@ paths: required: true schema: type: string - example: "123" + example: '"123"' description: The transaction responses: "200": @@ -171,9 +191,9 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /broadcast_tx_commit: + /v1/broadcast_tx_commit: get: - summary: Returns with the responses from CheckTx and DeliverTx. + summary: Submits a transaction to be included in the blockchain and returns CheckTx and DeliverTx results. tags: - Tx operationId: broadcast_tx_commit @@ -198,7 +218,7 @@ paths: required: true schema: type: string - example: "785" + example: "0x1234" description: The transaction responses: "200": @@ -213,7 +233,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /check_tx: + /v1/check_tx: get: summary: Checks the transaction without executing it. tags: @@ -248,14 +268,15 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /health: + /v1/health: get: - summary: Node heartbeat + summary: Gets the node's health status information. tags: - Info operationId: health description: | - Get node health. Returns empty result (200 OK) on success, no response - in case of an error. + Get node health status. + Returns empty result (200 OK) on success, no response - in case of an error. responses: "200": description: Gets Node Health @@ -269,7 +290,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /status: + /v1/status: get: summary: Node Status operationId: status @@ -290,7 +311,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /net_info: + /v1/net_info: get: summary: Network information operationId: net_info @@ -311,14 +332,14 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /dial_seeds: + /v1/dial_seeds: get: summary: Dial Seeds (Unsafe) operationId: dial_seeds tags: - Unsafe description: | - Dial a peer, this route in under unsafe, and has to manually enabled to use + Dial a peer, this route in under unsafe, and has to be manually enabled to use **Example:** curl 'localhost:26657/dial_seeds?seeds=\["f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:26656","0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:26656"\]' parameters: @@ -343,7 +364,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /dial_peers: + /v1/dial_peers: get: summary: Add Peers/Persistent Peers (unsafe) operationId: dial_peers @@ -393,7 +414,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /blockchain: + /v1/blockchain: get: summary: "Get block headers (max: 20) for minHeight <= height <= maxHeight." operationId: blockchain @@ -432,7 +453,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /header: + /v1/header: get: summary: Get header at a specified height operationId: header @@ -464,7 +485,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /header_by_hash: + /v1/header_by_hash: get: summary: Get header by hash operationId: header_by_hash @@ -496,7 +517,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /block: + /v1/block: get: summary: Get block at a specified height operationId: block @@ -528,7 +549,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /block_by_hash: + /v1/block_by_hash: get: summary: Get block by hash operationId: block_by_hash @@ -560,7 +581,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /block_results: + /v1/block_results: get: summary: Get block results at a specified height operationId: block_results @@ -592,7 +613,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /commit: + /v1/commit: get: summary: Get commit results at a specified height operationId: commit @@ -627,7 +648,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /validators: + /v1/validators: get: summary: Get validator set at a specified height operationId: validators @@ -676,7 +697,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /genesis: + /v1/genesis: get: summary: Get Genesis operationId: genesis @@ -700,7 +721,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /genesis_chunked: + /v1/genesis_chunked: get: summary: Get Genesis in multiple chunks operationId: genesis_chunked @@ -735,7 +756,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /dump_consensus_state: + /v1/dump_consensus_state: get: summary: Get consensus state operationId: dump_consensus_state @@ -761,7 +782,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /consensus_state: + /v1/consensus_state: get: summary: Get consensus state operationId: consensus_state @@ -784,7 +805,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /consensus_params: + /v1/consensus_params: get: summary: Get consensus parameters operationId: consensus_params @@ -816,7 +837,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /unconfirmed_txs: + /v1/unconfirmed_txs: get: summary: Get the list of unconfirmed transactions operationId: unconfirmed_txs @@ -846,7 +867,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /num_unconfirmed_txs: + /v1/num_unconfirmed_txs: get: summary: Get data about unconfirmed transactions operationId: num_unconfirmed_txs @@ -867,7 +888,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /tx_search: + /v1/tx_search: get: summary: Search for transactions description: | @@ -913,8 +934,8 @@ paths: required: false schema: type: string - default: "asc" - example: "asc" + default: '"asc"' + example: '"asc"' tags: - Info responses: @@ -930,7 +951,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /block_search: + /v1/block_search: get: summary: Search for blocks by FinalizeBlock events description: | @@ -968,8 +989,8 @@ paths: required: false schema: type: string - default: "desc" - example: "asc" + default: '"desc"' + example: '"asc"' tags: - Info responses: @@ -985,8 +1006,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - - /tx: + /v1/tx: get: summary: Get transactions by hash operationId: tx @@ -1026,7 +1046,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /abci_info: + /v1/abci_info: get: summary: Get info about the application. operationId: abci_info @@ -1050,7 +1070,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /abci_query: + /v1/abci_query: get: summary: Query the application for some information. operationId: abci_query @@ -1112,7 +1132,7 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorResponse" - /broadcast_evidence: + /v1/broadcast_evidence: get: summary: Broadcast evidence of the misbehavior. operationId: broadcast_evidence @@ -1208,7 +1228,7 @@ components: example: "cosmoshub-2" version: type: string - example: "0.32.1" + example: "0.34.27" channels: type: string example: "4020212223303800" @@ -1220,7 +1240,7 @@ components: properties: tx_index: type: string - example: "on" + example: '"on"' rpc_address: type: string example: "tcp:0.0.0.0:26657" diff --git a/spec/rpc/README.md b/spec/rpc/README.md index e8f2b9fa2d9..a9743bc642d 100644 --- a/spec/rpc/README.md +++ b/spec/rpc/README.md @@ -65,13 +65,13 @@ None ##### HTTP ```sh -curl http://127.0.0.1:26657/health +curl http://127.0.0.1:26657/v1/health ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"health\"}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"health\"}" ``` #### Response @@ -97,13 +97,13 @@ None ##### HTTP ```sh -curl http://127.0.0.1:26657/status +curl http://127.0.0.1:26657/v1/status ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"status\"}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"status\"}" ``` #### Response @@ -166,13 +166,13 @@ None ##### HTTP ```sh -curl http://127.0.0.1:26657/net_info +curl http://127.0.0.1:26657/v1/net_info ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"net_info\"}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"net_info\"}" ``` #### Response @@ -211,15 +211,15 @@ Get block headers. Returned in descending order. May be limited in quantity. ##### HTTP ```sh -curl http://127.0.0.1:26657/blockchain +curl http://127.0.0.1:26657/v1/blockchain -curl http://127.0.0.1:26657/blockchain?minHeight=1&maxHeight=2 +curl http://127.0.0.1:26657/v1/blockchain?minHeight=1&maxHeight=2 ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"blockchain\",\"params\":{\"minHeight\":\"1\", \"maxHeight\":\"2\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"blockchain\",\"params\":{\"minHeight\":\"1\", \"maxHeight\":\"2\"}}" ``` #### Response @@ -285,15 +285,15 @@ Get block at a specified height. ##### HTTP ```sh -curl http://127.0.0.1:26657/block +curl http://127.0.0.1:26657/v1/block -curl http://127.0.0.1:26657/block?height=1 +curl http://127.0.0.1:26657/v1/block?height=1 ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"block\",\"params\":{\"height\":\"1\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"block\",\"params\":{\"height\":\"1\"}}" ``` #### Response @@ -400,13 +400,13 @@ curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\ ##### HTTP ```sh -curl http://127.0.0.1:26657/block_by_hash?hash=0xD70952032620CC4E2737EB8AC379806359D8E0B17B0488F627997A0B043ABDED +curl http://127.0.0.1:26657/v1/block_by_hash?hash=0xD70952032620CC4E2737EB8AC379806359D8E0B17B0488F627997A0B043ABDED ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"block_by_hash\",\"params\":{\"hash\":\"0xD70952032620CC4E2737EB8AC379806359D8E0B17B0488F627997A0B043ABDED\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"block_by_hash\",\"params\":{\"hash\":\"0xD70952032620CC4E2737EB8AC379806359D8E0B17B0488F627997A0B043ABDED\"}}" ``` #### Response @@ -513,16 +513,16 @@ curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\ ##### HTTP ```sh -curl http://127.0.0.1:26657/block_results +curl http://127.0.0.1:26657/v1/block_results -curl http://127.0.0.1:26657/block_results?height=1 +curl http://127.0.0.1:26657/v1/block_results?height=1 ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"block_results\",\"params\":{\"height\":\"1\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"block_results\",\"params\":{\"height\":\"1\"}}" ``` #### Response @@ -620,16 +620,16 @@ curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\ ##### HTTP ```sh -curl http://127.0.0.1:26657/commit +curl http://127.0.0.1:26657/v1/commit -curl http://127.0.0.1:26657/commit?height=1 +curl http://127.0.0.1:26657/v1/commit?height=1 ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"commit\",\"params\":{\"height\":\"1\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"commit\",\"params\":{\"height\":\"1\"}}" ``` #### Response @@ -703,13 +703,13 @@ curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\ ##### HTTP ```sh -curl http://127.0.0.1:26657/validators +curl http://127.0.0.1:26657/v1/validators ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"validators\",\"params\":{\"height\":\"1\", \"page\":\"1\", \"per_page\":\"20\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"validators\",\"params\":{\"height\":\"1\", \"page\":\"1\", \"per_page\":\"20\"}}" ``` #### Response @@ -747,13 +747,13 @@ will return an error: use `genesis_chunked` instead. ##### HTTP ```sh -curl http://127.0.0.1:26657/genesis +curl http://127.0.0.1:26657/v1/genesis ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"genesis\"}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"genesis\"}" ``` #### Response @@ -814,13 +814,13 @@ Get the genesis document in a chunks to support easily transferring larger docum ##### HTTP ```sh -curl http://127.0.0.1:26657/genesis_chunked?chunk=0 +curl http://127.0.0.1:26657/v1/genesis_chunked?chunk=0 ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"genesis_chunked\",\"params\":{\"chunk\":0}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"genesis_chunked\",\"params\":{\"chunk\":0}}" ``` #### Response @@ -850,13 +850,13 @@ Get the consensus parameters. ##### HTTP ```sh -curl http://127.0.0.1:26657/consensus_params +curl http://127.0.0.1:26657/v1/consensus_params ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"consensus_params\"}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"consensus_params\"}" ``` #### Response @@ -899,13 +899,13 @@ Get a list of unconfirmed transactions. ##### HTTP ```sh -curl http://127.0.0.1:26657/unconfirmed_txs +curl http://127.0.0.1:26657/v1/unconfirmed_txs ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"unconfirmed_txs\, \"params\":{\"limit\":\"20\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"unconfirmed_txs\, \"params\":{\"limit\":\"20\"}}" ``` #### Response @@ -938,13 +938,13 @@ None ##### HTTP ```sh -curl http://127.0.0.1:26657/num_unconfirmed_txs +curl http://127.0.0.1:26657/v1/num_unconfirmed_txs ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"num_unconfirmed_txs\"}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"num_unconfirmed_txs\"}" ``` #### Response @@ -973,13 +973,13 @@ curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\ ##### HTTP ```sh -curl http://127.0.0.1:26657/num_unconfirmed_txs +curl http://127.0.0.1:26657/v1/num_unconfirmed_txs ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"num_unconfirmed_txs\"}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"num_unconfirmed_txs\"}" ``` #### Response @@ -1024,13 +1024,13 @@ Returns with the response from CheckTx. Does not wait for DeliverTx result. ##### HTTP ```sh -curl http://127.0.0.1:26657/broadcast_tx_sync?tx=encoded_tx +curl http://127.0.0.1:26657/v1/broadcast_tx_sync?tx=encoded_tx ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"broadcast_tx_sync\",\"params\":{\"tx\":\"a/encoded_tx/c\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"broadcast_tx_sync\",\"params\":{\"tx\":\"a/encoded_tx/c\"}}" ``` #### Response @@ -1063,13 +1063,13 @@ Returns right away, with no response. Does not wait for CheckTx nor DeliverTx re ##### HTTP ```sh -curl http://127.0.0.1:26657/broadcast_tx_async?tx=encoded_tx +curl http://127.0.0.1:26657/v1/broadcast_tx_async?tx=encoded_tx ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"broadcast_tx_async\",\"params\":{\"tx\":\"a/encoded_tx/c\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"broadcast_tx_async\",\"params\":{\"tx\":\"a/encoded_tx/c\"}}" ``` #### Response @@ -1102,13 +1102,13 @@ Checks the transaction without executing it. ##### HTTP ```sh -curl http://127.0.0.1:26657/check_tx?tx=encoded_tx +curl http://127.0.0.1:26657/v1/check_tx?tx=encoded_tx ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"check_tx\",\"params\":{\"tx\":\"a/encoded_tx/c\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"check_tx\",\"params\":{\"tx\":\"a/encoded_tx/c\"}}" ``` #### Response @@ -1157,13 +1157,13 @@ None ##### HTTP ```sh -curl http://127.0.0.1:26657/abci_info +curl http://127.0.0.1:26657/v1/abci_info ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"abci_info\"}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"abci_info\"}" ``` #### Response @@ -1198,13 +1198,13 @@ Query the application for some information. ##### HTTP ```sh -curl 'http://127.0.0.1:26657/abci_query?path="/store/foo/key"&data=0x636f6d6574626674&height=1&prove=true' +curl 'http://127.0.0.1:26657/v1/abci_query?path="/store/foo/key"&data=0x636f6d6574626674&height=1&prove=true' ``` ##### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"abci_query\",\"params\":{\"path\":\"/store/foo/key\", \"data\":\"636f6d6574626674\", \"height\":\"1\", \"prove\":\"true\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"abci_query\",\"params\":{\"path\":\"/store/foo/key\", \"data\":\"636f6d6574626674\", \"height\":\"1\", \"prove\":\"true\"}}" ``` #### Response @@ -1243,13 +1243,13 @@ Broadcast evidence of the misbehavior. ##### HTTP ```sh -curl http://localhost:26657/broadcast_evidence?evidence=JSON_EVIDENCE_encoded +curl http://localhost:26657/v1/broadcast_evidence?evidence=JSON_EVIDENCE_encoded ``` #### JSONRPC ```sh -curl -X POST https://localhost:26657 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"broadcast_evidence\",\"params\":{\"evidence\":\"JSON_EVIDENCE_encoded\"}}" +curl -X POST https://localhost:26657/v1 -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"broadcast_evidence\",\"params\":{\"evidence\":\"JSON_EVIDENCE_encoded\"}}" ``` #### Response diff --git a/statesync/stateprovider.go b/statesync/stateprovider.go index 25bfcb39bfa..3dc87dfabe1 100644 --- a/statesync/stateprovider.go +++ b/statesync/stateprovider.go @@ -196,7 +196,7 @@ func rpcClient(server string) (*rpchttp.HTTP, error) { if !strings.Contains(server, "://") { server = "http://" + server } - c, err := rpchttp.New(server, "/websocket") + c, err := rpchttp.New(server) if err != nil { return nil, err } diff --git a/test/e2e/pkg/testnet.go b/test/e2e/pkg/testnet.go index 7b9dd32865f..f14bd3a9372 100644 --- a/test/e2e/pkg/testnet.go +++ b/test/e2e/pkg/testnet.go @@ -553,7 +553,7 @@ func (n Node) AddressRPC() string { // Client returns an RPC client for the node. func (n Node) Client() (*rpchttp.HTTP, error) { - return rpchttp.New(fmt.Sprintf("http://%s:%v", n.ExternalIP, n.RPCProxyPort), "/websocket") + return rpchttp.New(fmt.Sprintf("http://%s:%v/v1", n.ExternalIP, n.RPCProxyPort)) } // GRPCClient creates a gRPC client for the node. diff --git a/test/loadtime/README.md b/test/loadtime/README.md index c19880c4917..7f61ed42d02 100644 --- a/test/loadtime/README.md +++ b/test/loadtime/README.md @@ -33,7 +33,7 @@ on `localhost:25567` ./build/load \ -c 1 -T 10 -r 1000 -s 1024 \ --broadcast-tx-method sync \ - --endpoints ws://localhost:26657/websocket + --endpoints ws://localhost:26657/v1/websocket ``` ## `report` diff --git a/test/loadtime/basic.sh b/test/loadtime/basic.sh index b135232b8db..a7eab7c275b 100755 --- a/test/loadtime/basic.sh +++ b/test/loadtime/basic.sh @@ -7,5 +7,5 @@ set -euo pipefail ./build/load \ -c 1 -T 10 -r 1000 -s 1024 \ --broadcast-tx-method sync \ - --endpoints ws://localhost:26657/websocket + --endpoints ws://localhost:26657/v1/websocket