package services import ( "encoding/json" "fmt" "testing" "github.com/ethereum/go-ethereum/accounts/keystore" acc "github.com/status-im/status-go/geth/account" "github.com/status-im/status-go/geth/params" "github.com/status-im/status-go/geth/signal" "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" ) const ( signDataString = "0xBAADBEEF" accountNotExists = "0x00164ca341326a03b547c05B343b2E21eFAe2400" ) type testParams struct { Title string EnableUpstream bool Account string Password string HandlerFactory func(string, string) func(string) ExpectedError error DontSelectAccount bool // to take advantage of the fact, that the default is `false` } func TestPersonalSignSuite(t *testing.T) { s := new(PersonalSignSuite) s.upstream = false suite.Run(t, s) } func TestPersonalSignSuiteUpstream(t *testing.T) { s := new(PersonalSignSuite) s.upstream = true suite.Run(t, s) } type PersonalSignSuite struct { e2e.BackendTestSuite upstream bool } func (s *PersonalSignSuite) TestPersonalSignSuccess() { s.testPersonalSign(testParams{ Account: TestConfig.Account1.Address, Password: TestConfig.Account1.Password, }) } func (s *PersonalSignSuite) TestPersonalSignWrongPassword() { s.testPersonalSign(testParams{ Account: TestConfig.Account1.Address, Password: TestConfig.Account1.Password, HandlerFactory: s.notificationHandlerWrongPassword, }) } func (s *PersonalSignSuite) TestPersonalSignNoSuchAccount() { s.testPersonalSign(testParams{ Account: accountNotExists, Password: TestConfig.Account1.Password, ExpectedError: personal.ErrInvalidPersonalSignAccount, HandlerFactory: s.notificationHandlerNoAccount, }) } func (s *PersonalSignSuite) TestPersonalSignWrongAccount() { s.testPersonalSign(testParams{ Account: TestConfig.Account2.Address, Password: TestConfig.Account2.Password, ExpectedError: personal.ErrInvalidPersonalSignAccount, HandlerFactory: s.notificationHandlerInvalidAccount, }) } func (s *PersonalSignSuite) TestPersonalSignNoAccountSelected() { s.testPersonalSign(testParams{ Account: TestConfig.Account1.Address, Password: TestConfig.Account1.Password, HandlerFactory: s.notificationHandlerNoAccountSelected, DontSelectAccount: true, }) } // Utility methods func (s *PersonalSignSuite) notificationHandlerSuccess(account string, pass string) func(string) { return func(jsonEvent string) { s.notificationHandler(account, pass, nil)(jsonEvent) } } func (s *PersonalSignSuite) notificationHandlerWrongPassword(account string, pass string) func(string) { return func(jsonEvent string) { s.notificationHandler(account, pass+"wrong", keystore.ErrDecrypt)(jsonEvent) s.notificationHandlerSuccess(account, pass)(jsonEvent) } } func (s *PersonalSignSuite) notificationHandlerNoAccount(account string, pass string) func(string) { return func(jsonEvent string) { s.notificationHandler(account, pass, personal.ErrInvalidPersonalSignAccount)(jsonEvent) } } func (s *PersonalSignSuite) notificationHandlerInvalidAccount(account string, pass string) func(string) { return func(jsonEvent string) { s.notificationHandler(account, pass, personal.ErrInvalidPersonalSignAccount)(jsonEvent) } } func (s *PersonalSignSuite) notificationHandlerNoAccountSelected(account string, pass string) func(string) { return func(jsonEvent string) { s.notificationHandler(account, pass, acc.ErrNoAccountSelected)(jsonEvent) envelope := unmarshalEnvelope(jsonEvent) if envelope.Type == sign.EventSignRequestAdded { err := s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password) s.NoError(err) } s.notificationHandlerSuccess(account, pass)(jsonEvent) } } 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) if envelope.Type == sign.EventSignRequestAdded { event := envelope.Event.(map[string]interface{}) id := event["id"].(string) s.T().Logf("Sign request added (will be completed shortly): {id: %s}\n", id) //check for the correct method name method := event["method"].(string) s.Equal(params.PersonalSignMethodName, method) //check the event data args := event["args"].(map[string]interface{}) s.Equal(signDataString, args["data"].(string)) s.Equal(account, args["account"].(string)) e := s.Backend.ApproveSignRequest(id, pass).Error s.T().Logf("Sign request approved. {id: %s, acc: %s, err: %v}", id, account, e) if expectedError == nil { s.NoError(e, "cannot complete sign reauest[%v]: %v", id, e) } else { s.EqualError(e, expectedError.Error()) } } } } func (s *PersonalSignSuite) testPersonalSign(testParams testParams) { // Test upstream if that's not StatusChain if s.upstream && GetNetworkID() == params.StatusChainNetworkID { s.T().Skip() return } if testParams.HandlerFactory == nil { testParams.HandlerFactory = s.notificationHandlerSuccess } err := s.initTest(s.upstream) s.NoError(err) defer func() { err := s.Backend.StopNode() s.NoError(err) }() signal.SetDefaultNodeNotificationHandler(testParams.HandlerFactory(testParams.Account, testParams.Password)) if testParams.DontSelectAccount { s.NoError(s.Backend.Logout()) } else { s.NoError(s.Backend.SelectAccount(TestConfig.Account1.Address, TestConfig.Account1.Password)) } basicCall := fmt.Sprintf( `{"jsonrpc":"2.0","method":"personal_sign","params":["%s", "%s"],"id":67}`, signDataString, testParams.Account) result := s.Backend.CallRPC(basicCall) if testParams.ExpectedError == nil { s.NotContains(result, "error") } else { s.Contains(result, testParams.ExpectedError.Error()) } } func unmarshalEnvelope(jsonEvent string) signal.Envelope { var envelope signal.Envelope if e := json.Unmarshal([]byte(jsonEvent), &envelope); e != nil { panic(e) } return envelope }