Refactor shh_requestMessages method (#513)

This commit is contained in:
Adam Babik 2017-12-21 11:26:01 +01:00 committed by GitHub
parent 3ff44f545b
commit b7fb51d92a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 984 additions and 278 deletions

View File

@ -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

View File

@ -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`)
}

View File

@ -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)

View File

@ -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())

View File

@ -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

View File

@ -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
}

View File

@ -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(&params)
if err != nil {
return nil, err
}
return message.Wrap(&params)
}
// 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
}

View File

@ -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()
}

View File

@ -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))
}

View File

@ -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() {

View File

@ -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(&params)
if err != nil {
return nil, err
}
return message.Wrap(&params)
}
//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
}

View File

@ -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))

417
package-lock.json generated Normal file
View File

@ -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
}
}
}

View File

@ -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": {

View File

@ -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');
done();
rimraf('wnode-data-1', done);
});
});
it('Should be online', () => {
expect(nodeA).to.not.be.null;
expect(nodeA.isConnected()).to.be.true;
});
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 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');
done();
rimraf('wnode-data-2', 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');
});
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();
}).catch(done);
})
it('Should request and receive old messages', (done) => {
const mailServerSymKeyID = nodeB.shh.generateSymKeyFromPassword('status-offline-inbox');
const symKeyId = nodeB.shh.addSymKey(sharedSymKey);
nodeB.shh.newMessageFilter({
topics: [topic],
symKeyID: symKeyId,
allowP2P: true
}, (err, data) => {
if (!err) {
done(err);
return;
}
done();
}, (err) => {
done(err)
});
let requestedForMessages = false;
// (optional)
// nodeB.shh.addMailServer({ ... });
// 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) {
done(err);
return;
}
// send a request for old messages
// nodeB.shh.requestMessages({
// start: timestamp,
// end: timestamp,
// mailServerID: (optional)
// });
expect(nodeB.toAscii(data.payload)).to.equal('hello!');
if (requestedForMessages) {
done();
} 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
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);
});
});
});