Make sure that only `personal_sign` and `personal_ecRecover` are exported.
This commit is contained in:
parent
6598371dc0
commit
f0f55d408f
|
@ -134,7 +134,7 @@ func (b *StatusBackend) startNode(config *params.NodeConfig) (err error) {
|
||||||
|
|
||||||
b.transactor.SetNetworkID(config.NetworkID)
|
b.transactor.SetNetworkID(config.NetworkID)
|
||||||
b.transactor.SetRPC(b.statusNode.RPCClient(), rpc.DefaultCallTimeout)
|
b.transactor.SetRPC(b.statusNode.RPCClient(), rpc.DefaultCallTimeout)
|
||||||
b.personalAPI.SetRPC(b.statusNode.RPCClient(), rpc.DefaultCallTimeout)
|
b.personalAPI.SetRPC(b.statusNode.RPCPrivateClient(), rpc.DefaultCallTimeout)
|
||||||
if err := b.registerHandlers(); err != nil {
|
if err := b.registerHandlers(); err != nil {
|
||||||
b.log.Error("Handler registration failed", "err", err)
|
b.log.Error("Handler registration failed", "err", err)
|
||||||
}
|
}
|
||||||
|
@ -281,6 +281,7 @@ func (b *StatusBackend) registerHandlers() error {
|
||||||
})
|
})
|
||||||
|
|
||||||
rpcClient.RegisterHandler(params.PersonalSignMethodName, b.PersonalAPI().Sign)
|
rpcClient.RegisterHandler(params.PersonalSignMethodName, b.PersonalAPI().Sign)
|
||||||
|
rpcClient.RegisterHandler(params.PersonalRecoverMethodName, b.PersonalAPI().Recover)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ func MakeNode(config *params.NodeConfig) (*node.Node, error) {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
|
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// `personal_sign` and `personal_recover` methods are important to
|
// `personal_sign` and `personal_ecRecover` methods are important to
|
||||||
// keep DApps working.
|
// keep DApps working.
|
||||||
// Usually, they are provided by an ETH or a LES service, but when using
|
// Usually, they are provided by an ETH or a LES service, but when using
|
||||||
// upstream, we don't start any of these, so we need to start our own
|
// upstream, we don't start any of these, so we need to start our own
|
||||||
|
|
|
@ -30,7 +30,8 @@ const (
|
||||||
ListenAddr = ":0"
|
ListenAddr = ":0"
|
||||||
|
|
||||||
// APIModules is a list of modules to expose via any type of RPC (HTTP, IPC, in-proc)
|
// APIModules is a list of modules to expose via any type of RPC (HTTP, IPC, in-proc)
|
||||||
APIModules = "eth,net,web3,shh,shhext,personal"
|
// we also expose 2 limited personal APIs by overriding them in `api/backend.go`
|
||||||
|
APIModules = "eth,net,web3,shh,shhext"
|
||||||
|
|
||||||
// SendTransactionMethodName defines the name for a giving transaction.
|
// SendTransactionMethodName defines the name for a giving transaction.
|
||||||
SendTransactionMethodName = "eth_sendTransaction"
|
SendTransactionMethodName = "eth_sendTransaction"
|
||||||
|
@ -41,6 +42,9 @@ const (
|
||||||
// PersonalSignMethodName defines the name for `personal.sign` API.
|
// PersonalSignMethodName defines the name for `personal.sign` API.
|
||||||
PersonalSignMethodName = "personal_sign"
|
PersonalSignMethodName = "personal_sign"
|
||||||
|
|
||||||
|
// PersonalRecoverMethodName defines the name for `personal.recover` API.
|
||||||
|
PersonalRecoverMethodName = "personal_ecRecover"
|
||||||
|
|
||||||
// MaxPeers is the maximum number of global peers
|
// MaxPeers is the maximum number of global peers
|
||||||
MaxPeers = 25
|
MaxPeers = 25
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,16 @@ func (api *PublicAPI) SetRPC(rpcClient *rpc.Client, timeout time.Duration) {
|
||||||
api.rpcTimeout = timeout
|
api.rpcTimeout = timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recover is an implementation of `personal_ecRecover` or `web3.personal.ecRecover` API
|
||||||
|
func (api *PublicAPI) Recover(context context.Context, rpcParams ...interface{}) (interface{}, error) {
|
||||||
|
var response interface{}
|
||||||
|
|
||||||
|
err := api.rpcClient.CallContextIgnoringLocalHandlers(
|
||||||
|
context, &response, params.PersonalRecoverMethodName, rpcParams...)
|
||||||
|
|
||||||
|
return response, err
|
||||||
|
}
|
||||||
|
|
||||||
// Sign is an implementation of `personal_sign` or `web3.personal.sign` API
|
// Sign is an implementation of `personal_sign` or `web3.personal.sign` API
|
||||||
func (api *PublicAPI) Sign(context context.Context, rpcParams ...interface{}) (interface{}, error) {
|
func (api *PublicAPI) Sign(context context.Context, rpcParams ...interface{}) (interface{}, error) {
|
||||||
metadata, err := newMetadata(rpcParams)
|
metadata, err := newMetadata(rpcParams)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package services
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
@ -12,15 +13,23 @@ import (
|
||||||
"github.com/status-im/status-go/services/personal"
|
"github.com/status-im/status-go/services/personal"
|
||||||
"github.com/status-im/status-go/sign"
|
"github.com/status-im/status-go/sign"
|
||||||
e2e "github.com/status-im/status-go/t/e2e"
|
e2e "github.com/status-im/status-go/t/e2e"
|
||||||
. "github.com/status-im/status-go/t/utils"
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
. "github.com/status-im/status-go/t/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
signDataString = "0xBAADBEEF"
|
signDataString = "0xBAADBEEF"
|
||||||
accountNotExists = "0x00164ca341326a03b547c05B343b2E21eFAe2400"
|
accountNotExists = "0x00164ca341326a03b547c05B343b2E21eFAe2400"
|
||||||
|
|
||||||
|
// see vendor/github.com/ethereum/go-ethereum/rpc/errors.go:L27
|
||||||
|
methodNotFoundErrorCode = -32601
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type rpcError struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
}
|
||||||
|
|
||||||
type testParams struct {
|
type testParams struct {
|
||||||
Title string
|
Title string
|
||||||
EnableUpstream bool
|
EnableUpstream bool
|
||||||
|
@ -48,6 +57,48 @@ type PersonalSignSuite struct {
|
||||||
upstream bool
|
upstream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PersonalSignSuite) TestRestrictedPersonalAPIs() {
|
||||||
|
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
|
||||||
|
s.T().Skip()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.initTest(s.upstream)
|
||||||
|
s.NoError(err)
|
||||||
|
defer func() {
|
||||||
|
err := s.Backend.StopNode()
|
||||||
|
s.NoError(err)
|
||||||
|
}()
|
||||||
|
// These personal APIs should be available
|
||||||
|
s.testAPIExported("personal_sign", true)
|
||||||
|
s.testAPIExported("personal_ecRecover", true)
|
||||||
|
// These personal APIs shouldn't be exported
|
||||||
|
s.testAPIExported("personal_sendTransaction", false)
|
||||||
|
s.testAPIExported("personal_unlockAccount", false)
|
||||||
|
s.testAPIExported("personal_newAccount", false)
|
||||||
|
s.testAPIExported("personal_lockAccount", false)
|
||||||
|
s.testAPIExported("personal_listAccounts", false)
|
||||||
|
s.testAPIExported("personal_importRawKey", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PersonalSignSuite) testAPIExported(method string, expectExported bool) {
|
||||||
|
cmd := fmt.Sprintf(`{"jsonrpc":"2.0", "method": "%s", "params": []}`, method)
|
||||||
|
|
||||||
|
result := s.Backend.CallRPC(cmd)
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Error *rpcError `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
s.NoError(json.Unmarshal([]byte(result), &response))
|
||||||
|
|
||||||
|
hidden := (response.Error != nil && response.Error.Code == methodNotFoundErrorCode)
|
||||||
|
|
||||||
|
s.Equal(expectExported, !hidden,
|
||||||
|
"method %s should be %s, but it isn't",
|
||||||
|
method, map[bool]string{true: "exported", false: "hidden"}[expectExported])
|
||||||
|
}
|
||||||
|
|
||||||
func (s *PersonalSignSuite) TestPersonalSignSuccess() {
|
func (s *PersonalSignSuite) TestPersonalSignSuccess() {
|
||||||
s.testPersonalSign(testParams{
|
s.testPersonalSign(testParams{
|
||||||
Account: TestConfig.Account1.Address,
|
Account: TestConfig.Account1.Address,
|
||||||
|
@ -128,24 +179,6 @@ func (s *PersonalSignSuite) notificationHandlerNoAccountSelected(account string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PersonalSignSuite) initTest(upstreamEnabled bool) error {
|
|
||||||
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
|
|
||||||
s.NoError(err)
|
|
||||||
|
|
||||||
nodeConfig.IPCEnabled = false
|
|
||||||
nodeConfig.HTTPHost = "" // to make sure that no HTTP interface is started
|
|
||||||
|
|
||||||
if upstreamEnabled {
|
|
||||||
networkURL, err := GetRemoteURL()
|
|
||||||
s.NoError(err)
|
|
||||||
|
|
||||||
nodeConfig.UpstreamConfig.Enabled = true
|
|
||||||
nodeConfig.UpstreamConfig.URL = networkURL
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.Backend.StartNode(nodeConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *PersonalSignSuite) notificationHandler(account string, pass string, expectedError error) func(string) {
|
func (s *PersonalSignSuite) notificationHandler(account string, pass string, expectedError error) func(string) {
|
||||||
return func(jsonEvent string) {
|
return func(jsonEvent string) {
|
||||||
envelope := unmarshalEnvelope(jsonEvent)
|
envelope := unmarshalEnvelope(jsonEvent)
|
||||||
|
@ -173,11 +206,11 @@ func (s *PersonalSignSuite) notificationHandler(account string, pass string, exp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PersonalSignSuite) testPersonalSign(testParams testParams) {
|
func (s *PersonalSignSuite) testPersonalSign(testParams testParams) string {
|
||||||
// Test upstream if that's not StatusChain
|
// Test upstream if that's not StatusChain
|
||||||
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
|
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
|
||||||
s.T().Skip()
|
s.T().Skip()
|
||||||
return
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if testParams.HandlerFactory == nil {
|
if testParams.HandlerFactory == nil {
|
||||||
|
@ -207,9 +240,11 @@ func (s *PersonalSignSuite) testPersonalSign(testParams testParams) {
|
||||||
result := s.Backend.CallRPC(basicCall)
|
result := s.Backend.CallRPC(basicCall)
|
||||||
if testParams.ExpectedError == nil {
|
if testParams.ExpectedError == nil {
|
||||||
s.NotContains(result, "error")
|
s.NotContains(result, "error")
|
||||||
} else {
|
return s.extractResultFromRPCResponse(result)
|
||||||
s.Contains(result, testParams.ExpectedError.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.Contains(result, testParams.ExpectedError.Error())
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalEnvelope(jsonEvent string) signal.Envelope {
|
func unmarshalEnvelope(jsonEvent string) signal.Envelope {
|
||||||
|
@ -219,3 +254,64 @@ func unmarshalEnvelope(jsonEvent string) signal.Envelope {
|
||||||
}
|
}
|
||||||
return envelope
|
return envelope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PersonalSignSuite) TestPersonalRecoverSuccess() {
|
||||||
|
|
||||||
|
// 1. Sign
|
||||||
|
signedData := s.testPersonalSign(testParams{
|
||||||
|
Account: TestConfig.Account1.Address,
|
||||||
|
Password: TestConfig.Account1.Password,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test upstream if that's not StatusChain
|
||||||
|
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
|
||||||
|
s.T().Skip()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.initTest(s.upstream)
|
||||||
|
s.NoError(err)
|
||||||
|
defer func() {
|
||||||
|
err := s.Backend.StopNode()
|
||||||
|
s.NoError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 2. Test recover
|
||||||
|
basicCall := fmt.Sprintf(
|
||||||
|
`{"jsonrpc":"2.0","method":"personal_ecRecover","params":["%s", "%s"],"id":67}`,
|
||||||
|
signDataString,
|
||||||
|
signedData)
|
||||||
|
|
||||||
|
response := s.Backend.CallRPC(basicCall)
|
||||||
|
|
||||||
|
result := s.extractResultFromRPCResponse(response)
|
||||||
|
|
||||||
|
s.True(strings.EqualFold(result, TestConfig.Account1.Address))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PersonalSignSuite) initTest(upstreamEnabled bool) error {
|
||||||
|
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
nodeConfig.IPCEnabled = false
|
||||||
|
nodeConfig.HTTPHost = "" // to make sure that no HTTP interface is started
|
||||||
|
|
||||||
|
if upstreamEnabled {
|
||||||
|
networkURL, err := GetRemoteURL()
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
nodeConfig.UpstreamConfig.Enabled = true
|
||||||
|
nodeConfig.UpstreamConfig.URL = networkURL
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Backend.StartNode(nodeConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PersonalSignSuite) extractResultFromRPCResponse(response string) string {
|
||||||
|
var r struct {
|
||||||
|
Result string `json:"result"`
|
||||||
|
}
|
||||||
|
s.NoError(json.Unmarshal([]byte(response), &r))
|
||||||
|
|
||||||
|
return r.Result
|
||||||
|
}
|
Loading…
Reference in New Issue