Merge branch 'feature/push-notifications-fix' into develop

This commit is contained in:
Andrea Maria Piana 2020-08-20 09:00:36 +02:00
commit 6fb5cd2154
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
27 changed files with 857 additions and 379 deletions

View File

@ -1 +1 @@
0.56.9
0.57.0

View File

@ -18,6 +18,7 @@ import (
"github.com/status-im/status-go/protocol/datasync"
datasyncpeer "github.com/status-im/status-go/protocol/datasync/peer"
"github.com/status-im/status-go/protocol/encryption"
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/transport"
v1protocol "github.com/status-im/status-go/protocol/v1"
@ -60,6 +61,9 @@ type MessageProcessor struct {
scheduledMessagesSubscriptions []chan<- *RawMessage
featureFlags FeatureFlags
// handleSharedSecrets is a callback that is called every time a new shared secret is negotiated
handleSharedSecrets func([]*sharedsecret.Secret) error
}
func NewMessageProcessor(
@ -110,9 +114,14 @@ func (p *MessageProcessor) Stop() {
for _, c := range p.sentMessagesSubscriptions {
close(c)
}
p.sentMessagesSubscriptions = nil
p.datasync.Stop() // idempotent op
}
func (p *MessageProcessor) SetHandleSharedSecrets(handler func([]*sharedsecret.Secret) error) {
p.handleSharedSecrets = handler
}
// SendPrivate takes encoded data, encrypts it and sends through the wire.
func (p *MessageProcessor) SendPrivate(
ctx context.Context,
@ -203,7 +212,7 @@ func (p *MessageProcessor) sendPrivate(
} else if rawMessage.SkipEncryption {
// When SkipEncryption is set we don't pass the message to the encryption layer
messageIDs := [][]byte{messageID}
hash, newMessage, err := p.sendRawMessage(ctx, recipient, wrappedMessage, messageIDs)
hash, newMessage, err := p.sendPrivateRawMessage(ctx, recipient, wrappedMessage, messageIDs)
if err != nil {
return nil, errors.Wrap(err, "failed to send a message spec")
}
@ -216,6 +225,16 @@ func (p *MessageProcessor) sendPrivate(
return nil, errors.Wrap(err, "failed to encrypt message")
}
// The shared secret needs to be handle before we send a message
// otherwise the topic might not be set up before we receive a message
if p.handleSharedSecrets != nil {
err := p.handleSharedSecrets([]*sharedsecret.Secret{messageSpec.SharedSecret})
if err != nil {
return nil, err
}
}
messageIDs := [][]byte{messageID}
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec, messageIDs)
if err != nil {
@ -305,11 +324,23 @@ func (p *MessageProcessor) SendPublic(
return nil, errors.Wrap(err, "failed to wrap message")
}
newMessage := &types.NewMessage{
TTL: whisperTTL,
Payload: wrappedMessage,
PowTarget: calculatePoW(wrappedMessage),
PowTime: whisperPoWTime,
var newMessage *types.NewMessage
if !rawMessage.SkipEncryption {
messageSpec, err := p.protocol.BuildPublicMessage(p.identity, wrappedMessage)
if err != nil {
return nil, errors.Wrap(err, "failed to wrap a public message in the encryption layer")
}
newMessage, err = MessageSpecToWhisper(messageSpec)
if err != nil {
return nil, err
}
} else {
newMessage = &types.NewMessage{
TTL: whisperTTL,
Payload: wrappedMessage,
PowTarget: calculatePoW(wrappedMessage),
PowTime: whisperPoWTime,
}
}
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
@ -479,6 +510,16 @@ func (p *MessageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu
return errors.Wrap(err, "failed to encrypt message")
}
// The shared secret needs to be handle before we send a message
// otherwise the topic might not be set up before we receive a message
if p.handleSharedSecrets != nil {
err := p.handleSharedSecrets([]*sharedsecret.Secret{messageSpec.SharedSecret})
if err != nil {
return err
}
}
hash, newMessage, err := p.sendMessageSpec(ctx, publicKey, messageSpec, messageIDs)
if err != nil {
return err
@ -489,8 +530,8 @@ func (p *MessageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu
return nil
}
// sendRawMessage sends a message not wrapped in an encryption layer
func (p *MessageProcessor) sendRawMessage(ctx context.Context, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
// sendPrivateRawMessage sends a message not wrapped in an encryption layer
func (p *MessageProcessor) sendPrivateRawMessage(ctx context.Context, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
newMessage := &types.NewMessage{
TTL: whisperTTL,
Payload: payload,
@ -517,11 +558,11 @@ func (p *MessageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa
var hash []byte
switch {
case messageSpec.SharedSecret != nil:
// process shared secret
if messageSpec.AgreedSecret {
logger.Debug("sending using shared secret")
hash, err = p.transport.SendPrivateWithSharedSecret(ctx, newMessage, publicKey, messageSpec.SharedSecret)
default:
hash, err = p.transport.SendPrivateWithSharedSecret(ctx, newMessage, publicKey, messageSpec.SharedSecret.Key)
} else {
logger.Debug("sending partitioned topic")
hash, err = p.transport.SendPrivateWithPartitioned(ctx, newMessage, publicKey)
}

View File

@ -17,8 +17,6 @@ import (
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/encryption"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/sqlite"
transport "github.com/status-im/status-go/protocol/transport/whisper"
@ -63,15 +61,9 @@ func (s *MessageProcessorSuite) SetupTest() {
database, err := sqlite.Open(filepath.Join(s.tmpDir, "processor-test.sql"), "some-key")
s.Require().NoError(err)
onNewInstallations := func([]*multidevice.Installation) {}
onNewSharedSecret := func([]*sharedsecret.Secret) {}
onSendContactCode := func(*encryption.ProtocolMessageSpec) {}
encryptionProtocol := encryption.New(
database,
"installation-1",
onNewInstallations,
onNewSharedSecret,
onSendContactCode,
s.logger,
)
@ -206,9 +198,6 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() {
senderEncryptionProtocol := encryption.New(
senderDatabase,
"installation-2",
func([]*multidevice.Installation) {},
func([]*sharedsecret.Secret) {},
func(*encryption.ProtocolMessageSpec) {},
s.logger,
)

View File

@ -16,7 +16,6 @@ import (
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
)
const (
@ -65,9 +64,6 @@ func setupUser(user string, s *EncryptionServiceMultiDeviceSuite, n int) error {
protocol := New(
db,
installationID,
func(s []*multidevice.Installation) {},
func(s []*sharedsecret.Secret) {},
func(*ProtocolMessageSpec) {},
s.logger.With(zap.String("user", user)),
)
s.services[user].services[i] = protocol

View File

@ -18,9 +18,6 @@ import (
"go.uber.org/zap"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
)
var cleartext = []byte("hello")
@ -57,9 +54,6 @@ func (s *EncryptionServiceTestSuite) initDatabases(config encryptorConfig) {
db,
aliceInstallationID,
config,
func(s []*multidevice.Installation) {},
func(s []*sharedsecret.Secret) {},
func(*ProtocolMessageSpec) {},
s.logger.With(zap.String("user", "alice")),
)
@ -70,9 +64,6 @@ func (s *EncryptionServiceTestSuite) initDatabases(config encryptorConfig) {
db,
bobInstallationID,
config,
func(s []*multidevice.Installation) {},
func(s []*sharedsecret.Secret) {},
func(*ProtocolMessageSpec) {},
s.logger.With(zap.String("user", "bob")),
)
}
@ -130,7 +121,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() {
// On the receiver side, we should be able to decrypt using our private key and the ephemeral just sent
decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response1.Message, defaultMessageID)
s.Require().NoError(err)
s.Equal(cleartext, decryptedPayload1, "It correctly decrypts the payload using DH")
s.Equal(cleartext, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using DH")
// The next message will not be re-using the same key
response2, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext)
@ -147,7 +138,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() {
decryptedPayload2, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response2.Message, defaultMessageID)
s.Require().NoError(err)
s.Equal(cleartext, decryptedPayload2, "It correctly decrypts the payload using DH")
s.Equal(cleartext, decryptedPayload2.DecryptedMessage, "It correctly decrypts the payload using DH")
}
// Alice has Bob's bundle
@ -201,7 +192,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadBundle() {
// Bob is able to decrypt it using the bundle
decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response1.Message, defaultMessageID)
s.Require().NoError(err)
s.Equal(cleartext, decryptedPayload1, "It correctly decrypts the payload using X3DH")
s.Equal(cleartext, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using X3DH")
}
// Alice has Bob's bundle
@ -267,7 +258,7 @@ func (s *EncryptionServiceTestSuite) TestConsequentMessagesBundle() {
decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response.Message, defaultMessageID)
s.Require().NoError(err)
s.Equal(cleartext2, decryptedPayload1, "It correctly decrypts the payload using X3DH")
s.Equal(cleartext2, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using X3DH")
}
// Alice has Bob's bundle
@ -351,7 +342,7 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
decryptedPayload1, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, response.Message, defaultMessageID)
s.Require().NoError(err)
s.Equal(cleartext2, decryptedPayload1, "It correctly decrypts the payload using X3DH")
s.Equal(cleartext2, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using X3DH")
}
// Previous implementation allowed max maxSkip keys in the same receiving chain
@ -686,7 +677,7 @@ func receiver(
errChan <- err
return
}
if !reflect.DeepEqual(actualCleartext, cleartext) {
if !reflect.DeepEqual(actualCleartext.DecryptedMessage, cleartext) {
errChan <- errors.New("Decrypted value does not match")
return
}

View File

@ -38,7 +38,9 @@ type ProtocolMessageSpec struct {
// Installations is the targeted devices
Installations []*multidevice.Installation
// SharedSecret is a shared secret established among the installations
SharedSecret []byte
SharedSecret *sharedsecret.Secret
// AgreedSecret indicates whether the shared secret has been agreed
AgreedSecret bool
// Public means that the spec contains a public wrapped message
Public bool
}
@ -66,14 +68,11 @@ func (p *ProtocolMessageSpec) PartitionedTopicMode() PartitionTopicMode {
}
type Protocol struct {
encryptor *encryptor
secret *sharedsecret.SharedSecret
multidevice *multidevice.Multidevice
publisher *publisher.Publisher
onAddedBundlesHandler func([]*multidevice.Installation)
onNewSharedSecretHandler func([]*sharedsecret.Secret)
onSendContactCodeHandler func(*ProtocolMessageSpec)
encryptor *encryptor
secret *sharedsecret.SharedSecret
multidevice *multidevice.Multidevice
publisher *publisher.Publisher
subscriptions *Subscriptions
logger *zap.Logger
}
@ -87,18 +86,12 @@ var (
func New(
db *sql.DB,
installationID string,
addedBundlesHandler func([]*multidevice.Installation),
onNewSharedSecretHandler func([]*sharedsecret.Secret),
onSendContactCodeHandler func(*ProtocolMessageSpec),
logger *zap.Logger,
) *Protocol {
return NewWithEncryptorConfig(
db,
installationID,
defaultEncryptorConfig(installationID, logger),
addedBundlesHandler,
onNewSharedSecretHandler,
onSendContactCodeHandler,
logger,
)
}
@ -109,9 +102,6 @@ func NewWithEncryptorConfig(
db *sql.DB,
installationID string,
encryptorConfig encryptorConfig,
addedBundlesHandler func([]*multidevice.Installation),
onNewSharedSecretHandler func([]*sharedsecret.Secret),
onSendContactCodeHandler func(*ProtocolMessageSpec),
logger *zap.Logger,
) *Protocol {
return &Protocol{
@ -122,39 +112,36 @@ func NewWithEncryptorConfig(
ProtocolVersion: protocolVersion,
InstallationID: installationID,
}),
publisher: publisher.New(logger),
onAddedBundlesHandler: addedBundlesHandler,
onNewSharedSecretHandler: onNewSharedSecretHandler,
onSendContactCodeHandler: onSendContactCodeHandler,
logger: logger.With(zap.Namespace("Protocol")),
publisher: publisher.New(logger),
logger: logger.With(zap.Namespace("Protocol")),
}
}
func (p *Protocol) Start(myIdentity *ecdsa.PrivateKey) error {
type Subscriptions struct {
SharedSecrets []*sharedsecret.Secret
SendContactCode <-chan struct{}
Quit chan struct{}
}
func (p *Protocol) Start(myIdentity *ecdsa.PrivateKey) (*Subscriptions, error) {
// Propagate currently cached shared secrets.
secrets, err := p.secret.All()
if err != nil {
return errors.Wrap(err, "failed to get all secrets")
return nil, errors.Wrap(err, "failed to get all secrets")
}
p.onNewSharedSecretHandler(secrets)
// Handle Publisher system messages.
publisherCh := p.publisher.Start()
go func() {
for range publisherCh {
messageSpec, err := p.buildContactCodeMessage(myIdentity)
if err != nil {
p.logger.Error("failed to build contact code message",
zap.String("site", "Start"),
zap.Error(err))
continue
}
p.onSendContactCodeHandler(messageSpec)
}
}()
p.subscriptions = &Subscriptions{
SharedSecrets: secrets,
SendContactCode: p.publisher.Start(),
Quit: make(chan struct{}),
}
return p.subscriptions, nil
}
func (p *Protocol) Stop() error {
p.publisher.Stop()
if p.subscriptions != nil {
close(p.subscriptions.Quit)
}
return nil
}
@ -197,12 +184,6 @@ func (p *Protocol) BuildPublicMessage(myIdentityKey *ecdsa.PrivateKey, payload [
return &ProtocolMessageSpec{Message: message, Public: true}, nil
}
// buildContactCodeMessage creates a contact code message. It's a public message
// without any data but it carries bundle information.
func (p *Protocol) buildContactCodeMessage(myIdentityKey *ecdsa.PrivateKey) (*ProtocolMessageSpec, error) {
return p.BuildPublicMessage(myIdentityKey, nil)
}
// BuildDirectMessage returns a 1:1 chat message and optionally a negotiated topic given the user identity private key, the recipient's public key, and a payload
func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) {
logger := p.logger.With(
@ -253,18 +234,12 @@ func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey
zap.Bool("has-shared-secret", sharedSecret != nil),
zap.Bool("agreed", agreed))
// Call handler
if sharedSecret != nil {
p.onNewSharedSecretHandler([]*sharedsecret.Secret{sharedSecret})
}
spec := &ProtocolMessageSpec{
SharedSecret: sharedSecret,
AgreedSecret: agreed,
Message: message,
Installations: installations,
}
if agreed {
spec.SharedSecret = sharedSecret.Key
}
return spec, nil
}
@ -410,14 +385,21 @@ func (p *Protocol) ConfirmMessageProcessed(messageID []byte) error {
return p.encryptor.ConfirmMessageProcessed(messageID)
}
type DecryptMessageResponse struct {
DecryptedMessage []byte
Installations []*multidevice.Installation
SharedSecrets []*sharedsecret.Secret
}
// HandleMessage unmarshals a message and processes it, decrypting it if it is a 1:1 message.
func (p *Protocol) HandleMessage(
myIdentityKey *ecdsa.PrivateKey,
theirPublicKey *ecdsa.PublicKey,
protocolMessage *ProtocolMessage,
messageID []byte,
) ([]byte, error) {
) (*DecryptMessageResponse, error) {
logger := p.logger.With(zap.String("site", "HandleMessage"))
response := &DecryptMessageResponse{}
logger.Debug("received a protocol message", zap.Binary("sender-public-key", crypto.FromECDSAPub(theirPublicKey)), zap.Binary("message-id", messageID))
@ -428,19 +410,19 @@ func (p *Protocol) HandleMessage(
// Process bundles
for _, bundle := range protocolMessage.GetBundles() {
// Should we stop processing if the bundle cannot be verified?
addedBundles, err := p.ProcessPublicBundle(myIdentityKey, bundle)
newInstallations, err := p.ProcessPublicBundle(myIdentityKey, bundle)
if err != nil {
return nil, err
}
p.onAddedBundlesHandler(addedBundles)
response.Installations = newInstallations
}
// Check if it's a public message
if publicMessage := protocolMessage.GetPublicMessage(); publicMessage != nil {
logger.Debug("received a public message in direct message")
// Nothing to do, as already in cleartext
return publicMessage, nil
response.DecryptedMessage = publicMessage
return response, nil
}
// Decrypt message
@ -468,9 +450,10 @@ func (p *Protocol) HandleMessage(
return nil, err
}
p.onNewSharedSecretHandler([]*sharedsecret.Secret{sharedSecret})
response.SharedSecrets = []*sharedsecret.Secret{sharedSecret}
}
return message, nil
response.DecryptedMessage = message
return response, nil
}
// Return error

View File

@ -13,9 +13,6 @@ import (
"go.uber.org/zap"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
)
func TestProtocolServiceTestSuite(t *testing.T) {
@ -44,17 +41,11 @@ func (s *ProtocolServiceTestSuite) SetupTest() {
s.Require().NoError(err)
bobDBKey := "bob"
addedBundlesHandler := func(addedBundles []*multidevice.Installation) {}
onNewSharedSecretHandler := func(secret []*sharedsecret.Secret) {}
db, err := sqlite.Open(s.aliceDBPath.Name(), aliceDBKey)
s.Require().NoError(err)
s.alice = New(
db,
"1",
addedBundlesHandler,
onNewSharedSecretHandler,
func(*ProtocolMessageSpec) {},
s.logger.With(zap.String("user", "alice")),
)
@ -63,9 +54,6 @@ func (s *ProtocolServiceTestSuite) SetupTest() {
s.bob = New(
db,
"2",
addedBundlesHandler,
onNewSharedSecretHandler,
func(*ProtocolMessageSpec) {},
s.logger.With(zap.String("user", "bob")),
)
}
@ -142,7 +130,6 @@ func (s *ProtocolServiceTestSuite) TestBuildAndReadDirectMessage() {
}
func (s *ProtocolServiceTestSuite) TestSecretNegotiation() {
var secretResponse []*sharedsecret.Secret
bobKey, err := crypto.GenerateKey()
s.NoError(err)
aliceKey, err := crypto.GenerateKey()
@ -150,12 +137,13 @@ func (s *ProtocolServiceTestSuite) TestSecretNegotiation() {
payload := []byte("test")
s.bob.onNewSharedSecretHandler = func(secret []*sharedsecret.Secret) {
secretResponse = secret
}
_, err = s.bob.Start(bobKey)
s.Require().NoError(err)
msgSpec, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload)
s.NoError(err)
s.NotNil(msgSpec, "It creates a message spec")
s.Require().NotNil(msgSpec.SharedSecret)
bundle := msgSpec.Message.GetBundles()[0]
s.Require().NotNil(bundle)
@ -171,12 +159,10 @@ func (s *ProtocolServiceTestSuite) TestSecretNegotiation() {
_, err = s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, msgSpec.Message, []byte("message-id"))
s.NoError(err)
s.Require().NotNil(secretResponse)
s.Require().NoError(s.bob.Stop())
}
func (s *ProtocolServiceTestSuite) TestPropagatingSavedSharedSecretsOnStart() {
var secretResponse []*sharedsecret.Secret
aliceKey, err := crypto.GenerateKey()
s.NoError(err)
bobKey, err := crypto.GenerateKey()
@ -186,15 +172,14 @@ func (s *ProtocolServiceTestSuite) TestPropagatingSavedSharedSecretsOnStart() {
generatedSecret, err := s.alice.secret.Generate(aliceKey, &bobKey.PublicKey, "installation-1")
s.NoError(err)
s.alice.onNewSharedSecretHandler = func(secret []*sharedsecret.Secret) {
secretResponse = secret
}
subscriptions, err := s.alice.Start(aliceKey)
s.Require().NoError(err)
err = s.alice.Start(aliceKey)
s.NoError(err)
secretResponse := subscriptions.SharedSecrets
s.Require().NotNil(secretResponse)
s.Require().Len(secretResponse, 1)
s.Equal(crypto.FromECDSAPub(generatedSecret.Identity), crypto.FromECDSAPub(secretResponse[0].Identity))
s.Equal(generatedSecret.Key, secretResponse[0].Key)
s.Require().NoError(s.alice.Stop())
}

View File

@ -46,7 +46,7 @@ func (p *Publisher) Start() <-chan struct{} {
logger.Info("starting publisher")
p.notifyCh = make(chan struct{})
p.notifyCh = make(chan struct{}, 100)
p.quit = make(chan struct{})
go p.tickerLoop()
@ -55,6 +55,10 @@ func (p *Publisher) Start() <-chan struct{} {
}
func (p *Publisher) Stop() {
// If hasn't started, ignore
if p.quit == nil {
return
}
select {
case _, ok := <-p.quit:
if !ok {
@ -101,7 +105,11 @@ func (p *Publisher) notify() error {
return errNotEnoughTimePassed
}
p.notifyCh <- struct{}{}
select {
case p.notifyCh <- struct{}{}:
default:
p.logger.Warn("publisher channel full, dropping message")
}
p.persistence.setLastPublished(now)
return nil

View File

@ -56,6 +56,7 @@ var (
// mailservers because they can also be managed by the user.
type Messenger struct {
node types.Node
config *config
identity *ecdsa.PrivateKey
persistence *sqlitePersistence
transport transport.Transport
@ -127,50 +128,6 @@ func NewMessenger(
}
}
onNewInstallationsHandler := func(installations []*multidevice.Installation) {
for _, installation := range installations {
if installation.Identity == contactIDFromPublicKey(&messenger.identity.PublicKey) {
if _, ok := messenger.allInstallations[installation.ID]; !ok {
messenger.allInstallations[installation.ID] = installation
messenger.modifiedInstallations[installation.ID] = true
}
}
}
}
// Set default config fields.
onNewSharedSecretHandler := func(secrets []*sharedsecret.Secret) {
filters, err := messenger.handleSharedSecrets(secrets)
if err != nil {
slogger := logger.With(zap.String("site", "onNewSharedSecretHandler"))
slogger.Warn("failed to process secrets", zap.Error(err))
}
if c.onNegotiatedFilters != nil {
c.onNegotiatedFilters(filters)
}
}
if c.onSendContactCodeHandler == nil {
c.onSendContactCodeHandler = func(messageSpec *encryption.ProtocolMessageSpec) {
slogger := logger.With(zap.String("site", "onSendContactCodeHandler"))
slogger.Debug("received a SendContactCode request")
newMessage, err := common.MessageSpecToWhisper(messageSpec)
if err != nil {
slogger.Warn("failed to convert spec to Whisper message", zap.Error(err))
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
chatName := transport.ContactCodeTopic(&messenger.identity.PublicKey)
_, err = messenger.transport.SendPublic(ctx, newMessage, chatName)
if err != nil {
slogger.Warn("failed to send a contact code", zap.Error(err))
}
}
}
if c.systemMessagesTranslations == nil {
c.systemMessagesTranslations = defaultSystemMessagesTranslations
}
@ -232,9 +189,6 @@ func NewMessenger(
encryptionProtocol := encryption.New(
database,
installationID,
onNewInstallationsHandler,
onNewSharedSecretHandler,
c.onSendContactCodeHandler,
logger,
)
@ -267,8 +221,6 @@ func NewMessenger(
// Overriding until we handle different identities
pushNotificationClientConfig.Identity = identity
// Hardcoding this for now, as it's the only one we support
pushNotificationClientConfig.TokenType = protobuf.PushNotificationRegistration_APN_TOKEN
pushNotificationClientConfig.Logger = logger
pushNotificationClientConfig.InstallationID = installationID
@ -277,6 +229,7 @@ func NewMessenger(
handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database})
messenger = &Messenger{
config: &c,
node: node,
identity: identity,
persistence: &sqlitePersistence{db: database},
@ -299,6 +252,7 @@ func NewMessenger(
shutdownTasks: []func() error{
database.Close,
pushNotificationClient.Stop,
encryptionProtocol.Stop,
transp.ResetFilters,
transp.Stop,
func() error { processor.Stop(); return nil },
@ -325,12 +279,147 @@ func (m *Messenger) Start() error {
// Start push notification client
if m.pushNotificationClient != nil {
m.handlePushNotificationClientRegistrations(m.pushNotificationClient.SubscribeToRegistrations())
if err := m.pushNotificationClient.Start(); err != nil {
return err
}
}
return m.encryptor.Start(m.identity)
// set shared secret handles
m.processor.SetHandleSharedSecrets(m.handleSharedSecrets)
subscriptions, err := m.encryptor.Start(m.identity)
if err != nil {
return err
}
// handle stored shared secrets
err = m.handleSharedSecrets(subscriptions.SharedSecrets)
if err != nil {
return err
}
m.handleEncryptionLayerSubscriptions(subscriptions)
return nil
}
func (m *Messenger) buildContactCodeAdvertisement() (*protobuf.ContactCodeAdvertisement, error) {
if m.pushNotificationClient == nil || !m.pushNotificationClient.Enabled() {
return nil, nil
}
m.logger.Debug("adding push notification info to contact code bundle")
info, err := m.pushNotificationClient.MyPushNotificationQueryInfo()
if err != nil {
return nil, err
}
if len(info) == 0 {
return nil, nil
}
return &protobuf.ContactCodeAdvertisement{
PushNotificationInfo: info,
}, nil
}
// handleSendContactCode sends a public message wrapped in the encryption
// layer, which will propagate our bundle
func (m *Messenger) handleSendContactCode() error {
var payload []byte
m.logger.Debug("sending contact code")
contactCodeAdvertisement, err := m.buildContactCodeAdvertisement()
if err != nil {
m.logger.Error("could not build contact code advertisement", zap.Error(err))
}
if contactCodeAdvertisement != nil {
payload, err = proto.Marshal(contactCodeAdvertisement)
if err != nil {
return err
}
}
contactCodeTopic := transport.ContactCodeTopic(&m.identity.PublicKey)
rawMessage := common.RawMessage{
LocalChatID: contactCodeTopic,
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT,
Payload: payload,
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_, err = m.processor.SendPublic(ctx, contactCodeTopic, rawMessage)
if err != nil {
m.logger.Warn("failed to send a contact code", zap.Error(err))
}
return err
}
// handleSharedSecrets process the negotiated secrets received from the encryption layer
func (m *Messenger) handleSharedSecrets(secrets []*sharedsecret.Secret) error {
logger := m.logger.With(zap.String("site", "handleSharedSecrets"))
var result []*transport.Filter
for _, secret := range secrets {
logger.Debug("received shared secret", zap.Binary("identity", crypto.FromECDSAPub(secret.Identity)))
fSecret := types.NegotiatedSecret{
PublicKey: secret.Identity,
Key: secret.Key,
}
filter, err := m.transport.ProcessNegotiatedSecret(fSecret)
if err != nil {
return err
}
result = append(result, filter)
}
if m.config.onNegotiatedFilters != nil {
m.config.onNegotiatedFilters(result)
}
return nil
}
// handleInstallations adds the installations in the installations map
func (m *Messenger) handleInstallations(installations []*multidevice.Installation) {
for _, installation := range installations {
if installation.Identity == contactIDFromPublicKey(&m.identity.PublicKey) {
if _, ok := m.allInstallations[installation.ID]; !ok {
m.allInstallations[installation.ID] = installation
m.modifiedInstallations[installation.ID] = true
}
}
}
}
// handleEncryptionLayerSubscriptions handles events from the encryption layer
func (m *Messenger) handleEncryptionLayerSubscriptions(subscriptions *encryption.Subscriptions) {
go func() {
for {
select {
case <-subscriptions.SendContactCode:
if err := m.handleSendContactCode(); err != nil {
m.logger.Error("failed to publish contact code", zap.Error(err))
}
case <-subscriptions.Quit:
m.logger.Debug("quitting encryption subscription loop")
return
}
}
}()
}
// handlePushNotificationClientRegistration handles registration events
func (m *Messenger) handlePushNotificationClientRegistrations(c chan struct{}) {
go func() {
for {
_, more := <-c
if !more {
return
}
if err := m.handleSendContactCode(); err != nil {
m.logger.Error("failed to publish contact code", zap.Error(err))
}
}
}()
}
// Init analyzes chats and contacts in order to setup filters
@ -436,24 +525,6 @@ func (m *Messenger) Shutdown() (err error) {
return
}
func (m *Messenger) handleSharedSecrets(secrets []*sharedsecret.Secret) ([]*transport.Filter, error) {
logger := m.logger.With(zap.String("site", "handleSharedSecrets"))
var result []*transport.Filter
for _, secret := range secrets {
logger.Debug("received shared secret", zap.Binary("identity", crypto.FromECDSAPub(secret.Identity)))
fSecret := types.NegotiatedSecret{
PublicKey: secret.Identity,
Key: secret.Key,
}
filter, err := m.transport.ProcessNegotiatedSecret(fSecret)
if err != nil {
return nil, err
}
result = append(result, filter)
}
return result, nil
}
func (m *Messenger) EnableInstallation(id string) error {
m.mutex.Lock()
defer m.mutex.Unlock()
@ -1152,7 +1223,7 @@ func (m *Messenger) saveContact(contact *Contact) error {
}
// We check if it should re-register with the push notification server
shouldReregisterForPushNotifications := m.pushNotificationClient != nil && (m.isNewContact(contact) || m.removedContact(contact))
shouldReregisterForPushNotifications := (m.isNewContact(contact) || m.removedContact(contact))
err = m.persistence.SaveContact(contact, nil)
if err != nil {
@ -1163,17 +1234,22 @@ func (m *Messenger) saveContact(contact *Contact) error {
// Reregister only when data has changed
if shouldReregisterForPushNotifications {
m.logger.Info("contact state changed, re-registering for push notification")
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
err := m.pushNotificationClient.Reregister(contactIDs, mutedChatIDs)
if err != nil {
return err
}
return m.reregisterForPushNotifications()
}
return nil
}
func (m *Messenger) reregisterForPushNotifications() error {
m.logger.Info("contact state changed, re-registering for push notification")
if m.pushNotificationClient == nil {
return nil
}
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
return m.pushNotificationClient.Reregister(contactIDs, mutedChatIDs)
}
func (m *Messenger) SaveContact(contact *Contact) error {
m.mutex.Lock()
defer m.mutex.Unlock()
@ -1816,6 +1892,13 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
for _, msg := range statusMessages {
publicKey := msg.SigPubKey()
m.handleInstallations(msg.Installations)
err := m.handleSharedSecrets(msg.SharedSecrets)
if err != nil {
// log and continue, non-critical error
logger.Warn("failed to handle shared secrets")
}
// Check for messages from blocked users
senderID := contactIDFromPublicKey(publicKey)
if _, ok := messageState.AllContacts[senderID]; ok && messageState.AllContacts[senderID].IsBlocked() {
@ -1828,6 +1911,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
logger.Warn("failed to check message exists", zap.Error(err))
}
if exists {
logger.Debug("messageExists", zap.String("messageID", messageID))
continue
}
@ -1999,6 +2083,18 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
}
// We continue in any case, no changes to messenger
continue
case protobuf.ContactCodeAdvertisement:
logger.Debug("Received ContactCodeAdvertisement")
if m.pushNotificationClient == nil {
continue
}
logger.Debug("Handling ContactCodeAdvertisement")
if err := m.pushNotificationClient.HandleContactCodeAdvertisement(publicKey, msg.ParsedMessage.Interface().(protobuf.ContactCodeAdvertisement)); err != nil {
logger.Warn("failed to handle ContactCodeAdvertisement", zap.Error(err))
}
// We continue in any case, no changes to messenger
continue
case protobuf.PushNotificationResponse:
logger.Debug("Received PushNotificationResponse")
if m.pushNotificationClient == nil {
@ -2060,6 +2156,8 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
logger.Debug("message not handled", zap.Any("messageType", reflect.TypeOf(msg.ParsedMessage.Interface())))
}
} else {
logger.Debug("parsed message is nil")
}
}
}
@ -2251,7 +2349,8 @@ func (m *Messenger) MuteChat(chatID string) error {
chat.Muted = true
m.allChats[chat.ID] = chat
return nil
return m.reregisterForPushNotifications()
}
// UnmuteChat signals to the messenger that we want to be notified
@ -2271,7 +2370,7 @@ func (m *Messenger) UnmuteChat(chatID string) error {
chat.Muted = false
m.allChats[chat.ID] = chat
return nil
return m.reregisterForPushNotifications()
}
func (m *Messenger) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) error {
@ -3146,7 +3245,7 @@ func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string
}
// RegisterForPushNotification register deviceToken with any push notification server enabled
func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) error {
func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType) error {
if m.pushNotificationClient == nil {
return errors.New("push notification client not enabled")
}
@ -3154,7 +3253,12 @@ func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToke
defer m.mutex.Unlock()
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs)
err := m.pushNotificationClient.Register(deviceToken, apnTopic, tokenType, contactIDs, mutedChatIDs)
if err != nil {
m.logger.Error("failed to register for push notifications", zap.Error(err))
return err
}
return nil
}
// RegisteredForPushNotifications returns whether we successfully registered with all the servers

View File

@ -6,7 +6,6 @@ import (
"go.uber.org/zap"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/encryption"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/pushnotificationclient"
"github.com/status-im/status-go/protocol/pushnotificationserver"
@ -18,8 +17,6 @@ type config struct {
// as otherwise the client is not notified of a new filter and
// won't be pulling messages from mailservers until it reloads the chats/filters
onNegotiatedFilters func([]*transport.Filter)
// DEPRECATED: no need to expose it
onSendContactCodeHandler func(*encryption.ProtocolMessageSpec)
// systemMessagesTranslations holds translations for system-messages
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string

View File

@ -44,6 +44,11 @@ func (s *MessengerContactUpdateSuite) SetupTest() {
s.m = s.newMessenger(s.shh)
s.privateKey = s.m.identity
s.Require().NoError(s.m.Start())
}
func (s *MessengerContactUpdateSuite) TearDownTest() {
s.Require().NoError(s.m.Shutdown())
}
func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
@ -85,6 +90,7 @@ func (s *MessengerContactUpdateSuite) TestReceiveContactUpdate() {
contactID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey))
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirContactID := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
response, err := theirMessenger.SendContactUpdate(context.Background(), contactID, theirName, theirPicture)
@ -136,4 +142,5 @@ func (s *MessengerContactUpdateSuite) TestReceiveContactUpdate() {
s.Require().False(receivedContact.ENSVerified)
s.Require().True(receivedContact.HasBeenAdded())
s.Require().NotEmpty(receivedContact.LastUpdated)
s.Require().NoError(theirMessenger.Shutdown())
}

View File

@ -47,6 +47,11 @@ func (s *MessengerEmojiSuite) SetupTest() {
s.m = s.newMessenger(s.shh)
s.privateKey = s.m.identity
s.Require().NoError(s.m.Start())
}
func (s *MessengerEmojiSuite) TearDownTest() {
s.Require().NoError(s.m.Shutdown())
}
func (s *MessengerEmojiSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
@ -89,6 +94,7 @@ func (s *MessengerEmojiSuite) TestSendEmoji() {
s.Require().NoError(err)
bob := s.newMessengerWithKey(s.shh, key)
s.Require().NoError(bob.Start())
chatID := statusChatID
@ -162,11 +168,13 @@ func (s *MessengerEmojiSuite) TestSendEmoji() {
s.Require().Equal(response.EmojiReactions[0].ID(), emojiID)
s.Require().Equal(response.EmojiReactions[0].Type, protobuf.EmojiReaction_SAD)
s.Require().True(response.EmojiReactions[0].Retracted)
s.Require().NoError(bob.Shutdown())
}
func (s *MessengerEmojiSuite) TestEmojiPrivateGroup() {
bob := s.m
alice := s.newMessenger(s.shh)
s.Require().NoError(alice.Start())
response, err := bob.CreateGroupChatWithMembers(context.Background(), "test", []string{})
s.NoError(err)
@ -217,4 +225,5 @@ func (s *MessengerEmojiSuite) TestEmojiPrivateGroup() {
"no emoji reaction received",
)
s.Require().NoError(err)
s.Require().NoError(alice.Shutdown())
}

View File

@ -50,6 +50,12 @@ func (s *MessengerInstallationSuite) SetupTest() {
s.m = s.newMessenger(s.shh)
s.privateKey = s.m.identity
// We start the messenger in order to receive installations
s.Require().NoError(s.m.Start())
}
func (s *MessengerInstallationSuite) TearDownTest() {
s.Require().NoError(s.m.Shutdown())
}
func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
@ -88,6 +94,7 @@ func (s *MessengerInstallationSuite) newMessenger(shh types.Waku) *Messenger {
func (s *MessengerInstallationSuite) TestReceiveInstallation() {
theirMessenger := s.newMessengerWithKey(s.shh, s.privateKey)
s.Require().NoError(theirMessenger.Start())
err := theirMessenger.SetInstallationMetadata(theirMessenger.installationID, &multidevice.InstallationMetadata{
Name: "their-name",
@ -153,6 +160,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() {
actualChat := response.Chats[0]
s.Require().Equal(statusChatID, actualChat.ID)
s.Require().True(actualChat.Active)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerInstallationSuite) TestSyncInstallation() {
@ -174,6 +182,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
// pair
theirMessenger := s.newMessengerWithKey(s.shh, s.privateKey)
s.Require().NoError(theirMessenger.Start())
err = theirMessenger.SetInstallationMetadata(theirMessenger.installationID, &multidevice.InstallationMetadata{
Name: "their-name",
@ -240,6 +249,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
s.Require().NotNil(statusChat)
s.Require().True(actualContact.IsAdded())
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerInstallationSuite) TestSyncInstallationNewMessages() {
@ -247,7 +257,9 @@ func (s *MessengerInstallationSuite) TestSyncInstallationNewMessages() {
bob1 := s.m
// pair
bob2 := s.newMessengerWithKey(s.shh, s.privateKey)
s.Require().NoError(bob2.Start())
alice := s.newMessenger(s.shh)
s.Require().NoError(alice.Start())
err := bob2.SetInstallationMetadata(bob2.installationID, &multidevice.InstallationMetadata{
Name: "their-name",
@ -290,4 +302,6 @@ func (s *MessengerInstallationSuite) TestSyncInstallationNewMessages() {
"message not received",
)
s.Require().NoError(err)
s.Require().NoError(bob2.Shutdown())
s.Require().NoError(alice.Shutdown())
}

View File

@ -45,6 +45,11 @@ func (s *MessengerMuteSuite) SetupTest() {
s.m = s.newMessenger(s.shh)
s.privateKey = s.m.identity
s.Require().NoError(s.m.Start())
}
func (s *MessengerMuteSuite) TearDownTest() {
s.Require().NoError(s.m.Shutdown())
}
func (s *MessengerMuteSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
@ -86,6 +91,7 @@ func (s *MessengerMuteSuite) TestSetMute() {
s.Require().NoError(err)
theirMessenger := s.newMessengerWithKey(s.shh, key)
s.Require().NoError(theirMessenger.Start())
chatID := "status"
@ -107,4 +113,5 @@ func (s *MessengerMuteSuite) TestSetMute() {
s.Require().NoError(s.m.UnmuteChat(chatID))
s.Require().False(s.m.Chats()[0].Muted)
s.Require().NoError(theirMessenger.Shutdown())
}

View File

@ -102,6 +102,7 @@ func (s *MessengerSuite) SetupTest() {
s.m = s.newMessenger(s.shh)
s.privateKey = s.m.identity
s.Require().NoError(s.m.Start())
}
func (s *MessengerSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
@ -503,6 +504,7 @@ func (s *MessengerSuite) TestRetrieveOwnPublic() {
// Retrieve their public message
func (s *MessengerSuite) TestRetrieveTheirPublic() {
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirChat := CreatePublicChat("status", s.m.transport)
err := theirMessenger.SaveChat(&theirChat)
s.Require().NoError(err)
@ -539,10 +541,12 @@ func (s *MessengerSuite) TestRetrieveTheirPublic() {
s.Require().Equal(sentMessage.Clock, actualChat.LastClockValue)
// It sets the last message
s.Require().NotNil(actualChat.LastMessage)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerSuite) TestDeletedAtClockValue() {
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirChat := CreatePublicChat("status", s.m.transport)
err := theirMessenger.SaveChat(&theirChat)
s.Require().NoError(err)
@ -568,10 +572,12 @@ func (s *MessengerSuite) TestDeletedAtClockValue() {
response, err := s.m.RetrieveAll()
s.Require().NoError(err)
s.Require().Len(response.Messages, 0)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerSuite) TestRetrieveBlockedContact() {
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirChat := CreatePublicChat("status", s.m.transport)
err := theirMessenger.SaveChat(&theirChat)
s.Require().NoError(err)
@ -610,6 +616,7 @@ func (s *MessengerSuite) TestRetrieveBlockedContact() {
// Resend their public message, receive only once
func (s *MessengerSuite) TestResendPublicMessage() {
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirChat := CreatePublicChat("status", s.m.transport)
err := theirMessenger.SaveChat(&theirChat)
s.Require().NoError(err)
@ -663,6 +670,7 @@ func (s *MessengerSuite) TestResendPublicMessage() {
// Test receiving a message on an existing private chat
func (s *MessengerSuite) TestRetrieveTheirPrivateChatExisting() {
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirChat := CreateOneToOneChat("XXX", &s.privateKey.PublicKey, s.m.transport)
err := theirMessenger.SaveChat(&theirChat)
s.Require().NoError(err)
@ -698,11 +706,13 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateChatExisting() {
// It sets the last message
s.Require().NotNil(actualChat.LastMessage)
s.Require().True(actualChat.Active)
s.Require().NoError(theirMessenger.Shutdown())
}
// Test receiving a message on an non-existing private chat
func (s *MessengerSuite) TestRetrieveTheirPrivateChatNonExisting() {
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
chat := CreateOneToOneChat("XXX", &s.privateKey.PublicKey, s.m.transport)
err := theirMessenger.SaveChat(&chat)
s.NoError(err)
@ -739,6 +749,7 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateChatNonExisting() {
// Test receiving a message on an non-existing public chat
func (s *MessengerSuite) TestRetrieveTheirPublicChatNonExisting() {
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
chat := CreatePublicChat("test-chat", s.m.transport)
err := theirMessenger.SaveChat(&chat)
s.NoError(err)
@ -756,12 +767,14 @@ func (s *MessengerSuite) TestRetrieveTheirPublicChatNonExisting() {
s.Require().Equal(len(response.Messages), 0)
s.Require().Equal(len(response.Chats), 0)
s.Require().NoError(theirMessenger.Shutdown())
}
// Test receiving a message on an existing private group chat
func (s *MessengerSuite) TestRetrieveTheirPrivateGroupChat() {
var response *MessengerResponse
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
response, err := s.m.CreateGroupChatWithMembers(context.Background(), "id", []string{})
s.NoError(err)
s.Require().Len(response.Chats, 1)
@ -823,6 +836,7 @@ func (s *MessengerSuite) TestRetrieveTheirPrivateGroupChat() {
func (s *MessengerSuite) TestChangeNameGroupChat() {
var response *MessengerResponse
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
response, err := s.m.CreateGroupChatWithMembers(context.Background(), "old-name", []string{})
s.NoError(err)
s.Require().Len(response.Chats, 1)
@ -869,12 +883,14 @@ func (s *MessengerSuite) TestChangeNameGroupChat() {
s.Require().Len(response.Chats, 1)
actualChat := response.Chats[0]
s.Require().Equal(newName, actualChat.Name)
s.Require().NoError(theirMessenger.Shutdown())
}
// Test being re-invited to a group chat
func (s *MessengerSuite) TestReInvitedToGroupChat() {
var response *MessengerResponse
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
response, err := s.m.CreateGroupChatWithMembers(context.Background(), "old-name", []string{})
s.NoError(err)
s.Require().Len(response.Chats, 1)
@ -928,6 +944,7 @@ func (s *MessengerSuite) TestReInvitedToGroupChat() {
s.Require().Len(response.Chats, 1)
s.Require().True(response.Chats[0].Active)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerSuite) TestChatPersistencePublic() {
@ -1388,7 +1405,7 @@ func (s *MessengerSuite) TestContactPersistenceUpdate() {
}
func (s *MessengerSuite) TestSharedSecretHandler() {
_, err := s.m.handleSharedSecrets(nil)
err := s.m.handleSharedSecrets(nil)
s.NoError(err)
}
@ -1434,6 +1451,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() {
value := testValue
contract := testContract
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
chat := CreateOneToOneChat(theirPkString, &theirMessenger.identity.PublicKey, s.m.transport)
@ -1516,6 +1534,7 @@ func (s *MessengerSuite) TestDeclineRequestAddressForTransaction() {
s.Require().Equal(CommandStateRequestAddressForTransactionDeclined, receiverMessage.CommandParameters.CommandState)
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
s.Require().Equal(initialCommandID, receiverMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerSuite) TestSendEthTransaction() {
@ -1523,6 +1542,7 @@ func (s *MessengerSuite) TestSendEthTransaction() {
contract := testContract
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
receiverAddress := crypto.PubkeyToAddress(theirMessenger.identity.PublicKey)
@ -1617,6 +1637,7 @@ func (s *MessengerSuite) TestSendEthTransaction() {
s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState)
s.Require().Equal(senderMessage.ID, receiverMessage.ID)
s.Require().Equal("", receiverMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerSuite) TestSendTokenTransaction() {
@ -1624,6 +1645,7 @@ func (s *MessengerSuite) TestSendTokenTransaction() {
contract := testContract
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
receiverAddress := crypto.PubkeyToAddress(theirMessenger.identity.PublicKey)
@ -1718,12 +1740,14 @@ func (s *MessengerSuite) TestSendTokenTransaction() {
s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState)
s.Require().Equal(senderMessage.ID, receiverMessage.ID)
s.Require().Equal(senderMessage.Replace, senderMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() {
value := testValue
contract := testContract
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
myAddress := crypto.PubkeyToAddress(s.m.identity.PublicKey)
@ -1808,6 +1832,7 @@ func (s *MessengerSuite) TestAcceptRequestAddressForTransaction() {
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
s.Require().Equal("some-address", receiverMessage.CommandParameters.Address)
s.Require().Equal(initialCommandID, receiverMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerSuite) TestDeclineRequestTransaction() {
@ -1816,6 +1841,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() {
receiverAddress := crypto.PubkeyToAddress(s.m.identity.PublicKey)
receiverAddressString := strings.ToLower(receiverAddress.Hex())
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
chat := CreateOneToOneChat(theirPkString, &theirMessenger.identity.PublicKey, s.m.transport)
@ -1895,6 +1921,7 @@ func (s *MessengerSuite) TestDeclineRequestTransaction() {
s.Require().Equal(initialCommandID, receiverMessage.CommandParameters.ID)
s.Require().Equal(initialCommandID, receiverMessage.Replace)
s.Require().Equal(CommandStateRequestTransactionDeclined, receiverMessage.CommandParameters.CommandState)
s.Require().NoError(theirMessenger.Shutdown())
}
func (s *MessengerSuite) TestRequestTransaction() {
@ -1903,6 +1930,7 @@ func (s *MessengerSuite) TestRequestTransaction() {
receiverAddress := crypto.PubkeyToAddress(s.m.identity.PublicKey)
receiverAddressString := strings.ToLower(receiverAddress.Hex())
theirMessenger := s.newMessenger(s.shh)
s.Require().NoError(theirMessenger.Start())
theirPkString := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
chat := CreateOneToOneChat(theirPkString, &theirMessenger.identity.PublicKey, s.m.transport)
@ -2039,6 +2067,7 @@ func (s *MessengerSuite) TestRequestTransaction() {
s.Require().Equal(CommandStateTransactionSent, receiverMessage.CommandParameters.CommandState)
s.Require().Equal(senderMessage.ID, receiverMessage.ID)
s.Require().Equal(senderMessage.Replace, senderMessage.Replace)
s.Require().NoError(theirMessenger.Shutdown())
}
type MockTransaction struct {

View File

@ -82,6 +82,34 @@ func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte,
return fileDescriptor_200acd86044eaa5d, []int{1, 0}
}
type PushNotification_PushNotificationType int32
const (
PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE PushNotification_PushNotificationType = 0
PushNotification_MESSAGE PushNotification_PushNotificationType = 1
PushNotification_MENTION PushNotification_PushNotificationType = 2
)
var PushNotification_PushNotificationType_name = map[int32]string{
0: "UNKNOWN_PUSH_NOTIFICATION_TYPE",
1: "MESSAGE",
2: "MENTION",
}
var PushNotification_PushNotificationType_value = map[string]int32{
"UNKNOWN_PUSH_NOTIFICATION_TYPE": 0,
"MESSAGE": 1,
"MENTION": 2,
}
func (x PushNotification_PushNotificationType) String() string {
return proto.EnumName(PushNotification_PushNotificationType_name, int32(x))
}
func (PushNotification_PushNotificationType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_200acd86044eaa5d, []int{6, 0}
}
type PushNotificationReport_ErrorType int32
const (
@ -125,6 +153,7 @@ type PushNotificationRegistration struct {
Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"`
Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"`
AllowFromContactsOnly bool `protobuf:"varint,11,opt,name=allow_from_contacts_only,json=allowFromContactsOnly,proto3" json:"allow_from_contacts_only,omitempty"`
ApnTopic string `protobuf:"bytes,12,opt,name=apn_topic,json=apnTopic,proto3" json:"apn_topic,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -232,6 +261,13 @@ func (m *PushNotificationRegistration) GetAllowFromContactsOnly() bool {
return false
}
func (m *PushNotificationRegistration) GetApnTopic() string {
if m != nil {
return m.ApnTopic
}
return ""
}
type PushNotificationRegistrationResponse struct {
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"`
@ -508,14 +544,15 @@ func (m *PushNotificationQueryResponse) GetSuccess() bool {
}
type PushNotification struct {
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
Message []byte `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
Message []byte `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"`
Type PushNotification_PushNotificationType `protobuf:"varint,6,opt,name=type,proto3,enum=protobuf.PushNotification_PushNotificationType" json:"type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PushNotification) Reset() { *m = PushNotification{} }
@ -578,6 +615,13 @@ func (m *PushNotification) GetMessage() []byte {
return nil
}
func (m *PushNotification) GetType() PushNotification_PushNotificationType {
if m != nil {
return m.Type
}
return PushNotification_UNKNOWN_PUSH_NOTIFICATION_TYPE
}
type PushNotificationRequest struct {
Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"`
MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
@ -738,6 +782,7 @@ func (m *PushNotificationResponse) GetReports() []*PushNotificationReport {
func init() {
proto.RegisterEnum("protobuf.PushNotificationRegistration_TokenType", PushNotificationRegistration_TokenType_name, PushNotificationRegistration_TokenType_value)
proto.RegisterEnum("protobuf.PushNotificationRegistrationResponse_ErrorType", PushNotificationRegistrationResponse_ErrorType_name, PushNotificationRegistrationResponse_ErrorType_value)
proto.RegisterEnum("protobuf.PushNotification_PushNotificationType", PushNotification_PushNotificationType_name, PushNotification_PushNotificationType_value)
proto.RegisterEnum("protobuf.PushNotificationReport_ErrorType", PushNotificationReport_ErrorType_name, PushNotificationReport_ErrorType_value)
proto.RegisterType((*PushNotificationRegistration)(nil), "protobuf.PushNotificationRegistration")
proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse")
@ -754,60 +799,65 @@ func init() {
func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) }
var fileDescriptor_200acd86044eaa5d = []byte{
// 878 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x6f, 0xeb, 0x44,
0x17, 0xfd, 0x9c, 0xa4, 0x4d, 0x72, 0x93, 0x2f, 0x4d, 0x47, 0x6d, 0x9f, 0x79, 0xa2, 0x10, 0x0c,
0x12, 0x51, 0x17, 0x11, 0x2a, 0x12, 0xef, 0x89, 0x15, 0xa1, 0x75, 0x8b, 0xd5, 0xc6, 0x0e, 0x13,
0x97, 0xa7, 0x27, 0x21, 0x59, 0x8e, 0x3d, 0x69, 0xad, 0xba, 0x1e, 0x33, 0x33, 0x2e, 0xca, 0x8e,
0x1f, 0xc0, 0x86, 0x2d, 0x1b, 0xfe, 0x02, 0xe2, 0x17, 0x22, 0x8f, 0xed, 0xe0, 0x36, 0x6e, 0x5a,
0x24, 0x56, 0xf6, 0x9c, 0xb9, 0xf7, 0xce, 0xcc, 0x39, 0xf7, 0x5c, 0x50, 0xe3, 0x84, 0xdf, 0x38,
0x11, 0x15, 0xc1, 0x22, 0xf0, 0x5c, 0x11, 0xd0, 0x88, 0x8f, 0x62, 0x46, 0x05, 0x45, 0x2d, 0xf9,
0x99, 0x27, 0x0b, 0xed, 0x8f, 0x06, 0x7c, 0x38, 0x4d, 0xf8, 0x8d, 0x59, 0x8a, 0xc2, 0xe4, 0x3a,
0xe0, 0x82, 0xc9, 0x7f, 0x64, 0x01, 0x08, 0x7a, 0x4b, 0x22, 0x47, 0x2c, 0x63, 0xa2, 0x2a, 0x03,
0x65, 0xd8, 0x3b, 0xfe, 0x62, 0x54, 0xe4, 0x8f, 0x36, 0xe5, 0x8e, 0xec, 0x34, 0xd1, 0x5e, 0xc6,
0x04, 0xb7, 0x45, 0xf1, 0x8b, 0x3e, 0x81, 0xae, 0x4f, 0xee, 0x03, 0x8f, 0x38, 0x12, 0x53, 0x6b,
0x03, 0x65, 0xd8, 0xc6, 0x9d, 0x0c, 0x93, 0x19, 0xe8, 0x73, 0xd8, 0x09, 0x22, 0x2e, 0xdc, 0x30,
0x94, 0x75, 0x9c, 0xc0, 0x57, 0xeb, 0x32, 0xaa, 0x57, 0x86, 0x0d, 0x3f, 0xad, 0xe5, 0x7a, 0x1e,
0xe1, 0x3c, 0xaf, 0xd5, 0xc8, 0x6a, 0x65, 0x58, 0x56, 0x4b, 0x85, 0x26, 0x89, 0xdc, 0x79, 0x48,
0x7c, 0x75, 0x6b, 0xa0, 0x0c, 0x5b, 0xb8, 0x58, 0xa6, 0x3b, 0xf7, 0x84, 0xf1, 0x80, 0x46, 0xea,
0xf6, 0x40, 0x19, 0x36, 0x70, 0xb1, 0x44, 0x43, 0xe8, 0xbb, 0x61, 0x48, 0x7f, 0x26, 0xbe, 0x73,
0x4b, 0x96, 0x4e, 0x18, 0x70, 0xa1, 0x36, 0x07, 0xf5, 0x61, 0x17, 0xf7, 0x72, 0xfc, 0x82, 0x2c,
0x2f, 0x03, 0x2e, 0xd0, 0x11, 0xec, 0xce, 0x43, 0xea, 0xdd, 0x12, 0xdf, 0xf1, 0x6e, 0x5c, 0x91,
0x85, 0xb6, 0x64, 0xe8, 0x4e, 0xbe, 0x71, 0x72, 0xe3, 0x0a, 0x19, 0xfb, 0x11, 0x40, 0x12, 0x31,
0xc9, 0x0f, 0x61, 0x6a, 0x5b, 0x5e, 0xa6, 0x84, 0xa0, 0x3d, 0xd8, 0xba, 0x66, 0x6e, 0x24, 0x54,
0x18, 0x28, 0xc3, 0x2e, 0xce, 0x16, 0xe8, 0x0d, 0xa8, 0xf2, 0x4c, 0x67, 0xc1, 0xe8, 0x9d, 0xe3,
0xd1, 0x48, 0xb8, 0x9e, 0xe0, 0x0e, 0x8d, 0xc2, 0xa5, 0xda, 0x91, 0x35, 0xf6, 0xe5, 0xfe, 0x19,
0xa3, 0x77, 0x27, 0xf9, 0xae, 0x15, 0x85, 0x4b, 0xed, 0x0c, 0xda, 0x2b, 0xfe, 0xd1, 0x01, 0xa0,
0x2b, 0xf3, 0xc2, 0xb4, 0xde, 0x99, 0x8e, 0x6d, 0x5d, 0xe8, 0xa6, 0x63, 0xbf, 0x9f, 0xea, 0xfd,
0xff, 0xa1, 0xff, 0x43, 0x7b, 0x3c, 0xcd, 0xb1, 0xbe, 0x82, 0x10, 0xf4, 0xce, 0x0c, 0xac, 0x7f,
0x3b, 0x9e, 0xe9, 0x39, 0x56, 0xd3, 0xfe, 0xaa, 0xc1, 0x67, 0x9b, 0x54, 0xc6, 0x84, 0xc7, 0x34,
0xe2, 0x24, 0xe5, 0x93, 0x27, 0x92, 0x79, 0xd9, 0x26, 0x2d, 0x5c, 0x2c, 0x91, 0x09, 0x5b, 0x84,
0x31, 0xca, 0xa4, 0xd6, 0xbd, 0xe3, 0xb7, 0x2f, 0x6b, 0x9f, 0xa2, 0xf0, 0x48, 0x4f, 0x73, 0x65,
0x1b, 0x65, 0x65, 0xd0, 0x21, 0x00, 0x23, 0x3f, 0x25, 0x84, 0x8b, 0xa2, 0x35, 0xba, 0xb8, 0x9d,
0x23, 0x86, 0xaf, 0xfd, 0xa2, 0x40, 0x7b, 0x95, 0x53, 0x7e, 0xba, 0x8e, 0xb1, 0x85, 0x8b, 0xa7,
0xef, 0xc3, 0xee, 0x64, 0x7c, 0x79, 0x66, 0xe1, 0x89, 0x7e, 0xea, 0x4c, 0xf4, 0xd9, 0x6c, 0x7c,
0xae, 0xf7, 0x15, 0xb4, 0x07, 0xfd, 0x1f, 0x74, 0x3c, 0x33, 0x2c, 0xd3, 0x99, 0x18, 0xb3, 0xc9,
0xd8, 0x3e, 0xf9, 0xae, 0x5f, 0x43, 0xaf, 0xe1, 0xe0, 0xca, 0x9c, 0x5d, 0x4d, 0xa7, 0x16, 0xb6,
0xf5, 0xd3, 0x32, 0x87, 0xf5, 0x94, 0x34, 0xc3, 0xb4, 0x75, 0x6c, 0x8e, 0x2f, 0xb3, 0x13, 0xfa,
0x0d, 0x2d, 0x01, 0x35, 0x17, 0xe3, 0x84, 0xfa, 0x64, 0xec, 0xdf, 0x13, 0x26, 0x02, 0x4e, 0xee,
0x48, 0x24, 0xd0, 0x7b, 0x38, 0x58, 0x33, 0xa6, 0x13, 0x44, 0x0b, 0xaa, 0x2a, 0x83, 0xfa, 0xb0,
0x73, 0xfc, 0xe9, 0xd3, 0xf4, 0x7c, 0x9f, 0x10, 0xb6, 0x34, 0xa2, 0x05, 0xc5, 0x7b, 0xf1, 0xa3,
0xad, 0x14, 0xd5, 0xde, 0xc2, 0x7e, 0x65, 0x0a, 0xfa, 0x18, 0x3a, 0x71, 0x32, 0x0f, 0x03, 0x2f,
0x6d, 0x68, 0x2e, 0x0f, 0xea, 0x62, 0xc8, 0xa0, 0x0b, 0xb2, 0xe4, 0xda, 0xaf, 0x35, 0xf8, 0xe0,
0xc9, 0xd3, 0xd6, 0x7c, 0xa6, 0xac, 0xfb, 0xac, 0xc2, 0xb3, 0xb5, 0x4a, 0xcf, 0x1e, 0x02, 0xfc,
0x73, 0x95, 0x42, 0xbc, 0xd5, 0x4d, 0x2a, 0xbd, 0xd7, 0xa8, 0xf4, 0xde, 0xca, 0x2f, 0x5b, 0x65,
0xbf, 0x3c, 0xed, 0xea, 0x23, 0xd8, 0xe5, 0x84, 0xdd, 0x13, 0xe6, 0x94, 0xce, 0x6f, 0xca, 0xdc,
0x9d, 0x6c, 0x63, 0x5a, 0xdc, 0x42, 0xfb, 0x4d, 0x81, 0xc3, 0x4a, 0x3a, 0x56, 0xdd, 0xfe, 0x06,
0x1a, 0xff, 0x56, 0x33, 0x99, 0x90, 0xbe, 0xff, 0x8e, 0x70, 0xee, 0x5e, 0x93, 0x82, 0xa3, 0x2e,
0x6e, 0xe7, 0x88, 0xe1, 0x97, 0x5d, 0x54, 0x7f, 0xe0, 0x22, 0xed, 0x4f, 0x05, 0xfa, 0x8f, 0x8b,
0xbf, 0x44, 0x99, 0x57, 0xd0, 0x94, 0xb3, 0x69, 0xa5, 0xc8, 0x76, 0xba, 0x7c, 0x5e, 0x89, 0x0a,
0x45, 0x1b, 0x95, 0x8a, 0xaa, 0xd0, 0xcc, 0xef, 0x9f, 0x4b, 0x51, 0x2c, 0xb5, 0x18, 0x5e, 0xad,
0x3b, 0x5c, 0xda, 0x14, 0x7d, 0x05, 0xad, 0xdc, 0xb1, 0x3c, 0xe7, 0xf0, 0xf5, 0x86, 0xb1, 0xb0,
0x8a, 0x7d, 0x86, 0x3e, 0xed, 0xf7, 0x1a, 0x1c, 0xac, 0x1f, 0x19, 0x53, 0x26, 0x36, 0xcc, 0xa7,
0x6f, 0x1e, 0xce, 0xa7, 0xa3, 0x4d, 0xf3, 0x29, 0x2d, 0x55, 0x39, 0x91, 0xfe, 0x0b, 0x2a, 0xb5,
0x1f, 0x5f, 0x32, 0xb9, 0x76, 0xa0, 0xf3, 0x0e, 0x5b, 0xe6, 0x79, 0x79, 0x6c, 0x3f, 0x9a, 0x40,
0xb5, 0x14, 0x33, 0x2d, 0xdb, 0xc1, 0xfa, 0xb9, 0x31, 0xb3, 0x75, 0xac, 0x9f, 0xf6, 0xeb, 0xe9,
0x54, 0x5a, 0x7f, 0x50, 0xde, 0xcf, 0x0f, 0x79, 0x55, 0x1e, 0xb7, 0xe5, 0xd7, 0xd0, 0x64, 0xf2,
0xed, 0x5c, 0xad, 0x49, 0xb5, 0x06, 0xcf, 0x91, 0x84, 0x8b, 0x84, 0xf9, 0xb6, 0x8c, 0xfc, 0xf2,
0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6f, 0xe8, 0xb9, 0x16, 0x90, 0x08, 0x00, 0x00,
// 952 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdd, 0x6e, 0xe3, 0x44,
0x14, 0xc6, 0x4e, 0xda, 0x24, 0x27, 0x21, 0x75, 0x47, 0x6d, 0xd7, 0x2c, 0x74, 0x09, 0x06, 0x89,
0xa8, 0x17, 0x01, 0x15, 0x89, 0x5d, 0x71, 0x45, 0x48, 0x9d, 0xae, 0xd5, 0xc6, 0x0e, 0x13, 0x97,
0xd5, 0x4a, 0x48, 0x96, 0x63, 0x4f, 0x5a, 0xab, 0xae, 0xc7, 0x78, 0x26, 0x45, 0xb9, 0xe3, 0x01,
0xb8, 0xe1, 0x96, 0xc7, 0xe0, 0x9a, 0x37, 0xe0, 0xa5, 0x90, 0xc7, 0x76, 0x9a, 0x36, 0x6e, 0x5a,
0x24, 0xae, 0x92, 0xf3, 0x9d, 0x9f, 0x99, 0x39, 0xdf, 0xf9, 0x8e, 0x41, 0x8d, 0xe7, 0xec, 0xca,
0x89, 0x28, 0x0f, 0x66, 0x81, 0xe7, 0xf2, 0x80, 0x46, 0xac, 0x17, 0x27, 0x94, 0x53, 0x54, 0x17,
0x3f, 0xd3, 0xf9, 0x4c, 0xfb, 0xbb, 0x0a, 0x9f, 0x8c, 0xe7, 0xec, 0xca, 0x5c, 0x89, 0xc2, 0xe4,
0x32, 0x60, 0x3c, 0x11, 0xff, 0x91, 0x05, 0xc0, 0xe9, 0x35, 0x89, 0x1c, 0xbe, 0x88, 0x89, 0x2a,
0x75, 0xa4, 0x6e, 0xfb, 0xf8, 0xeb, 0x5e, 0x91, 0xdf, 0xdb, 0x94, 0xdb, 0xb3, 0xd3, 0x44, 0x7b,
0x11, 0x13, 0xdc, 0xe0, 0xc5, 0x5f, 0xf4, 0x19, 0xb4, 0x7c, 0x72, 0x1b, 0x78, 0xc4, 0x11, 0x98,
0x2a, 0x77, 0xa4, 0x6e, 0x03, 0x37, 0x33, 0x4c, 0x64, 0xa0, 0x2f, 0x61, 0x27, 0x88, 0x18, 0x77,
0xc3, 0x50, 0xd4, 0x71, 0x02, 0x5f, 0xad, 0x88, 0xa8, 0xf6, 0x2a, 0x6c, 0xf8, 0x69, 0x2d, 0xd7,
0xf3, 0x08, 0x63, 0x79, 0xad, 0x6a, 0x56, 0x2b, 0xc3, 0xb2, 0x5a, 0x2a, 0xd4, 0x48, 0xe4, 0x4e,
0x43, 0xe2, 0xab, 0x5b, 0x1d, 0xa9, 0x5b, 0xc7, 0x85, 0x99, 0x7a, 0x6e, 0x49, 0xc2, 0x02, 0x1a,
0xa9, 0xdb, 0x1d, 0xa9, 0x5b, 0xc5, 0x85, 0x89, 0xba, 0xa0, 0xb8, 0x61, 0x48, 0x7f, 0x25, 0xbe,
0x73, 0x4d, 0x16, 0x4e, 0x18, 0x30, 0xae, 0xd6, 0x3a, 0x95, 0x6e, 0x0b, 0xb7, 0x73, 0xfc, 0x8c,
0x2c, 0xce, 0x03, 0xc6, 0xd1, 0x11, 0xec, 0x4e, 0x43, 0xea, 0x5d, 0x13, 0xdf, 0xf1, 0xae, 0x5c,
0x9e, 0x85, 0xd6, 0x45, 0xe8, 0x4e, 0xee, 0x18, 0x5c, 0xb9, 0x5c, 0xc4, 0xbe, 0x02, 0x98, 0x47,
0x89, 0xe8, 0x0f, 0x49, 0xd4, 0x86, 0xb8, 0xcc, 0x0a, 0x82, 0xf6, 0x60, 0xeb, 0x32, 0x71, 0x23,
0xae, 0x42, 0x47, 0xea, 0xb6, 0x70, 0x66, 0xa0, 0xd7, 0xa0, 0x8a, 0x33, 0x9d, 0x59, 0x42, 0x6f,
0x1c, 0x8f, 0x46, 0xdc, 0xf5, 0x38, 0x73, 0x68, 0x14, 0x2e, 0xd4, 0xa6, 0xa8, 0xb1, 0x2f, 0xfc,
0xc3, 0x84, 0xde, 0x0c, 0x72, 0xaf, 0x15, 0x85, 0x0b, 0xf4, 0x31, 0x34, 0xdc, 0x38, 0x72, 0x38,
0x8d, 0x03, 0x4f, 0x6d, 0x89, 0xc6, 0xd4, 0xdd, 0x38, 0xb2, 0x53, 0x5b, 0x1b, 0x42, 0x63, 0x49,
0x0e, 0x3a, 0x00, 0x74, 0x61, 0x9e, 0x99, 0xd6, 0x3b, 0xd3, 0xb1, 0xad, 0x33, 0xdd, 0x74, 0xec,
0xf7, 0x63, 0x5d, 0xf9, 0x00, 0x7d, 0x08, 0x8d, 0xfe, 0x38, 0xc7, 0x14, 0x09, 0x21, 0x68, 0x0f,
0x0d, 0xac, 0xff, 0xd0, 0x9f, 0xe8, 0x39, 0x26, 0x6b, 0x7f, 0xc9, 0xf0, 0xc5, 0xa6, 0x11, 0xc0,
0x84, 0xc5, 0x34, 0x62, 0x24, 0x6d, 0x36, 0x9b, 0x0b, 0x5a, 0xc4, 0x0c, 0xd5, 0x71, 0x61, 0x22,
0x13, 0xb6, 0x48, 0x92, 0xd0, 0x44, 0x0c, 0x42, 0xfb, 0xf8, 0xcd, 0xf3, 0x66, 0xab, 0x28, 0xdc,
0xd3, 0xd3, 0x5c, 0x31, 0x63, 0x59, 0x19, 0x74, 0x08, 0x90, 0x90, 0x5f, 0xe6, 0x84, 0xf1, 0x62,
0x6e, 0x5a, 0xb8, 0x91, 0x23, 0x86, 0xaf, 0xfd, 0x26, 0x41, 0x63, 0x99, 0xb3, 0xfa, 0x74, 0x1d,
0x63, 0x0b, 0x17, 0x4f, 0xdf, 0x87, 0xdd, 0x51, 0xff, 0x7c, 0x68, 0xe1, 0x91, 0x7e, 0xe2, 0x8c,
0xf4, 0xc9, 0xa4, 0x7f, 0xaa, 0x2b, 0x12, 0xda, 0x03, 0xe5, 0x27, 0x1d, 0x4f, 0x0c, 0xcb, 0x74,
0x46, 0xc6, 0x64, 0xd4, 0xb7, 0x07, 0x6f, 0x15, 0x19, 0xbd, 0x84, 0x83, 0x0b, 0x73, 0x72, 0x31,
0x1e, 0x5b, 0xd8, 0xd6, 0x4f, 0x56, 0x7b, 0x58, 0x49, 0x9b, 0x66, 0x98, 0xb6, 0x8e, 0xcd, 0xfe,
0x79, 0x76, 0x82, 0x52, 0xd5, 0xe6, 0xa0, 0xe6, 0x4c, 0x0d, 0xa8, 0x4f, 0xfa, 0xfe, 0x2d, 0x49,
0x78, 0xc0, 0xc8, 0x0d, 0x89, 0x38, 0x7a, 0x0f, 0x07, 0x6b, 0xaa, 0x75, 0x82, 0x68, 0x46, 0x55,
0xa9, 0x53, 0xe9, 0x36, 0x8f, 0x3f, 0x7f, 0xbc, 0x3d, 0x3f, 0xce, 0x49, 0xb2, 0x30, 0xa2, 0x19,
0xc5, 0x7b, 0xf1, 0x03, 0x57, 0x8a, 0x6a, 0x6f, 0x60, 0xbf, 0x34, 0x05, 0x7d, 0x0a, 0xcd, 0x78,
0x3e, 0x0d, 0x03, 0x2f, 0x9d, 0x76, 0x26, 0x0e, 0x6a, 0x61, 0xc8, 0xa0, 0x33, 0xb2, 0x60, 0xda,
0xef, 0x32, 0x7c, 0xf4, 0xe8, 0x69, 0x6b, 0x22, 0x94, 0xd6, 0x45, 0x58, 0x22, 0x68, 0xb9, 0x54,
0xd0, 0x87, 0x00, 0x77, 0x57, 0x29, 0xc8, 0x5b, 0xde, 0xa4, 0x54, 0x98, 0xd5, 0x52, 0x61, 0x2e,
0xc5, 0xb4, 0xb5, 0x2a, 0xa6, 0xc7, 0x25, 0x7f, 0x04, 0xbb, 0x8c, 0x24, 0xb7, 0x24, 0x71, 0x56,
0xce, 0xaf, 0x89, 0xdc, 0x9d, 0xcc, 0x31, 0x2e, 0x6e, 0xa1, 0xfd, 0x21, 0xc1, 0x61, 0x69, 0x3b,
0x96, 0xd3, 0xfe, 0x1a, 0xaa, 0xff, 0x95, 0x33, 0x91, 0x90, 0xbe, 0xff, 0x86, 0x30, 0xe6, 0x5e,
0x92, 0xa2, 0x47, 0x2d, 0xdc, 0xc8, 0x11, 0xc3, 0x5f, 0x55, 0x51, 0xe5, 0x9e, 0x8a, 0xb4, 0x7f,
0x64, 0x50, 0x1e, 0x16, 0x7f, 0x0e, 0x33, 0x2f, 0xa0, 0x26, 0x16, 0xd7, 0x92, 0x91, 0xed, 0xd4,
0x7c, 0x9a, 0x89, 0x12, 0x46, 0xab, 0xa5, 0x8c, 0xaa, 0x50, 0xcb, 0xef, 0x9f, 0x53, 0x51, 0x98,
0x68, 0x00, 0x55, 0xf1, 0x4d, 0xd9, 0x16, 0xba, 0xff, 0xea, 0xf1, 0x26, 0xad, 0x01, 0x42, 0xee,
0x22, 0x59, 0xb3, 0x61, 0xaf, 0xcc, 0x8b, 0x34, 0x78, 0x55, 0x08, 0x7b, 0x7c, 0x31, 0x79, 0xeb,
0x98, 0x96, 0x6d, 0x0c, 0x8d, 0x41, 0xdf, 0x4e, 0xb5, 0x9b, 0x8b, 0xbc, 0x09, 0xb5, 0x3b, 0x69,
0x0b, 0xc3, 0x4c, 0xdd, 0x8a, 0xac, 0xc5, 0xf0, 0x62, 0x7d, 0xf9, 0x88, 0x0d, 0x82, 0xbe, 0x85,
0x7a, 0xbe, 0x4c, 0x58, 0x4e, 0xef, 0xcb, 0x0d, 0x1b, 0x6b, 0x19, 0xfb, 0x04, 0xb3, 0xda, 0x9f,
0x32, 0x1c, 0xac, 0x1f, 0x19, 0xd3, 0x84, 0x6f, 0x58, 0x9d, 0xdf, 0xdf, 0x5f, 0x9d, 0x47, 0x9b,
0x56, 0x67, 0x5a, 0xaa, 0x74, 0x59, 0xfe, 0x1f, 0x2c, 0x6b, 0x3f, 0x3f, 0x67, 0xa9, 0xee, 0x40,
0xf3, 0x1d, 0xb6, 0xcc, 0xd3, 0xd5, 0x2f, 0xca, 0x83, 0xe5, 0x28, 0xa7, 0x98, 0x69, 0xd9, 0x0e,
0xd6, 0x4f, 0x8d, 0x89, 0xad, 0x63, 0xfd, 0x44, 0xa9, 0xa4, 0x0b, 0x73, 0xfd, 0x41, 0xb9, 0xd4,
0xee, 0xf7, 0x55, 0x7a, 0xa8, 0x98, 0xef, 0xa0, 0x96, 0x88, 0xb7, 0x33, 0x55, 0x16, 0x6c, 0x75,
0x9e, 0x6a, 0x12, 0x2e, 0x12, 0xa6, 0xdb, 0x22, 0xf2, 0x9b, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff,
0xc6, 0x95, 0x5d, 0x48, 0x48, 0x09, 0x00, 0x00,
}

View File

@ -19,6 +19,7 @@ message PushNotificationRegistration {
bool unregister = 9;
bytes grant = 10;
bool allow_from_contacts_only = 11;
string apn_topic = 12;
}
message PushNotificationRegistrationResponse {
@ -65,6 +66,12 @@ message PushNotification {
bytes public_key = 3;
string installation_id = 4;
bytes message = 5;
PushNotificationType type = 6;
enum PushNotificationType {
UNKNOWN_PUSH_NOTIFICATION_TYPE = 0;
MESSAGE = 1;
MENTION = 2;
}
}
message PushNotificationRequest {

View File

@ -17,6 +17,7 @@ import (
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/pushnotificationclient"
"github.com/status-im/status-go/protocol/pushnotificationserver"
"github.com/status-im/status-go/protocol/tt"
@ -26,6 +27,7 @@ import (
const (
bob1DeviceToken = "token-1"
bob2DeviceToken = "token-2"
testAPNTopic = "topic"
)
func TestMessengerPushNotificationSuite(t *testing.T) {
@ -54,6 +56,7 @@ func (s *MessengerPushNotificationSuite) SetupTest() {
s.m = s.newMessenger(s.shh)
s.privateKey = s.m.identity
s.Require().NoError(s.m.Start())
}
func (s *MessengerPushNotificationSuite) TearDownTest() {
@ -124,6 +127,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
bob1 := s.m
bob2 := s.newMessengerWithKey(s.shh, s.m.identity)
s.Require().NoError(bob2.Start())
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
@ -139,7 +143,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
s.Require().NoError(err)
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
@ -169,7 +173,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
s.Require().NoError(err)
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken)
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
err = tt.RetryWithBackOff(func() error {
@ -319,7 +323,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO
err = bob.EnablePushNotificationsFromContactsOnly()
s.Require().NoError(err)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
// Pull servers and check we registered
@ -430,6 +434,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
alice := s.newMessenger(s.shh)
// another contact to invalidate the token
frank := s.newMessenger(s.shh)
s.Require().NoError(frank.Start())
// start alice and enable push notifications
s.Require().NoError(alice.Start())
s.Require().NoError(alice.EnableSendingPushNotifications())
@ -463,7 +468,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
err = bob.EnablePushNotificationsFromContactsOnly()
s.Require().NoError(err)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
// Pull servers and check we registered
@ -650,7 +655,7 @@ func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer(
err := bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
s.Require().NoError(err)
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
@ -680,7 +685,7 @@ func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer(
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
s.Require().NoError(err)
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken)
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
s.Require().NoError(err)
err = tt.RetryWithBackOff(func() error {
@ -796,3 +801,54 @@ func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer(
s.Require().NoError(bob2.Shutdown())
s.Require().NoError(alice.Shutdown())
}
func (s *MessengerPushNotificationSuite) TestContactCode() {
bob1 := s.m
serverKey, err := crypto.GenerateKey()
s.Require().NoError(err)
server := s.newPushNotificationServer(s.shh, serverKey)
alice := s.newMessenger(s.shh)
// start alice and enable sending push notifications
s.Require().NoError(alice.Start())
s.Require().NoError(alice.EnableSendingPushNotifications())
// Register bob1
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
s.Require().NoError(err)
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken, testAPNTopic, protobuf.PushNotificationRegistration_APN_TOKEN)
// Pull servers and check we registered
err = tt.RetryWithBackOff(func() error {
_, err = server.RetrieveAll()
if err != nil {
return err
}
_, err = bob1.RetrieveAll()
if err != nil {
return err
}
registered, err := bob1.RegisteredForPushNotifications()
if err != nil {
return err
}
if !registered {
return errors.New("not registered")
}
return nil
})
// Make sure we receive it
s.Require().NoError(err)
contactCodeAdvertisement, err := bob1.buildContactCodeAdvertisement()
s.Require().NoError(err)
s.Require().NotNil(contactCodeAdvertisement)
s.Require().NoError(alice.pushNotificationClient.HandleContactCodeAdvertisement(&bob1.identity.PublicKey, *contactCodeAdvertisement))
s.Require().NoError(alice.Shutdown())
s.Require().NoError(server.Shutdown())
}

View File

@ -121,9 +121,6 @@ type Config struct {
InstallationID string
Logger *zap.Logger
// TokenType is the type of token
TokenType protobuf.PushNotificationRegistration_TokenType
}
type Client struct {
@ -141,6 +138,10 @@ type Client struct {
AccessToken string
// deviceToken is the device token for this device
deviceToken string
// TokenType is the type of token
tokenType protobuf.PushNotificationRegistration_TokenType
// APNTopic is the topic of the apn topic for push notification
apnTopic string
// randomReader only used for testing so we have deterministic encryption
reader io.Reader
@ -155,6 +156,9 @@ type Client struct {
resendingLoopQuitChan chan struct{}
quit chan struct{}
// registrationSubscriptions is a list of chan of client subscribed to the registration event
registrationSubscriptions []chan struct{}
}
func New(persistence *Persistence, config *Config, processor *common.MessageProcessor) *Client {
@ -179,16 +183,38 @@ func (c *Client) Start() error {
c.subscribeForSentMessages()
c.subscribeForScheduledMessages()
// We start even if push notifications are disabled, as we might
// actually be sending an unregister message
c.startRegistrationLoop()
c.startResendingLoop()
return nil
}
func (c *Client) publishOnRegistrationSubscriptions() {
// Publish on channels, drop if buffer is full
for _, s := range c.registrationSubscriptions {
select {
case s <- struct{}{}:
default:
c.config.Logger.Warn("subscription channel full, dropping message")
}
}
}
func (c *Client) quitRegistrationSubscriptions() {
for _, s := range c.registrationSubscriptions {
close(s)
}
}
func (c *Client) Stop() error {
close(c.quit)
c.stopRegistrationLoop()
c.stopResendingLoop()
c.quitRegistrationSubscriptions()
return nil
}
@ -197,6 +223,8 @@ func (c *Client) Unregister() error {
// stop registration loop
c.stopRegistrationLoop()
c.config.RemoteNotificationsEnabled = false
registration := c.buildPushNotificationUnregisterMessage()
err := c.saveLastPushNotificationRegistration(registration, nil)
if err != nil {
@ -230,6 +258,12 @@ func (c *Client) Registered() (bool, error) {
return true, nil
}
func (c *Client) SubscribeToRegistrations() chan struct{} {
s := make(chan struct{}, 100)
c.registrationSubscriptions = append(c.registrationSubscriptions, s)
return s
}
func (c *Client) GetSentNotification(hashedPublicKey []byte, installationID string, messageID []byte) (*SentNotification, error) {
return c.persistence.GetSentNotification(hashedPublicKey, installationID, messageID)
}
@ -245,14 +279,21 @@ func (c *Client) Reregister(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string
return nil
}
return c.Register(c.deviceToken, contactIDs, mutedChatIDs)
if !c.config.RemoteNotificationsEnabled {
c.config.Logger.Info("remote notifications not enabled, not registering")
return nil
}
return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs)
}
// Register registers with all the servers
func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error {
func (c *Client) Register(deviceToken, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType, contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error {
// stop registration loop
c.stopRegistrationLoop()
c.config.RemoteNotificationsEnabled = true
// reset servers
err := c.resetServers()
if err != nil {
@ -260,6 +301,8 @@ func (c *Client) Register(deviceToken string, contactIDs []*ecdsa.PublicKey, mut
}
c.deviceToken = deviceToken
c.apnTopic = apnTopic
c.tokenType = tokenType
registration, err := c.buildPushNotificationRegistrationMessage(contactIDs, mutedChatIDs)
if err != nil {
@ -299,7 +342,60 @@ func (c *Client) HandlePushNotificationRegistrationResponse(publicKey *ecdsa.Pub
server.Registered = true
server.RegisteredAt = time.Now().Unix()
return c.persistence.UpsertServer(server)
err = c.persistence.UpsertServer(server)
if err != nil {
return err
}
c.publishOnRegistrationSubscriptions()
return nil
}
// processQueryInfo takes info about push notifications and validates them
func (c *Client) processQueryInfo(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, info *protobuf.PushNotificationQueryInfo) error {
// make sure the public key matches
if !bytes.Equal(info.PublicKey, common.HashPublicKey(clientPublicKey)) {
c.config.Logger.Warn("reply for different key, ignoring")
return errors.New("reply for a different key, ignoring")
}
accessToken := info.AccessToken
// the user wants notification from contacts only, try to decrypt the access token to see if we are in their contacts
if len(accessToken) == 0 && len(info.AllowedKeyList) != 0 {
accessToken = c.handleAllowedKeyList(clientPublicKey, info.AllowedKeyList)
}
// no luck
if len(accessToken) == 0 {
c.config.Logger.Debug("not in the allowed key list")
return nil
}
// We check the user has allowed this server to store this particular
// access token, otherwise anyone could reply with a fake token
// and receive notifications for a user
if err := c.handleGrant(clientPublicKey, serverPublicKey, info.Grant, accessToken); err != nil {
c.config.Logger.Warn("grant verification failed, ignoring", zap.Error(err))
return err
}
pushNotificationInfo := &PushNotificationInfo{
PublicKey: clientPublicKey,
ServerPublicKey: serverPublicKey,
AccessToken: accessToken,
InstallationID: info.InstallationId,
Version: info.Version,
RetrievedAt: time.Now().Unix(),
}
err := c.persistence.SavePushNotificationInfo([]*PushNotificationInfo{pushNotificationInfo})
if err != nil {
c.config.Logger.Error("failed to save push notifications", zap.Error(err))
return err
}
return nil
}
// HandlePushNotificationQueryResponse should update the data in the database for a given user
@ -310,63 +406,55 @@ func (c *Client) HandlePushNotificationQueryResponse(serverPublicKey *ecdsa.Publ
}
// get the public key associated with this query
publicKey, err := c.persistence.GetQueryPublicKey(response.MessageId)
clientPublicKey, err := c.persistence.GetQueryPublicKey(response.MessageId)
if err != nil {
return err
}
if publicKey == nil {
if clientPublicKey == nil {
c.config.Logger.Debug("query not found")
return nil
}
var pushNotificationInfo []*PushNotificationInfo
// process query, make sure to validate grant as coming from the server
for _, info := range response.Info {
// make sure the public key matches
if !bytes.Equal(info.PublicKey, common.HashPublicKey(publicKey)) {
c.config.Logger.Warn("reply for different key, ignoring")
err := c.processQueryInfo(clientPublicKey, serverPublicKey, info)
if err != nil {
c.config.Logger.Warn("failed to process info", zap.Any("info", info), zap.Error(err))
continue
}
accessToken := info.AccessToken
// the user wants notification from contacts only, try to decrypt the access token to see if we are in their contacts
if len(accessToken) == 0 && len(info.AllowedKeyList) != 0 {
accessToken = c.handleAllowedKeyList(publicKey, info.AllowedKeyList)
}
// no luck
if len(accessToken) == 0 {
c.config.Logger.Debug("not in the allowed key list")
continue
}
// We check the user has allowed this server to store this particular
// access token, otherwise anyone could reply with a fake token
// and receive notifications for a user
if err := c.handleGrant(publicKey, serverPublicKey, info.Grant, accessToken); err != nil {
c.config.Logger.Warn("grant verification failed, ignoring", zap.Error(err))
continue
}
pushNotificationInfo = append(pushNotificationInfo, &PushNotificationInfo{
PublicKey: publicKey,
ServerPublicKey: serverPublicKey,
AccessToken: accessToken,
InstallationID: info.InstallationId,
Version: info.Version,
RetrievedAt: time.Now().Unix(),
})
}
err = c.persistence.SavePushNotificationInfo(pushNotificationInfo)
if err != nil {
c.config.Logger.Error("failed to save push notifications", zap.Error(err))
return err
}
return nil
}
// HandleContactCodeAdvertisement checks if there are any info and process them
func (c *Client) HandleContactCodeAdvertisement(clientPublicKey *ecdsa.PublicKey, message protobuf.ContactCodeAdvertisement) error {
// nothing to do for our own pubkey
if common.IsPubKeyEqual(clientPublicKey, &c.config.Identity.PublicKey) {
return nil
}
c.config.Logger.Debug("received contact code advertisement", zap.Any("advertisement", message))
for _, info := range message.PushNotificationInfo {
c.config.Logger.Debug("handling push notification query info")
serverPublicKey, err := crypto.DecompressPubkey(info.ServerPublicKey)
if err != nil {
c.config.Logger.Error("could not unmarshal server pubkey", zap.Binary("server-key", info.ServerPublicKey))
return err
}
err = c.processQueryInfo(clientPublicKey, serverPublicKey, info)
if err != nil {
return err
}
}
// Save query so that we won't query again to early
// NOTE: this is not very accurate as we might fetch an historical message,
// prolonging the time that we fetch new info.
// Most of the times it should work fine, as if the info are stale they'd be
// fetched again because of an error response from the push notification server
return c.persistence.SavePushNotificationQuery(clientPublicKey, []byte(uuid.New().String()))
}
// HandlePushNotificationResponse should set the request as processed
@ -427,6 +515,10 @@ func (c *Client) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installatio
return c.persistence.GetPushNotificationInfo(publicKey, installationIDs)
}
func (c *Client) Enabled() bool {
return c.config.RemoteNotificationsEnabled
}
func (c *Client) EnableSending() {
c.config.SendEnabled = true
}
@ -436,17 +528,21 @@ func (c *Client) DisableSending() {
}
func (c *Client) EnablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error {
c.config.Logger.Debug("enabling push notification from contacts only")
c.config.AllowFromContactsOnly = true
if c.lastPushNotificationRegistration != nil {
return c.Register(c.deviceToken, contactIDs, mutedChatIDs)
if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled {
c.config.Logger.Debug("re-registering after enabling push notifications from contacts only")
return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs)
}
return nil
}
func (c *Client) DisablePushNotificationsFromContactsOnly(contactIDs []*ecdsa.PublicKey, mutedChatIDs []string) error {
c.config.Logger.Debug("disabling push notification from contacts only")
c.config.AllowFromContactsOnly = false
if c.lastPushNotificationRegistration != nil {
return c.Register(c.deviceToken, contactIDs, mutedChatIDs)
if c.lastPushNotificationRegistration != nil && c.config.RemoteNotificationsEnabled {
c.config.Logger.Debug("re-registering after disabling push notifications from contacts only")
return c.Register(c.deviceToken, c.apnTopic, c.tokenType, contactIDs, mutedChatIDs)
}
return nil
}
@ -545,8 +641,9 @@ func (c *Client) loadLastPushNotificationRegistration() error {
c.lastContactIDs = lastContactIDs
c.lastPushNotificationRegistration = lastRegistration
c.deviceToken = lastRegistration.DeviceToken
c.apnTopic = lastRegistration.ApnTopic
c.tokenType = lastRegistration.TokenType
return nil
}
func (c *Client) stopRegistrationLoop() {
@ -807,7 +904,7 @@ func (c *Client) allowedKeyList(token []byte, contactIDs []*ecdsa.PublicKey) ([]
// and return a new one in that case. A token is refreshed only if it's not set
// or if a contact has been removed
func (c *Client) getToken(contactIDs []*ecdsa.PublicKey) string {
if c.lastPushNotificationRegistration == nil || len(c.lastPushNotificationRegistration.AccessToken) == 0 || c.shouldRefreshToken(c.lastContactIDs, contactIDs) {
if c.lastPushNotificationRegistration == nil || len(c.lastPushNotificationRegistration.AccessToken) == 0 || c.shouldRefreshToken(c.lastContactIDs, contactIDs, c.lastPushNotificationRegistration.AllowFromContactsOnly, c.config.AllowFromContactsOnly) {
c.config.Logger.Info("refreshing access token")
return uuid.New().String()
}
@ -830,7 +927,8 @@ func (c *Client) buildPushNotificationRegistrationMessage(contactIDs []*ecdsa.Pu
options := &protobuf.PushNotificationRegistration{
AccessToken: token,
TokenType: c.config.TokenType,
TokenType: c.tokenType,
ApnTopic: c.apnTopic,
Version: c.getVersion(),
InstallationId: c.config.InstallationID,
DeviceToken: c.deviceToken,
@ -851,8 +949,17 @@ func (c *Client) buildPushNotificationUnregisterMessage() *protobuf.PushNotifica
return options
}
// shouldRefreshToken tells us whether we should pull a new token, that's only necessary when a contact is removed
func (c *Client) shouldRefreshToken(oldContactIDs, newContactIDs []*ecdsa.PublicKey) bool {
// shouldRefreshToken tells us whether we should create a new token,
// that's only necessary when a contact is removed
// or allowFromContactsOnly is enabled.
// In both cases we want to invalidate any existing token
func (c *Client) shouldRefreshToken(oldContactIDs, newContactIDs []*ecdsa.PublicKey, oldAllowFromContactsOnly, newAllowFromContactsOnly bool) bool {
// Check if allowFromContactsOnly has just been enabled
if !oldAllowFromContactsOnly && newAllowFromContactsOnly {
return true
}
newContactIDsMap := make(map[string]bool)
for _, pk := range newContactIDs {
newContactIDsMap[types.EncodeHex(crypto.FromECDSAPub(pk))] = true
@ -1017,6 +1124,7 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs []
for _, i := range infos {
// TODO: Add ChatID, message, public_key
pushNotifications = append(pushNotifications, &protobuf.PushNotification{
Type: protobuf.PushNotification_MESSAGE,
AccessToken: i.AccessToken,
PublicKey: common.HashPublicKey(publicKey),
InstallationId: i.InstallationID,
@ -1193,7 +1301,6 @@ func (c *Client) saveLastPushNotificationRegistration(registration *protobuf.Pus
c.lastPushNotificationRegistration = registration
c.lastContactIDs = contactIDs
c.startRegistrationLoop()
return nil
}
@ -1245,6 +1352,48 @@ func (c *Client) handleAllowedKeyList(publicKey *ecdsa.PublicKey, allowedKeyList
return ""
}
func (c *Client) MyPushNotificationQueryInfo() ([]*protobuf.PushNotificationQueryInfo, error) {
// Nothing to do
if c.lastPushNotificationRegistration == nil || c.lastPushNotificationRegistration.Unregister {
return nil, nil
}
var response []*protobuf.PushNotificationQueryInfo
servers, err := c.persistence.GetServers()
if err != nil {
return nil, err
}
for _, server := range servers {
// ignore non-registered servers
if !server.Registered {
continue
}
// build grant for this specific server
grant, err := c.buildGrantSignature(server.PublicKey, c.lastPushNotificationRegistration.AccessToken)
if err != nil {
c.config.Logger.Error("failed to build grant", zap.Error(err))
return nil, err
}
queryInfo := &protobuf.PushNotificationQueryInfo{
InstallationId: c.config.InstallationID,
// is this the right key?
PublicKey: common.HashPublicKey(&c.config.Identity.PublicKey),
Version: c.lastPushNotificationRegistration.Version,
Grant: grant,
ServerPublicKey: crypto.CompressPubkey(server.PublicKey),
}
if c.lastPushNotificationRegistration.AllowFromContactsOnly {
queryInfo.AllowedKeyList = c.lastPushNotificationRegistration.AllowedKeyList
} else {
queryInfo.AccessToken = c.lastPushNotificationRegistration.AccessToken
}
response = append(response, queryInfo)
}
return response, nil
}
// queryPushNotificationInfo sends a message to any server who has the given user registered.
// it uses an ephemeral key so the identity of the client querying is not disclosed
func (c *Client) queryPushNotificationInfo(publicKey *ecdsa.PublicKey) error {

View File

@ -219,12 +219,17 @@ func (s *ClientSuite) TestShouldRefreshToken() {
s.Require().NoError(err)
// Contacts are added
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey, &key4.PublicKey}))
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey, &key4.PublicKey}, true, true))
// everything the same
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}))
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, true, true))
// A contact is removed
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey}))
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey}, true, true))
// allow from contacts only is disabled
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, true, false))
// allow from contacts only is enabled
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}, false, true))
}

View File

@ -4,8 +4,11 @@ import (
"bytes"
"encoding/hex"
"encoding/json"
"io/ioutil"
"net/http"
"go.uber.org/zap"
"github.com/status-im/status-go/protocol/protobuf"
)
@ -21,6 +24,7 @@ type GoRushRequestNotification struct {
Tokens []string `json:"tokens"`
Platform uint `json:"platform"`
Message string `json:"message"`
Topic string `json:"topic"`
Data *GoRushRequestData `json:"data"`
}
@ -53,6 +57,7 @@ func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*Requ
Tokens: []string{registration.DeviceToken},
Platform: tokenTypeToGoRushPlatform(registration.TokenType),
Message: defaultNotificationMessage,
Topic: registration.ApnTopic,
Data: &GoRushRequestData{
EncryptedMessage: hex.EncodeToString(request.Message),
ChatID: request.ChatId,
@ -63,15 +68,20 @@ func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*Requ
return goRushRequests
}
func sendGoRushNotification(request *GoRushRequest, url string) error {
func sendGoRushNotification(request *GoRushRequest, url string, logger *zap.Logger) error {
payload, err := json.Marshal(request)
if err != nil {
return err
}
_, err = http.Post(url+"/api/push", "application/json", bytes.NewReader(payload))
response, err := http.Post(url+"/api/push", "application/json", bytes.NewReader(payload))
if err != nil {
return err
}
defer response.Body.Close()
body, _ := ioutil.ReadAll(response.Body)
logger.Info("Sent gorush request", zap.String("response", string(body)))
return nil
}

View File

@ -16,9 +16,14 @@ type Persistence interface {
GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error)
// GetPushNotificationRegistrationByPublicKey retrieve all the push notification registrations from storage given a public key
GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error)
//GetPushNotificationRegistrationPublicKeys return all the public keys stored
// GetPushNotificationRegistrationPublicKeys return all the public keys stored
GetPushNotificationRegistrationPublicKeys() ([][]byte, error)
//GetPushNotificationRegistrationVersion returns the latest version or 0 for a given pk and installationID
GetPushNotificationRegistrationVersion(publicKey []byte, installationID string) (uint64, error)
// UnregisterPushNotificationRegistration unregister a given pk/installationID
UnregisterPushNotificationRegistration(publicKey []byte, installationID string, version uint64) error
// DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id
DeletePushNotificationRegistration(publicKey []byte, installationID string) error
// SavePushNotificationRegistration saves a push notification option to the db
@ -39,7 +44,7 @@ func NewSQLitePersistence(db *sql.DB) Persistence {
func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error) {
var marshaledRegistration []byte
err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID).Scan(&marshaledRegistration)
err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ? AND registration IS NOT NULL`, publicKey, installationID).Scan(&marshaledRegistration)
if err == sql.ErrNoRows {
return nil, nil
@ -48,13 +53,25 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstall
}
registration := &protobuf.PushNotificationRegistration{}
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
return nil, err
}
return registration, nil
}
func (p *SQLitePersistence) GetPushNotificationRegistrationVersion(publicKey []byte, installationID string) (uint64, error) {
var version uint64
err := p.db.QueryRow(`SELECT version FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID).Scan(&version)
if err == sql.ErrNoRows {
return 0, nil
} else if err != nil {
return 0, err
}
return version, nil
}
type PushNotificationIDAndRegistration struct {
ID []byte
Registration *protobuf.PushNotificationRegistration
@ -70,7 +87,7 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKe
inVector := strings.Repeat("?, ", len(publicKeys)-1) + "?"
rows, err := p.db.Query(`SELECT public_key,registration FROM push_notification_server_registrations WHERE public_key IN (`+inVector+`)`, publicKeyArgs...) // nolint: gosec
rows, err := p.db.Query(`SELECT public_key,registration FROM push_notification_server_registrations WHERE registration IS NOT NULL AND public_key IN (`+inVector+`)`, publicKeyArgs...) // nolint: gosec
if err != nil {
return nil, err
}
@ -86,6 +103,10 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKe
}
registration := &protobuf.PushNotificationRegistration{}
// Skip if there's no registration
if marshaledRegistration == nil {
continue
}
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
return nil, err
@ -97,7 +118,7 @@ func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKe
}
func (p *SQLitePersistence) GetPushNotificationRegistrationPublicKeys() ([][]byte, error) {
rows, err := p.db.Query(`SELECT public_key FROM push_notification_server_registrations`)
rows, err := p.db.Query(`SELECT public_key FROM push_notification_server_registrations WHERE registration IS NOT NULL`)
if err != nil {
return nil, err
}
@ -126,6 +147,11 @@ func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey []byte, r
return err
}
func (p *SQLitePersistence) UnregisterPushNotificationRegistration(publicKey []byte, installationID string, version uint64) error {
_, err := p.db.Exec(`UPDATE push_notification_server_registrations SET registration = NULL, version = ? WHERE public_key = ? AND installation_id = ?`, version, publicKey, installationID)
return err
}
func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey []byte, installationID string) error {
_, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID)
return err

View File

@ -233,12 +233,12 @@ func (s *Server) validateRegistration(publicKey *ecdsa.PublicKey, payload []byte
return nil, ErrMalformedPushNotificationRegistrationInstallationID
}
previousRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(publicKey), registration.InstallationId)
previousVersion, err := s.persistence.GetPushNotificationRegistrationVersion(common.HashPublicKey(publicKey), registration.InstallationId)
if err != nil {
return nil, err
}
if previousRegistration != nil && registration.Version <= previousRegistration.Version {
if registration.Version <= previousVersion {
return nil, ErrInvalidPushNotificationRegistrationVersion
}
@ -290,6 +290,7 @@ func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotifica
for _, idAndResponse := range registrations {
registration := idAndResponse.Registration
info := &protobuf.PushNotificationQueryInfo{
PublicKey: idAndResponse.ID,
Grant: registration.Grant,
@ -333,6 +334,11 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request
InstallationId: pn.InstallationId,
}
if pn.Type != protobuf.PushNotification_MESSAGE {
s.config.Logger.Warn("unhandled type")
continue
}
if err != nil {
s.config.Logger.Error("failed to retrieve registration", zap.Error(err))
report.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
@ -340,7 +346,6 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request
s.config.Logger.Warn("empty registration")
report.Error = protobuf.PushNotificationReport_NOT_REGISTERED
} else if registration.AccessToken != pn.AccessToken {
s.config.Logger.Warn("invalid access token")
report.Error = protobuf.PushNotificationReport_WRONG_TOKEN
} else {
// For now we just assume that the notification will be successful
@ -362,7 +367,7 @@ func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request
// This can be done asynchronously
goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations)
err := sendGoRushNotification(goRushRequest, s.config.GorushURL)
err := sendGoRushNotification(goRushRequest, s.config.GorushURL, s.config.Logger)
if err != nil {
s.config.Logger.Error("failed to send go rush notification", zap.Error(err))
// TODO: handle this error?
@ -403,12 +408,9 @@ func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.Publ
}
if registration.Unregister {
s.config.Logger.Info("unregistering client")
// We save an empty registration, only keeping version and installation-id
emptyRegistration := &protobuf.PushNotificationRegistration{
Version: registration.Version,
InstallationId: registration.InstallationId,
}
if err := s.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), emptyRegistration); err != nil {
if err := s.persistence.UnregisterPushNotificationRegistration(common.HashPublicKey(publicKey), registration.InstallationId, registration.Version); err != nil {
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
s.config.Logger.Error("failed to unregister ", zap.Error(err))
return response

View File

@ -500,16 +500,16 @@ func (s *ServerSuite) TestPushNotificationHandleRegistration() {
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
s.Require().NotNil(response)
s.Require().True(response.Success)
s.Require().Equal(common.Shake256(cyphertext), response.RequestId)
// Check is gone from the db
retrievedRegistration, err = s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&s.key.PublicKey), s.installationID)
s.Require().NoError(err)
s.Require().NotNil(retrievedRegistration)
s.Require().Empty(retrievedRegistration.AccessToken)
s.Require().Empty(retrievedRegistration.DeviceToken)
s.Require().Equal(uint64(2), retrievedRegistration.Version)
s.Require().Equal(s.installationID, retrievedRegistration.InstallationId)
s.Require().Equal(common.Shake256(cyphertext), response.RequestId)
s.Require().Nil(retrievedRegistration)
// Check version is maintained
version, err := s.persistence.GetPushNotificationRegistrationVersion(common.HashPublicKey(&s.key.PublicKey), s.installationID)
s.Require().NoError(err)
s.Require().Equal(uint64(2), version)
}
func (s *ServerSuite) TestbuildPushNotificationQueryResponseNoFiltering() {

View File

@ -14,6 +14,8 @@ import (
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/datasync"
"github.com/status-im/status-go/protocol/encryption"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
"github.com/status-im/status-go/protocol/protobuf"
)
@ -45,6 +47,11 @@ type StatusMessage struct {
TransportLayerSigPubKey *ecdsa.PublicKey `json:"-"`
// ApplicationMetadataLayerPubKey contains the public key provided by the application metadata layer
ApplicationMetadataLayerSigPubKey *ecdsa.PublicKey `json:"-"`
// Installations is the new installations returned by the encryption layer
Installations []*multidevice.Installation
// SharedSecret is the shared secret returned by the encryption layer
SharedSecrets []*sharedsecret.Secret
}
// Temporary JSON marshaling for those messages that are not yet processed
@ -117,7 +124,7 @@ func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecd
return errors.Wrap(err, "failed to unmarshal ProtocolMessage")
}
payload, err := enc.HandleMessage(
response, err := enc.HandleMessage(
myKey,
senderKey,
&protocolMessage,
@ -128,7 +135,9 @@ func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecd
return errors.Wrap(err, "failed to handle Encryption message")
}
m.DecryptedPayload = payload
m.DecryptedPayload = response.DecryptedMessage
m.Installations = response.Installations
m.SharedSecrets = response.SharedSecrets
return nil
}
@ -217,7 +226,6 @@ func (m *StatusMessage) HandleApplication() error {
case protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION:
return m.unmarshalProtobufData(new(protobuf.PairInstallation))
case protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT:
return m.unmarshalProtobufData(new(protobuf.ContactCodeAdvertisement))
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST:

View File

@ -430,7 +430,7 @@ func (api *PublicAPI) StopPushNotificationsServer() error {
// PushNotification client endpoints
func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) error {
func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string, apnTopic string, tokenType protobuf.PushNotificationRegistration_TokenType) error {
// We set both for now as they are equivalent
err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", true)
if err != nil {
@ -441,10 +441,10 @@ func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceTo
return err
}
return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken)
return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken, apnTopic, tokenType)
}
func (api *PublicAPI) UnregisterForPushNotifications(ctx context.Context) error {
func (api *PublicAPI) UnregisterFromPushNotifications(ctx context.Context) error {
err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", false)
if err != nil {
return err

View File

@ -169,10 +169,14 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z
func (s *Service) StartMessenger() error {
// Start a loop that retrieves all messages and propagates them to status-react.
s.cancelMessenger = make(chan struct{})
err := s.messenger.Start()
if err != nil {
return err
}
go s.retrieveMessagesLoop(time.Second, s.cancelMessenger)
go s.verifyTransactionLoop(30*time.Second, s.cancelMessenger)
go s.verifyENSLoop(30*time.Second, s.cancelMessenger)
return s.messenger.Start()
return nil
}
func (s *Service) retrieveMessagesLoop(tick time.Duration, cancel <-chan struct{}) {
@ -456,6 +460,7 @@ func buildMessengerOptions(
) ([]protocol.Option, error) {
options := []protocol.Option{
protocol.WithCustomLogger(logger),
protocol.WithPushNotifications(),
protocol.WithDatabase(db),
protocol.WithEnvelopesMonitorConfig(envelopesMonitorConfig),
protocol.WithOnNegotiatedFilters(onNegotiatedFilters),