Create whisperv6 patch versions of `0004-whisper-notifications.patch` and `0009-whisper-envelopes-tracing.patch`. Closes #637
This commit is contained in:
parent
9976018978
commit
857b72e9fd
|
@ -109,7 +109,7 @@
|
|||
"whisper/notifications",
|
||||
"whisper/whisperv5"
|
||||
]
|
||||
revision = "09f08d50335df2d8c9b9f062b18f0ebd3a84133d"
|
||||
revision = "5b2cc44bf2b32bb482def02d7c8fa32ba08d0bf4"
|
||||
source = "https://github.com/status-im/go-ethereum.git"
|
||||
|
||||
[[projects]]
|
||||
|
|
|
@ -0,0 +1,324 @@
|
|||
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++ {
|
||||
diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go
|
||||
index 8ae2882e1..7c97f0680 100644
|
||||
--- a/whisper/whisperv6/api.go
|
||||
+++ b/whisper/whisperv6/api.go
|
||||
@@ -319,6 +319,16 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
|
||||
return true, api.w.Send(env)
|
||||
}
|
||||
|
||||
+// UninstallFilter is alias for Unsubscribe
|
||||
+func (api *PublicWhisperAPI) UninstallFilter(id string) {
|
||||
+ api.w.Unsubscribe(id)
|
||||
+}
|
||||
+
|
||||
+// Unsubscribe disables and removes an existing filter.
|
||||
+func (api *PublicWhisperAPI) Unsubscribe(id string) {
|
||||
+ api.w.Unsubscribe(id)
|
||||
+}
|
||||
+
|
||||
//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
|
||||
@@ -67,7 +69,7 @@ const (
|
||||
|
||||
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
|
||||
DefaultMaxMessageSize = uint32(1024 * 1024)
|
||||
- DefaultMinimumPoW = 0.2
|
||||
+ DefaultMinimumPoW = 0.001
|
||||
|
||||
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
|
||||
messageQueueLimit = 1024
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
- id, err := GenerateRandomID()
|
||||
+ id, err := toDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
|
||||
if err != nil {
|
||||
- return "", fmt.Errorf("failed to generate ID: %s", err)
|
||||
+ return "", err
|
||||
}
|
||||
|
||||
whisper.keyMu.Lock()
|
||||
@@ -397,11 +403,16 @@ func (whisper *Whisper) NewKeyPair() (string, error) {
|
||||
|
||||
// DeleteKeyPair deletes the specified key if it exists.
|
||||
func (whisper *Whisper) DeleteKeyPair(key string) bool {
|
||||
+ deterministicID, err := toDeterministicID(key, keyIDSize)
|
||||
+ if err != nil {
|
||||
+ return false
|
||||
+ }
|
||||
+
|
||||
whisper.keyMu.Lock()
|
||||
defer whisper.keyMu.Unlock()
|
||||
|
||||
- if whisper.privateKeys[key] != nil {
|
||||
- delete(whisper.privateKeys, key)
|
||||
+ if whisper.privateKeys[deterministicID] != nil {
|
||||
+ delete(whisper.privateKeys, deterministicID)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -409,31 +420,73 @@ func (whisper *Whisper) DeleteKeyPair(key string) bool {
|
||||
|
||||
// AddKeyPair imports a asymmetric private key and returns it identifier.
|
||||
func (whisper *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
|
||||
- id, err := GenerateRandomID()
|
||||
+ id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
|
||||
if err != nil {
|
||||
- return "", fmt.Errorf("failed to generate ID: %s", err)
|
||||
+ return "", err
|
||||
+ }
|
||||
+ if whisper.HasKeyPair(id) {
|
||||
+ return id, nil // no need to re-inject
|
||||
}
|
||||
|
||||
whisper.keyMu.Lock()
|
||||
whisper.privateKeys[id] = key
|
||||
whisper.keyMu.Unlock()
|
||||
+ log.Info("Whisper identity added", "id", id, "pubkey", common.ToHex(crypto.FromECDSAPub(&key.PublicKey)))
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
+// SelectKeyPair adds cryptographic identity, and makes sure
|
||||
+// that it is the only private key known to the node.
|
||||
+func (whisper *Whisper) SelectKeyPair(key *ecdsa.PrivateKey) error {
|
||||
+ id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
|
||||
+ if err != nil {
|
||||
+ return err
|
||||
+ }
|
||||
+
|
||||
+ whisper.keyMu.Lock()
|
||||
+ defer whisper.keyMu.Unlock()
|
||||
+
|
||||
+ whisper.privateKeys = make(map[string]*ecdsa.PrivateKey) // reset key store
|
||||
+ whisper.privateKeys[id] = key
|
||||
+
|
||||
+ log.Info("Whisper identity selected", "id", id, "key", common.ToHex(crypto.FromECDSAPub(&key.PublicKey)))
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
+// DeleteKeyPairs removes all cryptographic identities known to the node
|
||||
+func (whisper *Whisper) DeleteKeyPairs() error {
|
||||
+ whisper.keyMu.Lock()
|
||||
+ defer whisper.keyMu.Unlock()
|
||||
+
|
||||
+ whisper.privateKeys = make(map[string]*ecdsa.PrivateKey)
|
||||
+
|
||||
+ return nil
|
||||
+}
|
||||
+
|
||||
// HasKeyPair checks if the the whisper node is configured with the private key
|
||||
// of the specified public pair.
|
||||
func (whisper *Whisper) HasKeyPair(id string) bool {
|
||||
+ deterministicID, err := toDeterministicID(id, keyIDSize)
|
||||
+ if err != nil {
|
||||
+ return false
|
||||
+ }
|
||||
+
|
||||
whisper.keyMu.RLock()
|
||||
defer whisper.keyMu.RUnlock()
|
||||
- return whisper.privateKeys[id] != nil
|
||||
+ return whisper.privateKeys[deterministicID] != nil
|
||||
}
|
||||
|
||||
// GetPrivateKey retrieves the private key of the specified identity.
|
||||
func (whisper *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
|
||||
+ deterministicID, err := toDeterministicID(id, keyIDSize)
|
||||
+ if err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
+
|
||||
whisper.keyMu.RLock()
|
||||
defer whisper.keyMu.RUnlock()
|
||||
- key := whisper.privateKeys[id]
|
||||
+ key := whisper.privateKeys[deterministicID]
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf("invalid id")
|
||||
}
|
||||
@@ -465,6 +518,23 @@ func (whisper *Whisper) GenerateSymKey() (string, error) {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
+// AddSymKey stores the key with a given id.
|
||||
+func (whisper *Whisper) AddSymKey(id string, key []byte) (string, error) {
|
||||
+ deterministicID, err := toDeterministicID(id, keyIDSize)
|
||||
+ if err != nil {
|
||||
+ return "", err
|
||||
+ }
|
||||
+
|
||||
+ whisper.keyMu.Lock()
|
||||
+ defer whisper.keyMu.Unlock()
|
||||
+
|
||||
+ if whisper.symKeys[deterministicID] != nil {
|
||||
+ return "", fmt.Errorf("key already exists: %v", id)
|
||||
+ }
|
||||
+ whisper.symKeys[deterministicID] = key
|
||||
+ return deterministicID, nil
|
||||
+}
|
||||
+
|
||||
// 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
|
||||
}
|
||||
|
||||
+// makeDeterministicID generates a deterministic ID, based on a given input
|
||||
+func makeDeterministicID(input string, keyLen int) (id string, err error) {
|
||||
+ buf := pbkdf2.Key([]byte(input), nil, 4096, keyLen, sha256.New)
|
||||
+ if !validateDataIntegrity(buf, keyIDSize) {
|
||||
+ return "", fmt.Errorf("error in GenerateDeterministicID: failed to generate key")
|
||||
+ }
|
||||
+ id = common.Bytes2Hex(buf)
|
||||
+ return id, err
|
||||
+}
|
||||
+
|
||||
+// toDeterministicID reviews incoming id, and transforms it to format
|
||||
+// expected internally be private key store. Originally, public keys
|
||||
+// were used as keys, now random keys are being used. And in order to
|
||||
+// make it easier to consume, we now allow both random IDs and public
|
||||
+// keys to be passed.
|
||||
+func toDeterministicID(id string, expectedLen int) (string, error) {
|
||||
+ if len(id) != (expectedLen * 2) { // we received hex key, so number of chars in id is doubled
|
||||
+ var err error
|
||||
+ id, err = makeDeterministicID(id, expectedLen)
|
||||
+ if err != nil {
|
||||
+ return "", err
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return id, nil
|
||||
+}
|
||||
+
|
||||
func isFullNode(bloom []byte) bool {
|
||||
if bloom == nil {
|
||||
return true
|
|
@ -0,0 +1,109 @@
|
|||
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
|
||||
}
|
||||
+
|
||||
+type envelopeSource int
|
||||
+
|
||||
+const (
|
||||
+ _ = iota
|
||||
+ // peerSource indicates a source as a regular peer.
|
||||
+ peerSource envelopeSource = iota
|
||||
+ // p2pSource indicates that envelop was received from a trusted peer.
|
||||
+ p2pSource
|
||||
+)
|
||||
+
|
||||
+// EnvelopeMeta keeps metadata of received envelopes.
|
||||
+type EnvelopeMeta struct {
|
||||
+ Hash string
|
||||
+ Topic TopicType
|
||||
+ Size uint32
|
||||
+ Source envelopeSource
|
||||
+ IsNew bool
|
||||
+ Peer string
|
||||
+}
|
||||
+
|
||||
+// SourceString converts source to string.
|
||||
+func (m *EnvelopeMeta) SourceString() string {
|
||||
+ switch m.Source {
|
||||
+ case peerSource:
|
||||
+ return "peer"
|
||||
+ case p2pSource:
|
||||
+ return "p2p"
|
||||
+ default:
|
||||
+ return "unknown"
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// EnvelopeTracer tracks received envelopes.
|
||||
+type EnvelopeTracer interface {
|
||||
+ Trace(*EnvelopeMeta)
|
||||
+}
|
||||
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 {
|
||||
|
||||
mailServer MailServer // MailServer interface
|
||||
notificationServer NotificationServer
|
||||
+ 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
|
||||
}
|
||||
|
||||
+// RegisterEnvelopeTracer registers an EnveloperTracer to collect information
|
||||
+// about received envelopes.
|
||||
+func (whisper *Whisper) RegisterEnvelopeTracer(tracer EnvelopeTracer) {
|
||||
+ whisper.envelopeTracer = tracer
|
||||
+}
|
||||
+
|
||||
// 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 {
|
||||
|
||||
trouble := false
|
||||
for _, env := range envelopes {
|
||||
+ whisper.traceEnvelope(env, !whisper.isEnvelopeCached(env.Hash()), peerSource, p)
|
||||
cached, err := whisper.add(env)
|
||||
if err != nil {
|
||||
trouble = true
|
||||
@@ -810,6 +818,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
||||
return errors.New("invalid direct message")
|
||||
}
|
||||
whisper.postEvent(&envelope, true)
|
||||
+ whisper.traceEnvelope(&envelope, false, p2pSource, p)
|
||||
}
|
||||
case p2pRequestCode:
|
||||
// Must be processed if mail server is implemented. Otherwise ignore.
|
||||
@@ -906,6 +915,22 @@ func (whisper *Whisper) add(envelope *Envelope) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
+// traceEnvelope collects basic metadata about an envelope and sender peer.
|
||||
+func (whisper *Whisper) traceEnvelope(envelope *Envelope, isNew bool, source envelopeSource, peer *Peer) {
|
||||
+ if whisper.envelopeTracer == nil {
|
||||
+ return
|
||||
+ }
|
||||
+
|
||||
+ whisper.envelopeTracer.Trace(&EnvelopeMeta{
|
||||
+ Hash: envelope.Hash().String(),
|
||||
+ Topic: BytesToTopic(envelope.Topic[:]),
|
||||
+ Size: uint32(envelope.size()),
|
||||
+ Source: source,
|
||||
+ IsNew: isNew,
|
||||
+ Peer: peer.peer.Info().ID,
|
||||
+ })
|
||||
+}
|
||||
+
|
||||
// postEvent queues the message for further processing.
|
||||
func (whisper *Whisper) postEvent(envelope *Envelope, isP2P bool) {
|
||||
if isP2P {
|
|
@ -27,6 +27,8 @@ 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.patch`](./0013-whisperv6-notifications.patch) — adds Whisper v6 notifications (need to be reviewed and documented)
|
||||
- [`0014-whisperv6-envelopes-tracing.patch`](./0014-whisperv6-envelopes-tracing.patch) — adds Whisper v6 envelope tracing (need to be reviewed and documented)
|
||||
|
||||
# Updating upstream version
|
||||
|
||||
|
|
|
@ -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++ {
|
||||
|
|
|
@ -322,6 +322,16 @@ func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, er
|
|||
return true, api.w.Send(env)
|
||||
}
|
||||
|
||||
// UninstallFilter is alias for Unsubscribe
|
||||
func (api *PublicWhisperAPI) UninstallFilter(id string) {
|
||||
api.w.Unsubscribe(id)
|
||||
}
|
||||
|
||||
// Unsubscribe disables and removes an existing filter.
|
||||
func (api *PublicWhisperAPI) Unsubscribe(id string) {
|
||||
api.w.Unsubscribe(id)
|
||||
}
|
||||
|
||||
//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go
|
||||
|
||||
// Criteria holds various filter options for inbound messages.
|
||||
|
|
|
@ -35,6 +35,8 @@ package whisperv6
|
|||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
)
|
||||
|
||||
// Whisper protocol parameters
|
||||
|
@ -67,7 +69,7 @@ const (
|
|||
|
||||
MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
|
||||
DefaultMaxMessageSize = uint32(1024 * 1024)
|
||||
DefaultMinimumPoW = 0.2
|
||||
DefaultMinimumPoW = 0.001
|
||||
|
||||
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol
|
||||
messageQueueLimit = 1024
|
||||
|
@ -95,3 +97,52 @@ 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
|
||||
}
|
||||
|
||||
type envelopeSource int
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
// peerSource indicates a source as a regular peer.
|
||||
peerSource envelopeSource = iota
|
||||
// p2pSource indicates that envelop was received from a trusted peer.
|
||||
p2pSource
|
||||
)
|
||||
|
||||
// EnvelopeMeta keeps metadata of received envelopes.
|
||||
type EnvelopeMeta struct {
|
||||
Hash string
|
||||
Topic TopicType
|
||||
Size uint32
|
||||
Source envelopeSource
|
||||
IsNew bool
|
||||
Peer string
|
||||
}
|
||||
|
||||
// SourceString converts source to string.
|
||||
func (m *EnvelopeMeta) SourceString() string {
|
||||
switch m.Source {
|
||||
case peerSource:
|
||||
return "peer"
|
||||
case p2pSource:
|
||||
return "p2p"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// EnvelopeTracer tracks received envelopes.
|
||||
type EnvelopeTracer interface {
|
||||
Trace(*EnvelopeMeta)
|
||||
}
|
||||
|
|
|
@ -86,6 +86,8 @@ type Whisper struct {
|
|||
stats Statistics // Statistics of whisper node
|
||||
|
||||
mailServer MailServer // MailServer interface
|
||||
notificationServer NotificationServer
|
||||
envelopeTracer EnvelopeTracer // Service collecting envelopes metadata
|
||||
}
|
||||
|
||||
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
|
||||
|
@ -209,6 +211,17 @@ 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) {
|
||||
whisper.envelopeTracer = tracer
|
||||
}
|
||||
|
||||
// Protocols returns the whisper sub-protocols ran by this particular client.
|
||||
func (whisper *Whisper) Protocols() []p2p.Protocol {
|
||||
return []p2p.Protocol{whisper.protocol}
|
||||
|
@ -380,9 +393,9 @@ func (whisper *Whisper) NewKeyPair() (string, error) {
|
|||
return "", fmt.Errorf("failed to generate valid key")
|
||||
}
|
||||
|
||||
id, err := GenerateRandomID()
|
||||
id, err := toDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate ID: %s", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
whisper.keyMu.Lock()
|
||||
|
@ -397,11 +410,16 @@ func (whisper *Whisper) NewKeyPair() (string, error) {
|
|||
|
||||
// DeleteKeyPair deletes the specified key if it exists.
|
||||
func (whisper *Whisper) DeleteKeyPair(key string) bool {
|
||||
deterministicID, err := toDeterministicID(key, keyIDSize)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
whisper.keyMu.Lock()
|
||||
defer whisper.keyMu.Unlock()
|
||||
|
||||
if whisper.privateKeys[key] != nil {
|
||||
delete(whisper.privateKeys, key)
|
||||
if whisper.privateKeys[deterministicID] != nil {
|
||||
delete(whisper.privateKeys, deterministicID)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -409,31 +427,73 @@ func (whisper *Whisper) DeleteKeyPair(key string) bool {
|
|||
|
||||
// AddKeyPair imports a asymmetric private key and returns it identifier.
|
||||
func (whisper *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
|
||||
id, err := GenerateRandomID()
|
||||
id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate ID: %s", err)
|
||||
return "", err
|
||||
}
|
||||
if whisper.HasKeyPair(id) {
|
||||
return id, nil // no need to re-inject
|
||||
}
|
||||
|
||||
whisper.keyMu.Lock()
|
||||
whisper.privateKeys[id] = key
|
||||
whisper.keyMu.Unlock()
|
||||
log.Info("Whisper identity added", "id", id, "pubkey", common.ToHex(crypto.FromECDSAPub(&key.PublicKey)))
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// SelectKeyPair adds cryptographic identity, and makes sure
|
||||
// that it is the only private key known to the node.
|
||||
func (whisper *Whisper) SelectKeyPair(key *ecdsa.PrivateKey) error {
|
||||
id, err := makeDeterministicID(common.ToHex(crypto.FromECDSAPub(&key.PublicKey)), keyIDSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
whisper.keyMu.Lock()
|
||||
defer whisper.keyMu.Unlock()
|
||||
|
||||
whisper.privateKeys = make(map[string]*ecdsa.PrivateKey) // reset key store
|
||||
whisper.privateKeys[id] = key
|
||||
|
||||
log.Info("Whisper identity selected", "id", id, "key", common.ToHex(crypto.FromECDSAPub(&key.PublicKey)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteKeyPairs removes all cryptographic identities known to the node
|
||||
func (whisper *Whisper) DeleteKeyPairs() error {
|
||||
whisper.keyMu.Lock()
|
||||
defer whisper.keyMu.Unlock()
|
||||
|
||||
whisper.privateKeys = make(map[string]*ecdsa.PrivateKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasKeyPair checks if the the whisper node is configured with the private key
|
||||
// of the specified public pair.
|
||||
func (whisper *Whisper) HasKeyPair(id string) bool {
|
||||
deterministicID, err := toDeterministicID(id, keyIDSize)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
whisper.keyMu.RLock()
|
||||
defer whisper.keyMu.RUnlock()
|
||||
return whisper.privateKeys[id] != nil
|
||||
return whisper.privateKeys[deterministicID] != nil
|
||||
}
|
||||
|
||||
// GetPrivateKey retrieves the private key of the specified identity.
|
||||
func (whisper *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
|
||||
deterministicID, err := toDeterministicID(id, keyIDSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
whisper.keyMu.RLock()
|
||||
defer whisper.keyMu.RUnlock()
|
||||
key := whisper.privateKeys[id]
|
||||
key := whisper.privateKeys[deterministicID]
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf("invalid id")
|
||||
}
|
||||
|
@ -465,6 +525,23 @@ func (whisper *Whisper) GenerateSymKey() (string, error) {
|
|||
return id, nil
|
||||
}
|
||||
|
||||
// AddSymKey stores the key with a given id.
|
||||
func (whisper *Whisper) AddSymKey(id string, key []byte) (string, error) {
|
||||
deterministicID, err := toDeterministicID(id, keyIDSize)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
whisper.keyMu.Lock()
|
||||
defer whisper.keyMu.Unlock()
|
||||
|
||||
if whisper.symKeys[deterministicID] != nil {
|
||||
return "", fmt.Errorf("key already exists: %v", id)
|
||||
}
|
||||
whisper.symKeys[deterministicID] = key
|
||||
return deterministicID, nil
|
||||
}
|
||||
|
||||
// AddSymKeyDirect stores the key, and returns its id.
|
||||
func (whisper *Whisper) AddSymKeyDirect(key []byte) (string, error) {
|
||||
if len(key) != aesKeyLength {
|
||||
|
@ -599,7 +676,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 +685,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 +698,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
|
||||
}
|
||||
|
@ -673,6 +763,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
|||
|
||||
trouble := false
|
||||
for _, env := range envelopes {
|
||||
whisper.traceEnvelope(env, !whisper.isEnvelopeCached(env.Hash()), peerSource, p)
|
||||
cached, err := whisper.add(env)
|
||||
if err != nil {
|
||||
trouble = true
|
||||
|
@ -723,6 +814,7 @@ func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
|
|||
return errors.New("invalid direct message")
|
||||
}
|
||||
whisper.postEvent(&envelope, true)
|
||||
whisper.traceEnvelope(&envelope, false, p2pSource, p)
|
||||
}
|
||||
case p2pRequestCode:
|
||||
// Must be processed if mail server is implemented. Otherwise ignore.
|
||||
|
@ -819,6 +911,22 @@ func (whisper *Whisper) add(envelope *Envelope) (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// traceEnvelope collects basic metadata about an envelope and sender peer.
|
||||
func (whisper *Whisper) traceEnvelope(envelope *Envelope, isNew bool, source envelopeSource, peer *Peer) {
|
||||
if whisper.envelopeTracer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
whisper.envelopeTracer.Trace(&EnvelopeMeta{
|
||||
Hash: envelope.Hash().String(),
|
||||
Topic: BytesToTopic(envelope.Topic[:]),
|
||||
Size: uint32(envelope.size()),
|
||||
Source: source,
|
||||
IsNew: isNew,
|
||||
Peer: peer.peer.Info().ID,
|
||||
})
|
||||
}
|
||||
|
||||
// postEvent queues the message for further processing.
|
||||
func (whisper *Whisper) postEvent(envelope *Envelope, isP2P bool) {
|
||||
if isP2P {
|
||||
|
@ -1031,6 +1139,33 @@ func GenerateRandomID() (id string, err error) {
|
|||
return id, err
|
||||
}
|
||||
|
||||
// makeDeterministicID generates a deterministic ID, based on a given input
|
||||
func makeDeterministicID(input string, keyLen int) (id string, err error) {
|
||||
buf := pbkdf2.Key([]byte(input), nil, 4096, keyLen, sha256.New)
|
||||
if !validateDataIntegrity(buf, keyIDSize) {
|
||||
return "", fmt.Errorf("error in GenerateDeterministicID: failed to generate key")
|
||||
}
|
||||
id = common.Bytes2Hex(buf)
|
||||
return id, err
|
||||
}
|
||||
|
||||
// toDeterministicID reviews incoming id, and transforms it to format
|
||||
// expected internally be private key store. Originally, public keys
|
||||
// were used as keys, now random keys are being used. And in order to
|
||||
// make it easier to consume, we now allow both random IDs and public
|
||||
// keys to be passed.
|
||||
func toDeterministicID(id string, expectedLen int) (string, error) {
|
||||
if len(id) != (expectedLen * 2) { // we received hex key, so number of chars in id is doubled
|
||||
var err error
|
||||
id, err = makeDeterministicID(id, expectedLen)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func isFullNode(bloom []byte) bool {
|
||||
if bloom == nil {
|
||||
return true
|
||||
|
|
Loading…
Reference in New Issue