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.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 {
|
||||
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.PersonalRecoverMethodName, b.PersonalAPI().Recover)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ func MakeNode(config *params.NodeConfig) (*node.Node, error) {
|
|||
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
|
||||
}
|
||||
} else {
|
||||
// `personal_sign` and `personal_recover` methods are important to
|
||||
// `personal_sign` and `personal_ecRecover` methods are important to
|
||||
// keep DApps working.
|
||||
// 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
|
||||
|
|
|
@ -30,7 +30,8 @@ const (
|
|||
ListenAddr = ":0"
|
||||
|
||||
// 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 = "eth_sendTransaction"
|
||||
|
@ -41,6 +42,9 @@ const (
|
|||
// PersonalSignMethodName defines the name for `personal.sign` API.
|
||||
PersonalSignMethodName = "personal_sign"
|
||||
|
||||
// PersonalRecoverMethodName defines the name for `personal.recover` API.
|
||||
PersonalRecoverMethodName = "personal_ecRecover"
|
||||
|
||||
// MaxPeers is the maximum number of global peers
|
||||
MaxPeers = 25
|
||||
|
||||
|
|
|
@ -62,6 +62,16 @@ func (api *PublicAPI) SetRPC(rpcClient *rpc.Client, timeout time.Duration) {
|
|||
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
|
||||
func (api *PublicAPI) Sign(context context.Context, rpcParams ...interface{}) (interface{}, error) {
|
||||
metadata, err := newMetadata(rpcParams)
|
||||
|
|
|
@ -3,6 +3,7 @@ package services
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"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/sign"
|
||||
e2e "github.com/status-im/status-go/t/e2e"
|
||||
. "github.com/status-im/status-go/t/utils"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
. "github.com/status-im/status-go/t/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
signDataString = "0xBAADBEEF"
|
||||
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 {
|
||||
Title string
|
||||
EnableUpstream bool
|
||||
|
@ -48,6 +57,48 @@ type PersonalSignSuite struct {
|
|||
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() {
|
||||
s.testPersonalSign(testParams{
|
||||
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) {
|
||||
return func(jsonEvent string) {
|
||||
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
|
||||
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
|
||||
s.T().Skip()
|
||||
return
|
||||
return ""
|
||||
}
|
||||
|
||||
if testParams.HandlerFactory == nil {
|
||||
|
@ -207,9 +240,11 @@ func (s *PersonalSignSuite) testPersonalSign(testParams testParams) {
|
|||
result := s.Backend.CallRPC(basicCall)
|
||||
if testParams.ExpectedError == nil {
|
||||
s.NotContains(result, "error")
|
||||
} else {
|
||||
s.Contains(result, testParams.ExpectedError.Error())
|
||||
return s.extractResultFromRPCResponse(result)
|
||||
}
|
||||
|
||||
s.Contains(result, testParams.ExpectedError.Error())
|
||||
return ""
|
||||
}
|
||||
|
||||
func unmarshalEnvelope(jsonEvent string) signal.Envelope {
|
||||
|
@ -219,3 +254,64 @@ func unmarshalEnvelope(jsonEvent string) signal.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