Remove `notifications` package from go-ethereum.
This commit is contained in:
parent
d02c2f16d8
commit
c06d58addd
File diff suppressed because it is too large
Load Diff
|
@ -2,9 +2,9 @@ diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go
|
|||
index a6c9e610d..eb2b75210 100644
|
||||
--- a/whisper/whisperv5/doc.go
|
||||
+++ b/whisper/whisperv5/doc.go
|
||||
@@ -99,3 +99,40 @@ type NotificationServer interface {
|
||||
// Stop stops notification sending loop, releasing related resources
|
||||
Stop() error
|
||||
@@ -85,3 +85,40 @@ type MailServer interface {
|
||||
Archive(env *Envelope)
|
||||
DeliverMail(whisperPeer *Peer, request *Envelope)
|
||||
}
|
||||
+
|
||||
+type envelopeSource int
|
||||
|
@ -47,16 +47,18 @@ diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go
|
|||
index c39e8b3e0..631676328 100644
|
||||
--- a/whisper/whisperv5/whisper.go
|
||||
+++ b/whisper/whisperv5/whisper.go
|
||||
@@ -79,6 +79,7 @@ type Whisper struct {
|
||||
@@ -77,7 +77,8 @@ type Whisper struct {
|
||||
statsMu sync.Mutex // guard stats
|
||||
stats Statistics // Statistics of whisper node
|
||||
|
||||
mailServer MailServer // MailServer interface
|
||||
notificationServer NotificationServer
|
||||
- mailServer MailServer // MailServer interface
|
||||
+ mailServer MailServer // MailServer interface
|
||||
+ envelopeTracer EnvelopeTracer // Service collecting envelopes metadata
|
||||
}
|
||||
|
||||
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
|
||||
@@ -162,6 +163,12 @@ func (w *Whisper) RegisterNotificationServer(server NotificationServer) {
|
||||
w.notificationServer = server
|
||||
@@ -156,6 +157,12 @@ func (w *Whisper) RegisterServer(server MailServer) {
|
||||
w.mailServer = server
|
||||
}
|
||||
|
||||
+// RegisterEnvelopeTracer registers an EnveloperTracer to collect information
|
||||
|
@ -68,7 +70,7 @@ index c39e8b3e0..631676328 100644
|
|||
// Protocols returns the whisper sub-protocols ran by this particular client.
|
||||
func (w *Whisper) Protocols() []p2p.Protocol {
|
||||
return []p2p.Protocol{w.protocol}
|
||||
@@ -603,6 +610,7 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
@@ -584,6 +591,7 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||
return errors.New("invalid envelope")
|
||||
}
|
||||
|
@ -76,7 +78,7 @@ index c39e8b3e0..631676328 100644
|
|||
cached, err := wh.add(&envelope)
|
||||
if err != nil {
|
||||
log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
|
||||
@@ -623,6 +631,7 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
@@ -604,6 +612,7 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
return errors.New("invalid direct message")
|
||||
}
|
||||
wh.postEvent(&envelope, true)
|
||||
|
@ -84,7 +86,7 @@ index c39e8b3e0..631676328 100644
|
|||
}
|
||||
case p2pRequestCode:
|
||||
// Must be processed if mail server is implemented. Otherwise ignore.
|
||||
@@ -718,6 +727,22 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
|
||||
@@ -699,6 +708,22 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
diff --git a/whisper/notifications/utils.go b/whisper/notifications/utils.go
|
||||
index 106752186..cca3fba71 100644
|
||||
--- a/whisper/notifications/utils.go
|
||||
+++ b/whisper/notifications/utils.go
|
||||
@@ -1,11 +1,12 @@
|
||||
package notifications
|
||||
|
||||
import (
|
||||
+ "crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
- "crypto/sha256"
|
||||
|
||||
crand "crypto/rand"
|
||||
+
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
@@ -24,7 +25,7 @@ func makeSessionKey() ([]byte, error) {
|
||||
}
|
||||
|
||||
key := buf[:keyLen]
|
||||
- derived, err := deriveKeyMaterial(key, whisper.EnvelopeVersion)
|
||||
+ derived, err := deriveKeyMaterial(key, whisper.ProtocolVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !validateSymmetricKey(derived) {
|
||||
@@ -63,7 +64,7 @@ func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error
|
||||
}
|
||||
|
||||
// MakeTopic returns Whisper topic *as bytes array* by generating cryptographic key from the provided password
|
||||
-func MakeTopicAsBytes(password []byte) ([]byte) {
|
||||
+func MakeTopicAsBytes(password []byte) []byte {
|
||||
topic := make([]byte, int(whisper.TopicLength))
|
||||
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
|
||||
for i := 0; i < len(x); i++ {
|
|
@ -19,61 +19,10 @@ index 8ae2882e1..7c97f0680 100644
|
|||
//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go
|
||||
|
||||
// Criteria holds various filter options for inbound messages.
|
||||
diff --git a/whisper/whisperv6/doc.go b/whisper/whisperv6/doc.go
|
||||
index d5d7fed60..5ad660616 100644
|
||||
--- a/whisper/whisperv6/doc.go
|
||||
+++ b/whisper/whisperv6/doc.go
|
||||
@@ -35,6 +35,8 @@ package whisperv6
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
+
|
||||
+ "github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
// Whisper protocol parameters
|
||||
@@ -95,3 +97,15 @@ type MailServer interface {
|
||||
Archive(env *Envelope)
|
||||
DeliverMail(whisperPeer *Peer, request *Envelope)
|
||||
}
|
||||
+
|
||||
+// NotificationServer represents a notification server,
|
||||
+// capable of screening incoming envelopes for special
|
||||
+// topics, and once located, subscribe client nodes as
|
||||
+// recipients to notifications (push notifications atm)
|
||||
+type NotificationServer interface {
|
||||
+ // Start initializes notification sending loop
|
||||
+ Start(server *p2p.Server) error
|
||||
+
|
||||
+ // Stop stops notification sending loop, releasing related resources
|
||||
+ Stop() error
|
||||
+}
|
||||
diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go
|
||||
index d75ad04ac..54d7d0f24 100644
|
||||
--- a/whisper/whisperv6/whisper.go
|
||||
+++ b/whisper/whisperv6/whisper.go
|
||||
@@ -85,7 +85,8 @@ type Whisper struct {
|
||||
statsMu sync.Mutex // guard stats
|
||||
stats Statistics // Statistics of whisper node
|
||||
|
||||
- mailServer MailServer // MailServer interface
|
||||
+ mailServer MailServer // MailServer interface
|
||||
+ notificationServer NotificationServer
|
||||
}
|
||||
|
||||
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
|
||||
@@ -209,6 +210,11 @@ func (whisper *Whisper) RegisterServer(server MailServer) {
|
||||
whisper.mailServer = server
|
||||
}
|
||||
|
||||
+// RegisterNotificationServer registers notification server with Whisper
|
||||
+func (whisper *Whisper) RegisterNotificationServer(server NotificationServer) {
|
||||
+ whisper.notificationServer = server
|
||||
+}
|
||||
+
|
||||
// Protocols returns the whisper sub-protocols ran by this particular client.
|
||||
func (whisper *Whisper) Protocols() []p2p.Protocol {
|
||||
return []p2p.Protocol{whisper.protocol}
|
||||
@@ -380,9 +386,9 @@ func (whisper *Whisper) NewKeyPair() (string, error) {
|
||||
return "", fmt.Errorf("failed to generate valid key")
|
||||
}
|
||||
|
@ -207,42 +156,6 @@ index d75ad04ac..54d7d0f24 100644
|
|||
// AddSymKeyDirect stores the key, and returns its id.
|
||||
func (whisper *Whisper) AddSymKeyDirect(key []byte) (string, error) {
|
||||
if len(key) != aesKeyLength {
|
||||
@@ -599,7 +669,7 @@ func (whisper *Whisper) Send(envelope *Envelope) error {
|
||||
|
||||
// Start implements node.Service, starting the background data propagation thread
|
||||
// of the Whisper protocol.
|
||||
-func (whisper *Whisper) Start(*p2p.Server) error {
|
||||
+func (whisper *Whisper) Start(stack *p2p.Server) error {
|
||||
log.Info("started whisper v." + ProtocolVersionStr)
|
||||
go whisper.update()
|
||||
|
||||
@@ -608,6 +678,12 @@ func (whisper *Whisper) Start(*p2p.Server) error {
|
||||
go whisper.processQueue()
|
||||
}
|
||||
|
||||
+ if whisper.notificationServer != nil {
|
||||
+ if err := whisper.notificationServer.Start(stack); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -615,6 +691,13 @@ func (whisper *Whisper) Start(*p2p.Server) error {
|
||||
// of the Whisper protocol.
|
||||
func (whisper *Whisper) Stop() error {
|
||||
close(whisper.quit)
|
||||
+
|
||||
+ if whisper.notificationServer != nil {
|
||||
+ if err := whisper.notificationServer.Stop(); err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
log.Info("whisper stopped")
|
||||
return nil
|
||||
}
|
||||
@@ -1035,6 +1118,33 @@ func GenerateRandomID() (id string, err error) {
|
||||
return id, err
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ diff --git a/whisper/whisperv6/doc.go b/whisper/whisperv6/doc.go
|
|||
index 5ad660616..9659e6c46 100644
|
||||
--- a/whisper/whisperv6/doc.go
|
||||
+++ b/whisper/whisperv6/doc.go
|
||||
@@ -109,3 +109,40 @@ type NotificationServer interface {
|
||||
// Stop stops notification sending loop, releasing related resources
|
||||
Stop() error
|
||||
@@ -95,3 +95,40 @@ type MailServer interface {
|
||||
Archive(env *Envelope)
|
||||
DeliverMail(whisperPeer *Peer, request *Envelope)
|
||||
}
|
||||
+
|
||||
+type envelopeSource int
|
||||
|
@ -47,16 +47,18 @@ diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go
|
|||
index 54d7d0f24..ce9405dff 100644
|
||||
--- a/whisper/whisperv6/whisper.go
|
||||
+++ b/whisper/whisperv6/whisper.go
|
||||
@@ -87,6 +87,7 @@ type Whisper struct {
|
||||
@@ -85,7 +85,8 @@ type Whisper struct {
|
||||
statsMu sync.Mutex // guard stats
|
||||
stats Statistics // Statistics of whisper node
|
||||
|
||||
mailServer MailServer // MailServer interface
|
||||
notificationServer NotificationServer
|
||||
- mailServer MailServer // MailServer interface
|
||||
+ mailServer MailServer // MailServer interface
|
||||
+ envelopeTracer EnvelopeTracer // Service collecting envelopes metadata
|
||||
}
|
||||
|
||||
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
|
||||
@@ -215,6 +216,12 @@ func (whisper *Whisper) RegisterNotificationServer(server NotificationServer) {
|
||||
whisper.notificationServer = server
|
||||
@@ -209,6 +210,12 @@ func (whisper *Whisper) RegisterServer(server MailServer) {
|
||||
whisper.mailServer = server
|
||||
}
|
||||
|
||||
+// RegisterEnvelopeTracer registers an EnveloperTracer to collect information
|
||||
|
@ -68,7 +70,7 @@ index 54d7d0f24..ce9405dff 100644
|
|||
// Protocols returns the whisper sub-protocols ran by this particular client.
|
||||
func (whisper *Whisper) Protocols() []p2p.Protocol {
|
||||
return []p2p.Protocol{whisper.protocol}
|
||||
@@ -756,6 +763,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
@@ -737,6 +744,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
|
||||
trouble := false
|
||||
for _, env := range envelopes {
|
||||
|
@ -76,7 +78,7 @@ index 54d7d0f24..ce9405dff 100644
|
|||
cached, err := whisper.add(env)
|
||||
if err != nil {
|
||||
trouble = true
|
||||
@@ -810,6 +818,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
@@ -787,6 +795,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
return errors.New("invalid direct message")
|
||||
}
|
||||
whisper.postEvent(&envelope, true)
|
||||
|
@ -84,7 +86,7 @@ index 54d7d0f24..ce9405dff 100644
|
|||
}
|
||||
case p2pRequestCode:
|
||||
// Must be processed if mail server is implemented. Otherwise ignore.
|
||||
@@ -906,6 +915,22 @@ func (whisper *Whisper) add(envelope *Envelope) (bool, error) {
|
||||
@@ -883,6 +892,22 @@ func (whisper *Whisper) add(envelope *Envelope) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ Instructions for creating a patch from the command line:
|
|||
- [`0009-whisper-envelopes-tracing.patch`](./0009-whisper-envelopes-tracing.patch) — adds Whisper envelope tracing (need to be reviewed and documented)
|
||||
- [`0010-geth-17-fix-npe-in-filter-system.patch`](./0010-geth-17-fix-npe-in-filter-system.patch) - Temp patch for 1.7.x to fix a NPE in the filter system.
|
||||
- [`0011-geth-17-whisperv6-70fbc87.patch`](./0011-geth-17-whisperv6-70fbc87.patch) - Temp patch for 1.7.x to update whisper v6 to the upstream version at the `70fbc87` SHA1.
|
||||
- [`0013-whisperv6-notifications-envelopeversion.patch`](./0013-whisperv6-notifications-envelopeversion.patch) — replaces usage of EnvelopeVersion with ProtocolVersion in notifications for Whisper v6
|
||||
- [`0014-whisperv6-notifications.patch`](./0014-whisperv6-notifications.patch) — adds Whisper v6 notifications (need to be reviewed and documented)
|
||||
- [`0015-whisperv6-envelopes-tracing.patch`](./0015-whisperv6-envelopes-tracing.patch) — adds Whisper v6 envelope tracing (need to be reviewed and documented)
|
||||
|
||||
|
|
|
@ -55,7 +55,6 @@ var (
|
|||
// don't change the name of this flag, https://github.com/ethereum/go-ethereum/blob/master/metrics/metrics.go#L41
|
||||
_ = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call.")
|
||||
// shh stuff
|
||||
identityFile = flag.String("shh.identityfile", "", "Protocol identity file (private key used for asymmetric encryption)")
|
||||
passwordFile = flag.String("shh.passwordfile", "", "Password file (password is used for symmetric encryption)")
|
||||
minPow = flag.Float64("shh.pow", params.WhisperMinimumPoW, "PoW for messages to be added to queue, in float format")
|
||||
ttl = flag.Int("shh.ttl", params.WhisperTTL, "Time to live for messages, in seconds")
|
||||
|
@ -64,7 +63,6 @@ var (
|
|||
enableMailServer = flag.Bool("shh.mailserver", false, "Delivers expired messages on demand")
|
||||
|
||||
// Push Notification
|
||||
enablePN = flag.Bool("shh.notify", false, "Node is capable of sending Push Notifications")
|
||||
firebaseAuth = flag.String("shh.firebaseauth", "", "FCM Authorization Key used for sending Push Notifications")
|
||||
|
||||
syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished")
|
||||
|
|
|
@ -13,22 +13,10 @@ import (
|
|||
func whisperConfig(nodeConfig *params.NodeConfig) (*params.NodeConfig, error) {
|
||||
whisperConfig := nodeConfig.WhisperConfig
|
||||
whisperConfig.Enabled = true
|
||||
whisperConfig.IdentityFile = *identityFile
|
||||
whisperConfig.EnablePushNotification = *enablePN
|
||||
whisperConfig.EnableMailServer = *enableMailServer
|
||||
whisperConfig.MinimumPoW = *minPow
|
||||
whisperConfig.TTL = *ttl
|
||||
|
||||
if whisperConfig.EnablePushNotification && whisperConfig.IdentityFile == "" {
|
||||
return nil, errors.New("notification server requires -identity file to be specified")
|
||||
}
|
||||
|
||||
if whisperConfig.IdentityFile != "" {
|
||||
if _, err := whisperConfig.ReadIdentityFile(); err != nil {
|
||||
return nil, fmt.Errorf("read identity file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if whisperConfig.EnableMailServer {
|
||||
if *passwordFile == "" {
|
||||
return nil, errors.New("passwordfile should be specified if MailServer is enabled")
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/status-im/status-go/geth/jail"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/node"
|
||||
"github.com/status-im/status-go/geth/notification/fcm"
|
||||
"github.com/status-im/status-go/geth/notifications/push/fcm"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
"github.com/status-im/status-go/geth/signal"
|
||||
"github.com/status-im/status-go/geth/transactions"
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/whisper/mailserver"
|
||||
"github.com/ethereum/go-ethereum/whisper/notifications"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
|
@ -186,15 +185,6 @@ func activateShhService(stack *node.Node, config *params.NodeConfig) error {
|
|||
mailServer.Init(whisperService, whisperConfig.DataDir, whisperConfig.Password, whisperConfig.MinimumPoW)
|
||||
}
|
||||
|
||||
// enable notification service
|
||||
if whisperConfig.EnablePushNotification {
|
||||
log.Info("Register PushNotification server")
|
||||
|
||||
var notificationServer notifications.NotificationServer
|
||||
whisperService.RegisterNotificationServer(¬ificationServer)
|
||||
notificationServer.Init(whisperService, whisperConfig)
|
||||
}
|
||||
|
||||
return whisperService, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package params
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -12,7 +11,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/status-go/geth/log"
|
||||
"github.com/status-im/status-go/static"
|
||||
)
|
||||
|
@ -35,8 +33,6 @@ var (
|
|||
ErrMissingNetworkID = errors.New("missing required 'NetworkID' parameter")
|
||||
ErrEmptyPasswordFile = errors.New("password file cannot be empty")
|
||||
ErrNoPasswordFileValueSet = errors.New("password file path not set")
|
||||
ErrNoIdentityFileValueSet = errors.New("identity file path not set")
|
||||
ErrEmptyIdentityFile = errors.New("identity file cannot be empty")
|
||||
ErrEmptyAuthorizationKeyFile = errors.New("authorization key file cannot be empty")
|
||||
ErrAuthorizationKeyFileNotSet = errors.New("authorization key file is not set")
|
||||
)
|
||||
|
@ -88,10 +84,6 @@ type WhisperConfig struct {
|
|||
// Enabled flag specifies whether protocol is enabled
|
||||
Enabled bool
|
||||
|
||||
// IdentityFile path to private key, that will be loaded as identity into Whisper.
|
||||
// Currently, it's used by Push Notification service.
|
||||
IdentityFile string
|
||||
|
||||
// PasswordFile contains a password for symmetric encryption with MailServer.
|
||||
PasswordFile string
|
||||
|
||||
|
@ -102,9 +94,6 @@ type WhisperConfig struct {
|
|||
// EnableMailServer is mode when node is capable of delivering expired messages on demand
|
||||
EnableMailServer bool
|
||||
|
||||
// EnablePushNotification is mode when node is capable of sending Push (and probably other kinds) Notifications
|
||||
EnablePushNotification bool
|
||||
|
||||
// DataDir is the file system folder Whisper should use for any data storage needs.
|
||||
// For instance, MailServer will use this directory to store its data.
|
||||
DataDir string
|
||||
|
@ -140,24 +129,6 @@ func (c *WhisperConfig) ReadPasswordFile() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ReadIdentityFile reads and loads identity private key
|
||||
func (c *WhisperConfig) ReadIdentityFile() (*ecdsa.PrivateKey, error) {
|
||||
if len(c.IdentityFile) == 0 {
|
||||
return nil, ErrNoIdentityFileValueSet
|
||||
}
|
||||
|
||||
identity, err := crypto.LoadECDSA(c.IdentityFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if identity == nil {
|
||||
return nil, ErrEmptyIdentityFile
|
||||
}
|
||||
|
||||
return identity, nil
|
||||
}
|
||||
|
||||
// String dumps config object as nicely indented JSON
|
||||
func (c *WhisperConfig) String() string {
|
||||
data, _ := json.MarshalIndent(c, "", " ") // nolint: gas
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
// keys/test-account2-status-chain.pk
|
||||
// keys/test-account2.pk
|
||||
// keys/test-account3-before-eip55.pk
|
||||
// keys/wnodekey
|
||||
// keys/wnodepassword
|
||||
// testdata/jail/commands.js
|
||||
// testdata/jail/status.js
|
||||
|
@ -349,26 +348,6 @@ func keysTestAccount3BeforeEip55Pk() (*asset, error) {
|
|||
return a, nil
|
||||
}
|
||||
|
||||
var _keysWnodekey = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x04\xc0\x07\x15\x03\x31\x0c\x03\x50\x04\xe5\xa2\xf4\x29\x1e\x70\xe4\x11\xfe\x10\xee\xbb\xcf\x89\x9b\x76\x47\xa2\x61\xd7\x15\x5d\xe4\x33\xe0\x54\x65\x04\x05\xee\xfc\x5d\xcc\x92\xad\xf0\xe2\xf4\x70\xaf\x9a\xa8\xdf\x17\x00\x00\xff\xff\x28\x09\xef\xd3\x41\x00\x00\x00")
|
||||
|
||||
func keysWnodekeyBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_keysWnodekey,
|
||||
"keys/wnodekey",
|
||||
)
|
||||
}
|
||||
|
||||
func keysWnodekey() (*asset, error) {
|
||||
bytes, err := keysWnodekeyBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "keys/wnodekey", size: 65, mode: os.FileMode(420), modTime: time.Unix(1512651934, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _keysWnodepassword = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2a\x2e\x49\x2c\x29\x2d\xd6\xcd\x4f\x4b\xcb\xc9\xcc\x4b\xd5\xcd\xcc\x4b\xca\xaf\xe0\x02\x04\x00\x00\xff\xff\xef\xf3\x8b\x45\x15\x00\x00\x00")
|
||||
|
||||
func keysWnodepasswordBytes() ([]byte, error) {
|
||||
|
@ -594,7 +573,6 @@ var _bindata = map[string]func() (*asset, error){
|
|||
"keys/test-account2-status-chain.pk": keysTestAccount2StatusChainPk,
|
||||
"keys/test-account2.pk": keysTestAccount2Pk,
|
||||
"keys/test-account3-before-eip55.pk": keysTestAccount3BeforeEip55Pk,
|
||||
"keys/wnodekey": keysWnodekey,
|
||||
"keys/wnodepassword": keysWnodepassword,
|
||||
"testdata/jail/commands.js": testdataJailCommandsJs,
|
||||
"testdata/jail/status.js": testdataJailStatusJs,
|
||||
|
@ -659,7 +637,6 @@ var _bintree = &bintree{nil, map[string]*bintree{
|
|||
"test-account2-status-chain.pk": &bintree{keysTestAccount2StatusChainPk, map[string]*bintree{}},
|
||||
"test-account2.pk": &bintree{keysTestAccount2Pk, map[string]*bintree{}},
|
||||
"test-account3-before-eip55.pk": &bintree{keysTestAccount3BeforeEip55Pk, map[string]*bintree{}},
|
||||
"wnodekey": &bintree{keysWnodekey, map[string]*bintree{}},
|
||||
"wnodepassword": &bintree{keysWnodepassword, map[string]*bintree{}},
|
||||
}},
|
||||
"scripts": &bintree{nil, map[string]*bintree{
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
77d185965daa460ee7a8cb44f6001bb9884a04ed27a49ba6ea0f81cd4e5ac40b
|
|
@ -338,7 +338,6 @@ func (s *WhisperMailboxSuite) startMailboxBackend() (*api.StatusBackend, func())
|
|||
mailboxConfig.WhisperConfig.Enabled = true
|
||||
mailboxConfig.KeyStoreDir = datadir
|
||||
mailboxConfig.WhisperConfig.EnableMailServer = true
|
||||
mailboxConfig.WhisperConfig.IdentityFile = filepath.Join(RootDir, "/static/keys/wnodekey")
|
||||
mailboxConfig.WhisperConfig.PasswordFile = filepath.Join(RootDir, "/static/keys/wnodepassword")
|
||||
mailboxConfig.WhisperConfig.DataDir = filepath.Join(datadir, "data")
|
||||
mailboxConfig.DataDir = datadir
|
||||
|
|
|
@ -1,154 +0,0 @@
|
|||
package notifications
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
)
|
||||
|
||||
const (
|
||||
topicDiscoverServer = "DISCOVER_NOTIFICATION_SERVER"
|
||||
topicProposeServer = "PROPOSE_NOTIFICATION_SERVER"
|
||||
topicServerAccepted = "ACCEPT_NOTIFICATION_SERVER"
|
||||
topicAckClientSubscription = "ACK_NOTIFICATION_SERVER_SUBSCRIPTION"
|
||||
)
|
||||
|
||||
// discoveryService abstract notification server discovery protocol
|
||||
type discoveryService struct {
|
||||
server *NotificationServer
|
||||
|
||||
discoverFilterID string
|
||||
serverAcceptedFilterID string
|
||||
}
|
||||
|
||||
// messageProcessingFn is a callback used to process incoming client requests
|
||||
type messageProcessingFn func(*whisper.ReceivedMessage) error
|
||||
|
||||
func NewDiscoveryService(notificationServer *NotificationServer) *discoveryService {
|
||||
return &discoveryService{
|
||||
server: notificationServer,
|
||||
}
|
||||
}
|
||||
|
||||
// Start installs necessary filters to watch for incoming discovery requests,
|
||||
// then in separate routine starts watcher loop
|
||||
func (s *discoveryService) Start() error {
|
||||
var err error
|
||||
|
||||
// notification server discovery requests
|
||||
s.discoverFilterID, err = s.server.installKeyFilter(topicDiscoverServer, s.server.protocolKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
go s.server.requestProcessorLoop(s.discoverFilterID, topicDiscoverServer, s.processDiscoveryRequest)
|
||||
|
||||
// notification server accept/select requests
|
||||
s.serverAcceptedFilterID, err = s.server.installKeyFilter(topicServerAccepted, s.server.protocolKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
go s.server.requestProcessorLoop(s.serverAcceptedFilterID, topicServerAccepted, s.processServerAcceptedRequest)
|
||||
|
||||
log.Info("notification server discovery service started")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops all discovery processing loops
|
||||
func (s *discoveryService) Stop() error {
|
||||
s.server.whisper.Unsubscribe(s.discoverFilterID)
|
||||
s.server.whisper.Unsubscribe(s.serverAcceptedFilterID)
|
||||
|
||||
log.Info("notification server discovery service stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// processDiscoveryRequest processes incoming client requests of type:
|
||||
// when client tries to discover suitable notification server
|
||||
func (s *discoveryService) processDiscoveryRequest(msg *whisper.ReceivedMessage) error {
|
||||
// offer this node as notification server
|
||||
msgParams := whisper.MessageParams{
|
||||
Src: s.server.protocolKey,
|
||||
Dst: msg.Src,
|
||||
Topic: MakeTopic([]byte(topicProposeServer)),
|
||||
Payload: []byte(`{"server": "0x` + s.server.nodeID + `"}`),
|
||||
TTL: uint32(s.server.config.TTL),
|
||||
PoW: s.server.config.MinimumPoW,
|
||||
WorkTime: 5,
|
||||
}
|
||||
response, err := whisper.NewSentMessage(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create proposal message: %v", err)
|
||||
}
|
||||
env, err := response.Wrap(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wrap server proposal message: %v", err)
|
||||
}
|
||||
|
||||
if err := s.server.whisper.Send(env); err != nil {
|
||||
return fmt.Errorf("failed to send server proposal message: %v", err)
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("server proposal sent (server: %v, dst: %v, topic: %x)",
|
||||
s.server.nodeID, common.ToHex(crypto.FromECDSAPub(msgParams.Dst)), msgParams.Topic))
|
||||
return nil
|
||||
}
|
||||
|
||||
// processServerAcceptedRequest processes incoming client requests of type:
|
||||
// when client is ready to select the given node as its notification server
|
||||
func (s *discoveryService) processServerAcceptedRequest(msg *whisper.ReceivedMessage) error {
|
||||
var parsedMessage struct {
|
||||
ServerID string `json:"server"`
|
||||
}
|
||||
if err := json.Unmarshal(msg.Payload, &parsedMessage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.Src == nil {
|
||||
return errors.New("message 'from' field is required")
|
||||
}
|
||||
|
||||
// make sure that only requests made to the current node are processed
|
||||
if parsedMessage.ServerID != `0x`+s.server.nodeID {
|
||||
return nil
|
||||
}
|
||||
|
||||
// register client
|
||||
sessionKey, err := s.server.RegisterClientSession(&ClientSession{
|
||||
ClientKey: hex.EncodeToString(crypto.FromECDSAPub(msg.Src)),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// confirm that client has been successfully subscribed
|
||||
msgParams := whisper.MessageParams{
|
||||
Src: s.server.protocolKey,
|
||||
Dst: msg.Src,
|
||||
Topic: MakeTopic([]byte(topicAckClientSubscription)),
|
||||
Payload: []byte(`{"server": "0x` + s.server.nodeID + `", "key": "0x` + hex.EncodeToString(sessionKey) + `"}`),
|
||||
TTL: uint32(s.server.config.TTL),
|
||||
PoW: s.server.config.MinimumPoW,
|
||||
WorkTime: 5,
|
||||
}
|
||||
response, err := whisper.NewSentMessage(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create server proposal message: %v", err)
|
||||
}
|
||||
env, err := response.Wrap(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wrap server proposal message: %v", err)
|
||||
}
|
||||
|
||||
if err := s.server.whisper.Send(env); err != nil {
|
||||
return fmt.Errorf("failed to send server proposal message: %v", err)
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("server confirms client subscription (dst: %v, topic: %x)", msgParams.Dst, msgParams.Topic))
|
||||
return nil
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package notifications
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
)
|
||||
|
||||
// NotificationDeliveryProvider handles the notification delivery
|
||||
type NotificationDeliveryProvider interface {
|
||||
Send(id string, payload string) error
|
||||
}
|
||||
|
||||
// FirebaseProvider represents FCM provider
|
||||
type FirebaseProvider struct {
|
||||
AuthorizationKey string
|
||||
NotificationTriggerURL string
|
||||
}
|
||||
|
||||
// NewFirebaseProvider creates new FCM provider
|
||||
func NewFirebaseProvider(config *params.FirebaseConfig) *FirebaseProvider {
|
||||
authorizationKey, _ := config.ReadAuthorizationKeyFile()
|
||||
return &FirebaseProvider{
|
||||
NotificationTriggerURL: config.NotificationTriggerURL,
|
||||
AuthorizationKey: string(authorizationKey),
|
||||
}
|
||||
}
|
||||
|
||||
// Send triggers sending of Push Notification to a given device id
|
||||
func (p *FirebaseProvider) Send(id string, payload string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("panic: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
jsonRequest := strings.Replace(payload, "{{ ID }}", id, 3)
|
||||
req, err := http.NewRequest("POST", p.NotificationTriggerURL, bytes.NewBuffer([]byte(jsonRequest)))
|
||||
req.Header.Set("Authorization", "key="+p.AuthorizationKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
log.Debug("FCM response", "status", resp.Status, "header", resp.Header)
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
log.Debug("FCM response body", "body", string(body))
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,590 +0,0 @@
|
|||
package notifications
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"github.com/status-im/status-go/geth/params"
|
||||
)
|
||||
|
||||
const (
|
||||
topicSendNotification = "SEND_NOTIFICATION"
|
||||
topicNewChatSession = "NEW_CHAT_SESSION"
|
||||
topicAckNewChatSession = "ACK_NEW_CHAT_SESSION"
|
||||
topicNewDeviceRegistration = "NEW_DEVICE_REGISTRATION"
|
||||
topicAckDeviceRegistration = "ACK_DEVICE_REGISTRATION"
|
||||
topicCheckClientSession = "CHECK_CLIENT_SESSION"
|
||||
topicConfirmClientSession = "CONFIRM_CLIENT_SESSION"
|
||||
topicDropClientSession = "DROP_CLIENT_SESSION"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrServiceInitError = errors.New("notification service has not been properly initialized")
|
||||
)
|
||||
|
||||
// NotificationServer service capable of handling Push Notifications
|
||||
type NotificationServer struct {
|
||||
whisper *whisper.Whisper
|
||||
config *params.WhisperConfig
|
||||
|
||||
nodeID string // proposed server will feature this ID
|
||||
discovery *discoveryService // discovery service handles client/server negotiation, when server is selected
|
||||
protocolKey *ecdsa.PrivateKey // private key of service, used to encode handshake communication
|
||||
|
||||
clientSessions map[string]*ClientSession
|
||||
clientSessionsMu sync.RWMutex
|
||||
|
||||
chatSessions map[string]*ChatSession
|
||||
chatSessionsMu sync.RWMutex
|
||||
|
||||
deviceSubscriptions map[string]*DeviceSubscription
|
||||
deviceSubscriptionsMu sync.RWMutex
|
||||
|
||||
firebaseProvider NotificationDeliveryProvider
|
||||
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
// ClientSession abstracts notification client, which expects notifications whenever
|
||||
// some envelope can be decoded with session key (key hash is compared for optimization)
|
||||
type ClientSession struct {
|
||||
ClientKey string // public key uniquely identifying a client
|
||||
SessionKey []byte // actual symkey used for client - server communication
|
||||
SessionKeyHash common.Hash // The Keccak256Hash of the symmetric key, which is shared between server/client
|
||||
SessionKeyInput []byte // raw symkey used as input for actual SessionKey
|
||||
}
|
||||
|
||||
// ChatSession abstracts chat session, which some previously registered client can create.
|
||||
// ChatSession is used by client for sharing common secret, allowing others to register
|
||||
// themselves and eventually to trigger notifications.
|
||||
type ChatSession struct {
|
||||
ParentKey string // public key uniquely identifying a client session used to create a chat session
|
||||
ChatKey string // ID that uniquely identifies a chat session
|
||||
SessionKey []byte // actual symkey used for client - server communication
|
||||
SessionKeyHash common.Hash // The Keccak256Hash of the symmetric key, which is shared between server/client
|
||||
}
|
||||
|
||||
// DeviceSubscription stores enough information about a device (or group of devices),
|
||||
// so that Notification Server can trigger notification on that device(s)
|
||||
type DeviceSubscription struct {
|
||||
DeviceID string // ID that will be used as destination
|
||||
ChatSessionKeyHash common.Hash // The Keccak256Hash of the symmetric key, which is shared between server/client
|
||||
PubKey *ecdsa.PublicKey // public key of subscriber (to filter out when notification is triggered)
|
||||
}
|
||||
|
||||
// Init used for service initialization, making sure it is safe to call Start()
|
||||
func (s *NotificationServer) Init(whisperService *whisper.Whisper, whisperConfig *params.WhisperConfig) {
|
||||
s.whisper = whisperService
|
||||
s.config = whisperConfig
|
||||
|
||||
s.discovery = NewDiscoveryService(s)
|
||||
s.clientSessions = make(map[string]*ClientSession)
|
||||
s.chatSessions = make(map[string]*ChatSession)
|
||||
s.deviceSubscriptions = make(map[string]*DeviceSubscription)
|
||||
s.quit = make(chan struct{})
|
||||
|
||||
// setup providers (FCM only, for now)
|
||||
s.firebaseProvider = NewFirebaseProvider(whisperConfig.FirebaseConfig)
|
||||
}
|
||||
|
||||
// Start begins notification loop, in a separate go routine
|
||||
func (s *NotificationServer) Start(stack *p2p.Server) error {
|
||||
if s.whisper == nil {
|
||||
return ErrServiceInitError
|
||||
}
|
||||
|
||||
// configure nodeID
|
||||
if stack != nil {
|
||||
if nodeInfo := stack.NodeInfo(); nodeInfo != nil {
|
||||
s.nodeID = nodeInfo.ID
|
||||
}
|
||||
}
|
||||
|
||||
// configure keys
|
||||
identity, err := s.config.ReadIdentityFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.whisper.AddKeyPair(identity)
|
||||
s.protocolKey = identity
|
||||
log.Info("protocol pubkey", "key", common.ToHex(crypto.FromECDSAPub(&s.protocolKey.PublicKey)))
|
||||
|
||||
// start discovery protocol
|
||||
s.discovery.Start()
|
||||
|
||||
// client session status requests
|
||||
clientSessionStatusFilterID, err := s.installKeyFilter(topicCheckClientSession, s.protocolKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
go s.requestProcessorLoop(clientSessionStatusFilterID, topicDiscoverServer, s.processClientSessionStatusRequest)
|
||||
|
||||
// client session remove requests
|
||||
dropClientSessionFilterID, err := s.installKeyFilter(topicDropClientSession, s.protocolKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
go s.requestProcessorLoop(dropClientSessionFilterID, topicDropClientSession, s.processDropClientSessionRequest)
|
||||
|
||||
log.Info("Whisper Notification Server started")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop handles stopping the running notification loop, and all related resources
|
||||
func (s *NotificationServer) Stop() error {
|
||||
close(s.quit)
|
||||
|
||||
if s.whisper == nil {
|
||||
return ErrServiceInitError
|
||||
}
|
||||
|
||||
if s.discovery != nil {
|
||||
s.discovery.Stop()
|
||||
}
|
||||
|
||||
log.Info("Whisper Notification Server stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterClientSession forms a cryptographic link between server and client.
|
||||
// It does so by sharing a session SymKey and installing filter listening for messages
|
||||
// encrypted with that key. So, both server and client have a secure way to communicate.
|
||||
func (s *NotificationServer) RegisterClientSession(session *ClientSession) (sessionKey []byte, err error) {
|
||||
s.clientSessionsMu.Lock()
|
||||
defer s.clientSessionsMu.Unlock()
|
||||
|
||||
// generate random symmetric session key
|
||||
keyName := fmt.Sprintf("%s-%s", "ntfy-client", crypto.Keccak256Hash([]byte(session.ClientKey)).Hex())
|
||||
sessionKey, sessionKeyDerived, err := s.makeSessionKey(keyName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// populate session key hash (will be used to match decrypted message to a given client id)
|
||||
session.SessionKeyInput = sessionKey
|
||||
session.SessionKeyHash = crypto.Keccak256Hash(sessionKeyDerived)
|
||||
session.SessionKey = sessionKeyDerived
|
||||
|
||||
// append to list of known clients
|
||||
// so that it is trivial to go key hash -> client session info
|
||||
id := session.SessionKeyHash.Hex()
|
||||
s.clientSessions[id] = session
|
||||
|
||||
// setup filter, which will get all incoming messages, that are encrypted with SymKey
|
||||
filterID, err := s.installTopicFilter(topicNewChatSession, sessionKeyDerived)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
go s.requestProcessorLoop(filterID, topicNewChatSession, s.processNewChatSessionRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterChatSession forms a cryptographic link between server and client.
|
||||
// This link is meant to be shared with other clients, so that they can use
|
||||
// the shared SymKey to trigger notifications for devices attached to a given
|
||||
// chat session.
|
||||
func (s *NotificationServer) RegisterChatSession(session *ChatSession) (sessionKey []byte, err error) {
|
||||
s.chatSessionsMu.Lock()
|
||||
defer s.chatSessionsMu.Unlock()
|
||||
|
||||
// generate random symmetric session key
|
||||
keyName := fmt.Sprintf("%s-%s", "ntfy-chat", crypto.Keccak256Hash([]byte(session.ParentKey+session.ChatKey)).Hex())
|
||||
sessionKey, sessionKeyDerived, err := s.makeSessionKey(keyName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// populate session key hash (will be used to match decrypted message to a given client id)
|
||||
session.SessionKeyHash = crypto.Keccak256Hash(sessionKeyDerived)
|
||||
session.SessionKey = sessionKeyDerived
|
||||
|
||||
// append to list of known clients
|
||||
// so that it is trivial to go key hash -> client session info
|
||||
id := session.SessionKeyHash.Hex()
|
||||
s.chatSessions[id] = session
|
||||
|
||||
// setup filter, to process incoming device registration requests
|
||||
filterID1, err := s.installTopicFilter(topicNewDeviceRegistration, sessionKeyDerived)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
go s.requestProcessorLoop(filterID1, topicNewDeviceRegistration, s.processNewDeviceRegistrationRequest)
|
||||
|
||||
// setup filter, to process incoming notification trigger requests
|
||||
filterID2, err := s.installTopicFilter(topicSendNotification, sessionKeyDerived)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
go s.requestProcessorLoop(filterID2, topicSendNotification, s.processSendNotificationRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterDeviceSubscription persists device id, so that it can be used to trigger notifications.
|
||||
func (s *NotificationServer) RegisterDeviceSubscription(subscription *DeviceSubscription) error {
|
||||
s.deviceSubscriptionsMu.Lock()
|
||||
defer s.deviceSubscriptionsMu.Unlock()
|
||||
|
||||
// if one passes the same id again, we will just overwrite
|
||||
id := fmt.Sprintf("%s-%s", "ntfy-device",
|
||||
crypto.Keccak256Hash([]byte(subscription.ChatSessionKeyHash.Hex()+subscription.DeviceID)).Hex())
|
||||
s.deviceSubscriptions[id] = subscription
|
||||
|
||||
log.Info("device registered", "device", subscription.DeviceID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DropClientSession uninstalls session
|
||||
func (s *NotificationServer) DropClientSession(id string) {
|
||||
dropChatSessions := func(parentKey string) {
|
||||
s.chatSessionsMu.Lock()
|
||||
defer s.chatSessionsMu.Unlock()
|
||||
|
||||
for key, chatSession := range s.chatSessions {
|
||||
if chatSession.ParentKey == parentKey {
|
||||
delete(s.chatSessions, key)
|
||||
log.Info("drop chat session", "key", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropDeviceSubscriptions := func(parentKey string) {
|
||||
s.deviceSubscriptionsMu.Lock()
|
||||
defer s.deviceSubscriptionsMu.Unlock()
|
||||
|
||||
for key, subscription := range s.deviceSubscriptions {
|
||||
if hex.EncodeToString(crypto.FromECDSAPub(subscription.PubKey)) == parentKey {
|
||||
delete(s.deviceSubscriptions, key)
|
||||
log.Info("drop device subscription", "key", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.clientSessionsMu.Lock()
|
||||
if session, ok := s.clientSessions[id]; ok {
|
||||
delete(s.clientSessions, id)
|
||||
log.Info("server drops client session", "id", id)
|
||||
s.clientSessionsMu.Unlock()
|
||||
|
||||
dropDeviceSubscriptions(session.ClientKey)
|
||||
dropChatSessions(session.ClientKey)
|
||||
}
|
||||
}
|
||||
|
||||
// processNewChatSessionRequest processes incoming client requests of type:
|
||||
// client has a session key, and ready to create a new chat session (which is
|
||||
// a bag of subscribed devices, basically)
|
||||
func (s *NotificationServer) processNewChatSessionRequest(msg *whisper.ReceivedMessage) error {
|
||||
s.clientSessionsMu.RLock()
|
||||
defer s.clientSessionsMu.RUnlock()
|
||||
|
||||
var parsedMessage struct {
|
||||
ChatID string `json:"chat"`
|
||||
}
|
||||
if err := json.Unmarshal(msg.Payload, &parsedMessage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.Src == nil {
|
||||
return errors.New("message 'from' field is required")
|
||||
}
|
||||
|
||||
clientSession, ok := s.clientSessions[msg.SymKeyHash.Hex()]
|
||||
if !ok {
|
||||
return errors.New("client session not found")
|
||||
}
|
||||
|
||||
// register chat session
|
||||
parentKey := hex.EncodeToString(crypto.FromECDSAPub(msg.Src))
|
||||
sessionKey, err := s.RegisterChatSession(&ChatSession{
|
||||
ParentKey: parentKey,
|
||||
ChatKey: parsedMessage.ChatID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// confirm that chat has been successfully created
|
||||
msgParams := whisper.MessageParams{
|
||||
Dst: msg.Src,
|
||||
KeySym: clientSession.SessionKey,
|
||||
Topic: MakeTopic([]byte(topicAckNewChatSession)),
|
||||
Payload: []byte(`{"server": "0x` + s.nodeID + `", "key": "0x` + hex.EncodeToString(sessionKey) + `"}`),
|
||||
TTL: uint32(s.config.TTL),
|
||||
PoW: s.config.MinimumPoW,
|
||||
WorkTime: 5,
|
||||
}
|
||||
response, err := whisper.NewSentMessage(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create server response message: %v", err)
|
||||
}
|
||||
env, err := response.Wrap(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wrap server response message: %v", err)
|
||||
}
|
||||
|
||||
if err := s.whisper.Send(env); err != nil {
|
||||
return fmt.Errorf("failed to send server response message: %v", err)
|
||||
}
|
||||
|
||||
log.Info("server confirms chat creation", "dst",
|
||||
common.ToHex(crypto.FromECDSAPub(msgParams.Dst)), "topic", msgParams.Topic.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// processNewDeviceRegistrationRequest processes incoming client requests of type:
|
||||
// client has a session key, creates chat, and obtains chat SymKey (to be shared with
|
||||
// others). Then using that chat SymKey client registers it's device ID with server.
|
||||
func (s *NotificationServer) processNewDeviceRegistrationRequest(msg *whisper.ReceivedMessage) error {
|
||||
s.chatSessionsMu.RLock()
|
||||
defer s.chatSessionsMu.RUnlock()
|
||||
|
||||
var parsedMessage struct {
|
||||
DeviceID string `json:"device"`
|
||||
}
|
||||
if err := json.Unmarshal(msg.Payload, &parsedMessage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg.Src == nil {
|
||||
return errors.New("message 'from' field is required")
|
||||
}
|
||||
|
||||
chatSession, ok := s.chatSessions[msg.SymKeyHash.Hex()]
|
||||
if !ok {
|
||||
return errors.New("chat session not found")
|
||||
}
|
||||
|
||||
if len(parsedMessage.DeviceID) <= 0 {
|
||||
return errors.New("'device' cannot be empty")
|
||||
}
|
||||
|
||||
// register chat session
|
||||
err := s.RegisterDeviceSubscription(&DeviceSubscription{
|
||||
DeviceID: parsedMessage.DeviceID,
|
||||
ChatSessionKeyHash: chatSession.SessionKeyHash,
|
||||
PubKey: msg.Src,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// confirm that client has been successfully subscribed
|
||||
msgParams := whisper.MessageParams{
|
||||
Dst: msg.Src,
|
||||
KeySym: chatSession.SessionKey,
|
||||
Topic: MakeTopic([]byte(topicAckDeviceRegistration)),
|
||||
Payload: []byte(`{"server": "0x` + s.nodeID + `"}`),
|
||||
TTL: uint32(s.config.TTL),
|
||||
PoW: s.config.MinimumPoW,
|
||||
WorkTime: 5,
|
||||
}
|
||||
response, err := whisper.NewSentMessage(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create server response message: %v", err)
|
||||
}
|
||||
env, err := response.Wrap(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wrap server response message: %v", err)
|
||||
}
|
||||
|
||||
if err := s.whisper.Send(env); err != nil {
|
||||
return fmt.Errorf("failed to send server response message: %v", err)
|
||||
}
|
||||
|
||||
log.Info("server confirms device registration", "dst",
|
||||
common.ToHex(crypto.FromECDSAPub(msgParams.Dst)), "topic", msgParams.Topic.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// processSendNotificationRequest processes incoming client requests of type:
|
||||
// when client has session key, and ready to use it to send notifications
|
||||
func (s *NotificationServer) processSendNotificationRequest(msg *whisper.ReceivedMessage) error {
|
||||
s.deviceSubscriptionsMu.RLock()
|
||||
defer s.deviceSubscriptionsMu.RUnlock()
|
||||
|
||||
for _, subscriber := range s.deviceSubscriptions {
|
||||
if subscriber.ChatSessionKeyHash == msg.SymKeyHash {
|
||||
if whisper.IsPubKeyEqual(msg.Src, subscriber.PubKey) {
|
||||
continue // no need to notify ourselves
|
||||
}
|
||||
|
||||
if s.firebaseProvider != nil {
|
||||
err := s.firebaseProvider.Send(subscriber.DeviceID, string(msg.Payload))
|
||||
if err != nil {
|
||||
log.Info("cannot send notification", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processClientSessionStatusRequest processes incoming client requests when:
|
||||
// client wants to learn whether it is already registered on some of the servers
|
||||
func (s *NotificationServer) processClientSessionStatusRequest(msg *whisper.ReceivedMessage) error {
|
||||
s.clientSessionsMu.RLock()
|
||||
defer s.clientSessionsMu.RUnlock()
|
||||
|
||||
if msg.Src == nil {
|
||||
return errors.New("message 'from' field is required")
|
||||
}
|
||||
|
||||
var sessionKey []byte
|
||||
pubKey := hex.EncodeToString(crypto.FromECDSAPub(msg.Src))
|
||||
for _, clientSession := range s.clientSessions {
|
||||
if clientSession.ClientKey == pubKey {
|
||||
sessionKey = clientSession.SessionKeyInput
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// session is not found
|
||||
if sessionKey == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// let client know that we have session for a given public key
|
||||
msgParams := whisper.MessageParams{
|
||||
Src: s.protocolKey,
|
||||
Dst: msg.Src,
|
||||
Topic: MakeTopic([]byte(topicConfirmClientSession)),
|
||||
Payload: []byte(`{"server": "0x` + s.nodeID + `", "key": "0x` + hex.EncodeToString(sessionKey) + `"}`),
|
||||
TTL: uint32(s.config.TTL),
|
||||
PoW: s.config.MinimumPoW,
|
||||
WorkTime: 5,
|
||||
}
|
||||
response, err := whisper.NewSentMessage(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create server response message: %v", err)
|
||||
}
|
||||
env, err := response.Wrap(&msgParams)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wrap server response message: %v", err)
|
||||
}
|
||||
|
||||
if err := s.whisper.Send(env); err != nil {
|
||||
return fmt.Errorf("failed to send server response message: %v", err)
|
||||
}
|
||||
|
||||
log.Info("server confirms client session", "dst",
|
||||
common.ToHex(crypto.FromECDSAPub(msgParams.Dst)), "topic", msgParams.Topic.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
// processDropClientSessionRequest processes incoming client requests when:
|
||||
// client wants to drop its sessions with notification servers (if they exist)
|
||||
func (s *NotificationServer) processDropClientSessionRequest(msg *whisper.ReceivedMessage) error {
|
||||
if msg.Src == nil {
|
||||
return errors.New("message 'from' field is required")
|
||||
}
|
||||
|
||||
s.clientSessionsMu.RLock()
|
||||
pubKey := hex.EncodeToString(crypto.FromECDSAPub(msg.Src))
|
||||
for _, clientSession := range s.clientSessions {
|
||||
if clientSession.ClientKey == pubKey {
|
||||
s.clientSessionsMu.RUnlock()
|
||||
s.DropClientSession(clientSession.SessionKeyHash.Hex())
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// installTopicFilter installs Whisper filter using symmetric key
|
||||
func (s *NotificationServer) installTopicFilter(topicName string, topicKey []byte) (filterID string, err error) {
|
||||
topic := MakeTopicAsBytes([]byte(topicName))
|
||||
filter := whisper.Filter{
|
||||
KeySym: topicKey,
|
||||
Topics: [][]byte{topic},
|
||||
AllowP2P: true,
|
||||
}
|
||||
filterID, err = s.whisper.Subscribe(&filter)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
|
||||
log.Debug(fmt.Sprintf("installed topic filter %v for topic %x (%s)", filterID, topic, topicName))
|
||||
return
|
||||
}
|
||||
|
||||
// installKeyFilter installs Whisper filter using asymmetric key
|
||||
func (s *NotificationServer) installKeyFilter(topicName string, key *ecdsa.PrivateKey) (filterID string, err error) {
|
||||
topic := MakeTopicAsBytes([]byte(topicName))
|
||||
filter := whisper.Filter{
|
||||
KeyAsym: key,
|
||||
Topics: [][]byte{topic},
|
||||
AllowP2P: true,
|
||||
}
|
||||
filterID, err = s.whisper.Subscribe(&filter)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed installing filter: %v", err)
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("installed key filter %v for topic %x (%s)", filterID, topic, topicName))
|
||||
return
|
||||
}
|
||||
|
||||
// requestProcessorLoop processes incoming client requests, by listening to a given filter,
|
||||
// and executing process function on each incoming message
|
||||
func (s *NotificationServer) requestProcessorLoop(filterID string, topicWatched string, fn messageProcessingFn) {
|
||||
log.Debug(fmt.Sprintf("request processor started: %s", topicWatched))
|
||||
|
||||
filter := s.whisper.GetFilter(filterID)
|
||||
if filter == nil {
|
||||
log.Warn(fmt.Sprintf("filter is not installed: %s (for topic '%s')", filterID, topicWatched))
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(time.Millisecond * 50)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
messages := filter.Retrieve()
|
||||
for _, msg := range messages {
|
||||
if err := fn(msg); err != nil {
|
||||
log.Warn("failed processing incoming request", "error", err)
|
||||
}
|
||||
}
|
||||
case <-s.quit:
|
||||
log.Debug("request processor stopped", "topic", topicWatched)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// makeSessionKey generates and saves random SymKey, allowing to establish secure
|
||||
// channel between server and client
|
||||
func (s *NotificationServer) makeSessionKey(keyName string) (sessionKey, sessionKeyDerived []byte, err error) {
|
||||
// wipe out previous occurrence of symmetric key
|
||||
s.whisper.DeleteSymKey(keyName)
|
||||
|
||||
sessionKey, err = makeSessionKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
keyName, err = s.whisper.AddSymKey(keyName, sessionKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sessionKeyDerived, err = s.whisper.GetSymKey(keyName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
package notifications
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
|
||||
crand "crypto/rand"
|
||||
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
// makeSessionKey returns pseudo-random symmetric key, which is used as
|
||||
// session key between notification client and server
|
||||
func makeSessionKey() ([]byte, error) {
|
||||
// generate random key
|
||||
const keyLen = 32
|
||||
buf := make([]byte, keyLen)
|
||||
_, err := crand.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !validateSymmetricKey(buf) {
|
||||
return nil, errors.New("error in GenerateSymKey: crypto/rand failed to generate random data")
|
||||
}
|
||||
|
||||
key := buf[:keyLen]
|
||||
derived, err := deriveKeyMaterial(key, whisper.ProtocolVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !validateSymmetricKey(derived) {
|
||||
return nil, errors.New("failed to derive valid key")
|
||||
}
|
||||
|
||||
return derived, nil
|
||||
}
|
||||
|
||||
// validateSymmetricKey returns false if the key contains all zeros
|
||||
func validateSymmetricKey(k []byte) bool {
|
||||
return len(k) > 0 && !containsOnlyZeros(k)
|
||||
}
|
||||
|
||||
// containsOnlyZeros checks if data is empty or not
|
||||
func containsOnlyZeros(data []byte) bool {
|
||||
for _, b := range data {
|
||||
if b != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// deriveKeyMaterial derives symmetric key material from the key or password./~~~
|
||||
// pbkdf2 is used for security, in case people use password instead of randomly generated keys.
|
||||
func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) {
|
||||
if version == 0 {
|
||||
// kdf should run no less than 0.1 seconds on average compute,
|
||||
// because it's a once in a session experience
|
||||
derivedKey := pbkdf2.Key(key, nil, 65356, 32, sha256.New)
|
||||
return derivedKey, nil
|
||||
} else {
|
||||
return nil, errors.New("unknown version")
|
||||
}
|
||||
}
|
||||
|
||||
// MakeTopic returns Whisper topic *as bytes array* by generating cryptographic key from the provided password
|
||||
func MakeTopicAsBytes(password []byte) []byte {
|
||||
topic := make([]byte, int(whisper.TopicLength))
|
||||
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
|
||||
for i := 0; i < len(x); i++ {
|
||||
topic[i%whisper.TopicLength] ^= x[i]
|
||||
}
|
||||
|
||||
return topic
|
||||
}
|
||||
|
||||
// MakeTopic returns Whisper topic by generating cryptographic key from the provided password
|
||||
func MakeTopic(password []byte) (topic whisper.TopicType) {
|
||||
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
|
||||
for i := 0; i < len(x); i++ {
|
||||
topic[i%whisper.TopicLength] ^= x[i]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -32,8 +32,6 @@ package whisperv5
|
|||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -88,18 +86,6 @@ type MailServer interface {
|
|||
DeliverMail(whisperPeer *Peer, request *Envelope)
|
||||
}
|
||||
|
||||
// NotificationServer represents a notification server,
|
||||
// capable of screening incoming envelopes for special
|
||||
// topics, and once located, subscribe client nodes as
|
||||
// recipients to notifications (push notifications atm)
|
||||
type NotificationServer interface {
|
||||
// Start initializes notification sending loop
|
||||
Start(server *p2p.Server) error
|
||||
|
||||
// Stop stops notification sending loop, releasing related resources
|
||||
Stop() error
|
||||
}
|
||||
|
||||
type envelopeSource int
|
||||
|
||||
const (
|
||||
|
|
|
@ -78,7 +78,6 @@ type Whisper struct {
|
|||
stats Statistics // Statistics of whisper node
|
||||
|
||||
mailServer MailServer // MailServer interface
|
||||
notificationServer NotificationServer
|
||||
envelopeTracer EnvelopeTracer // Service collecting envelopes metadata
|
||||
}
|
||||
|
||||
|
@ -158,11 +157,6 @@ func (w *Whisper) RegisterServer(server MailServer) {
|
|||
w.mailServer = server
|
||||
}
|
||||
|
||||
// RegisterNotificationServer registers notification server with Whisper
|
||||
func (w *Whisper) RegisterNotificationServer(server NotificationServer) {
|
||||
w.notificationServer = server
|
||||
}
|
||||
|
||||
// RegisterEnvelopeTracer registers an EnveloperTracer to collect information
|
||||
// about received envelopes.
|
||||
func (w *Whisper) RegisterEnvelopeTracer(tracer EnvelopeTracer) {
|
||||
|
@ -526,7 +520,7 @@ func (w *Whisper) Send(envelope *Envelope) error {
|
|||
|
||||
// Start implements node.Service, starting the background data propagation thread
|
||||
// of the Whisper protocol.
|
||||
func (w *Whisper) Start(stack *p2p.Server) error {
|
||||
func (w *Whisper) Start(*p2p.Server) error {
|
||||
log.Info("started whisper v." + ProtocolVersionStr)
|
||||
go w.update()
|
||||
|
||||
|
@ -535,12 +529,6 @@ func (w *Whisper) Start(stack *p2p.Server) error {
|
|||
go w.processQueue()
|
||||
}
|
||||
|
||||
if w.notificationServer != nil {
|
||||
if err := w.notificationServer.Start(stack); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -548,13 +536,6 @@ func (w *Whisper) Start(stack *p2p.Server) error {
|
|||
// of the Whisper protocol.
|
||||
func (w *Whisper) Stop() error {
|
||||
close(w.quit)
|
||||
|
||||
if w.notificationServer != nil {
|
||||
if err := w.notificationServer.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("whisper stopped")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -35,8 +35,6 @@ package whisperv6
|
|||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
// Whisper protocol parameters
|
||||
|
@ -98,18 +96,6 @@ type MailServer interface {
|
|||
DeliverMail(whisperPeer *Peer, request *Envelope)
|
||||
}
|
||||
|
||||
// NotificationServer represents a notification server,
|
||||
// capable of screening incoming envelopes for special
|
||||
// topics, and once located, subscribe client nodes as
|
||||
// recipients to notifications (push notifications atm)
|
||||
type NotificationServer interface {
|
||||
// Start initializes notification sending loop
|
||||
Start(server *p2p.Server) error
|
||||
|
||||
// Stop stops notification sending loop, releasing related resources
|
||||
Stop() error
|
||||
}
|
||||
|
||||
type envelopeSource int
|
||||
|
||||
const (
|
||||
|
|
|
@ -86,7 +86,6 @@ type Whisper struct {
|
|||
stats Statistics // Statistics of whisper node
|
||||
|
||||
mailServer MailServer // MailServer interface
|
||||
notificationServer NotificationServer
|
||||
envelopeTracer EnvelopeTracer // Service collecting envelopes metadata
|
||||
}
|
||||
|
||||
|
@ -211,11 +210,6 @@ func (whisper *Whisper) RegisterServer(server MailServer) {
|
|||
whisper.mailServer = server
|
||||
}
|
||||
|
||||
// RegisterNotificationServer registers notification server with Whisper
|
||||
func (whisper *Whisper) RegisterNotificationServer(server NotificationServer) {
|
||||
whisper.notificationServer = server
|
||||
}
|
||||
|
||||
// RegisterEnvelopeTracer registers an EnveloperTracer to collect information
|
||||
// about received envelopes.
|
||||
func (whisper *Whisper) RegisterEnvelopeTracer(tracer EnvelopeTracer) {
|
||||
|
@ -676,7 +670,7 @@ func (whisper *Whisper) Send(envelope *Envelope) error {
|
|||
|
||||
// Start implements node.Service, starting the background data propagation thread
|
||||
// of the Whisper protocol.
|
||||
func (whisper *Whisper) Start(stack *p2p.Server) error {
|
||||
func (whisper *Whisper) Start(*p2p.Server) error {
|
||||
log.Info("started whisper v." + ProtocolVersionStr)
|
||||
go whisper.update()
|
||||
|
||||
|
@ -685,12 +679,6 @@ func (whisper *Whisper) Start(stack *p2p.Server) error {
|
|||
go whisper.processQueue()
|
||||
}
|
||||
|
||||
if whisper.notificationServer != nil {
|
||||
if err := whisper.notificationServer.Start(stack); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -698,13 +686,6 @@ func (whisper *Whisper) Start(stack *p2p.Server) error {
|
|||
// of the Whisper protocol.
|
||||
func (whisper *Whisper) Stop() error {
|
||||
close(whisper.quit)
|
||||
|
||||
if whisper.notificationServer != nil {
|
||||
if err := whisper.notificationServer.Stop(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("whisper stopped")
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue