feat(browser connect)_: Implementing signTypedData_V4
This commit bundles the personal sign and the signTypedData_V4 sign request in the same command. The only difference between these two requests is the order of address and challenge in the params array. What has changed: 1. PersonalSign.. has been renamed to Sign 2. `getPersonalSignParams` renamed to `getSignParams` and implements the parsing for both personal sign and signTypedData_V4
This commit is contained in:
parent
43f355a391
commit
e953cb6c95
|
@ -30,7 +30,11 @@ func NewAPI(s *Service) *API {
|
||||||
Db: s.db,
|
Db: s.db,
|
||||||
ClientHandler: c,
|
ClientHandler: c,
|
||||||
})
|
})
|
||||||
r.Register("personal_sign", &commands.PersonalSignCommand{
|
r.Register("personal_sign", &commands.SignCommand{
|
||||||
|
Db: s.db,
|
||||||
|
ClientHandler: c,
|
||||||
|
})
|
||||||
|
r.Register("eth_signTypedData_v4", &commands.SignCommand{
|
||||||
Db: s.db,
|
Db: s.db,
|
||||||
ClientHandler: c,
|
ClientHandler: c,
|
||||||
})
|
})
|
||||||
|
@ -144,10 +148,10 @@ func (api *API) SendTransactionRejected(args commands.RejectedArgs) error {
|
||||||
return api.c.SendTransactionRejected(args)
|
return api.c.SendTransactionRejected(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) PersonalSignAccepted(args commands.PersonalSignAcceptedArgs) error {
|
func (api *API) SignAccepted(args commands.SignAcceptedArgs) error {
|
||||||
return api.c.PersonalSignAccepted(args)
|
return api.c.SignAccepted(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) PersonalSignRejected(args commands.RejectedArgs) error {
|
func (api *API) SignRejected(args commands.RejectedArgs) error {
|
||||||
return api.c.PersonalSignRejected(args)
|
return api.c.SignRejected(args)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ var (
|
||||||
ErrEmptyAccountsShared = fmt.Errorf("empty accounts were shared by wallet")
|
ErrEmptyAccountsShared = fmt.Errorf("empty accounts were shared by wallet")
|
||||||
ErrRequestAccountsRejectedByUser = fmt.Errorf("request accounts was rejected by user")
|
ErrRequestAccountsRejectedByUser = fmt.Errorf("request accounts was rejected by user")
|
||||||
ErrSendTransactionRejectedByUser = fmt.Errorf("send transaction was rejected by user")
|
ErrSendTransactionRejectedByUser = fmt.Errorf("send transaction was rejected by user")
|
||||||
ErrPersonalSignRejectedByUser = fmt.Errorf("personal sign was rejected by user")
|
ErrSignRejectedByUser = fmt.Errorf("sign was rejected by user")
|
||||||
ErrEmptyRequestID = fmt.Errorf("empty requestID")
|
ErrEmptyRequestID = fmt.Errorf("empty requestID")
|
||||||
ErrAnotherConnectorOperationIsAwaitingFor = fmt.Errorf("another connector operation is awaiting for user input")
|
ErrAnotherConnectorOperationIsAwaitingFor = fmt.Errorf("another connector operation is awaiting for user input")
|
||||||
ErrEmptyUrl = fmt.Errorf("empty URL")
|
ErrEmptyUrl = fmt.Errorf("empty URL")
|
||||||
|
@ -34,7 +34,7 @@ type MessageType int
|
||||||
const (
|
const (
|
||||||
RequestAccountsAccepted MessageType = iota
|
RequestAccountsAccepted MessageType = iota
|
||||||
SendTransactionAccepted
|
SendTransactionAccepted
|
||||||
PersonalSignAccepted
|
SignAccepted
|
||||||
Rejected
|
Rejected
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -203,14 +203,14 @@ func (c *ClientSideHandler) SendTransactionRejected(args RejectedArgs) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientSideHandler) RequestPersonalSign(dApp signal.ConnectorDApp, challenge, address string) (string, error) {
|
func (c *ClientSideHandler) RequestSign(dApp signal.ConnectorDApp, challenge, address string, method string) (string, error) {
|
||||||
if !c.setRequestRunning() {
|
if !c.setRequestRunning() {
|
||||||
return "", ErrAnotherConnectorOperationIsAwaitingFor
|
return "", ErrAnotherConnectorOperationIsAwaitingFor
|
||||||
}
|
}
|
||||||
defer c.clearRequestRunning()
|
defer c.clearRequestRunning()
|
||||||
|
|
||||||
requestID := c.generateRequestID(dApp)
|
requestID := c.generateRequestID(dApp)
|
||||||
signal.SendConnectorPersonalSign(dApp, requestID, challenge, address)
|
signal.SendConnectorSign(dApp, requestID, challenge, address, method)
|
||||||
|
|
||||||
timeout := time.After(WalletResponseMaxInterval)
|
timeout := time.After(WalletResponseMaxInterval)
|
||||||
|
|
||||||
|
@ -218,15 +218,15 @@ func (c *ClientSideHandler) RequestPersonalSign(dApp signal.ConnectorDApp, chall
|
||||||
select {
|
select {
|
||||||
case msg := <-c.responseChannel:
|
case msg := <-c.responseChannel:
|
||||||
switch msg.Type {
|
switch msg.Type {
|
||||||
case PersonalSignAccepted:
|
case SignAccepted:
|
||||||
response := msg.Data.(PersonalSignAcceptedArgs)
|
response := msg.Data.(SignAcceptedArgs)
|
||||||
if response.RequestID == requestID {
|
if response.RequestID == requestID {
|
||||||
return response.Signature, nil
|
return response.Signature, nil
|
||||||
}
|
}
|
||||||
case Rejected:
|
case Rejected:
|
||||||
response := msg.Data.(RejectedArgs)
|
response := msg.Data.(RejectedArgs)
|
||||||
if response.RequestID == requestID {
|
if response.RequestID == requestID {
|
||||||
return "", ErrPersonalSignRejectedByUser
|
return "", ErrSignRejectedByUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case <-timeout:
|
case <-timeout:
|
||||||
|
@ -235,16 +235,16 @@ func (c *ClientSideHandler) RequestPersonalSign(dApp signal.ConnectorDApp, chall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientSideHandler) PersonalSignAccepted(args PersonalSignAcceptedArgs) error {
|
func (c *ClientSideHandler) SignAccepted(args SignAcceptedArgs) error {
|
||||||
if args.RequestID == "" {
|
if args.RequestID == "" {
|
||||||
return ErrEmptyRequestID
|
return ErrEmptyRequestID
|
||||||
}
|
}
|
||||||
|
|
||||||
c.responseChannel <- Message{Type: PersonalSignAccepted, Data: args}
|
c.responseChannel <- Message{Type: SignAccepted, Data: args}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClientSideHandler) PersonalSignRejected(args RejectedArgs) error {
|
func (c *ClientSideHandler) SignRejected(args RejectedArgs) error {
|
||||||
if args.RequestID == "" {
|
if args.RequestID == "" {
|
||||||
return ErrEmptyRequestID
|
return ErrEmptyRequestID
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
|
||||||
"github.com/status-im/status-go/signal"
|
|
||||||
)
|
|
||||||
|
|
||||||
func preparePersonalSignRequest(dApp signal.ConnectorDApp, challenge, address string) (RPCRequest, error) {
|
|
||||||
return ConstructRPCRequest("personal_sign", []interface{}{challenge, address}, &dApp)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailToPersonalSignWithMissingDAppFields(t *testing.T) {
|
|
||||||
state, close := setupCommand(t, Method_PersonalSign)
|
|
||||||
t.Cleanup(close)
|
|
||||||
|
|
||||||
// Missing DApp fields
|
|
||||||
request, err := ConstructRPCRequest("personal_sign", []interface{}{}, nil)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
result, err := state.cmd.Execute(state.ctx, request)
|
|
||||||
assert.Equal(t, ErrRequestMissingDAppData, err)
|
|
||||||
assert.Empty(t, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailToPersonalSignForUnpermittedDApp(t *testing.T) {
|
|
||||||
state, close := setupCommand(t, Method_PersonalSign)
|
|
||||||
t.Cleanup(close)
|
|
||||||
|
|
||||||
request, err := preparePersonalSignRequest(testDAppData,
|
|
||||||
"0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e",
|
|
||||||
"0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7",
|
|
||||||
)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
result, err := state.cmd.Execute(state.ctx, request)
|
|
||||||
assert.Equal(t, ErrDAppIsNotPermittedByUser, err)
|
|
||||||
assert.Empty(t, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailToPersonalSignWithoutParams(t *testing.T) {
|
|
||||||
state, close := setupCommand(t, Method_PersonalSign)
|
|
||||||
t.Cleanup(close)
|
|
||||||
|
|
||||||
request, err := ConstructRPCRequest("personal_sign", nil, &testDAppData)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
result, err := state.cmd.Execute(state.ctx, request)
|
|
||||||
assert.Equal(t, ErrEmptyRPCParams, err)
|
|
||||||
assert.Empty(t, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFailToPersonalSignWithSignalTimout(t *testing.T) {
|
|
||||||
state, close := setupCommand(t, Method_PersonalSign)
|
|
||||||
t.Cleanup(close)
|
|
||||||
|
|
||||||
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
request, err := preparePersonalSignRequest(testDAppData,
|
|
||||||
"0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e",
|
|
||||||
"0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7",
|
|
||||||
)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
backupWalletResponseMaxInterval := WalletResponseMaxInterval
|
|
||||||
WalletResponseMaxInterval = 1 * time.Millisecond
|
|
||||||
|
|
||||||
_, err = state.cmd.Execute(state.ctx, request)
|
|
||||||
assert.Equal(t, ErrWalletResponseTimeout, err)
|
|
||||||
WalletResponseMaxInterval = backupWalletResponseMaxInterval
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPersonalSignWithSignalAccepted(t *testing.T) {
|
|
||||||
state, close := setupCommand(t, Method_PersonalSign)
|
|
||||||
t.Cleanup(close)
|
|
||||||
|
|
||||||
fakedSignature := "0x051"
|
|
||||||
|
|
||||||
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
challenge := "0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e"
|
|
||||||
address := "0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
|
|
||||||
request, err := preparePersonalSignRequest(testDAppData, challenge, address)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
signal.SetMobileSignalHandler(signal.MobileSignalHandler(func(s []byte) {
|
|
||||||
var evt EventType
|
|
||||||
err := json.Unmarshal(s, &evt)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
switch evt.Type {
|
|
||||||
case signal.EventConnectorPersonalSign:
|
|
||||||
var ev signal.ConnectorPersonalSignSignal
|
|
||||||
err := json.Unmarshal(evt.Event, &ev)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, ev.Challenge, challenge)
|
|
||||||
assert.Equal(t, ev.Address, address)
|
|
||||||
|
|
||||||
err = state.handler.PersonalSignAccepted(PersonalSignAcceptedArgs{
|
|
||||||
Signature: fakedSignature,
|
|
||||||
RequestID: ev.RequestID,
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
t.Cleanup(signal.ResetMobileSignalHandler)
|
|
||||||
|
|
||||||
response, err := state.cmd.Execute(state.ctx, request)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, response, fakedSignature)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPersonalSignWithSignalRejected(t *testing.T) {
|
|
||||||
state, close := setupCommand(t, Method_PersonalSign)
|
|
||||||
t.Cleanup(close)
|
|
||||||
|
|
||||||
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
challenge := "0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e"
|
|
||||||
address := "0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
|
|
||||||
request, err := preparePersonalSignRequest(testDAppData, challenge, address)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
signal.SetMobileSignalHandler(signal.MobileSignalHandler(func(s []byte) {
|
|
||||||
var evt EventType
|
|
||||||
err := json.Unmarshal(s, &evt)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
switch evt.Type {
|
|
||||||
case signal.EventConnectorPersonalSign:
|
|
||||||
var ev signal.ConnectorPersonalSignSignal
|
|
||||||
err := json.Unmarshal(evt.Event, &ev)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
err = state.handler.PersonalSignRejected(RejectedArgs{
|
|
||||||
RequestID: ev.RequestID,
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
t.Cleanup(signal.ResetMobileSignalHandler)
|
|
||||||
|
|
||||||
_, err = state.cmd.Execute(state.ctx, request)
|
|
||||||
assert.Equal(t, ErrPersonalSignRejectedByUser, err)
|
|
||||||
}
|
|
|
@ -17,6 +17,7 @@ const (
|
||||||
Method_EthRequestAccounts = "eth_requestAccounts"
|
Method_EthRequestAccounts = "eth_requestAccounts"
|
||||||
Method_EthChainId = "eth_chainId"
|
Method_EthChainId = "eth_chainId"
|
||||||
Method_PersonalSign = "personal_sign"
|
Method_PersonalSign = "personal_sign"
|
||||||
|
Method_SignTypedDataV4 = "eth_signTypedData_v4"
|
||||||
Method_EthSendTransaction = "eth_sendTransaction"
|
Method_EthSendTransaction = "eth_sendTransaction"
|
||||||
Method_RequestPermissions = "wallet_requestPermissions"
|
Method_RequestPermissions = "wallet_requestPermissions"
|
||||||
Method_RevokePermissions = "wallet_revokePermissions"
|
Method_RevokePermissions = "wallet_revokePermissions"
|
||||||
|
@ -56,7 +57,7 @@ type SendTransactionAcceptedArgs struct {
|
||||||
Hash types.Hash `json:"hash"`
|
Hash types.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PersonalSignAcceptedArgs struct {
|
type SignAcceptedArgs struct {
|
||||||
RequestID string `json:"requestId"`
|
RequestID string `json:"requestId"`
|
||||||
Signature string `json:"signature"`
|
Signature string `json:"signature"`
|
||||||
}
|
}
|
||||||
|
@ -79,9 +80,9 @@ type ClientSideHandlerInterface interface {
|
||||||
SendTransactionAccepted(args SendTransactionAcceptedArgs) error
|
SendTransactionAccepted(args SendTransactionAcceptedArgs) error
|
||||||
SendTransactionRejected(args RejectedArgs) error
|
SendTransactionRejected(args RejectedArgs) error
|
||||||
|
|
||||||
RequestPersonalSign(dApp signal.ConnectorDApp, challenge, address string) (string, error)
|
RequestSign(dApp signal.ConnectorDApp, challenge, address string, method string) (string, error)
|
||||||
PersonalSignAccepted(args PersonalSignAcceptedArgs) error
|
SignAccepted(args SignAcceptedArgs) error
|
||||||
PersonalSignRejected(args RejectedArgs) error
|
SignRejected(args RejectedArgs) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetworkManagerInterface interface {
|
type NetworkManagerInterface interface {
|
||||||
|
|
|
@ -12,19 +12,25 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrInvalidParamsStructure = errors.New("invalid params structure")
|
ErrInvalidParamsStructure = errors.New("invalid params structure")
|
||||||
|
ErrInvalidMethod = errors.New("invalid method")
|
||||||
)
|
)
|
||||||
|
|
||||||
type PersonalSignCommand struct {
|
type SignCommand struct {
|
||||||
Db *sql.DB
|
Db *sql.DB
|
||||||
ClientHandler ClientSideHandlerInterface
|
ClientHandler ClientSideHandlerInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
type PersonalSignParams struct {
|
type SignParams struct {
|
||||||
Challenge string `json:"challenge"`
|
Challenge string `json:"challenge"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
|
Method string `json:"method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RPCRequest) getPersonalSignParams() (*PersonalSignParams, error) {
|
func (r *RPCRequest) getSignParams() (*SignParams, error) {
|
||||||
|
if r.Method != Method_PersonalSign && r.Method != Method_SignTypedDataV4 {
|
||||||
|
return nil, ErrInvalidMethod
|
||||||
|
}
|
||||||
|
|
||||||
if r.Params == nil || len(r.Params) == 0 {
|
if r.Params == nil || len(r.Params) == 0 {
|
||||||
return nil, ErrEmptyRPCParams
|
return nil, ErrEmptyRPCParams
|
||||||
}
|
}
|
||||||
|
@ -33,31 +39,40 @@ func (r *RPCRequest) getPersonalSignParams() (*PersonalSignParams, error) {
|
||||||
return nil, ErrInvalidParamsStructure
|
return nil, ErrInvalidParamsStructure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
challengeIndex := 0
|
||||||
|
addressIndex := 1
|
||||||
|
|
||||||
|
if r.Method == Method_SignTypedDataV4 {
|
||||||
|
challengeIndex = 1
|
||||||
|
addressIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
// Extract the Challenge and Address fields from paramsArray
|
// Extract the Challenge and Address fields from paramsArray
|
||||||
challenge, ok := r.Params[0].(string)
|
challenge, ok := r.Params[challengeIndex].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("missing or invalid 'challenge' field")
|
return nil, fmt.Errorf("missing or invalid 'challenge' field")
|
||||||
}
|
}
|
||||||
|
|
||||||
address, ok := r.Params[1].(string)
|
address, ok := r.Params[addressIndex].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("missing or invalid 'address' field")
|
return nil, fmt.Errorf("missing or invalid 'address' field")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and return the PersonalSignParams
|
// Create and return the PersonalSignParams
|
||||||
return &PersonalSignParams{
|
return &SignParams{
|
||||||
Challenge: challenge,
|
Challenge: challenge,
|
||||||
Address: address,
|
Address: address,
|
||||||
|
Method: r.Method,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PersonalSignCommand) Execute(ctx context.Context, request RPCRequest) (interface{}, error) {
|
func (c *SignCommand) Execute(ctx context.Context, request RPCRequest) (interface{}, error) {
|
||||||
err := request.Validate()
|
err := request.Validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
params, err := request.getPersonalSignParams()
|
params, err := request.getSignParams()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -71,9 +86,9 @@ func (c *PersonalSignCommand) Execute(ctx context.Context, request RPCRequest) (
|
||||||
return "", ErrDAppIsNotPermittedByUser
|
return "", ErrDAppIsNotPermittedByUser
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.ClientHandler.RequestPersonalSign(signal.ConnectorDApp{
|
return c.ClientHandler.RequestSign(signal.ConnectorDApp{
|
||||||
URL: request.URL,
|
URL: request.URL,
|
||||||
Name: request.Name,
|
Name: request.Name,
|
||||||
IconURL: request.IconURL,
|
IconURL: request.IconURL,
|
||||||
}, params.Challenge, params.Address)
|
}, params.Challenge, params.Address, params.Method)
|
||||||
}
|
}
|
|
@ -0,0 +1,275 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
"github.com/status-im/status-go/signal"
|
||||||
|
)
|
||||||
|
|
||||||
|
func preparePersonalSignRequest(dApp signal.ConnectorDApp, challenge, address string) (RPCRequest, error) {
|
||||||
|
return ConstructRPCRequest("personal_sign", []interface{}{challenge, address}, &dApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareTypedDataV4SignRequest(dApp signal.ConnectorDApp, challenge, address string) (RPCRequest, error) {
|
||||||
|
return ConstructRPCRequest("eth_signTypedData_v4", []interface{}{address, challenge}, &dApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFailToPersonalSignWithMissingDAppFields(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_PersonalSign)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
// Missing DApp fields
|
||||||
|
request, err := ConstructRPCRequest("personal_sign", []interface{}{}, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
result, err := state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.Equal(t, ErrRequestMissingDAppData, err)
|
||||||
|
assert.Empty(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFailToPersonalSignForUnpermittedDApp(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_PersonalSign)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
request, err := preparePersonalSignRequest(testDAppData,
|
||||||
|
"0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e",
|
||||||
|
"0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7",
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
result, err := state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.Equal(t, ErrDAppIsNotPermittedByUser, err)
|
||||||
|
assert.Empty(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFailToPersonalSignWithoutParams(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_PersonalSign)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
request, err := ConstructRPCRequest("personal_sign", nil, &testDAppData)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
result, err := state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.Equal(t, ErrEmptyRPCParams, err)
|
||||||
|
assert.Empty(t, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFailToPersonalSignWithSignalTimout(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_PersonalSign)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
request, err := preparePersonalSignRequest(testDAppData,
|
||||||
|
"0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e",
|
||||||
|
"0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7",
|
||||||
|
)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
backupWalletResponseMaxInterval := WalletResponseMaxInterval
|
||||||
|
WalletResponseMaxInterval = 1 * time.Millisecond
|
||||||
|
|
||||||
|
_, err = state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.Equal(t, ErrWalletResponseTimeout, err)
|
||||||
|
WalletResponseMaxInterval = backupWalletResponseMaxInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPersonalSignWithSignalAccepted(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_PersonalSign)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
fakedSignature := "0x051"
|
||||||
|
|
||||||
|
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
challenge := "0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e"
|
||||||
|
address := "0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
|
||||||
|
request, err := preparePersonalSignRequest(testDAppData, challenge, address)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
signal.SetMobileSignalHandler(signal.MobileSignalHandler(func(s []byte) {
|
||||||
|
var evt EventType
|
||||||
|
err := json.Unmarshal(s, &evt)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
switch evt.Type {
|
||||||
|
case signal.EventConnectorSign:
|
||||||
|
var ev signal.ConnectorSignSignal
|
||||||
|
err := json.Unmarshal(evt.Event, &ev)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, ev.Challenge, challenge)
|
||||||
|
assert.Equal(t, ev.Address, address)
|
||||||
|
|
||||||
|
err = state.handler.SignAccepted(SignAcceptedArgs{
|
||||||
|
Signature: fakedSignature,
|
||||||
|
RequestID: ev.RequestID,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
t.Cleanup(signal.ResetMobileSignalHandler)
|
||||||
|
|
||||||
|
response, err := state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, response, fakedSignature)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPersonalSignWithSignalRejected(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_PersonalSign)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
challenge := "0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e"
|
||||||
|
address := "0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
|
||||||
|
request, err := preparePersonalSignRequest(testDAppData, challenge, address)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
signal.SetMobileSignalHandler(signal.MobileSignalHandler(func(s []byte) {
|
||||||
|
var evt EventType
|
||||||
|
err := json.Unmarshal(s, &evt)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
switch evt.Type {
|
||||||
|
case signal.EventConnectorSign:
|
||||||
|
var ev signal.ConnectorSignSignal
|
||||||
|
err := json.Unmarshal(evt.Event, &ev)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = state.handler.SignRejected(RejectedArgs{
|
||||||
|
RequestID: ev.RequestID,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
t.Cleanup(signal.ResetMobileSignalHandler)
|
||||||
|
|
||||||
|
_, err = state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.Equal(t, ErrSignRejectedByUser, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypedDataV4SignRequestWithSignalAccepted(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_SignTypedDataV4)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
fakedSignature := "0x051"
|
||||||
|
|
||||||
|
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
challenge := "{\"domain\":{\"chainId\":\"1\",\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}],\"attachment\":\"0x\"},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"},{\"name\":\"attachment\",\"type\":\"bytes\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}"
|
||||||
|
address := "0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
|
||||||
|
request, err := prepareTypedDataV4SignRequest(testDAppData, challenge, address)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
signal.SetMobileSignalHandler(signal.MobileSignalHandler(func(s []byte) {
|
||||||
|
var evt EventType
|
||||||
|
err := json.Unmarshal(s, &evt)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
switch evt.Type {
|
||||||
|
case signal.EventConnectorSign:
|
||||||
|
var ev signal.ConnectorSignSignal
|
||||||
|
err := json.Unmarshal(evt.Event, &ev)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, ev.Challenge, challenge)
|
||||||
|
assert.Equal(t, ev.Address, address)
|
||||||
|
|
||||||
|
err = state.handler.SignAccepted(SignAcceptedArgs{
|
||||||
|
Signature: fakedSignature,
|
||||||
|
RequestID: ev.RequestID,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
t.Cleanup(signal.ResetMobileSignalHandler)
|
||||||
|
|
||||||
|
response, err := state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, response, fakedSignature)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTypedDataV4SignRequestWithSignalRejected(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_SignTypedDataV4)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
challenge := "{\"domain\":{\"chainId\":\"1\",\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}],\"attachment\":\"0x\"},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"},{\"name\":\"attachment\",\"type\":\"bytes\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}"
|
||||||
|
address := "0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
|
||||||
|
request, err := preparePersonalSignRequest(testDAppData, challenge, address)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
signal.SetMobileSignalHandler(signal.MobileSignalHandler(func(s []byte) {
|
||||||
|
var evt EventType
|
||||||
|
err := json.Unmarshal(s, &evt)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
switch evt.Type {
|
||||||
|
case signal.EventConnectorSign:
|
||||||
|
var ev signal.ConnectorSignSignal
|
||||||
|
err := json.Unmarshal(evt.Event, &ev)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = state.handler.SignRejected(RejectedArgs{
|
||||||
|
RequestID: ev.RequestID,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
t.Cleanup(signal.ResetMobileSignalHandler)
|
||||||
|
|
||||||
|
_, err = state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.Equal(t, ErrSignRejectedByUser, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnsupportedSignMethod(t *testing.T) {
|
||||||
|
state, close := setupCommand(t, Method_PersonalSign)
|
||||||
|
t.Cleanup(close)
|
||||||
|
|
||||||
|
err := PersistDAppData(state.walletDb, testDAppData, types.Address{0x01}, uint64(0x1))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
challenge := "0x506c65617365207369676e2074686973206d65737361676520746f20636f6e6669726d20796f7572206964656e746974792e"
|
||||||
|
address := "0x4B0897b0513FdBeEc7C469D9aF4fA6C0752aBea7"
|
||||||
|
request, err := preparePersonalSignRequest(testDAppData, challenge, address)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
request.Method = "eth_signTypedData"
|
||||||
|
fakedSignature := "0x051"
|
||||||
|
|
||||||
|
signal.SetMobileSignalHandler(signal.MobileSignalHandler(func(s []byte) {
|
||||||
|
var evt EventType
|
||||||
|
err := json.Unmarshal(s, &evt)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
switch evt.Type {
|
||||||
|
case signal.EventConnectorSign:
|
||||||
|
var ev signal.ConnectorSignSignal
|
||||||
|
err := json.Unmarshal(evt.Event, &ev)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, ev.Challenge, challenge)
|
||||||
|
assert.Equal(t, ev.Address, address)
|
||||||
|
|
||||||
|
err = state.handler.SignAccepted(SignAcceptedArgs{
|
||||||
|
Signature: fakedSignature,
|
||||||
|
RequestID: ev.RequestID,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
t.Cleanup(signal.ResetMobileSignalHandler)
|
||||||
|
|
||||||
|
response, err := state.cmd.Execute(state.ctx, request)
|
||||||
|
assert.Equal(t, ErrInvalidMethod, err)
|
||||||
|
assert.Equal(t, response, "")
|
||||||
|
}
|
|
@ -102,7 +102,12 @@ func setupCommand(t *testing.T, method string) (state testState, close func()) {
|
||||||
NetworkManager: networkManager,
|
NetworkManager: networkManager,
|
||||||
}
|
}
|
||||||
case Method_PersonalSign:
|
case Method_PersonalSign:
|
||||||
state.cmd = &PersonalSignCommand{
|
state.cmd = &SignCommand{
|
||||||
|
Db: state.walletDb,
|
||||||
|
ClientHandler: state.handler,
|
||||||
|
}
|
||||||
|
case Method_SignTypedDataV4:
|
||||||
|
state.cmd = &SignCommand{
|
||||||
Db: state.walletDb,
|
Db: state.walletDb,
|
||||||
ClientHandler: state.handler,
|
ClientHandler: state.handler,
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,12 +60,12 @@ func TestRequestAccountsSwitchChainAndSendTransactionFlow(t *testing.T) {
|
||||||
Hash: expectedHash,
|
Hash: expectedHash,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
case signal.EventConnectorPersonalSign:
|
case signal.EventConnectorSign:
|
||||||
var ev signal.ConnectorPersonalSignSignal
|
var ev signal.ConnectorSignSignal
|
||||||
err := json.Unmarshal(evt.Event, &ev)
|
err := json.Unmarshal(evt.Event, &ev)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
err = state.api.PersonalSignAccepted(commands.PersonalSignAcceptedArgs{
|
err = state.api.SignAccepted(commands.SignAcceptedArgs{
|
||||||
RequestID: ev.RequestID,
|
RequestID: ev.RequestID,
|
||||||
Signature: expectedSignature,
|
Signature: expectedSignature,
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
const (
|
const (
|
||||||
EventConnectorSendRequestAccounts = "connector.sendRequestAccounts"
|
EventConnectorSendRequestAccounts = "connector.sendRequestAccounts"
|
||||||
EventConnectorSendTransaction = "connector.sendTransaction"
|
EventConnectorSendTransaction = "connector.sendTransaction"
|
||||||
EventConnectorPersonalSign = "connector.personalSign"
|
EventConnectorSign = "connector.sign"
|
||||||
EventConnectorDAppPermissionGranted = "connector.dAppPermissionGranted"
|
EventConnectorDAppPermissionGranted = "connector.dAppPermissionGranted"
|
||||||
EventConnectorDAppPermissionRevoked = "connector.dAppPermissionRevoked"
|
EventConnectorDAppPermissionRevoked = "connector.dAppPermissionRevoked"
|
||||||
EventConnectorDAppChainIdSwitched = "connector.dAppChainIdSwitched"
|
EventConnectorDAppChainIdSwitched = "connector.dAppChainIdSwitched"
|
||||||
|
@ -39,11 +39,12 @@ type ConnectorSendDappPermissionGrantedSignal struct {
|
||||||
SharedAccount types.Address `json:"sharedAccount"`
|
SharedAccount types.Address `json:"sharedAccount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectorPersonalSignSignal struct {
|
type ConnectorSignSignal struct {
|
||||||
ConnectorDApp
|
ConnectorDApp
|
||||||
RequestID string `json:"requestId"`
|
RequestID string `json:"requestId"`
|
||||||
Challenge string `json:"challenge"`
|
Challenge string `json:"challenge"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
|
Method string `json:"method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectorDAppChainIdSwitchedSignal struct {
|
type ConnectorDAppChainIdSwitchedSignal struct {
|
||||||
|
@ -67,12 +68,13 @@ func SendConnectorSendTransaction(dApp ConnectorDApp, chainID uint64, txArgs str
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendConnectorPersonalSign(dApp ConnectorDApp, requestID, challenge, address string) {
|
func SendConnectorSign(dApp ConnectorDApp, requestID, challenge, address string, method string) {
|
||||||
send(EventConnectorPersonalSign, ConnectorPersonalSignSignal{
|
send(EventConnectorSign, ConnectorSignSignal{
|
||||||
ConnectorDApp: dApp,
|
ConnectorDApp: dApp,
|
||||||
RequestID: requestID,
|
RequestID: requestID,
|
||||||
Challenge: challenge,
|
Challenge: challenge,
|
||||||
Address: address,
|
Address: address,
|
||||||
|
Method: method,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue