Expose status specific methods (login/signup/joinpublicchannel) through the RPC api (#877)
This commit is contained in:
parent
953c26e8cf
commit
8c9db81bec
1
Makefile
1
Makefile
|
@ -116,6 +116,7 @@ mock: ##@other Regenerate mocks
|
||||||
mockgen -package=fake -destination=geth/transactions/fake/mock.go -source=geth/transactions/fake/txservice.go
|
mockgen -package=fake -destination=geth/transactions/fake/mock.go -source=geth/transactions/fake/txservice.go
|
||||||
mockgen -package=account -destination=geth/account/accounts_mock.go -source=geth/account/accounts.go
|
mockgen -package=account -destination=geth/account/accounts_mock.go -source=geth/account/accounts.go
|
||||||
mockgen -package=jail -destination=geth/jail/cell_mock.go -source=geth/jail/cell.go
|
mockgen -package=jail -destination=geth/jail/cell_mock.go -source=geth/jail/cell.go
|
||||||
|
mockgen -package=status -destination=services/status/account_mock.go -source=services/status/service.go
|
||||||
|
|
||||||
docker-test: ##@tests Run tests in a docker container with golang.
|
docker-test: ##@tests Run tests in a docker container with golang.
|
||||||
docker run --privileged --rm -it -v "$(shell pwd):$(DOCKER_TEST_WORKDIR)" -w "$(DOCKER_TEST_WORKDIR)" $(DOCKER_TEST_IMAGE) go test ${ARGS}
|
docker run --privileged --rm -it -v "$(shell pwd):$(DOCKER_TEST_WORKDIR)" -w "$(DOCKER_TEST_WORKDIR)" $(DOCKER_TEST_IMAGE) go test ${ARGS}
|
||||||
|
|
|
@ -150,6 +150,10 @@ func (b *StatusBackend) startNode(config *params.NodeConfig) (err error) {
|
||||||
}
|
}
|
||||||
b.log.Info("Account reselected")
|
b.log.Info("Account reselected")
|
||||||
|
|
||||||
|
if st, err := b.statusNode.StatusService(); err == nil {
|
||||||
|
st.SetAccountManager(b.AccountManager())
|
||||||
|
}
|
||||||
|
|
||||||
signal.SendNodeReady()
|
signal.SendNodeReady()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
shhmetrics "github.com/status-im/status-go/metrics/whisper"
|
shhmetrics "github.com/status-im/status-go/metrics/whisper"
|
||||||
"github.com/status-im/status-go/services/personal"
|
"github.com/status-im/status-go/services/personal"
|
||||||
"github.com/status-im/status-go/services/shhext"
|
"github.com/status-im/status-go/services/shhext"
|
||||||
|
"github.com/status-im/status-go/services/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Errors related to node and services creation.
|
// Errors related to node and services creation.
|
||||||
|
@ -33,6 +34,7 @@ var (
|
||||||
ErrWhisperServiceRegistrationFailure = errors.New("failed to register the Whisper service")
|
ErrWhisperServiceRegistrationFailure = errors.New("failed to register the Whisper service")
|
||||||
ErrLightEthRegistrationFailure = errors.New("failed to register the LES service")
|
ErrLightEthRegistrationFailure = errors.New("failed to register the LES service")
|
||||||
ErrPersonalServiceRegistrationFailure = errors.New("failed to register the personal api service")
|
ErrPersonalServiceRegistrationFailure = errors.New("failed to register the personal api service")
|
||||||
|
ErrStatusServiceRegistrationFailure = errors.New("failed to register the Status service")
|
||||||
)
|
)
|
||||||
|
|
||||||
// All general log messages in this package should be routed through this logger.
|
// All general log messages in this package should be routed through this logger.
|
||||||
|
@ -93,6 +95,11 @@ func MakeNode(config *params.NodeConfig) (*node.Node, error) {
|
||||||
return nil, fmt.Errorf("%v: %v", ErrWhisperServiceRegistrationFailure, err)
|
return nil, fmt.Errorf("%v: %v", ErrWhisperServiceRegistrationFailure, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start status service.
|
||||||
|
if err := activateStatusService(stack, config); err != nil {
|
||||||
|
return nil, fmt.Errorf("%v: %v", ErrStatusServiceRegistrationFailure, err)
|
||||||
|
}
|
||||||
|
|
||||||
return stack, nil
|
return stack, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +170,22 @@ func activatePersonalService(stack *node.Node, config *params.NodeConfig) error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func activateStatusService(stack *node.Node, config *params.NodeConfig) error {
|
||||||
|
if !config.StatusServiceEnabled {
|
||||||
|
logger.Info("Status service api is disabled")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
|
var whisper *whisper.Whisper
|
||||||
|
if err := ctx.Service(&whisper); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
svc := status.New(whisper)
|
||||||
|
return svc, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// activateShhService configures Whisper and adds it to the given node.
|
// activateShhService configures Whisper and adds it to the given node.
|
||||||
func activateShhService(stack *node.Node, config *params.NodeConfig) (err error) {
|
func activateShhService(stack *node.Node, config *params.NodeConfig) (err error) {
|
||||||
if config.WhisperConfig == nil || !config.WhisperConfig.Enabled {
|
if config.WhisperConfig == nil || !config.WhisperConfig.Enabled {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/status-im/status-go/geth/peers"
|
"github.com/status-im/status-go/geth/peers"
|
||||||
"github.com/status-im/status-go/geth/rpc"
|
"github.com/status-im/status-go/geth/rpc"
|
||||||
"github.com/status-im/status-go/services/shhext"
|
"github.com/status-im/status-go/services/shhext"
|
||||||
|
"github.com/status-im/status-go/services/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// tickerResolution is the delta to check blockchain sync progress.
|
// tickerResolution is the delta to check blockchain sync progress.
|
||||||
|
@ -393,6 +394,19 @@ func (n *StatusNode) LightEthereumService() (l *les.LightEthereum, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatusService exposes reference to status service running on top of the node
|
||||||
|
func (n *StatusNode) StatusService() (st *status.Service, err error) {
|
||||||
|
n.mu.RLock()
|
||||||
|
defer n.mu.RUnlock()
|
||||||
|
|
||||||
|
err = n.gethService(&st)
|
||||||
|
if err == node.ErrServiceUnknown {
|
||||||
|
err = ErrServiceUnknown
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// WhisperService exposes reference to Whisper service running on top of the node
|
// WhisperService exposes reference to Whisper service running on top of the node
|
||||||
func (n *StatusNode) WhisperService() (w *whisper.Whisper, err error) {
|
func (n *StatusNode) WhisperService() (w *whisper.Whisper, err error) {
|
||||||
n.mu.RLock()
|
n.mu.RLock()
|
||||||
|
|
|
@ -306,6 +306,9 @@ type NodeConfig struct {
|
||||||
|
|
||||||
RegisterTopics []discv5.Topic `json:"RegisterTopics"`
|
RegisterTopics []discv5.Topic `json:"RegisterTopics"`
|
||||||
RequireTopics map[discv5.Topic]Limits `json:"RequireTopics"`
|
RequireTopics map[discv5.Topic]Limits `json:"RequireTopics"`
|
||||||
|
|
||||||
|
// StatusServiceEnabled enables status service api
|
||||||
|
StatusServiceEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNodeConfig creates new node configuration object
|
// NewNodeConfig creates new node configuration object
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: services/status/service.go
|
||||||
|
|
||||||
|
// Package status is a generated GoMock package.
|
||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
ecdsa "crypto/ecdsa"
|
||||||
|
accounts "github.com/ethereum/go-ethereum/accounts"
|
||||||
|
keystore "github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
reflect "reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockWhisper is a mock of Whisper interface
|
||||||
|
type MockWhisper struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockWhisperMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockWhisperMockRecorder is the mock recorder for MockWhisper
|
||||||
|
type MockWhisperMockRecorder struct {
|
||||||
|
mock *MockWhisper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockWhisper creates a new mock instance
|
||||||
|
func NewMockWhisper(ctrl *gomock.Controller) *MockWhisper {
|
||||||
|
mock := &MockWhisper{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockWhisperMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockWhisper) EXPECT() *MockWhisperMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddKeyPair mocks base method
|
||||||
|
func (m *MockWhisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
|
||||||
|
ret := m.ctrl.Call(m, "AddKeyPair", key)
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddKeyPair indicates an expected call of AddKeyPair
|
||||||
|
func (mr *MockWhisperMockRecorder) AddKeyPair(key interface{}) *gomock.Call {
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddKeyPair", reflect.TypeOf((*MockWhisper)(nil).AddKeyPair), key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockAccountManager is a mock of AccountManager interface
|
||||||
|
type MockAccountManager struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockAccountManagerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockAccountManagerMockRecorder is the mock recorder for MockAccountManager
|
||||||
|
type MockAccountManagerMockRecorder struct {
|
||||||
|
mock *MockAccountManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockAccountManager creates a new mock instance
|
||||||
|
func NewMockAccountManager(ctrl *gomock.Controller) *MockAccountManager {
|
||||||
|
mock := &MockAccountManager{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockAccountManagerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockAccountManager) EXPECT() *MockAccountManagerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressToDecryptedAccount mocks base method
|
||||||
|
func (m *MockAccountManager) AddressToDecryptedAccount(arg0, arg1 string) (accounts.Account, *keystore.Key, error) {
|
||||||
|
ret := m.ctrl.Call(m, "AddressToDecryptedAccount", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(accounts.Account)
|
||||||
|
ret1, _ := ret[1].(*keystore.Key)
|
||||||
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressToDecryptedAccount indicates an expected call of AddressToDecryptedAccount
|
||||||
|
func (mr *MockAccountManagerMockRecorder) AddressToDecryptedAccount(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddressToDecryptedAccount", reflect.TypeOf((*MockAccountManager)(nil).AddressToDecryptedAccount), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectAccount mocks base method
|
||||||
|
func (m *MockAccountManager) SelectAccount(address, password string) error {
|
||||||
|
ret := m.ctrl.Call(m, "SelectAccount", address, password)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectAccount indicates an expected call of SelectAccount
|
||||||
|
func (mr *MockAccountManagerMockRecorder) SelectAccount(address, password interface{}) *gomock.Call {
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAccount", reflect.TypeOf((*MockAccountManager)(nil).SelectAccount), address, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAccount mocks base method
|
||||||
|
func (m *MockAccountManager) CreateAccount(password string) (string, string, string, error) {
|
||||||
|
ret := m.ctrl.Call(m, "CreateAccount", password)
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(string)
|
||||||
|
ret2, _ := ret[2].(string)
|
||||||
|
ret3, _ := ret[3].(error)
|
||||||
|
return ret0, ret1, ret2, ret3
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAccount indicates an expected call of CreateAccount
|
||||||
|
func (mr *MockAccountManagerMockRecorder) CreateAccount(password interface{}) *gomock.Call {
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAccount", reflect.TypeOf((*MockAccountManager)(nil).CreateAccount), password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockWhisperService is a mock of WhisperService interface
|
||||||
|
type MockWhisperService struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockWhisperServiceMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockWhisperServiceMockRecorder is the mock recorder for MockWhisperService
|
||||||
|
type MockWhisperServiceMockRecorder struct {
|
||||||
|
mock *MockWhisperService
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockWhisperService creates a new mock instance
|
||||||
|
func NewMockWhisperService(ctrl *gomock.Controller) *MockWhisperService {
|
||||||
|
mock := &MockWhisperService{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockWhisperServiceMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use
|
||||||
|
func (m *MockWhisperService) EXPECT() *MockWhisperServiceMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressToDecryptedAccount mocks base method
|
||||||
|
func (m *MockWhisperService) AddressToDecryptedAccount(arg0, arg1 string) (accounts.Account, *keystore.Key, error) {
|
||||||
|
ret := m.ctrl.Call(m, "AddressToDecryptedAccount", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(accounts.Account)
|
||||||
|
ret1, _ := ret[1].(*keystore.Key)
|
||||||
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddressToDecryptedAccount indicates an expected call of AddressToDecryptedAccount
|
||||||
|
func (mr *MockWhisperServiceMockRecorder) AddressToDecryptedAccount(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddressToDecryptedAccount", reflect.TypeOf((*MockWhisperService)(nil).AddressToDecryptedAccount), arg0, arg1)
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PublicAPI represents a set of APIs from the `web3.status` namespace.
|
||||||
|
type PublicAPI struct {
|
||||||
|
s *Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAPI creates an instance of the status API.
|
||||||
|
func NewAPI(s *Service) *PublicAPI {
|
||||||
|
return &PublicAPI{s: s}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginRequest : json request for status_login.
|
||||||
|
type LoginRequest struct {
|
||||||
|
Addr string `json:"address"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginResponse : json response returned by status_login.
|
||||||
|
type LoginResponse struct {
|
||||||
|
AddressKeyID string `json:"address_key_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login is an implementation of `status_login` or `web3.status.login` API
|
||||||
|
func (api *PublicAPI) Login(context context.Context, req LoginRequest) (res LoginResponse, err error) {
|
||||||
|
_, accountKey, err := api.s.am.AddressToDecryptedAccount(req.Addr, req.Password)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.AddressKeyID, err = api.s.w.AddKeyPair(accountKey.PrivateKey); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = api.s.am.SelectAccount(req.Addr, req.Password); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignupRequest : json request for status_signup.
|
||||||
|
type SignupRequest struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignupResponse : json response returned by status_signup.
|
||||||
|
type SignupResponse struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
Pubkey string `json:"pubkey"`
|
||||||
|
Mnemonic string `json:"mnemonic"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signup is an implementation of `status_signup` or `web3.status.signup` API
|
||||||
|
func (api *PublicAPI) Signup(context context.Context, req SignupRequest) (res SignupResponse, err error) {
|
||||||
|
if res.Address, res.Pubkey, res.Mnemonic, err = api.s.am.CreateAccount(req.Password); err != nil {
|
||||||
|
err = errors.New("could not create the specified account : " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStatusSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(StatusSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
am *MockAccountManager
|
||||||
|
w *MockWhisper
|
||||||
|
api *PublicAPI
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusSuite) SetupTest() {
|
||||||
|
ctrl := gomock.NewController(s.T())
|
||||||
|
s.am = NewMockAccountManager(ctrl)
|
||||||
|
s.w = NewMockWhisper(ctrl)
|
||||||
|
service := New(s.w)
|
||||||
|
service.SetAccountManager(s.am)
|
||||||
|
|
||||||
|
s.api = NewAPI(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
var logintests = []struct {
|
||||||
|
name string
|
||||||
|
expectedAddressKey string
|
||||||
|
expectedError error
|
||||||
|
prepareExpectations func(*StatusSuite)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success login",
|
||||||
|
expectedAddressKey: "addressKey",
|
||||||
|
expectedError: nil,
|
||||||
|
prepareExpectations: func(s *StatusSuite) {
|
||||||
|
key := keystore.Key{
|
||||||
|
PrivateKey: &ecdsa.PrivateKey{},
|
||||||
|
}
|
||||||
|
s.am.EXPECT().AddressToDecryptedAccount("address...", "password").Return(accounts.Account{}, &key, nil)
|
||||||
|
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("addressKey", nil)
|
||||||
|
s.am.EXPECT().SelectAccount("address...", "password").Return(nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error when decrypting account from address",
|
||||||
|
expectedAddressKey: "",
|
||||||
|
expectedError: errors.New("foo"),
|
||||||
|
prepareExpectations: func(s *StatusSuite) {
|
||||||
|
key := keystore.Key{
|
||||||
|
PrivateKey: &ecdsa.PrivateKey{},
|
||||||
|
}
|
||||||
|
s.am.EXPECT().AddressToDecryptedAccount("address...", "password").Return(accounts.Account{}, &key, errors.New("foo"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error when adding key pair to whisper",
|
||||||
|
expectedAddressKey: "",
|
||||||
|
expectedError: errors.New("foo"),
|
||||||
|
prepareExpectations: func(s *StatusSuite) {
|
||||||
|
key := keystore.Key{
|
||||||
|
PrivateKey: &ecdsa.PrivateKey{},
|
||||||
|
}
|
||||||
|
s.am.EXPECT().AddressToDecryptedAccount("address...", "password").Return(accounts.Account{}, &key, nil)
|
||||||
|
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("", errors.New("foo"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error when selecting account",
|
||||||
|
expectedAddressKey: "",
|
||||||
|
expectedError: errors.New("foo"),
|
||||||
|
prepareExpectations: func(s *StatusSuite) {
|
||||||
|
key := keystore.Key{
|
||||||
|
PrivateKey: &ecdsa.PrivateKey{},
|
||||||
|
}
|
||||||
|
s.am.EXPECT().AddressToDecryptedAccount("address...", "password").Return(accounts.Account{}, &key, nil)
|
||||||
|
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("", nil)
|
||||||
|
s.am.EXPECT().SelectAccount("address...", "password").Return(errors.New("foo"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusSuite) TestLogin() {
|
||||||
|
for _, t := range logintests {
|
||||||
|
req := LoginRequest{Addr: "address...", Password: "password"}
|
||||||
|
|
||||||
|
t.prepareExpectations(s)
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
res, err := s.api.Login(ctx, req)
|
||||||
|
s.Equal(t.expectedAddressKey, res.AddressKeyID, "failed scenario : "+t.name)
|
||||||
|
s.Equal(t.expectedError, err, "failed scenario : "+t.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var signuptests = []struct {
|
||||||
|
name string
|
||||||
|
expectedResponse SignupResponse
|
||||||
|
expectedError error
|
||||||
|
prepareExpectations func(*StatusSuite)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "success signup",
|
||||||
|
expectedResponse: SignupResponse{
|
||||||
|
Address: "addr",
|
||||||
|
Pubkey: "pubkey",
|
||||||
|
Mnemonic: "mnemonic",
|
||||||
|
},
|
||||||
|
expectedError: nil,
|
||||||
|
prepareExpectations: func(s *StatusSuite) {
|
||||||
|
s.am.EXPECT().CreateAccount("password").Return("addr", "pubkey", "mnemonic", nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "success signup",
|
||||||
|
expectedResponse: SignupResponse{
|
||||||
|
Address: "",
|
||||||
|
Pubkey: "",
|
||||||
|
Mnemonic: "",
|
||||||
|
},
|
||||||
|
expectedError: errors.New("could not create the specified account : foo"),
|
||||||
|
prepareExpectations: func(s *StatusSuite) {
|
||||||
|
s.am.EXPECT().CreateAccount("password").Return("", "", "", errors.New("foo"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusSuite) TestSignup() {
|
||||||
|
for _, t := range signuptests {
|
||||||
|
t.prepareExpectations(s)
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
res, err := s.api.Signup(ctx, SignupRequest{Password: "password"})
|
||||||
|
s.Equal(t.expectedResponse.Address, res.Address, "failed scenario : "+t.name)
|
||||||
|
s.Equal(t.expectedResponse.Pubkey, res.Pubkey, "failed scenario : "+t.name)
|
||||||
|
s.Equal(t.expectedResponse.Mnemonic, res.Mnemonic, "failed scenario : "+t.name)
|
||||||
|
s.Equal(t.expectedError, err, "failed scenario : "+t.name)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Make sure that Service implements node.Service interface.
|
||||||
|
var _ node.Service = (*Service)(nil)
|
||||||
|
|
||||||
|
// WhisperService whisper interface to add key pairs
|
||||||
|
type WhisperService interface {
|
||||||
|
AddKeyPair(key *ecdsa.PrivateKey) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountManager interface to manage account actions
|
||||||
|
type AccountManager interface {
|
||||||
|
AddressToDecryptedAccount(string, string) (accounts.Account, *keystore.Key, error)
|
||||||
|
SelectAccount(address, password string) error
|
||||||
|
CreateAccount(password string) (address, pubKey, mnemonic string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service represents out own implementation of status status operations.
|
||||||
|
type Service struct {
|
||||||
|
am AccountManager
|
||||||
|
w WhisperService
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Service.
|
||||||
|
func New(w WhisperService) *Service {
|
||||||
|
return &Service{w: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocols returns a new protocols list. In this case, there are none.
|
||||||
|
func (s *Service) Protocols() []p2p.Protocol {
|
||||||
|
return []p2p.Protocol{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIs returns a list of new APIs.
|
||||||
|
func (s *Service) APIs() []rpc.API {
|
||||||
|
|
||||||
|
return []rpc.API{
|
||||||
|
{
|
||||||
|
Namespace: "status",
|
||||||
|
Version: "1.0",
|
||||||
|
Service: NewAPI(s),
|
||||||
|
Public: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAccountManager sets account manager for the API calls.
|
||||||
|
func (s *Service) SetAccountManager(a AccountManager) {
|
||||||
|
s.am = a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start is run when a service is started.
|
||||||
|
// It does nothing in this case but is required by `node.Service` interface.
|
||||||
|
func (s *Service) Start(server *p2p.Server) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop is run when a service is stopped.
|
||||||
|
// It does nothing in this case but is required by `node.Service` interface.
|
||||||
|
func (s *Service) Stop() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/geth/api"
|
||||||
|
"github.com/status-im/status-go/geth/params"
|
||||||
|
"github.com/status-im/status-go/signal"
|
||||||
|
"github.com/status-im/status-go/t/e2e"
|
||||||
|
|
||||||
|
. "github.com/status-im/status-go/t/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseJSONRPCSuite struct {
|
||||||
|
e2e.BackendTestSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseJSONRPCSuite) AssertAPIMethodUnexported(method string) {
|
||||||
|
exported := s.isMethodExported(method, false)
|
||||||
|
s.False(exported,
|
||||||
|
"method %s should be hidden, but it isn't",
|
||||||
|
method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseJSONRPCSuite) AssertAPIMethodExported(method string) {
|
||||||
|
exported := s.isMethodExported(method, false)
|
||||||
|
s.True(exported,
|
||||||
|
"method %s should be exported, but it isn't",
|
||||||
|
method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseJSONRPCSuite) AssertAPIMethodExportedPrivately(method string) {
|
||||||
|
exported := s.isMethodExported(method, true)
|
||||||
|
s.True(exported,
|
||||||
|
"method %s should be exported, but it isn't",
|
||||||
|
method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseJSONRPCSuite) isMethodExported(method string, private bool) bool {
|
||||||
|
var result string
|
||||||
|
|
||||||
|
cmd := fmt.Sprintf(`{"jsonrpc":"2.0", "method": "%s", "params": []}`, method)
|
||||||
|
if private {
|
||||||
|
result = s.Backend.CallPrivateRPC(cmd)
|
||||||
|
} else {
|
||||||
|
result = s.Backend.CallRPC(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var response struct {
|
||||||
|
Error *rpcError `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
s.NoError(json.Unmarshal([]byte(result), &response))
|
||||||
|
|
||||||
|
return !(response.Error != nil && response.Error.Code == methodNotFoundErrorCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseJSONRPCSuite) SetupTest(upstreamEnabled bool, statusServiceEnabled bool) error {
|
||||||
|
s.Backend = api.NewStatusBackend()
|
||||||
|
s.NotNil(s.Backend)
|
||||||
|
|
||||||
|
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
nodeConfig.IPCEnabled = false
|
||||||
|
nodeConfig.StatusServiceEnabled = statusServiceEnabled
|
||||||
|
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 *BaseJSONRPCSuite) notificationHandler(account string, pass string, expectedError error) func(string) {
|
||||||
|
return func(jsonEvent string) {
|
||||||
|
envelope := unmarshalEnvelope(jsonEvent)
|
||||||
|
if envelope.Type == signal.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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/status-im/status-go/geth/params"
|
"github.com/status-im/status-go/geth/params"
|
||||||
"github.com/status-im/status-go/services/personal"
|
"github.com/status-im/status-go/services/personal"
|
||||||
"github.com/status-im/status-go/signal"
|
"github.com/status-im/status-go/signal"
|
||||||
e2e "github.com/status-im/status-go/t/e2e"
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
. "github.com/status-im/status-go/t/utils"
|
. "github.com/status-im/status-go/t/utils"
|
||||||
|
@ -52,7 +51,7 @@ func TestPersonalSignSuiteUpstream(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type PersonalSignSuite struct {
|
type PersonalSignSuite struct {
|
||||||
e2e.BackendTestSuite
|
BaseJSONRPCSuite
|
||||||
upstream bool
|
upstream bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,40 +61,22 @@ func (s *PersonalSignSuite) TestRestrictedPersonalAPIs() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.initTest(s.upstream)
|
err := s.SetupTest(s.upstream, false)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
defer func() {
|
defer func() {
|
||||||
err := s.Backend.StopNode()
|
err := s.Backend.StopNode()
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
}()
|
}()
|
||||||
// These personal APIs should be available
|
// These personal APIs should be available
|
||||||
s.testAPIExported("personal_sign", true)
|
s.AssertAPIMethodExported("personal_sign")
|
||||||
s.testAPIExported("personal_ecRecover", true)
|
s.AssertAPIMethodExported("personal_ecRecover")
|
||||||
// These personal APIs shouldn't be exported
|
// These personal APIs shouldn't be exported
|
||||||
s.testAPIExported("personal_sendTransaction", false)
|
s.AssertAPIMethodUnexported("personal_sendTransaction")
|
||||||
s.testAPIExported("personal_unlockAccount", false)
|
s.AssertAPIMethodUnexported("personal_unlockAccount")
|
||||||
s.testAPIExported("personal_newAccount", false)
|
s.AssertAPIMethodUnexported("personal_newAccount")
|
||||||
s.testAPIExported("personal_lockAccount", false)
|
s.AssertAPIMethodUnexported("personal_lockAccount")
|
||||||
s.testAPIExported("personal_listAccounts", false)
|
s.AssertAPIMethodUnexported("personal_listAccounts")
|
||||||
s.testAPIExported("personal_importRawKey", false)
|
s.AssertAPIMethodUnexported("personal_importRawKey")
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
||||||
|
@ -141,12 +122,6 @@ func (s *PersonalSignSuite) TestPersonalSignNoAccountSelected() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility methods
|
// 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) {
|
func (s *PersonalSignSuite) notificationHandlerWrongPassword(account string, pass string) func(string) {
|
||||||
return func(jsonEvent string) {
|
return func(jsonEvent string) {
|
||||||
s.notificationHandler(account, pass+"wrong", keystore.ErrDecrypt)(jsonEvent)
|
s.notificationHandler(account, pass+"wrong", keystore.ErrDecrypt)(jsonEvent)
|
||||||
|
@ -216,7 +191,7 @@ func (s *PersonalSignSuite) testPersonalSign(testParams testParams) string {
|
||||||
testParams.HandlerFactory = s.notificationHandlerSuccess
|
testParams.HandlerFactory = s.notificationHandlerSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.initTest(s.upstream)
|
err := s.SetupTest(s.upstream, false)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
defer func() {
|
defer func() {
|
||||||
err := s.Backend.StopNode()
|
err := s.Backend.StopNode()
|
||||||
|
@ -246,6 +221,15 @@ func (s *PersonalSignSuite) testPersonalSign(testParams testParams) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PersonalSignSuite) extractResultFromRPCResponse(response string) string {
|
||||||
|
var r struct {
|
||||||
|
Result string `json:"result"`
|
||||||
|
}
|
||||||
|
s.NoError(json.Unmarshal([]byte(response), &r))
|
||||||
|
|
||||||
|
return r.Result
|
||||||
|
}
|
||||||
|
|
||||||
func unmarshalEnvelope(jsonEvent string) signal.Envelope {
|
func unmarshalEnvelope(jsonEvent string) signal.Envelope {
|
||||||
var envelope signal.Envelope
|
var envelope signal.Envelope
|
||||||
if e := json.Unmarshal([]byte(jsonEvent), &envelope); e != nil {
|
if e := json.Unmarshal([]byte(jsonEvent), &envelope); e != nil {
|
||||||
|
@ -268,7 +252,7 @@ func (s *PersonalSignSuite) TestPersonalRecoverSuccess() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.initTest(s.upstream)
|
err := s.SetupTest(s.upstream, false)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
defer func() {
|
defer func() {
|
||||||
err := s.Backend.StopNode()
|
err := s.Backend.StopNode()
|
||||||
|
@ -288,29 +272,8 @@ func (s *PersonalSignSuite) TestPersonalRecoverSuccess() {
|
||||||
s.True(strings.EqualFold(result, TestConfig.Account1.Address))
|
s.True(strings.EqualFold(result, TestConfig.Account1.Address))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PersonalSignSuite) initTest(upstreamEnabled bool) error {
|
func (s *BaseJSONRPCSuite) notificationHandlerSuccess(account string, pass string) func(string) {
|
||||||
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
|
return func(jsonEvent string) {
|
||||||
s.NoError(err)
|
s.notificationHandler(account, pass, nil)(jsonEvent)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/geth/account"
|
||||||
|
"github.com/status-im/status-go/geth/params"
|
||||||
|
"github.com/status-im/status-go/services/status"
|
||||||
|
"github.com/status-im/status-go/signal"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
. "github.com/status-im/status-go/t/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type statusTestParams struct {
|
||||||
|
Address string
|
||||||
|
Password string
|
||||||
|
HandlerFactory func(string, string) func(string)
|
||||||
|
ExpectedError error
|
||||||
|
ChannelName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatusAPISuite(t *testing.T) {
|
||||||
|
s := new(StatusAPISuite)
|
||||||
|
s.upstream = false
|
||||||
|
suite.Run(t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatusAPISuiteUpstream(t *testing.T) {
|
||||||
|
s := new(StatusAPISuite)
|
||||||
|
s.upstream = true
|
||||||
|
suite.Run(t, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusAPISuite struct {
|
||||||
|
BaseJSONRPCSuite
|
||||||
|
upstream bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusAPISuite) TestAccessibleStatusAPIs() {
|
||||||
|
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
|
||||||
|
s.T().Skip()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.SetupTest(s.upstream, true)
|
||||||
|
s.NoError(err)
|
||||||
|
defer func() {
|
||||||
|
err := s.Backend.StopNode()
|
||||||
|
s.NoError(err)
|
||||||
|
}()
|
||||||
|
// These status APIs should be unavailable
|
||||||
|
s.AssertAPIMethodUnexported("status_login")
|
||||||
|
s.AssertAPIMethodUnexported("status_signup")
|
||||||
|
|
||||||
|
// These status APIs should be available only for IPC
|
||||||
|
s.AssertAPIMethodExportedPrivately("status_login")
|
||||||
|
s.AssertAPIMethodExportedPrivately("status_signup")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusAPISuite) TestStatusLoginSuccess() {
|
||||||
|
addressKeyID := s.testStatusLogin(statusTestParams{
|
||||||
|
Address: TestConfig.Account1.Address,
|
||||||
|
Password: TestConfig.Account1.Password,
|
||||||
|
})
|
||||||
|
s.NotEmpty(addressKeyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusAPISuite) TestStatusLoginInvalidAddress() {
|
||||||
|
s.testStatusLogin(statusTestParams{
|
||||||
|
Address: "invalidaccount",
|
||||||
|
Password: TestConfig.Account1.Password,
|
||||||
|
ExpectedError: account.ErrAddressToAccountMappingFailure,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusAPISuite) TestStatusLoginInvalidPassword() {
|
||||||
|
s.testStatusLogin(statusTestParams{
|
||||||
|
Address: "invalidaccount",
|
||||||
|
Password: TestConfig.Account1.Password,
|
||||||
|
ExpectedError: account.ErrAddressToAccountMappingFailure,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusAPISuite) TestStatusSignupSuccess() {
|
||||||
|
var pwd = "randompassword"
|
||||||
|
|
||||||
|
res := s.testStatusSignup(statusTestParams{
|
||||||
|
Password: pwd,
|
||||||
|
})
|
||||||
|
s.NotEmpty(res.Address)
|
||||||
|
s.NotEmpty(res.Pubkey)
|
||||||
|
s.Equal(12, len(strings.Split(res.Mnemonic, " ")))
|
||||||
|
|
||||||
|
// I should be able to login with the newly created account
|
||||||
|
_ = s.testStatusLogin(statusTestParams{
|
||||||
|
Address: res.Address,
|
||||||
|
Password: pwd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusAPISuite) testStatusLogin(testParams statusTestParams) *status.LoginResponse {
|
||||||
|
// Test upstream if that's not StatusChain
|
||||||
|
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
|
||||||
|
s.T().Skip()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if testParams.HandlerFactory == nil {
|
||||||
|
testParams.HandlerFactory = s.notificationHandlerSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.SetupTest(s.upstream, true)
|
||||||
|
s.NoError(err)
|
||||||
|
defer func() {
|
||||||
|
err := s.Backend.StopNode()
|
||||||
|
s.NoError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
signal.SetDefaultNodeNotificationHandler(testParams.HandlerFactory(testParams.Address, testParams.Password))
|
||||||
|
|
||||||
|
req := status.LoginRequest{
|
||||||
|
Addr: testParams.Address,
|
||||||
|
Password: testParams.Password,
|
||||||
|
}
|
||||||
|
body, _ := json.Marshal(req)
|
||||||
|
|
||||||
|
basicCall := fmt.Sprintf(
|
||||||
|
`{"jsonrpc":"2.0","method":"status_login","params":[%s],"id":67}`,
|
||||||
|
body)
|
||||||
|
|
||||||
|
result := s.Backend.CallPrivateRPC(basicCall)
|
||||||
|
if testParams.ExpectedError == nil {
|
||||||
|
s.NotContains(result, "error")
|
||||||
|
var r struct {
|
||||||
|
Result *status.LoginResponse `json:"result"`
|
||||||
|
}
|
||||||
|
s.NoError(json.Unmarshal([]byte(result), &r))
|
||||||
|
|
||||||
|
return r.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Contains(result, testParams.ExpectedError.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StatusAPISuite) testStatusSignup(testParams statusTestParams) *status.SignupResponse {
|
||||||
|
// Test upstream if that's not StatusChain
|
||||||
|
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
|
||||||
|
s.T().Skip()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if testParams.HandlerFactory == nil {
|
||||||
|
testParams.HandlerFactory = s.notificationHandlerSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.SetupTest(s.upstream, true)
|
||||||
|
s.NoError(err)
|
||||||
|
defer func() {
|
||||||
|
err := s.Backend.StopNode()
|
||||||
|
s.NoError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
signal.SetDefaultNodeNotificationHandler(testParams.HandlerFactory(testParams.Address, testParams.Password))
|
||||||
|
|
||||||
|
req := status.SignupRequest{
|
||||||
|
Password: testParams.Password,
|
||||||
|
}
|
||||||
|
body, _ := json.Marshal(req)
|
||||||
|
|
||||||
|
basicCall := fmt.Sprintf(
|
||||||
|
`{"jsonrpc":"2.0","method":"status_signup","params":[%s],"id":67}`,
|
||||||
|
body)
|
||||||
|
|
||||||
|
result := s.Backend.CallPrivateRPC(basicCall)
|
||||||
|
|
||||||
|
if testParams.ExpectedError == nil {
|
||||||
|
s.NotContains(result, "error")
|
||||||
|
var r struct {
|
||||||
|
Result *status.SignupResponse `json:"result"`
|
||||||
|
}
|
||||||
|
s.NoError(json.Unmarshal([]byte(result), &r))
|
||||||
|
|
||||||
|
return r.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Contains(result, testParams.ExpectedError.Error())
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue