fixed that shh_newMessageFilter was erroneously routed to the upstream instead of local (#345)

An issue arose that shh_newMessageFilter was routed to the upstream instead of local node. This PR fixes that. It also revisits routing logic and makes all requests go to the local node by default.
This commit is contained in:
Ivan Tomilov 2017-09-19 12:52:38 +03:00 committed by GitHub
parent 78fb5e963f
commit 9d01f7aa26
3 changed files with 70 additions and 56 deletions

View File

@ -72,9 +72,8 @@ func (c *Client) Call(result interface{}, method string, args ...interface{}) er
//
// It uses custom routing scheme for calls.
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
if c.router.routeLocally(method) {
return c.local.CallContext(ctx, result, method, args...)
if c.router.routeRemote(method) {
return c.upstream.CallContext(ctx, result, method, args...)
}
return c.upstream.CallContext(ctx, result, method, args...)
return c.local.CallContext(ctx, result, method, args...)
}

View File

@ -15,60 +15,73 @@ func newRouter(upstreamEnabled bool) *router {
upstreamEnabled: upstreamEnabled,
}
for _, m := range localMethods {
for _, m := range remoteMethods {
r.methods[m] = true
}
return r
}
// routeLocally returns true if given method should be routed to
// the local node
func (r *router) routeLocally(method string) bool {
// if upstream is disabled, always route to
// the local node
// routeRemote returns true if given method should be routed to the remote node
func (r *router) routeRemote(method string) bool {
if !r.upstreamEnabled {
return true
return false
}
// else check route using the methods list
return r.methods[method]
}
// localMethods contains methods that should be routed to
// the local node; the rest is considered to be routed to
// the upstream node.
// TODO: in the future, default option may be "local node",
// so it'd be convinient to keep track of "remoteMethods" here.
var localMethods = [...]string{
//Whisper commands
"shh_post",
"shh_version",
"shh_newIdentity",
"shh_hasIdentity",
"shh_newGroup",
"shh_addToGroup",
"shh_newFilter",
"shh_uninstallFilter",
"shh_getFilterChanges",
"shh_getMessages",
// DB commands
"db_putString",
"db_getString",
"db_putHex",
"db_getHex",
// Other commands
"net_version",
"net_peerCount",
"net_listening",
// blockchain commands
"eth_sign",
"eth_accounts",
"eth_getCompilers",
"eth_compileLLL",
"eth_compileSolidity",
"eth_compileSerpent",
// remoteMethods contains methods that should be routed to
// the upstream node; the rest is considered to be routed to
// the local node.
// TODO(tiabc): Write a test on each of these methods to ensure they're all routed to the proper node and ensure they really work.
// Although it's tempting to only list methods coming to the local node as there're fewer of them
// but it's deceptive: we want to ensure that only known requests leave our zone of responsibility.
// Also, we want new requests in newer Geth versions not to be accidentally routed to the upstream.
// The list of methods: https://github.com/ethereum/wiki/wiki/JSON-RPC
var remoteMethods = [...]string{
"eth_protocolVersion",
"eth_syncing",
"eth_coinbase",
"eth_mining",
"eth_hashrate",
"eth_gasPrice",
//"eth_accounts", // goes to the local because we handle sub-accounts
"eth_blockNumber",
"eth_getBalance",
"eth_getStorageAt",
"eth_getTransactionCount",
"eth_getBlockTransactionCountByHash",
"eth_getBlockTransactionCountByNumber",
"eth_getUncleCountByBlockHash",
"eth_getUncleCountByBlockNumber",
"eth_getCode",
//"eth_sign", // goes to the local because only the local node has an injected account to sign the payload with
"eth_sendTransaction",
"eth_sendRawTransaction",
"eth_call",
"eth_estimateGas",
"eth_getBlockByHash",
"eth_getBlockByNumber",
"eth_getTransactionByHash",
"eth_getTransactionByBlockHashAndIndex",
"eth_getTransactionByBlockNumberAndIndex",
"eth_getTransactionReceipt",
"eth_getUncleByBlockHashAndIndex",
"eth_getUncleByBlockNumberAndIndex",
//"eth_getCompilers", // goes to the local because there's no need to send it anywhere
//"eth_compileLLL", // goes to the local because there's no need to send it anywhere
//"eth_compileSolidity", // goes to the local because there's no need to send it anywhere
//"eth_compileSerpent", // goes to the local because there's no need to send it anywhere
"eth_newFilter",
"eth_newBlockFilter",
"eth_newPendingTransactionFilter",
"eth_uninstallFilter",
"eth_getFilterChanges",
"eth_getFilterLogs",
"eth_getLogs",
"eth_getWork",
"eth_submitWork",
"eth_submitHashrate",
}

View File

@ -6,28 +6,30 @@ import (
)
// some of the upstream examples
var upstreamMethods = []string{"some_weirdo_method", "eth_syncing", "eth_getBalance", "eth_call", "eth_getTransactionReceipt"}
var localMethods = []string{"some_weirdo_method", "shh_newMessageFilter", "net_version"}
func TestRouteWithUpstream(t *testing.T) {
router := newRouter(true)
for _, method := range localMethods {
require.True(t, router.routeLocally(method))
for _, method := range remoteMethods {
require.True(t, router.routeRemote(method))
}
for _, method := range upstreamMethods {
require.False(t, router.routeLocally(method))
for _, method := range localMethods {
t.Run(method, func(t *testing.T) {
require.False(t, router.routeRemote(method))
})
}
}
func TestRouteWithoutUpstream(t *testing.T) {
router := newRouter(false)
for _, method := range localMethods {
require.True(t, router.routeLocally(method))
for _, method := range remoteMethods {
require.True(t, router.routeRemote(method))
}
for _, method := range upstreamMethods {
require.True(t, router.routeLocally(method))
for _, method := range localMethods {
require.True(t, router.routeRemote(method))
}
}