mirror of
https://github.com/status-im/status-go.git
synced 2025-01-21 20:20:29 +00:00
Add CallPrivateRPC binding (#825)
This PR provides CallPrivateRPC binding, which can call both public and private bindings but should not be used in web3.js provider implementations.
This commit is contained in:
parent
0b123ed407
commit
ef160e8720
@ -103,11 +103,16 @@ func (api *StatusAPI) ResetChainDataAsync() <-chan error {
|
||||
return runAsync(api.ResetChainData)
|
||||
}
|
||||
|
||||
// CallRPC executes RPC request on node's in-proc RPC server
|
||||
// CallRPC executes public RPC requests on node's in-proc RPC server.
|
||||
func (api *StatusAPI) CallRPC(inputJSON string) string {
|
||||
return api.b.CallRPC(inputJSON)
|
||||
}
|
||||
|
||||
// CallPrivateRPC executes public and private RPC requests on node's in-proc RPC server.
|
||||
func (api *StatusAPI) CallPrivateRPC(inputJSON string) string {
|
||||
return api.b.CallPrivateRPC(inputJSON)
|
||||
}
|
||||
|
||||
// CreateAccount creates an internal geth account
|
||||
// BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
|
||||
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
||||
|
@ -194,12 +194,18 @@ func (b *StatusBackend) ResetChainData() error {
|
||||
return b.startNode(&newcfg)
|
||||
}
|
||||
|
||||
// CallRPC executes RPC request on node's in-proc RPC server
|
||||
// CallRPC executes public RPC requests on node's in-proc RPC server.
|
||||
func (b *StatusBackend) CallRPC(inputJSON string) string {
|
||||
client := b.statusNode.RPCClient()
|
||||
return client.CallRaw(inputJSON)
|
||||
}
|
||||
|
||||
// CallPrivateRPC executes public and private RPC requests on node's in-proc RPC server.
|
||||
func (b *StatusBackend) CallPrivateRPC(inputJSON string) string {
|
||||
client := b.statusNode.RPCPrivateClient()
|
||||
return client.CallRaw(inputJSON)
|
||||
}
|
||||
|
||||
// SendTransaction creates a new transaction and waits until it's complete.
|
||||
func (b *StatusBackend) SendTransaction(ctx context.Context, args transactions.SendTxArgs) (hash gethcommon.Hash, err error) {
|
||||
return b.transactor.SendTransaction(ctx, args)
|
||||
|
@ -46,9 +46,10 @@ type EthNodeError error
|
||||
type StatusNode struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
config *params.NodeConfig // Status node configuration
|
||||
gethNode *node.Node // reference to Geth P2P stack/node
|
||||
rpcClient *rpc.Client // reference to public RPC client
|
||||
config *params.NodeConfig // Status node configuration
|
||||
gethNode *node.Node // reference to Geth P2P stack/node
|
||||
rpcClient *rpc.Client // reference to public RPC client
|
||||
rpcPrivateClient *rpc.Client // reference to private RPC client (can call private APIs)
|
||||
|
||||
register *peers.Register
|
||||
peerPool *peers.PeerPool
|
||||
@ -95,21 +96,42 @@ func (n *StatusNode) start(config *params.NodeConfig, services []node.ServiceCon
|
||||
if err := ethNode.Start(); err != nil {
|
||||
return EthNodeError(err)
|
||||
}
|
||||
|
||||
// init RPC client for this node
|
||||
localRPCClient, err := n.gethNode.AttachPublic()
|
||||
if err == nil {
|
||||
n.rpcClient, err = rpc.NewClient(localRPCClient, n.config.UpstreamConfig)
|
||||
}
|
||||
if err != nil {
|
||||
if err := n.setupRPCClient(); err != nil {
|
||||
n.log.Error("Failed to create an RPC client", "error", err)
|
||||
return RPCClientError(err)
|
||||
}
|
||||
|
||||
// start peer pool only if Discovery V5 is enabled
|
||||
if ethNode.Server().DiscV5 != nil {
|
||||
return n.startPeerPool()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *StatusNode) setupRPCClient() (err error) {
|
||||
// setup public RPC client
|
||||
gethNodeClient, err := n.gethNode.AttachPublic()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n.rpcClient, err = rpc.NewClient(gethNodeClient, n.config.UpstreamConfig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// setup private RPC client
|
||||
gethNodePrivateClient, err := n.gethNode.Attach()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n.rpcPrivateClient, err = rpc.NewClient(gethNodePrivateClient, n.config.UpstreamConfig)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (n *StatusNode) startPeerPool() error {
|
||||
statusDB, err := db.Create(filepath.Join(n.config.DataDir, params.StatusDatabase))
|
||||
if err != nil {
|
||||
@ -152,6 +174,7 @@ func (n *StatusNode) stop() error {
|
||||
n.gethNode = nil
|
||||
n.config = nil
|
||||
n.rpcClient = nil
|
||||
n.rpcPrivateClient = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -377,6 +400,14 @@ func (n *StatusNode) RPCClient() *rpc.Client {
|
||||
return n.rpcClient
|
||||
}
|
||||
|
||||
// RPCPrivateClient exposes reference to RPC client connected to the running node
|
||||
// that can call both public and private APIs.
|
||||
func (n *StatusNode) RPCPrivateClient() *rpc.Client {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
return n.rpcPrivateClient
|
||||
}
|
||||
|
||||
// isAvailable check if we have a node running and make sure is fully started
|
||||
func (n *StatusNode) isAvailable() error {
|
||||
if n.gethNode == nil || n.gethNode.Server() == nil {
|
||||
|
@ -86,7 +86,7 @@ func TestNodeRPCClientCallOnlyPublicAPIs(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "some method result", result)
|
||||
|
||||
// call private API
|
||||
// call private API with public RPC client
|
||||
err = client.Call(&result, "pri_someMethod")
|
||||
require.EqualError(t, err, "The method pri_someMethod does not exist/is not available")
|
||||
}
|
||||
@ -114,3 +114,26 @@ func TestNodeRPCClientCallWhitelistedPrivateService(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "some method result", result)
|
||||
}
|
||||
|
||||
func TestNodeRPCPrivateClientCallPrivateService(t *testing.T) {
|
||||
var err error
|
||||
|
||||
config, err := MakeTestNodeConfig(GetNetworkID())
|
||||
require.NoError(t, err)
|
||||
|
||||
statusNode, err := createStatusNode(config)
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
err := statusNode.Stop()
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
client := statusNode.RPCPrivateClient()
|
||||
require.NotNil(t, client)
|
||||
|
||||
// call private API with private RPC client
|
||||
var result string
|
||||
err = client.Call(&result, "pri_someMethod")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "some method result", result)
|
||||
}
|
||||
|
@ -101,13 +101,20 @@ func ResetChainData() *C.char {
|
||||
return makeJSONResponse(nil)
|
||||
}
|
||||
|
||||
//CallRPC calls status node via rpc
|
||||
//CallRPC calls public APIs via RPC
|
||||
//export CallRPC
|
||||
func CallRPC(inputJSON *C.char) *C.char {
|
||||
outputJSON := statusAPI.CallRPC(C.GoString(inputJSON))
|
||||
return C.CString(outputJSON)
|
||||
}
|
||||
|
||||
//CallPrivateRPC calls both public and private APIs via RPC
|
||||
//export CallPrivateRPC
|
||||
func CallPrivateRPC(inputJSON *C.char) *C.char {
|
||||
outputJSON := statusAPI.CallPrivateRPC(C.GoString(inputJSON))
|
||||
return C.CString(outputJSON)
|
||||
}
|
||||
|
||||
//CreateAccount is equivalent to creating an account from the command line,
|
||||
// just modified to handle the function arg passing
|
||||
//export CreateAccount
|
||||
|
@ -96,6 +96,14 @@ func testExportedAPI(t *testing.T, done chan struct{}) {
|
||||
"call RPC on in-proc handler",
|
||||
testCallRPC,
|
||||
},
|
||||
{
|
||||
"call private API using RPC",
|
||||
testCallRPCWithPrivateAPI,
|
||||
},
|
||||
{
|
||||
"call private API using private RPC client",
|
||||
testCallPrivateRPCWithPrivateAPI,
|
||||
},
|
||||
{
|
||||
"create main and child accounts",
|
||||
testCreateChildAccount,
|
||||
@ -381,6 +389,29 @@ func testCallRPC(t *testing.T) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func testCallRPCWithPrivateAPI(t *testing.T) bool {
|
||||
expected := `{"jsonrpc":"2.0","id":64,"error":{"code":-32601,"message":"The method admin_nodeInfo does not exist/is not available"}}`
|
||||
rawResponse := CallRPC(C.CString(`{"jsonrpc":"2.0","method":"admin_nodeInfo","params":[],"id":64}`))
|
||||
received := C.GoString(rawResponse)
|
||||
if expected != received {
|
||||
t.Errorf("unexpected response: expected: %v, got: %v", expected, received)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func testCallPrivateRPCWithPrivateAPI(t *testing.T) bool {
|
||||
rawResponse := CallPrivateRPC(C.CString(`{"jsonrpc":"2.0","method":"admin_nodeInfo","params":[],"id":64}`))
|
||||
received := C.GoString(rawResponse)
|
||||
if strings.Contains(received, "error") {
|
||||
t.Errorf("unexpected response containing error: %v", received)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func testCreateChildAccount(t *testing.T) bool { //nolint: gocyclo
|
||||
// to make sure that we start with empty account (which might get populated during previous tests)
|
||||
if err := statusAPI.Logout(); err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user