Anton Kaliaev d741c7b478
limit number of /subscribe clients and queries per client (#3269)
* limit number of /subscribe clients and queries per client

Add the following config variables (under [rpc] section):
  * max_subscription_clients
  * max_subscriptions_per_client
  * timeout_broadcast_tx_commit

Fixes #2826

new HTTPClient interface for subscriptions

finalize HTTPClient events interface

remove EventSubscriber

fix data race

```
WARNING: DATA RACE
Read at 0x00c000a36060 by goroutine 129:
  github.com/tendermint/tendermint/rpc/client.(*Local).Subscribe.func1()
      /go/src/github.com/tendermint/tendermint/rpc/client/localclient.go:168 +0x1f0

Previous write at 0x00c000a36060 by goroutine 132:
  github.com/tendermint/tendermint/rpc/client.(*Local).Subscribe()
      /go/src/github.com/tendermint/tendermint/rpc/client/localclient.go:191 +0x4e0
  github.com/tendermint/tendermint/rpc/client.WaitForOneEvent()
      /go/src/github.com/tendermint/tendermint/rpc/client/helpers.go:64 +0x178
  github.com/tendermint/tendermint/rpc/client_test.TestTxEventsSentWithBroadcastTxSync.func1()
      /go/src/github.com/tendermint/tendermint/rpc/client/event_test.go:139 +0x298
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162

Goroutine 129 (running) created at:
  github.com/tendermint/tendermint/rpc/client.(*Local).Subscribe()
      /go/src/github.com/tendermint/tendermint/rpc/client/localclient.go:164 +0x4b7
  github.com/tendermint/tendermint/rpc/client.WaitForOneEvent()
      /go/src/github.com/tendermint/tendermint/rpc/client/helpers.go:64 +0x178
  github.com/tendermint/tendermint/rpc/client_test.TestTxEventsSentWithBroadcastTxSync.func1()
      /go/src/github.com/tendermint/tendermint/rpc/client/event_test.go:139 +0x298
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162

Goroutine 132 (running) created at:
  testing.(*T).Run()
      /usr/local/go/src/testing/testing.go:878 +0x659
  github.com/tendermint/tendermint/rpc/client_test.TestTxEventsSentWithBroadcastTxSync()
      /go/src/github.com/tendermint/tendermint/rpc/client/event_test.go:119 +0x186
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162
==================
```

lite client works (tested manually)

godoc comments

httpclient: do not close the out channel

use TimeoutBroadcastTxCommit

no timeout for unsubscribe

but 1s Local (5s HTTP) timeout for resubscribe

format code

change Subscribe#out cap to 1

and replace config vars with RPCConfig

TimeoutBroadcastTxCommit can't be greater than rpcserver.WriteTimeout

rpc: Context as first parameter to all functions

reformat code

fixes after my own review

fixes after Ethan's review

add test stubs

fix config.toml

* fixes after manual testing

- rpc: do not recommend to use BroadcastTxCommit because it's slow and wastes
Tendermint resources (pubsub)
- rpc: better error in Subscribe and BroadcastTxCommit
- HTTPClient: do not resubscribe if err = ErrAlreadySubscribed

* fixes after Ismail's review

* Update rpc/grpc/grpc_test.go

Co-Authored-By: melekes <anton.kalyaev@gmail.com>
2019-03-11 22:45:58 +04:00

254 lines
6.4 KiB
Go

package core
import (
"fmt"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/lib/types"
)
// Get network info.
//
// ```shell
// curl 'localhost:26657/net_info'
// ```
//
// ```go
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
// err := client.Start()
// if err != nil {
// // handle error
// }
// defer client.Stop()
// info, err := client.NetInfo()
// ```
//
// > The above command returns JSON structured like this:
//
// ```json
// {
// "jsonrpc": "2.0",
// "id": "",
// "result": {
// "listening": true,
// "listeners": [
// "Listener(@)"
// ],
// "n_peers": "3",
// "peers": [
// {
// "node_info": {
// "protocol_version": {
// "p2p": "7",
// "block": "8",
// "app": "1"
// },
// "id": "93529da3435c090d02251a050342b6a488d4ab56",
// "listen_addr": "tcp://0.0.0.0:26656",
// "network": "chain-RFo6qC",
// "version": "0.30.0",
// "channels": "4020212223303800",
// "moniker": "fc89e4ed23f2",
// "other": {
// "tx_index": "on",
// "rpc_address": "tcp://0.0.0.0:26657"
// }
// },
// "is_outbound": true,
// "connection_status": {
// "Duration": "3475230558",
// "SendMonitor": {
// "Active": true,
// "Start": "2019-02-14T12:40:47.52Z",
// "Duration": "3480000000",
// "Idle": "240000000",
// "Bytes": "4512",
// "Samples": "9",
// "InstRate": "1338",
// "CurRate": "2046",
// "AvgRate": "1297",
// "PeakRate": "6570",
// "BytesRem": "0",
// "TimeRem": "0",
// "Progress": 0
// },
// "RecvMonitor": {
// "Active": true,
// "Start": "2019-02-14T12:40:47.52Z",
// "Duration": "3480000000",
// "Idle": "280000000",
// "Bytes": "4489",
// "Samples": "10",
// "InstRate": "1821",
// "CurRate": "1663",
// "AvgRate": "1290",
// "PeakRate": "5512",
// "BytesRem": "0",
// "TimeRem": "0",
// "Progress": 0
// },
// "Channels": [
// {
// "ID": 48,
// "SendQueueCapacity": "1",
// "SendQueueSize": "0",
// "Priority": "5",
// "RecentlySent": "0"
// },
// {
// "ID": 64,
// "SendQueueCapacity": "1000",
// "SendQueueSize": "0",
// "Priority": "10",
// "RecentlySent": "14"
// },
// {
// "ID": 32,
// "SendQueueCapacity": "100",
// "SendQueueSize": "0",
// "Priority": "5",
// "RecentlySent": "619"
// },
// {
// "ID": 33,
// "SendQueueCapacity": "100",
// "SendQueueSize": "0",
// "Priority": "10",
// "RecentlySent": "1363"
// },
// {
// "ID": 34,
// "SendQueueCapacity": "100",
// "SendQueueSize": "0",
// "Priority": "5",
// "RecentlySent": "2145"
// },
// {
// "ID": 35,
// "SendQueueCapacity": "2",
// "SendQueueSize": "0",
// "Priority": "1",
// "RecentlySent": "0"
// },
// {
// "ID": 56,
// "SendQueueCapacity": "1",
// "SendQueueSize": "0",
// "Priority": "5",
// "RecentlySent": "0"
// },
// {
// "ID": 0,
// "SendQueueCapacity": "10",
// "SendQueueSize": "0",
// "Priority": "1",
// "RecentlySent": "10"
// }
// ]
// },
// "remote_ip": "192.167.10.3"
// },
// ...
// }
// ```
func NetInfo(ctx *rpctypes.Context) (*ctypes.ResultNetInfo, error) {
out, in, _ := p2pPeers.NumPeers()
peers := make([]ctypes.Peer, 0, out+in)
for _, peer := range p2pPeers.Peers().List() {
nodeInfo, ok := peer.NodeInfo().(p2p.DefaultNodeInfo)
if !ok {
return nil, fmt.Errorf("peer.NodeInfo() is not DefaultNodeInfo")
}
peers = append(peers, ctypes.Peer{
NodeInfo: nodeInfo,
IsOutbound: peer.IsOutbound(),
ConnectionStatus: peer.Status(),
RemoteIP: peer.RemoteIP().String(),
})
}
// TODO: Should we include PersistentPeers and Seeds in here?
// PRO: useful info
// CON: privacy
return &ctypes.ResultNetInfo{
Listening: p2pTransport.IsListening(),
Listeners: p2pTransport.Listeners(),
NPeers: len(peers),
Peers: peers,
}, nil
}
func UnsafeDialSeeds(ctx *rpctypes.Context, seeds []string) (*ctypes.ResultDialSeeds, error) {
if len(seeds) == 0 {
return &ctypes.ResultDialSeeds{}, errors.New("No seeds provided")
}
// starts go routines to dial each peer after random delays
logger.Info("DialSeeds", "addrBook", addrBook, "seeds", seeds)
err := p2pPeers.DialPeersAsync(addrBook, seeds, false)
if err != nil {
return &ctypes.ResultDialSeeds{}, err
}
return &ctypes.ResultDialSeeds{Log: "Dialing seeds in progress. See /net_info for details"}, nil
}
func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent bool) (*ctypes.ResultDialPeers, error) {
if len(peers) == 0 {
return &ctypes.ResultDialPeers{}, errors.New("No peers provided")
}
// starts go routines to dial each peer after random delays
logger.Info("DialPeers", "addrBook", addrBook, "peers", peers, "persistent", persistent)
err := p2pPeers.DialPeersAsync(addrBook, peers, persistent)
if err != nil {
return &ctypes.ResultDialPeers{}, err
}
return &ctypes.ResultDialPeers{Log: "Dialing peers in progress. See /net_info for details"}, nil
}
// Get genesis file.
//
// ```shell
// curl 'localhost:26657/genesis'
// ```
//
// ```go
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
// err := client.Start()
// if err != nil {
// // handle error
// }
// defer client.Stop()
// genesis, err := client.Genesis()
// ```
//
// > The above command returns JSON structured like this:
//
// ```json
// {
// "error": "",
// "result": {
// "genesis": {
// "app_hash": "",
// "validators": [
// {
// "name": "",
// "power": "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(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) {
return &ctypes.ResultGenesis{Genesis: genDoc}, nil
}