Refactor shh_requestMessages method (#513)
This commit is contained in:
parent
3ff44f545b
commit
b7fb51d92a
1
Makefile
1
Makefile
|
@ -100,6 +100,7 @@ mock-install: ##@other Install mocking tools
|
|||
|
||||
mock: ##@other Regenerate mocks
|
||||
mockgen -source=geth/common/types.go -destination=geth/common/types_mock.go -package=common
|
||||
mockgen -source=geth/mailservice/mailservice.go -destination=geth/mailservice/mailservice_mock.go -package=mailservice
|
||||
mockgen -source=geth/common/notification.go -destination=geth/common/notification_mock.go -package=common -imports fcm=github.com/NaySoftware/go-fcm
|
||||
mockgen -source=geth/notification/fcm/client.go -destination=geth/notification/fcm/client_mock.go -package=fcm -imports fcm=github.com/NaySoftware/go-fcm
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package whisper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/status-im/status-go/e2e"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
func TestMailServiceSuite(t *testing.T) {
|
||||
suite.Run(t, new(MailServiceSuite))
|
||||
}
|
||||
|
||||
type MailServiceSuite struct {
|
||||
e2e.NodeManagerTestSuite
|
||||
}
|
||||
|
||||
func (s *MailServiceSuite) SetupTest() {
|
||||
s.NodeManager = node.NewNodeManager()
|
||||
}
|
||||
|
||||
// TestShhRequestMessagesRPCMethodAvailability tests if `shh_requestMessages` is available
|
||||
// through inproc and HTTP interfaces.
|
||||
func (s *MailServiceSuite) TestShhRequestMessagesRPCMethodAvailability() {
|
||||
r := s.Require()
|
||||
|
||||
s.StartTestNode(func(config *params.NodeConfig) {
|
||||
config.RPCEnabled = true
|
||||
})
|
||||
defer s.StopTestNode()
|
||||
|
||||
client := s.NodeManager.RPCClient()
|
||||
r.NotNil(client)
|
||||
|
||||
// This error means that the method is available through inproc communication
|
||||
// as the validation of params occurred.
|
||||
err := client.Call(nil, "shh_requestMessages", map[string]interface{}{})
|
||||
r.EqualError(err, `invalid mailServerPeer value: invalid URL scheme, want "enode"`)
|
||||
|
||||
// Do the same but using HTTP interface.
|
||||
req, err := http.NewRequest("POST", "http://localhost:8645", bytes.NewBuffer([]byte(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "shh_requestMessages",
|
||||
"params": [{}]
|
||||
}`)))
|
||||
r.NoError(err)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
r.NoError(err)
|
||||
defer resp.Body.Close()
|
||||
r.Equal(200, resp.StatusCode)
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
r.NoError(err)
|
||||
r.Contains(string(data), `invalid mailServerPeer value`)
|
||||
}
|
|
@ -117,7 +117,20 @@ func (s *WhisperMailboxSuite) TestRequestMessageFromMailboxAsync() {
|
|||
s.Require().NoError(err)
|
||||
s.Require().Nil(postResp.Err)
|
||||
|
||||
//There are no messages, because it's a sender filter
|
||||
// Propagate the sent message.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Receive the sent message.
|
||||
resp = rpcClient.CallRaw(`{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "shh_getFilterMessages",
|
||||
"params": ["` + messageFilterID + `"],
|
||||
"id": 1}`)
|
||||
err = json.Unmarshal([]byte(resp), &messages)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(1, len(messages.Result))
|
||||
|
||||
// Make sure there are no new messages.
|
||||
resp = rpcClient.CallRaw(`{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "shh_getFilterMessages",
|
||||
|
@ -135,11 +148,11 @@ func (s *WhisperMailboxSuite) TestRequestMessageFromMailboxAsync() {
|
|||
"id": 1,
|
||||
"method": "shh_requestMessages",
|
||||
"params": [{
|
||||
"peer":"` + mailboxPeerStr + `",
|
||||
"mailServerPeer":"` + mailboxPeerStr + `",
|
||||
"topic":"` + topic.String() + `",
|
||||
"symKeyID":"` + MailServerKeyID + `",
|
||||
"from":0,
|
||||
"to":` + strconv.FormatInt(time.Now().UnixNano()/int64(time.Second), 10) + `
|
||||
"to":` + strconv.FormatInt(time.Now().UTC().Unix(), 10) + `
|
||||
}]
|
||||
}`
|
||||
resp = rpcClient.CallRaw(reqMessagesBody)
|
||||
|
@ -198,18 +211,7 @@ func (s *WhisperMailboxSuite) TestRequestMessageFromMailboxAsync() {
|
|||
time.Sleep(time.Second)
|
||||
|
||||
//Request each one messages from mailbox using enode
|
||||
resp = rpcClient.CallRaw(`{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"method": "shh_requestMessages",
|
||||
"params": [{
|
||||
"enode":"` + mailboxEnode + `",
|
||||
"topic":"` + topic.String() + `",
|
||||
"symKeyID":"` + MailServerKeyID + `",
|
||||
"from":0,
|
||||
"to":` + strconv.FormatInt(time.Now().UnixNano()/int64(time.Second), 10) + `
|
||||
}]
|
||||
}`)
|
||||
resp = rpcClient.CallRaw(reqMessagesBody)
|
||||
reqMessagesResp = baseRPCResponse{}
|
||||
err = json.Unmarshal([]byte(resp), &reqMessagesResp)
|
||||
s.Require().NoError(err)
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
"github.com/status-im/status-go/geth/txqueue"
|
||||
"github.com/status-im/status-go/geth/whisper"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -244,7 +243,6 @@ func (m *StatusBackend) registerHandlers() error {
|
|||
return node.ErrRPCClient
|
||||
}
|
||||
|
||||
rpcClient.RegisterHandler("shh_requestMessages", whisper.RequestHistoricMessagesHandler(m.nodeManager))
|
||||
rpcClient.RegisterHandler("eth_accounts", m.accountManager.AccountsRPCHandler())
|
||||
rpcClient.RegisterHandler("eth_sendTransaction", m.txQueueManager.SendTransactionRPCHandler)
|
||||
m.txQueueManager.SetTransactionQueueHandler(m.txQueueManager.TransactionQueueHandler())
|
||||
|
|
|
@ -746,8 +746,10 @@ func (mr *MockJailCellMockRecorder) Call(item, this interface{}, args ...interfa
|
|||
}
|
||||
|
||||
// Stop mocks base method
|
||||
func (m *MockJailCell) Stop() {
|
||||
m.ctrl.Call(m, "Stop")
|
||||
func (m *MockJailCell) Stop() error {
|
||||
ret := m.ctrl.Call(m, "Stop")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Stop indicates an expected call of Stop
|
||||
|
@ -778,18 +780,6 @@ func (m *MockJailManager) EXPECT() *MockJailManagerMockRecorder {
|
|||
return m.recorder
|
||||
}
|
||||
|
||||
// Parse mocks base method
|
||||
func (m *MockJailManager) Parse(chatID, js string) string {
|
||||
ret := m.ctrl.Call(m, "Parse", chatID, js)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Parse indicates an expected call of Parse
|
||||
func (mr *MockJailManagerMockRecorder) Parse(chatID, js interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockJailManager)(nil).Parse), chatID, js)
|
||||
}
|
||||
|
||||
// Call mocks base method
|
||||
func (m *MockJailManager) Call(chatID, this, args string) string {
|
||||
ret := m.ctrl.Call(m, "Call", chatID, this, args)
|
||||
|
@ -802,17 +792,46 @@ func (mr *MockJailManagerMockRecorder) Call(chatID, this, args interface{}) *gom
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockJailManager)(nil).Call), chatID, this, args)
|
||||
}
|
||||
|
||||
// NewCell mocks base method
|
||||
func (m *MockJailManager) NewCell(chatID string) (JailCell, error) {
|
||||
ret := m.ctrl.Call(m, "NewCell", chatID)
|
||||
// CreateCell mocks base method
|
||||
func (m *MockJailManager) CreateCell(chatID string) (JailCell, error) {
|
||||
ret := m.ctrl.Call(m, "CreateCell", chatID)
|
||||
ret0, _ := ret[0].(JailCell)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// NewCell indicates an expected call of NewCell
|
||||
func (mr *MockJailManagerMockRecorder) NewCell(chatID interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewCell", reflect.TypeOf((*MockJailManager)(nil).NewCell), chatID)
|
||||
// CreateCell indicates an expected call of CreateCell
|
||||
func (mr *MockJailManagerMockRecorder) CreateCell(chatID interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCell", reflect.TypeOf((*MockJailManager)(nil).CreateCell), chatID)
|
||||
}
|
||||
|
||||
// Parse mocks base method
|
||||
func (m *MockJailManager) Parse(chatID, js string) string {
|
||||
ret := m.ctrl.Call(m, "Parse", chatID, js)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Parse indicates an expected call of Parse
|
||||
func (mr *MockJailManagerMockRecorder) Parse(chatID, js interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parse", reflect.TypeOf((*MockJailManager)(nil).Parse), chatID, js)
|
||||
}
|
||||
|
||||
// CreateAndInitCell mocks base method
|
||||
func (m *MockJailManager) CreateAndInitCell(chatID string, code ...string) string {
|
||||
varargs := []interface{}{chatID}
|
||||
for _, a := range code {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "CreateAndInitCell", varargs...)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CreateAndInitCell indicates an expected call of CreateAndInitCell
|
||||
func (mr *MockJailManagerMockRecorder) CreateAndInitCell(chatID interface{}, code ...interface{}) *gomock.Call {
|
||||
varargs := append([]interface{}{chatID}, code...)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAndInitCell", reflect.TypeOf((*MockJailManager)(nil).CreateAndInitCell), varargs...)
|
||||
}
|
||||
|
||||
// Cell mocks base method
|
||||
|
@ -828,14 +847,26 @@ func (mr *MockJailManagerMockRecorder) Cell(chatID interface{}) *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cell", reflect.TypeOf((*MockJailManager)(nil).Cell), chatID)
|
||||
}
|
||||
|
||||
// BaseJS mocks base method
|
||||
func (m *MockJailManager) BaseJS(js string) {
|
||||
m.ctrl.Call(m, "BaseJS", js)
|
||||
// Execute mocks base method
|
||||
func (m *MockJailManager) Execute(chatID, code string) string {
|
||||
ret := m.ctrl.Call(m, "Execute", chatID, code)
|
||||
ret0, _ := ret[0].(string)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// BaseJS indicates an expected call of BaseJS
|
||||
func (mr *MockJailManagerMockRecorder) BaseJS(js interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BaseJS", reflect.TypeOf((*MockJailManager)(nil).BaseJS), js)
|
||||
// Execute indicates an expected call of Execute
|
||||
func (mr *MockJailManagerMockRecorder) Execute(chatID, code interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Execute", reflect.TypeOf((*MockJailManager)(nil).Execute), chatID, code)
|
||||
}
|
||||
|
||||
// SetBaseJS mocks base method
|
||||
func (m *MockJailManager) SetBaseJS(js string) {
|
||||
m.ctrl.Call(m, "SetBaseJS", js)
|
||||
}
|
||||
|
||||
// SetBaseJS indicates an expected call of SetBaseJS
|
||||
func (mr *MockJailManagerMockRecorder) SetBaseJS(js interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetBaseJS", reflect.TypeOf((*MockJailManager)(nil).SetBaseJS), js)
|
||||
}
|
||||
|
||||
// Stop mocks base method
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
package mailservice
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
)
|
||||
|
||||
// ServiceProvider provides node and required services.
|
||||
type ServiceProvider interface {
|
||||
Node() (*node.Node, error)
|
||||
WhisperService() (*whisper.Whisper, error)
|
||||
}
|
||||
|
||||
// MailService is a service that provides some additional Whisper API.
|
||||
type MailService struct {
|
||||
provider ServiceProvider
|
||||
}
|
||||
|
||||
// Make sure that MailService implements node.Service interface.
|
||||
var _ node.Service = (*MailService)(nil)
|
||||
|
||||
// New returns a new MailService.
|
||||
func New(provider ServiceProvider) *MailService {
|
||||
return &MailService{provider}
|
||||
}
|
||||
|
||||
// Protocols returns a new protocols list. In this case, there are none.
|
||||
func (s *MailService) Protocols() []p2p.Protocol {
|
||||
return []p2p.Protocol{}
|
||||
}
|
||||
|
||||
// APIs returns a list of new APIs.
|
||||
func (s *MailService) APIs() []rpc.API {
|
||||
return []rpc.API{
|
||||
{
|
||||
Namespace: "shh",
|
||||
Version: "1.0",
|
||||
Service: NewPublicAPI(s.provider),
|
||||
Public: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Start is run when a service is started.
|
||||
// It does nothing in this case but is required by `node.Service` interface.
|
||||
func (s *MailService) 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 *MailService) Stop() error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package mailservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultWorkTime is a work time reported in messages sent to MailServer nodes.
|
||||
defaultWorkTime = 5
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidMailServerPeer is returned when it fails to parse enode from params.
|
||||
ErrInvalidMailServerPeer = errors.New("invalid mailServerPeer value")
|
||||
// ErrInvalidSymKeyID is returned when it fails to get a symmetric key.
|
||||
ErrInvalidSymKeyID = errors.New("invalid symKeyID value")
|
||||
)
|
||||
|
||||
// PublicAPI defines a MailServer public API.
|
||||
type PublicAPI struct {
|
||||
provider ServiceProvider
|
||||
}
|
||||
|
||||
// NewPublicAPI returns a new PublicAPI.
|
||||
func NewPublicAPI(provider ServiceProvider) *PublicAPI {
|
||||
return &PublicAPI{provider}
|
||||
}
|
||||
|
||||
// MessagesRequest is a payload send to a MailServer to get messages.
|
||||
type MessagesRequest struct {
|
||||
// MailServerPeer is MailServer's enode address.
|
||||
MailServerPeer string `json:"mailServerPeer"`
|
||||
|
||||
// From is a lower bound of time range (optional).
|
||||
// Default is 24 hours back from now.
|
||||
From uint32 `json:"from"`
|
||||
|
||||
// To is a upper bound of time range (optional).
|
||||
// Default is now.
|
||||
To uint32 `json:"to"`
|
||||
|
||||
// Topic is a regular Whisper topic.
|
||||
Topic whisper.TopicType `json:"topic"`
|
||||
|
||||
// SymKeyID is an ID of a symmetric key to authenticate to MailServer.
|
||||
// It's derived from MailServer password.
|
||||
SymKeyID string `json:"symKeyID"`
|
||||
}
|
||||
|
||||
func setMessagesRequestDefaults(r *MessagesRequest) {
|
||||
// set From and To defaults
|
||||
if r.From == 0 && r.To == 0 {
|
||||
r.From = uint32(time.Now().UTC().Add(-24 * time.Hour).Unix())
|
||||
r.To = uint32(time.Now().UTC().Unix())
|
||||
}
|
||||
}
|
||||
|
||||
// RequestMessages sends a request for historic messages to a MailServer.
|
||||
func (api *PublicAPI) RequestMessages(_ context.Context, r MessagesRequest) (bool, error) {
|
||||
log.Info("RequestMessages", "request", r)
|
||||
|
||||
setMessagesRequestDefaults(&r)
|
||||
|
||||
shh, err := api.provider.WhisperService()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
node, err := api.provider.Node()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
mailServerNode, err := discover.ParseNode(r.MailServerPeer)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%v: %v", ErrInvalidMailServerPeer, err)
|
||||
}
|
||||
|
||||
symKey, err := shh.GetSymKey(r.SymKeyID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%v: %v", ErrInvalidSymKeyID, err)
|
||||
}
|
||||
|
||||
envelope, err := makeEnvelop(makePayload(r), symKey, node.Server().PrivateKey, shh.MinPow())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := shh.RequestHistoricMessages(mailServerNode.ID[:], envelope); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// makeEnvelop makes an envelop for a historic messages request.
|
||||
// Symmetric key is used to authenticate to MailServer.
|
||||
// PK is the current node ID.
|
||||
func makeEnvelop(payload []byte, symKey []byte, nodeID *ecdsa.PrivateKey, pow float64) (*whisper.Envelope, error) {
|
||||
params := whisper.MessageParams{
|
||||
PoW: pow,
|
||||
Payload: payload,
|
||||
KeySym: symKey,
|
||||
WorkTime: defaultWorkTime,
|
||||
Src: nodeID,
|
||||
}
|
||||
message, err := whisper.NewSentMessage(¶ms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return message.Wrap(¶ms)
|
||||
}
|
||||
|
||||
// makePayload makes a specific payload for MailServer to request historic messages.
|
||||
func makePayload(r MessagesRequest) []byte {
|
||||
// first 8 bytes are lowed and upper bounds as uint32
|
||||
data := make([]byte, 8+whisper.TopicLength)
|
||||
binary.BigEndian.PutUint32(data, r.From)
|
||||
binary.BigEndian.PutUint32(data[4:], r.To)
|
||||
copy(data[8:], r.Topic[:])
|
||||
return data
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package mailservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRequestMessagesDefaults(t *testing.T) {
|
||||
r := MessagesRequest{}
|
||||
setMessagesRequestDefaults(&r)
|
||||
require.NotZero(t, r.From)
|
||||
require.InEpsilon(t, uint32(time.Now().UTC().Unix()), r.To, 1.0)
|
||||
}
|
||||
|
||||
func TestRequestMessagesFailures(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
provider := NewMockServiceProvider(ctrl)
|
||||
api := NewPublicAPI(provider)
|
||||
shh := whisper.New(nil)
|
||||
// Node is ephemeral (only in memory).
|
||||
nodeA, nodeErr := node.New(&node.Config{NoUSB: true})
|
||||
require.NoError(t, nodeErr)
|
||||
require.NoError(t, nodeA.Start())
|
||||
defer nodeA.Stop()
|
||||
|
||||
const (
|
||||
mailServerPeer = "enode://b7e65e1bedc2499ee6cbd806945af5e7df0e59e4070c96821570bd581473eade24a489f5ec95d060c0db118c879403ab88d827d3766978f28708989d35474f87@[::]:51920"
|
||||
)
|
||||
|
||||
var (
|
||||
result bool
|
||||
err error
|
||||
)
|
||||
|
||||
// invalid MailServer enode address
|
||||
provider.EXPECT().WhisperService().Return(nil, nil)
|
||||
provider.EXPECT().Node().Return(nil, nil)
|
||||
result, err = api.RequestMessages(context.TODO(), MessagesRequest{MailServerPeer: "invalid-address"})
|
||||
require.False(t, result)
|
||||
require.EqualError(t, err, "invalid mailServerPeer value: invalid URL scheme, want \"enode\"")
|
||||
|
||||
// non-existent symmetric key
|
||||
provider.EXPECT().WhisperService().Return(shh, nil)
|
||||
provider.EXPECT().Node().Return(nil, nil)
|
||||
result, err = api.RequestMessages(context.TODO(), MessagesRequest{
|
||||
MailServerPeer: mailServerPeer,
|
||||
})
|
||||
require.False(t, result)
|
||||
require.EqualError(t, err, "invalid symKeyID value: non-existent key ID")
|
||||
|
||||
// with a symmetric key
|
||||
symKeyID, symKeyErr := shh.AddSymKeyFromPassword("some-pass")
|
||||
require.NoError(t, symKeyErr)
|
||||
provider.EXPECT().WhisperService().Return(shh, nil)
|
||||
provider.EXPECT().Node().Return(nodeA, nil)
|
||||
result, err = api.RequestMessages(context.TODO(), MessagesRequest{
|
||||
MailServerPeer: mailServerPeer,
|
||||
SymKeyID: symKeyID,
|
||||
})
|
||||
require.Contains(t, err.Error(), "Could not find peer with ID")
|
||||
require.False(t, result)
|
||||
}
|
||||
|
||||
func TestRequestMessagesSuccess(t *testing.T) {
|
||||
// TODO(adam): next step would be to run a successful test, however,
|
||||
// it requires to set up emepheral nodes that can discover each other
|
||||
// without syncing blockchain. It requires a bit research how to do that.
|
||||
t.Skip()
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: geth/mailservice/mailservice.go
|
||||
|
||||
// Package mailservice is a generated GoMock package.
|
||||
package mailservice
|
||||
|
||||
import (
|
||||
node "github.com/ethereum/go-ethereum/node"
|
||||
whisperv5 "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockServiceProvider is a mock of ServiceProvider interface
|
||||
type MockServiceProvider struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockServiceProviderMockRecorder
|
||||
}
|
||||
|
||||
// MockServiceProviderMockRecorder is the mock recorder for MockServiceProvider
|
||||
type MockServiceProviderMockRecorder struct {
|
||||
mock *MockServiceProvider
|
||||
}
|
||||
|
||||
// NewMockServiceProvider creates a new mock instance
|
||||
func NewMockServiceProvider(ctrl *gomock.Controller) *MockServiceProvider {
|
||||
mock := &MockServiceProvider{ctrl: ctrl}
|
||||
mock.recorder = &MockServiceProviderMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockServiceProvider) EXPECT() *MockServiceProviderMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Node mocks base method
|
||||
func (m *MockServiceProvider) Node() (*node.Node, error) {
|
||||
ret := m.ctrl.Call(m, "Node")
|
||||
ret0, _ := ret[0].(*node.Node)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Node indicates an expected call of Node
|
||||
func (mr *MockServiceProviderMockRecorder) Node() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Node", reflect.TypeOf((*MockServiceProvider)(nil).Node))
|
||||
}
|
||||
|
||||
// WhisperService mocks base method
|
||||
func (m *MockServiceProvider) WhisperService() (*whisperv5.Whisper, error) {
|
||||
ret := m.ctrl.Call(m, "WhisperService")
|
||||
ret0, _ := ret[0].(*whisperv5.Whisper)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// WhisperService indicates an expected call of WhisperService
|
||||
func (mr *MockServiceProviderMockRecorder) WhisperService() *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WhisperService", reflect.TypeOf((*MockServiceProvider)(nil).WhisperService))
|
||||
}
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/mailservice"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
|
@ -74,6 +75,13 @@ func (m *NodeManager) startNode(config *params.NodeConfig) (<-chan struct{}, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// activate MailService required for Offline Inboxing
|
||||
if err := ethNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
|
||||
return mailservice.New(m), nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.nodeStarted = make(chan struct{}, 1)
|
||||
|
||||
go func() {
|
||||
|
|
|
@ -1,187 +0,0 @@
|
|||
package whisper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/geth/common"
|
||||
"github.com/status-im/status-go/geth/rpc"
|
||||
)
|
||||
|
||||
var (
|
||||
//ErrInvalidNumberOfArgs - error invalid aruments in request
|
||||
ErrInvalidNumberOfArgs = fmt.Errorf("invalid number of arguments, expected 1")
|
||||
//ErrInvalidArgs - error invalid request format
|
||||
ErrInvalidArgs = fmt.Errorf("invalid args")
|
||||
//ErrTopicNotExist - error topic field doesn't exist in request
|
||||
ErrTopicNotExist = fmt.Errorf("topic value does not exist")
|
||||
//ErrTopicNotString - error topic is not string type
|
||||
ErrTopicNotString = fmt.Errorf("topic value is not string")
|
||||
//ErrMailboxSymkeyIDNotExist - error symKeyID field doesn't exist in request
|
||||
ErrMailboxSymkeyIDNotExist = fmt.Errorf("symKeyID does not exist")
|
||||
//ErrMailboxSymkeyIDNotString - error symKeyID is not string type
|
||||
ErrMailboxSymkeyIDNotString = fmt.Errorf("symKeyID is not string")
|
||||
//ErrPeerNotExist - error peer field doesn't exist in request
|
||||
ErrPeerOrEnode = fmt.Errorf("enode or peer field should be not empty")
|
||||
)
|
||||
|
||||
const defaultWorkTime = 5
|
||||
|
||||
//RequestHistoricMessagesHandler returns an RPC handler which sends a p2p request for historic messages.
|
||||
func RequestHistoricMessagesHandler(nodeManager common.NodeManager) rpc.Handler {
|
||||
return func(ctx context.Context, args ...interface{}) (interface{}, error) {
|
||||
whisper, err := nodeManager.WhisperService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
node, err := nodeManager.Node()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := parseArgs(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
symkey, err := whisper.GetSymKey(r.SymkeyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.PoW = whisper.MinPow()
|
||||
env, err := makeEnvelop(r, symkey, node.Server().PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = whisper.RequestHistoricMessages(r.Peer, env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
type historicMessagesRequest struct {
|
||||
Peer []byte //mailbox peer
|
||||
TimeLow uint32 //resend messages from
|
||||
TimeUp uint32 //resend messages to
|
||||
Topic whisperv5.TopicType //resend messages by topic
|
||||
SymkeyID string //Mailbox symmetric key id
|
||||
PoW float64 //whisper proof of work
|
||||
}
|
||||
|
||||
func parseArgs(args ...interface{}) (historicMessagesRequest, error) {
|
||||
var (
|
||||
r = historicMessagesRequest{
|
||||
TimeLow: uint32(time.Now().Add(-24 * time.Hour).Unix()),
|
||||
TimeUp: uint32(time.Now().Unix()),
|
||||
}
|
||||
)
|
||||
|
||||
if len(args) != 1 {
|
||||
return historicMessagesRequest{}, ErrInvalidNumberOfArgs
|
||||
}
|
||||
|
||||
historicMessagesArgs, ok := args[0].(map[string]interface{})
|
||||
if !ok {
|
||||
return historicMessagesRequest{}, ErrInvalidArgs
|
||||
}
|
||||
|
||||
if t, ok := historicMessagesArgs["from"]; ok {
|
||||
if parsed, ok := t.(uint32); ok {
|
||||
r.TimeLow = parsed
|
||||
}
|
||||
}
|
||||
if t, ok := historicMessagesArgs["to"]; ok {
|
||||
if parsed, ok := t.(uint32); ok {
|
||||
r.TimeUp = parsed
|
||||
}
|
||||
}
|
||||
topicInterfaceValue, ok := historicMessagesArgs["topic"]
|
||||
if !ok {
|
||||
return historicMessagesRequest{}, ErrTopicNotExist
|
||||
}
|
||||
|
||||
topicStringValue, ok := topicInterfaceValue.(string)
|
||||
if !ok {
|
||||
return historicMessagesRequest{}, ErrTopicNotString
|
||||
}
|
||||
|
||||
if err := r.Topic.UnmarshalText([]byte(topicStringValue)); err != nil {
|
||||
return historicMessagesRequest{}, nil
|
||||
}
|
||||
|
||||
symkeyIDInterfaceValue, ok := historicMessagesArgs["symKeyID"]
|
||||
if !ok {
|
||||
return historicMessagesRequest{}, ErrMailboxSymkeyIDNotExist
|
||||
}
|
||||
r.SymkeyID, ok = symkeyIDInterfaceValue.(string)
|
||||
if !ok {
|
||||
return historicMessagesRequest{}, ErrMailboxSymkeyIDNotString
|
||||
}
|
||||
|
||||
peer, err := getPeerID(historicMessagesArgs)
|
||||
if err != nil {
|
||||
return historicMessagesRequest{}, err
|
||||
}
|
||||
r.Peer = peer
|
||||
return r, nil
|
||||
}
|
||||
|
||||
//makeEnvelop make envelop for request histtoric messages. symmetric key to authenticate to MailServer node and pk is the current node ID.
|
||||
func makeEnvelop(r historicMessagesRequest, symkey []byte, pk *ecdsa.PrivateKey) (*whisperv5.Envelope, error) {
|
||||
var params whisperv5.MessageParams
|
||||
params.PoW = r.PoW
|
||||
params.Payload = makePayloadData(r)
|
||||
params.KeySym = symkey
|
||||
params.WorkTime = defaultWorkTime
|
||||
params.Src = pk
|
||||
|
||||
message, err := whisperv5.NewSentMessage(¶ms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return message.Wrap(¶ms)
|
||||
}
|
||||
|
||||
//makePayloadData make specific payload for mailserver
|
||||
func makePayloadData(r historicMessagesRequest) []byte {
|
||||
data := make([]byte, 8+whisperv5.TopicLength)
|
||||
binary.BigEndian.PutUint32(data, r.TimeLow)
|
||||
binary.BigEndian.PutUint32(data[4:], r.TimeUp)
|
||||
copy(data[8:], r.Topic[:])
|
||||
return data
|
||||
}
|
||||
|
||||
//getPeerID is used to get peerID from string values of peerID or enode
|
||||
func getPeerID(m map[string]interface{}) ([]byte, error) {
|
||||
peerInterfaceValue, okPeer := m["peer"]
|
||||
enodeInterfaceValue, okEnode := m["enode"]
|
||||
|
||||
//only if existing peer or enode(!xor)
|
||||
if okPeer == okEnode {
|
||||
return nil, ErrPeerOrEnode
|
||||
}
|
||||
var peerOrEnode string
|
||||
if p, ok := peerInterfaceValue.(string); ok && okPeer {
|
||||
peerOrEnode = p
|
||||
} else if str, ok := enodeInterfaceValue.(string); ok && okEnode {
|
||||
peerOrEnode = str
|
||||
} else {
|
||||
return nil, ErrPeerOrEnode
|
||||
}
|
||||
|
||||
n, err := discover.ParseNode(peerOrEnode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return n.ID[:], nil
|
||||
}
|
|
@ -443,8 +443,7 @@ func NotifyUsers(message, payloadJSON, tokensArray *C.char) (outCBytes *C.char)
|
|||
return
|
||||
}
|
||||
|
||||
//AddPeer adds peer by enode,
|
||||
//eg 'enode://da3bf389a031f33fb55c9f5f54fde8473912402d27fffaa50efd74c0d0515f3a61daf6d52151f2876b19c15828e6f670352bff432b5ec457652e74755e8c864f@51.15.62.116:30303'
|
||||
// AddPeer adds an enode as a peer.
|
||||
//export AddPeer
|
||||
func AddPeer(enode *C.char) *C.char {
|
||||
err := statusAPI.NodeManager().AddPeer(C.GoString(enode))
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
{
|
||||
"name": "status-js",
|
||||
"version": "0.9.8",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"assertion-error": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz",
|
||||
"integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=",
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.17.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.17.1.tgz",
|
||||
"integrity": "sha1-LY4+XQvb1zJ/kbyBT1xXZg+Bgk0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"follow-redirects": "1.2.6",
|
||||
"is-buffer": "1.1.6"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"bignumber.js": {
|
||||
"version": "git://github.com/status-im/bignumber.js.git#cc066a0a3d6bfe0c436c9957f4ea8344bf963c89",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
|
||||
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"browser-stdout": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
|
||||
"integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
|
||||
"dev": true
|
||||
},
|
||||
"chai": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz",
|
||||
"integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assertion-error": "1.0.2",
|
||||
"check-error": "1.0.2",
|
||||
"deep-eql": "3.0.1",
|
||||
"get-func-name": "2.0.0",
|
||||
"pathval": "1.1.0",
|
||||
"type-detect": "4.0.5"
|
||||
}
|
||||
},
|
||||
"check-error": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
|
||||
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
|
||||
"integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-readlink": "1.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz",
|
||||
"integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=",
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.8",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
|
||||
"integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"deep-eql": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
|
||||
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-detect": "4.0.5"
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz",
|
||||
"integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.2.6.tgz",
|
||||
"integrity": "sha512-FrMqZ/FONtHnbqO651UPpfRUVukIEwJhXMfdr/JWAmrDbeYBu773b1J6gdWDyRIj4hvvzQEHoEOTrdR8o6KLYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "3.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||
"dev": true
|
||||
},
|
||||
"get-func-name": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz",
|
||||
"integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
}
|
||||
},
|
||||
"graceful-readlink": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
|
||||
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
|
||||
"dev": true
|
||||
},
|
||||
"growl": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
|
||||
"integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
|
||||
"integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
|
||||
"dev": true
|
||||
},
|
||||
"he": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
|
||||
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "1.4.0",
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||
"dev": true
|
||||
},
|
||||
"is-buffer": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
|
||||
"dev": true
|
||||
},
|
||||
"json3": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
|
||||
"integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._baseassign": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz",
|
||||
"integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._basecopy": "3.0.1",
|
||||
"lodash.keys": "3.1.2"
|
||||
}
|
||||
},
|
||||
"lodash._basecopy": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
|
||||
"integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._basecreate": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz",
|
||||
"integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._getnative": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
|
||||
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash._isiterateecall": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
|
||||
"integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.create": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz",
|
||||
"integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._baseassign": "3.2.0",
|
||||
"lodash._basecreate": "3.0.3",
|
||||
"lodash._isiterateecall": "3.0.9"
|
||||
}
|
||||
},
|
||||
"lodash.isarguments": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
|
||||
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isarray": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
|
||||
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.keys": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
|
||||
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash._getnative": "3.9.1",
|
||||
"lodash.isarguments": "3.1.0",
|
||||
"lodash.isarray": "3.0.4"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.8"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"mocha": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz",
|
||||
"integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browser-stdout": "1.3.0",
|
||||
"commander": "2.9.0",
|
||||
"debug": "2.6.8",
|
||||
"diff": "3.2.0",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"glob": "7.1.1",
|
||||
"growl": "1.9.2",
|
||||
"he": "1.1.1",
|
||||
"json3": "3.3.2",
|
||||
"lodash.create": "3.1.1",
|
||||
"mkdirp": "0.5.1",
|
||||
"supports-color": "3.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||
"dev": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"pathval": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
|
||||
"integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
|
||||
"dev": true
|
||||
},
|
||||
"requirejs": {
|
||||
"version": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.5.tgz",
|
||||
"integrity": "sha1-YXuay7yzNlQO9JFNeQMjqNS4YbA=",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
|
||||
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "7.1.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz",
|
||||
"integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "1.0.0"
|
||||
}
|
||||
},
|
||||
"type-detect": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.5.tgz",
|
||||
"integrity": "sha512-N9IvkQslUGYGC24RkJk1ba99foK6TkwC2FHAEBlQFBP0RxQZS8ZpJuAZcwiY/w9ZJHFQb1aOXBI60OdxhTrwEQ==",
|
||||
"dev": true
|
||||
},
|
||||
"utf8": {
|
||||
"version": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz",
|
||||
"integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY=",
|
||||
"dev": true
|
||||
},
|
||||
"web3": {
|
||||
"version": "git+https://github.com/status-im/web3.js.git#17b13f26044e60ac824d1b97052bfad1be5af9cc",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bignumber.js": "git://github.com/status-im/bignumber.js.git#cc066a0a3d6bfe0c436c9957f4ea8344bf963c89",
|
||||
"crypto-js": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz",
|
||||
"utf8": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz",
|
||||
"xhr2": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz",
|
||||
"xmlhttprequest": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
},
|
||||
"xhr2": {
|
||||
"version": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz",
|
||||
"integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=",
|
||||
"dev": true
|
||||
},
|
||||
"xmlhttprequest": {
|
||||
"version": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
|
||||
"integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,9 +8,11 @@
|
|||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"axios": "^0.17.1",
|
||||
"chai": "^4.1.2",
|
||||
"mocha": "^3.5.3",
|
||||
"requirejs": "^2.3.4",
|
||||
"rimraf": "^2.6.2",
|
||||
"web3": "https://github.com/status-im/web3.js#status-develop"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -1,24 +1,40 @@
|
|||
const crypto = require('crypto');
|
||||
const { spawn } = require('child_process');
|
||||
const { expect } = require('chai');
|
||||
const axios = require('axios');
|
||||
const rimraf = require('rimraf');
|
||||
const Web3 = require('web3');
|
||||
|
||||
describe('Whisper MailServer', () => {
|
||||
const identityA = '0x04eedbaafd6adf4a9233a13e7b1c3c14461fffeba2e9054b8d456ce5f6ebeafadcbf3dce3716253fbc391277fa5a086b60b283daf61fb5b1f26895f456c2f31ae3';
|
||||
const identityB = '0x0490161b00f2c47542d28c2e8908e77159b1720dccceb6393d7c001850122efc3b1709bcea490fd8f5634ba1a145aa0722d86b9330b0e39a8d493cb981fd459da2';
|
||||
const topic = `0x${crypto.randomBytes(4).toString('hex')}`;
|
||||
const sharedSymKey = '0x6c32583c0bc13ef90a10b36ed6f66baaa0e537d0677619993bfd72c819cba6f3';
|
||||
const mailServerEnode = 'enode://b7e65e1bedc2499ee6cbd806945af5e7df0e59e4070c96821570bd581473eade24a489f5ec95d060c0db118c879403ab88d827d3766978f28708989d35474f87@127.0.0.1:8549';
|
||||
const messageTTL = 5;
|
||||
|
||||
describe('Check prerequisites', () => {
|
||||
console.log('Expecting MailServer running.')
|
||||
console.log('./build/bin/wnode-status -mailserver -passwordfile=./static/keys/wnodepassword -http -httpport 8540 -listenaddr=127.0.0.1:8549 -identity=./static/keys/wnodekey')
|
||||
|
||||
it('MailServer should be running', () => {
|
||||
const mailServer = new Web3(new Web3.providers.HttpProvider('http://localhost:8540'));
|
||||
const version = mailServer.shh.version();
|
||||
expect(version).to.equal("5.0");
|
||||
});
|
||||
});
|
||||
|
||||
describe('NodeA', () => {
|
||||
let nodeA;
|
||||
let nodeAProcess;
|
||||
|
||||
before(() => {
|
||||
before((done) => {
|
||||
nodeAProcess = spawn(
|
||||
'./build/bin/wnode-status',
|
||||
['-datadir', 'wnode-data-1', '-http', '-httpport', '8590']
|
||||
);
|
||||
nodeA = new Web3(new Web3.providers.HttpProvider('http://localhost:8590'));
|
||||
|
||||
// need to wait a bit until the node is up and running
|
||||
setTimeout(done, 500);
|
||||
});
|
||||
|
||||
after((done) => {
|
||||
|
@ -26,30 +42,37 @@ describe('Whisper MailServer', () => {
|
|||
nodeAProcess.on('exit', (code, signal) => {
|
||||
expect(code).to.be.null;
|
||||
expect(signal).to.equal('SIGTERM');
|
||||
rimraf('wnode-data-1', done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should add MailServer as a peer', (done) => {
|
||||
// add MailServer as a peer
|
||||
axios.post(nodeA.currentProvider.host, {
|
||||
method: 'admin_addPeer',
|
||||
params: [mailServerEnode],
|
||||
id: 1
|
||||
}).then((resp) => {
|
||||
expect(resp.data.id).to.equal(1);
|
||||
expect(resp.data.result).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}).catch(done);
|
||||
})
|
||||
|
||||
it('Should be online', () => {
|
||||
expect(nodeA).to.not.be.null;
|
||||
expect(nodeA.isConnected()).to.be.true;
|
||||
});
|
||||
|
||||
it('Should use Whisper V5', () => {
|
||||
expect(nodeA.shh.version()).to.equal('5.0');
|
||||
});
|
||||
|
||||
it('Should send a message', () => {
|
||||
it('Should send a message', (done) => {
|
||||
const symKeyId = nodeA.shh.addSymKey(sharedSymKey);
|
||||
const result = nodeA.shh.post({
|
||||
symKeyID: symKeyId,
|
||||
topic: topic,
|
||||
payload: nodeA.toHex('hello!'),
|
||||
ttl: 60,
|
||||
ttl: messageTTL,
|
||||
powTime: 10,
|
||||
powTarget: 2.5
|
||||
});
|
||||
expect(result).to.be.true;
|
||||
|
||||
// give it some time to propagate before the node is shut down
|
||||
setTimeout(done, 500);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -57,12 +80,15 @@ describe('Whisper MailServer', () => {
|
|||
let nodeBProcess;
|
||||
let nodeB;
|
||||
|
||||
before(() => {
|
||||
before((done) => {
|
||||
nodeBProcess = spawn(
|
||||
'./build/bin/wnode-status',
|
||||
['-datadir', 'wnode-data-2', '-http', '-httpport', '8591']
|
||||
['-datadir', 'wnode-data-2', '-http', '-httpport', '8591', '-log', 'INFO', '-logfile', 'wnode-data-2/wnode.log']
|
||||
);
|
||||
nodeB = new Web3(new Web3.providers.HttpProvider('http://localhost:8591'));
|
||||
|
||||
// need to wait a bit until the node is up and running
|
||||
setTimeout(done, 500);
|
||||
});
|
||||
|
||||
after((done) => {
|
||||
|
@ -70,45 +96,71 @@ describe('Whisper MailServer', () => {
|
|||
nodeBProcess.on('exit', (code, signal) => {
|
||||
expect(code).to.be.null;
|
||||
expect(signal).to.equal('SIGTERM');
|
||||
rimraf('wnode-data-2', done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should add MailServer as a peer', (done) => {
|
||||
// add MailServer as a peer
|
||||
axios.post(nodeB.currentProvider.host, {
|
||||
method: 'admin_addPeer',
|
||||
params: [mailServerEnode],
|
||||
id: 1
|
||||
}).then((resp) => {
|
||||
expect(resp.data.id).to.equal(1);
|
||||
expect(resp.data.result).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be online', () => {
|
||||
expect(nodeB).to.not.be.null;
|
||||
expect(nodeB.isConnected()).to.be.true;
|
||||
});
|
||||
|
||||
it('Should use Whisper V5', () => {
|
||||
expect(nodeB.shh.version()).to.equal('5.0');
|
||||
});
|
||||
}).catch(done);
|
||||
})
|
||||
|
||||
it('Should request and receive old messages', (done) => {
|
||||
const mailServerSymKeyID = nodeB.shh.generateSymKeyFromPassword('status-offline-inbox');
|
||||
const symKeyId = nodeB.shh.addSymKey(sharedSymKey);
|
||||
|
||||
let requestedForMessages = false;
|
||||
|
||||
// wait until the message expires before setting up a filter
|
||||
setTimeout(() => {
|
||||
let counter = 0;
|
||||
nodeB.shh.newMessageFilter({
|
||||
topics: [topic],
|
||||
symKeyID: symKeyId,
|
||||
allowP2P: true
|
||||
}, (err, data) => {
|
||||
if (!err) {
|
||||
if (err) {
|
||||
done(err);
|
||||
return;
|
||||
}
|
||||
|
||||
expect(nodeB.toAscii(data.payload)).to.equal('hello!');
|
||||
|
||||
if (requestedForMessages) {
|
||||
done();
|
||||
}, (err) => {
|
||||
done(err)
|
||||
});
|
||||
|
||||
// (optional)
|
||||
// nodeB.shh.addMailServer({ ... });
|
||||
} else {
|
||||
done('should not receive the message before requesting it');
|
||||
}
|
||||
}, done);
|
||||
}, (messageTTL + 1) * 1000);
|
||||
|
||||
// request messages after the filter is set up and give it some addotional time
|
||||
// so we are sure that the message was received after requesting it
|
||||
setTimeout(() => {
|
||||
// send a request for old messages
|
||||
// nodeB.shh.requestMessages({
|
||||
// start: timestamp,
|
||||
// end: timestamp,
|
||||
// mailServerID: (optional)
|
||||
// });
|
||||
axios.post(nodeB.currentProvider.host, {
|
||||
method: 'shh_requestMessages',
|
||||
params: [{
|
||||
mailServerPeer: mailServerEnode,
|
||||
topic: topic,
|
||||
symKeyID: mailServerSymKeyID
|
||||
}],
|
||||
id: 2
|
||||
}).then((resp) => {
|
||||
requestedForMessages = true;
|
||||
|
||||
expect(resp.data.id).to.equal(2);
|
||||
expect(resp.data.result).to.equal(true);
|
||||
}).catch(done);
|
||||
}, (messageTTL + 5) * 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue