Move ssh_requestMessages to sshext_requestMessages (#862)
* Add RequestMessage to sshext * E2E tests now use shhext_requestMessages * Typo in comment * Enhanced maintainability * Drop former mailservice * Code reorg after review * Fix missed changes after update to 1.8.5
This commit is contained in:
parent
f3e2631c1d
commit
b543d32a31
|
@ -1,128 +0,0 @@
|
||||||
package mailservice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
|
||||||
|
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
|
||||||
)
|
|
||||||
|
|
||||||
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 {
|
|
||||||
service *MailService
|
|
||||||
log log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPublicAPI returns a new PublicAPI.
|
|
||||||
func NewPublicAPI(s *MailService) *PublicAPI {
|
|
||||||
return &PublicAPI{
|
|
||||||
service: s,
|
|
||||||
log: log.New("package", "status-go/geth/mailservice.PublicAPI"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
api.log.Info("RequestMessages", "request", r)
|
|
||||||
|
|
||||||
setMessagesRequestDefaults(&r)
|
|
||||||
|
|
||||||
shh := api.service.whisper
|
|
||||||
|
|
||||||
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, api.service.nodeID, 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.BloomFilterSize)
|
|
||||||
binary.BigEndian.PutUint32(data, r.From)
|
|
||||||
binary.BigEndian.PutUint32(data[4:], r.To)
|
|
||||||
copy(data[8:], whisper.TopicToBloom(r.Topic))
|
|
||||||
return data
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
package mailservice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"math"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
|
||||||
"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 TestRequestMessages(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
shh := whisper.New(nil)
|
|
||||||
aNode, err := node.New(&node.Config{
|
|
||||||
P2P: p2p.Config{
|
|
||||||
MaxPeers: math.MaxInt32,
|
|
||||||
NoDiscovery: true,
|
|
||||||
},
|
|
||||||
}) // in-memory node as no data dir
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = aNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
|
|
||||||
return shh, nil
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = aNode.Start()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer func() {
|
|
||||||
err := aNode.Stop()
|
|
||||||
require.NoError(t, err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
service := New(shh)
|
|
||||||
api := NewPublicAPI(service)
|
|
||||||
|
|
||||||
const (
|
|
||||||
mailServerPeer = "enode://b7e65e1bedc2499ee6cbd806945af5e7df0e59e4070c96821570bd581473eade24a489f5ec95d060c0db118c879403ab88d827d3766978f28708989d35474f87@[::]:51920"
|
|
||||||
)
|
|
||||||
|
|
||||||
var result bool
|
|
||||||
|
|
||||||
// invalid MailServer enode address
|
|
||||||
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
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
|
|
||||||
// with a peer acting as a mailserver
|
|
||||||
// prepare a node first
|
|
||||||
mailNode, err := node.New(&node.Config{
|
|
||||||
P2P: p2p.Config{
|
|
||||||
MaxPeers: math.MaxInt32,
|
|
||||||
NoDiscovery: true,
|
|
||||||
ListenAddr: ":0",
|
|
||||||
},
|
|
||||||
}) // in-memory node as no data dir
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = mailNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
|
|
||||||
return whisper.New(nil), nil
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = mailNode.Start()
|
|
||||||
require.NoError(t, err)
|
|
||||||
defer func() {
|
|
||||||
err := mailNode.Stop()
|
|
||||||
require.NoError(t, err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// add mailPeer as a peer
|
|
||||||
aNode.Server().AddPeer(mailNode.Server().Self())
|
|
||||||
time.Sleep(time.Second) // wait for the peer to be added
|
|
||||||
|
|
||||||
// send a request
|
|
||||||
result, err = api.RequestMessages(context.TODO(), MessagesRequest{
|
|
||||||
MailServerPeer: mailNode.Server().Self().String(),
|
|
||||||
SymKeyID: symKeyID,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, result)
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
package mailservice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
|
|
||||||
"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/whisperv6"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MailService is a service that provides some additional Whisper API.
|
|
||||||
type MailService struct {
|
|
||||||
whisper *whisper.Whisper
|
|
||||||
nodeID *ecdsa.PrivateKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that MailService implements node.Service interface.
|
|
||||||
var _ node.Service = (*MailService)(nil)
|
|
||||||
|
|
||||||
// New returns a new MailService.
|
|
||||||
func New(w *whisper.Whisper) *MailService {
|
|
||||||
return &MailService{whisper: w}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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),
|
|
||||||
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 {
|
|
||||||
s.nodeID = server.PrivateKey
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/whisper/mailserver"
|
"github.com/ethereum/go-ethereum/whisper/mailserver"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
"github.com/status-im/status-go/geth/mailservice"
|
|
||||||
"github.com/status-im/status-go/geth/params"
|
"github.com/status-im/status-go/geth/params"
|
||||||
shhmetrics "github.com/status-im/status-go/metrics/whisper"
|
shhmetrics "github.com/status-im/status-go/metrics/whisper"
|
||||||
"github.com/status-im/status-go/services/personal"
|
"github.com/status-im/status-go/services/personal"
|
||||||
|
@ -215,7 +214,7 @@ func activateShhService(stack *node.Node, config *params.NodeConfig) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dshulyak) add a config option to enable it by default, but disable if app is started from statusd
|
// TODO(dshulyak) add a config option to enable it by default, but disable if app is started from statusd
|
||||||
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
var whisper *whisper.Whisper
|
var whisper *whisper.Whisper
|
||||||
if err := ctx.Service(&whisper); err != nil {
|
if err := ctx.Service(&whisper); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -224,18 +223,6 @@ func activateShhService(stack *node.Node, config *params.NodeConfig) (err error)
|
||||||
svc := shhext.New(whisper, shhext.EnvelopeSignalHandler{})
|
svc := shhext.New(whisper, shhext.EnvelopeSignalHandler{})
|
||||||
return svc, nil
|
return svc, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
|
||||||
var whisper *whisper.Whisper
|
|
||||||
if err := ctx.Service(&whisper); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return mailservice.New(whisper), nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeIPCPath returns IPC-RPC filename
|
// makeIPCPath returns IPC-RPC filename
|
||||||
|
|
|
@ -2,26 +2,82 @@ package shhext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPublicAPI returns instance of the public API.
|
const (
|
||||||
func NewPublicAPI(w *whisper.Whisper, tracker *tracker) *PublicAPI {
|
// defaultWorkTime is a work time reported in messages sent to MailServer nodes.
|
||||||
return &PublicAPI{
|
defaultWorkTime = 5
|
||||||
w: w,
|
)
|
||||||
publicAPI: whisper.NewPublicWhisperAPI(w),
|
|
||||||
tracker: tracker,
|
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")
|
||||||
|
)
|
||||||
|
|
||||||
|
// -----
|
||||||
|
// PAYLOADS
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// 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 (r *MessagesRequest) setDefaults() {
|
||||||
|
// 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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----
|
||||||
|
// PUBLIC API
|
||||||
|
// -----
|
||||||
|
|
||||||
// PublicAPI extends whisper public API.
|
// PublicAPI extends whisper public API.
|
||||||
type PublicAPI struct {
|
type PublicAPI struct {
|
||||||
w *whisper.Whisper
|
service *Service
|
||||||
publicAPI *whisper.PublicWhisperAPI
|
publicAPI *whisper.PublicWhisperAPI
|
||||||
tracker *tracker
|
log log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPublicAPI returns instance of the public API.
|
||||||
|
func NewPublicAPI(s *Service) *PublicAPI {
|
||||||
|
return &PublicAPI{
|
||||||
|
service: s,
|
||||||
|
publicAPI: whisper.NewPublicWhisperAPI(s.w),
|
||||||
|
log: log.New("package", "status-go/services/sshext.PublicAPI"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post shamelessly copied from whisper codebase with slight modifications.
|
// Post shamelessly copied from whisper codebase with slight modifications.
|
||||||
|
@ -30,7 +86,70 @@ func (api *PublicAPI) Post(ctx context.Context, req whisper.NewMessage) (hash he
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var envHash common.Hash
|
var envHash common.Hash
|
||||||
copy(envHash[:], hash[:]) // slice can't be used as key
|
copy(envHash[:], hash[:]) // slice can't be used as key
|
||||||
api.tracker.Add(envHash)
|
api.service.tracker.Add(envHash)
|
||||||
}
|
}
|
||||||
return hash, err
|
return hash, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestMessages sends a request for historic messages to a MailServer.
|
||||||
|
func (api *PublicAPI) RequestMessages(_ context.Context, r MessagesRequest) (bool, error) {
|
||||||
|
api.log.Info("RequestMessages", "request", r)
|
||||||
|
|
||||||
|
r.setDefaults()
|
||||||
|
|
||||||
|
shh := api.service.w
|
||||||
|
|
||||||
|
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, api.service.nodeID, shh.MinPow())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := shh.RequestHistoricMessages(mailServerNode.ID[:], envelope); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----
|
||||||
|
// HELPER
|
||||||
|
// -----
|
||||||
|
|
||||||
|
// 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.BloomFilterSize)
|
||||||
|
binary.BigEndian.PutUint32(data, r.From)
|
||||||
|
binary.BigEndian.PutUint32(data[4:], r.To)
|
||||||
|
copy(data[8:], whisper.TopicToBloom(r.Topic))
|
||||||
|
return data
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package shhext
|
package shhext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
@ -31,6 +32,7 @@ type EnvelopeEventsHandler interface {
|
||||||
type Service struct {
|
type Service struct {
|
||||||
w *whisper.Whisper
|
w *whisper.Whisper
|
||||||
tracker *tracker
|
tracker *tracker
|
||||||
|
nodeID *ecdsa.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that Service implements node.Service interface.
|
// Make sure that Service implements node.Service interface.
|
||||||
|
@ -60,7 +62,7 @@ func (s *Service) APIs() []rpc.API {
|
||||||
{
|
{
|
||||||
Namespace: "shhext",
|
Namespace: "shhext",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: NewPublicAPI(s.w, s.tracker),
|
Service: NewPublicAPI(s),
|
||||||
Public: true,
|
Public: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -70,6 +72,7 @@ func (s *Service) APIs() []rpc.API {
|
||||||
// It does nothing in this case but is required by `node.Service` interface.
|
// It does nothing in this case but is required by `node.Service` interface.
|
||||||
func (s *Service) Start(server *p2p.Server) error {
|
func (s *Service) Start(server *p2p.Server) error {
|
||||||
s.tracker.Start()
|
s.tracker.Start()
|
||||||
|
s.nodeID = server.PrivateKey
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package shhext
|
package shhext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -126,6 +128,102 @@ func (s *ShhExtSuite) TestWaitMessageExpired() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ShhExtSuite) TestRequestMessagesDefaults() {
|
||||||
|
r := MessagesRequest{}
|
||||||
|
r.setDefaults()
|
||||||
|
s.NotZero(r.From)
|
||||||
|
s.InEpsilon(uint32(time.Now().UTC().Unix()), r.To, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ShhExtSuite) TestRequestMessages() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
shh := whisper.New(nil)
|
||||||
|
aNode, err := node.New(&node.Config{
|
||||||
|
P2P: p2p.Config{
|
||||||
|
MaxPeers: math.MaxInt32,
|
||||||
|
NoDiscovery: true,
|
||||||
|
},
|
||||||
|
}) // in-memory node as no data dir
|
||||||
|
s.NoError(err)
|
||||||
|
err = aNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
|
||||||
|
return shh, nil
|
||||||
|
})
|
||||||
|
s.NoError(err)
|
||||||
|
|
||||||
|
err = aNode.Start()
|
||||||
|
s.NoError(err)
|
||||||
|
defer func() {
|
||||||
|
err := aNode.Stop()
|
||||||
|
s.NoError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
mock := newHandlerMock(1)
|
||||||
|
service := New(shh, mock)
|
||||||
|
api := NewPublicAPI(service)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mailServerPeer = "enode://b7e65e1bedc2499ee6cbd806945af5e7df0e59e4070c96821570bd581473eade24a489f5ec95d060c0db118c879403ab88d827d3766978f28708989d35474f87@[::]:51920"
|
||||||
|
)
|
||||||
|
|
||||||
|
var result bool
|
||||||
|
|
||||||
|
// invalid MailServer enode address
|
||||||
|
result, err = api.RequestMessages(context.TODO(), MessagesRequest{MailServerPeer: "invalid-address"})
|
||||||
|
s.False(result)
|
||||||
|
s.EqualError(err, "invalid mailServerPeer value: invalid URL scheme, want \"enode\"")
|
||||||
|
|
||||||
|
// non-existent symmetric key
|
||||||
|
result, err = api.RequestMessages(context.TODO(), MessagesRequest{
|
||||||
|
MailServerPeer: mailServerPeer,
|
||||||
|
})
|
||||||
|
s.False(result)
|
||||||
|
s.EqualError(err, "invalid symKeyID value: non-existent key ID")
|
||||||
|
|
||||||
|
// with a symmetric key
|
||||||
|
symKeyID, symKeyErr := shh.AddSymKeyFromPassword("some-pass")
|
||||||
|
s.NoError(symKeyErr)
|
||||||
|
result, err = api.RequestMessages(context.TODO(), MessagesRequest{
|
||||||
|
MailServerPeer: mailServerPeer,
|
||||||
|
SymKeyID: symKeyID,
|
||||||
|
})
|
||||||
|
s.Contains(err.Error(), "Could not find peer with ID")
|
||||||
|
s.False(result)
|
||||||
|
|
||||||
|
// with a peer acting as a mailserver
|
||||||
|
// prepare a node first
|
||||||
|
mailNode, err := node.New(&node.Config{
|
||||||
|
P2P: p2p.Config{
|
||||||
|
MaxPeers: math.MaxInt32,
|
||||||
|
NoDiscovery: true,
|
||||||
|
ListenAddr: ":0",
|
||||||
|
},
|
||||||
|
}) // in-memory node as no data dir
|
||||||
|
s.NoError(err)
|
||||||
|
err = mailNode.Register(func(_ *node.ServiceContext) (node.Service, error) {
|
||||||
|
return whisper.New(nil), nil
|
||||||
|
})
|
||||||
|
s.NoError(err)
|
||||||
|
err = mailNode.Start()
|
||||||
|
s.NoError(err)
|
||||||
|
defer func() {
|
||||||
|
err := mailNode.Stop()
|
||||||
|
s.NoError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// add mailPeer as a peer
|
||||||
|
aNode.Server().AddPeer(mailNode.Server().Self())
|
||||||
|
time.Sleep(time.Second) // wait for the peer to be added
|
||||||
|
|
||||||
|
// send a request
|
||||||
|
result, err = api.RequestMessages(context.TODO(), MessagesRequest{
|
||||||
|
MailServerPeer: mailNode.Server().Self().String(),
|
||||||
|
SymKeyID: symKeyID,
|
||||||
|
})
|
||||||
|
s.NoError(err)
|
||||||
|
s.True(result)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ShhExtSuite) TearDown() {
|
func (s *ShhExtSuite) TearDown() {
|
||||||
for _, n := range s.nodes {
|
for _, n := range s.nodes {
|
||||||
s.NoError(n.Stop())
|
s.NoError(n.Stop())
|
||||||
|
|
|
@ -24,9 +24,9 @@ func (s *MailServiceSuite) SetupTest() {
|
||||||
s.StatusNode = node.New()
|
s.StatusNode = node.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestShhRequestMessagesRPCMethodAvailability tests if `shh_requestMessages` is available
|
// TestShhextRequestMessagesRPCMethodAvailability tests if `shhext_requestMessages` is available
|
||||||
// through inproc and HTTP interfaces.
|
// through inproc and HTTP interfaces.
|
||||||
func (s *MailServiceSuite) TestShhRequestMessagesRPCMethodAvailability() {
|
func (s *MailServiceSuite) TestShhextRequestMessagesRPCMethodAvailability() {
|
||||||
r := s.Require()
|
r := s.Require()
|
||||||
|
|
||||||
s.StartTestNode(func(config *params.NodeConfig) {
|
s.StartTestNode(func(config *params.NodeConfig) {
|
||||||
|
@ -39,14 +39,14 @@ func (s *MailServiceSuite) TestShhRequestMessagesRPCMethodAvailability() {
|
||||||
|
|
||||||
// This error means that the method is available through inproc communication
|
// This error means that the method is available through inproc communication
|
||||||
// as the validation of params occurred.
|
// as the validation of params occurred.
|
||||||
err := client.Call(nil, "shh_requestMessages", map[string]interface{}{})
|
err := client.Call(nil, "shhext_requestMessages", map[string]interface{}{})
|
||||||
r.EqualError(err, `invalid mailServerPeer value: invalid URL scheme, want "enode"`)
|
r.EqualError(err, `invalid mailServerPeer value: invalid URL scheme, want "enode"`)
|
||||||
|
|
||||||
// Do the same but using HTTP interface.
|
// Do the same but using HTTP interface.
|
||||||
req, err := http.NewRequest("POST", "http://localhost:8645", bytes.NewBuffer([]byte(`{
|
req, err := http.NewRequest("POST", "http://localhost:8645", bytes.NewBuffer([]byte(`{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"method": "shh_requestMessages",
|
"method": "shhext_requestMessages",
|
||||||
"params": [{}]
|
"params": [{}]
|
||||||
}`)))
|
}`)))
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
|
@ -98,7 +98,7 @@ func (s *WhisperMailboxSuite) TestRequestMessageFromMailboxAsync() {
|
||||||
reqMessagesBody := `{
|
reqMessagesBody := `{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"method": "shh_requestMessages",
|
"method": "shhext_requestMessages",
|
||||||
"params": [{
|
"params": [{
|
||||||
"mailServerPeer":"` + mailboxPeerStr + `",
|
"mailServerPeer":"` + mailboxPeerStr + `",
|
||||||
"topic":"` + topic.String() + `",
|
"topic":"` + topic.String() + `",
|
||||||
|
@ -479,7 +479,7 @@ func (s *WhisperMailboxSuite) requestHistoricMessages(rpcCli *rpc.Client, mailbo
|
||||||
resp := rpcCli.CallRaw(`{
|
resp := rpcCli.CallRaw(`{
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"method": "shh_requestMessages",
|
"method": "shhext_requestMessages",
|
||||||
"params": [{
|
"params": [{
|
||||||
"mailServerPeer":"` + mailboxEnode + `",
|
"mailServerPeer":"` + mailboxEnode + `",
|
||||||
"topic":"` + topic + `",
|
"topic":"` + topic + `",
|
||||||
|
|
Loading…
Reference in New Issue