Sync all devices after initial pairing (#3047)

This commit is contained in:
frank 2023-01-06 20:21:14 +08:00 committed by GitHub
parent bea710c8be
commit ec7c0e9c7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 2407 additions and 1079 deletions

View File

@ -1 +1 @@
0.117.3 0.118.0

View File

@ -31,6 +31,7 @@ import (
"github.com/status-im/status-go/node" "github.com/status-im/status-go/node"
"github.com/status-im/status-go/nodecfg" "github.com/status-im/status-go/nodecfg"
"github.com/status-im/status-go/params" "github.com/status-im/status-go/params"
"github.com/status-im/status-go/protocol"
"github.com/status-im/status-go/rpc" "github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/ext" "github.com/status-im/status-go/services/ext"
"github.com/status-im/status-go/services/personal" "github.com/status-im/status-go/services/personal"
@ -1349,6 +1350,17 @@ func (b *GethStatusBackend) SignGroupMembership(content string) (string, error)
return crypto.SignStringAsHex(content, selectedChatAccount.AccountKey.PrivateKey) return crypto.SignStringAsHex(content, selectedChatAccount.AccountKey.PrivateKey)
} }
func (b *GethStatusBackend) Messenger() *protocol.Messenger {
node := b.StatusNode()
if node != nil {
wakuExtService := node.WakuExtService()
if wakuExtService != nil {
return wakuExtService.Messenger()
}
}
return nil
}
// SignHash exposes vanilla ECDSA signing for signing a message for Swarm // SignHash exposes vanilla ECDSA signing for signing a message for Swarm
func (b *GethStatusBackend) SignHash(hexEncodedHash string) (string, error) { func (b *GethStatusBackend) SignHash(hexEncodedHash string) (string, error) {
hash, err := hexutil.Decode(hexEncodedHash) hash, err := hexutil.Decode(hexEncodedHash)

View File

@ -36,6 +36,7 @@ import (
"github.com/status-im/status-go/protocol/identity/colorhash" "github.com/status-im/status-go/protocol/identity/colorhash"
"github.com/status-im/status-go/protocol/identity/emojihash" "github.com/status-im/status-go/protocol/identity/emojihash"
"github.com/status-im/status-go/server" "github.com/status-im/status-go/server"
"github.com/status-im/status-go/server/pairing"
"github.com/status-im/status-go/services/personal" "github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/typeddata" "github.com/status-im/status-go/services/typeddata"
"github.com/status-im/status-go/signal" "github.com/status-im/status-go/signal"
@ -938,42 +939,40 @@ func GenerateImages(filepath string, aX, aY, bX, bY int) string {
return string(data) return string(data)
} }
// GetConnectionStringForBeingBootstrapped starts a server.Receiving server.PairingServer // GetConnectionStringForBeingBootstrapped starts a pairing.Receiving pairing.PairingServer
// then generates a server.ConnectionParams. Used when the device is Logged out or has no Account keys // then generates a pairing.ConnectionParams. Used when the device is Logged out or has no Account keys
// and the device has no camera to read a QR code with // and the device has no camera to read a QR code with
// //
// Example: A desktop device (device without camera) receiving account data from mobile (device with camera) // Example: A desktop device (device without camera) receiving account data from mobile (device with camera)
func GetConnectionStringForBeingBootstrapped(configJSON string) string { func GetConnectionStringForBeingBootstrapped(configJSON string) string {
if configJSON == "" { if configJSON == "" {
return makeJSONResponse(fmt.Errorf("no config given, PairingPayloadSourceConfig is expected")) return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
} }
cs, err := pairing.StartUpPairingServer(statusBackend, pairing.Receiving, configJSON)
cs, err := server.StartUpPairingServer(statusBackend.GetMultiaccountDB(), server.Receiving, configJSON)
if err != nil { if err != nil {
return makeJSONResponse(err) return makeJSONResponse(err)
} }
return cs return cs
} }
// GetConnectionStringForBootstrappingAnotherDevice starts a server.Sending server.PairingServer // GetConnectionStringForBootstrappingAnotherDevice starts a pairing.Sending pairing.Server
// then generates a server.ConnectionParams. Used when the device is Logged in and therefore has Account keys // then generates a pairing.ConnectionParams. Used when the device is Logged in and therefore has Account keys
// and the device might not have a camera // and the device might not have a camera
// //
// Example: A mobile or desktop device (devices that MAY have a camera but MUST have a screen) // Example: A mobile or desktop device (devices that MAY have a camera but MUST have a screen)
// sending account data to a mobile (device with camera) // sending account data to a mobile (device with camera)
func GetConnectionStringForBootstrappingAnotherDevice(configJSON string) string { func GetConnectionStringForBootstrappingAnotherDevice(configJSON string) string {
if configJSON == "" { if configJSON == "" {
return makeJSONResponse(fmt.Errorf("no config given, PairingPayloadSourceConfig is expected")) return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
} }
cs, err := pairing.StartUpPairingServer(statusBackend, pairing.Sending, configJSON)
cs, err := server.StartUpPairingServer(statusBackend.GetMultiaccountDB(), server.Sending, configJSON)
if err != nil { if err != nil {
return makeJSONResponse(err) return makeJSONResponse(err)
} }
return cs return cs
} }
// InputConnectionStringForBootstrapping starts a server.PairingClient // InputConnectionStringForBootstrapping starts a pairing.Client
// The given server.ConnectionParams string will determine the server.Mode // The given server.ConnectionParams string will determine the server.Mode
// //
// server.Mode = server.Sending // server.Mode = server.Sending
@ -988,10 +987,10 @@ func GetConnectionStringForBootstrappingAnotherDevice(configJSON string) string
// a device with a screen (mobile or desktop devices) // a device with a screen (mobile or desktop devices)
func InputConnectionStringForBootstrapping(cs, configJSON string) string { func InputConnectionStringForBootstrapping(cs, configJSON string) string {
if configJSON == "" { if configJSON == "" {
return makeJSONResponse(fmt.Errorf("no config given, PairingPayloadSourceConfig is expected")) return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
} }
err := server.StartUpPairingClient(statusBackend.GetMultiaccountDB(), cs, configJSON) err := pairing.StartUpPairingClient(statusBackend, cs, configJSON)
return makeJSONResponse(err) return makeJSONResponse(err)
} }

View File

@ -120,15 +120,29 @@ func NewDB(db *sql.DB) (*Database, error) {
} }
// DB Gets db sql.DB // DB Gets db sql.DB
func (db Database) DB() *sql.DB { func (db *Database) DB() *sql.DB {
return db.db return db.db
} }
// Close closes database. // Close closes database.
func (db Database) Close() error { func (db *Database) Close() error {
return db.db.Close() return db.db.Close()
} }
func (db *Database) GetAccountsByKeyUID(keyUID string) ([]*Account, error) {
accounts, err := db.GetAccounts()
if err != nil {
return nil, err
}
filteredAccounts := make([]*Account, 0)
for _, account := range accounts {
if account.KeyUID == keyUID {
filteredAccounts = append(filteredAccounts, account)
}
}
return filteredAccounts, nil
}
func (db *Database) GetAccounts() ([]*Account, error) { func (db *Database) GetAccounts() ([]*Account, error) {
rows, err := db.db.Query(`SELECT address, wallet, chat, type, storage, pubkey, path, name, emoji, color, hidden, derived_from, clock, key_uid rows, err := db.db.Query(`SELECT address, wallet, chat, type, storage, pubkey, path, name, emoji, color, hidden, derived_from, clock, key_uid
FROM accounts ORDER BY created_at`) FROM accounts ORDER BY created_at`)

View File

@ -211,6 +211,10 @@ func (b *StatusNode) AccountService() *accountssvc.Service {
return b.accountsSrvc return b.accountsSrvc
} }
func (b *StatusNode) BrowserService() *browsers.Service {
return b.browsersSrvc
}
func (b *StatusNode) WakuService() *waku.Waku { func (b *StatusNode) WakuService() *waku.Waku {
return b.wakuSrvc return b.wakuSrvc
} }

View File

@ -157,6 +157,9 @@ type Messenger struct {
mailPeersMutex sync.Mutex mailPeersMutex sync.Mutex
handleMessagesMutex sync.Mutex handleMessagesMutex sync.Mutex
handleImportMessagesMutex sync.Mutex handleImportMessagesMutex sync.Mutex
// flag to disable checking #hasPairedDevices
localPairing bool
} }
type connStatus int type connStatus int
@ -1335,7 +1338,7 @@ func (m *Messenger) watchIdentityImageChanges() {
for { for {
select { select {
case <-channel: case <-channel:
err := m.syncProfilePictures() err := m.syncProfilePictures(m.dispatchMessage)
if err != nil { if err != nil {
m.logger.Error("failed to sync profile pictures to paired devices", zap.Error(err)) m.logger.Error("failed to sync profile pictures to paired devices", zap.Error(err))
} }
@ -1718,9 +1721,16 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) err
return m.reSendRawMessage(ctx, messageID) return m.reSendRawMessage(ctx, messageID)
} }
func (m *Messenger) SetLocalPairing(localPairing bool) {
m.localPairing = localPairing
}
func (m *Messenger) hasPairedDevices() bool { func (m *Messenger) hasPairedDevices() bool {
logger := m.logger.Named("hasPairedDevices") logger := m.logger.Named("hasPairedDevices")
if m.localPairing {
return true
}
var count int var count int
m.allInstallations.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) { m.allInstallations.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
if installation.Enabled { if installation.Enabled {
@ -2166,7 +2176,7 @@ func (m *Messenger) ShareImageMessage(request *requests.ShareImageMessage) (*Mes
return response, nil return response, nil
} }
func (m *Messenger) syncProfilePictures() error { func (m *Messenger) syncProfilePictures(rawMessageHandler RawMessageHandler) error {
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
} }
@ -2207,12 +2217,14 @@ func (m *Messenger) syncProfilePictures() error {
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_PROFILE_PICTURE, MessageType: protobuf.ApplicationMetadataMessage_SYNC_PROFILE_PICTURE,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -2223,7 +2235,11 @@ func (m *Messenger) syncProfilePictures() error {
// SyncDevices sends all public chats and contacts to paired devices // SyncDevices sends all public chats and contacts to paired devices
// TODO remove use of photoPath in contacts // TODO remove use of photoPath in contacts
func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string) (err error) { func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string, rawMessageHandler RawMessageHandler) (err error) {
if rawMessageHandler == nil {
rawMessageHandler = m.dispatchMessage
}
myID := contactIDFromPublicKey(&m.identity.PublicKey) myID := contactIDFromPublicKey(&m.identity.PublicKey)
displayName, err := m.settings.DisplayName() displayName, err := m.settings.DisplayName()
@ -2231,14 +2247,14 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
return err return err
} }
if _, err = m.sendContactUpdate(ctx, myID, displayName, ensName, photoPath); err != nil { if _, err = m.sendContactUpdate(ctx, myID, displayName, ensName, photoPath, rawMessageHandler); err != nil {
return err return err
} }
m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) { m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
isPublicChat := !chat.Timeline() && !chat.ProfileUpdates() && chat.Public() isPublicChat := !chat.Timeline() && !chat.ProfileUpdates() && chat.Public()
if isPublicChat && chat.Active { if isPublicChat && chat.Active {
err = m.syncPublicChat(ctx, chat) err = m.syncPublicChat(ctx, chat, rawMessageHandler)
if err != nil { if err != nil {
return false return false
} }
@ -2251,7 +2267,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
} }
if !pending { if !pending {
err = m.syncChatRemoving(ctx, chatID) err = m.syncChatRemoving(ctx, chatID, rawMessageHandler)
if err != nil { if err != nil {
return false return false
} }
@ -2259,14 +2275,14 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
} }
if (isPublicChat || chat.OneToOne() || chat.PrivateGroupChat() || chat.CommunityChat()) && chat.Active { if (isPublicChat || chat.OneToOne() || chat.PrivateGroupChat() || chat.CommunityChat()) && chat.Active {
err := m.syncChatMessagesRead(ctx, chatID, chat.ReadMessagesAtClockValue) err := m.syncChatMessagesRead(ctx, chatID, chat.ReadMessagesAtClockValue, rawMessageHandler)
if err != nil { if err != nil {
return false return false
} }
} }
if isPublicChat && chat.Active && chat.DeletedAtClockValue > 0 { if isPublicChat && chat.Active && chat.DeletedAtClockValue > 0 {
err = m.syncClearHistory(ctx, chat) err = m.syncClearHistory(ctx, chat, rawMessageHandler)
if err != nil { if err != nil {
return false return false
} }
@ -2281,7 +2297,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) { m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
if contact.ID != myID && if contact.ID != myID &&
(contact.LocalNickname != "" || contact.Added || contact.Blocked) { (contact.LocalNickname != "" || contact.Added || contact.Blocked) {
if err = m.syncContact(ctx, contact); err != nil { if err = m.syncContact(ctx, contact, rawMessageHandler); err != nil {
return false return false
} }
} }
@ -2293,7 +2309,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
return err return err
} }
for _, c := range cs { for _, c := range cs {
if err = m.syncCommunity(ctx, c); err != nil { if err = m.syncCommunity(ctx, c, rawMessageHandler); err != nil {
return err return err
} }
} }
@ -2303,7 +2319,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
return err return err
} }
for _, b := range bookmarks { for _, b := range bookmarks {
if err = m.SyncBookmark(ctx, b); err != nil { if err = m.SyncBookmark(ctx, b, rawMessageHandler); err != nil {
return err return err
} }
} }
@ -2313,7 +2329,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
return err return err
} }
for id, ts := range trustedUsers { for id, ts := range trustedUsers {
if err = m.SyncTrustedUser(ctx, id, ts); err != nil { if err = m.SyncTrustedUser(ctx, id, ts, rawMessageHandler); err != nil {
return err return err
} }
} }
@ -2323,17 +2339,17 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
return err return err
} }
for i := range verificationRequests { for i := range verificationRequests {
if err = m.SyncVerificationRequest(ctx, &verificationRequests[i]); err != nil { if err = m.SyncVerificationRequest(ctx, &verificationRequests[i], rawMessageHandler); err != nil {
return err return err
} }
} }
err = m.syncSettings() err = m.syncSettings(rawMessageHandler)
if err != nil { if err != nil {
return err return err
} }
err = m.syncProfilePictures() err = m.syncProfilePictures(rawMessageHandler)
if err != nil { if err != nil {
return err return err
} }
@ -2352,14 +2368,14 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
for id, state := range ids { for id, state := range ids {
if state == common.ContactRequestStateAccepted || state == common.ContactRequestStateDismissed { if state == common.ContactRequestStateAccepted || state == common.ContactRequestStateDismissed {
accepted := state == common.ContactRequestStateAccepted accepted := state == common.ContactRequestStateAccepted
err := m.syncContactRequestDecision(ctx, id, accepted) err := m.syncContactRequestDecision(ctx, id, accepted, rawMessageHandler)
if err != nil { if err != nil {
return err return err
} }
} }
} }
err = m.syncWallets(accounts) err = m.syncWallets(accounts, rawMessageHandler)
if err != nil { if err != nil {
return err return err
} }
@ -2372,7 +2388,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
for i := range savedAddresses { for i := range savedAddresses {
sa := savedAddresses[i] sa := savedAddresses[i]
err = m.syncSavedAddress(ctx, sa) err = m.syncSavedAddress(ctx, sa, rawMessageHandler)
if err != nil { if err != nil {
return err return err
} }
@ -2385,7 +2401,7 @@ func (m *Messenger) SaveAccounts(accs []*accounts.Account) error {
if err != nil { if err != nil {
return err return err
} }
return m.syncWallets(accs) return m.syncWallets(accs, m.dispatchMessage)
} }
func (m *Messenger) DeleteAccount(address types.Address) error { func (m *Messenger) DeleteAccount(address types.Address) error {
@ -2402,11 +2418,11 @@ func (m *Messenger) DeleteAccount(address types.Address) error {
acc.Removed = true acc.Removed = true
accs := []*accounts.Account{acc} accs := []*accounts.Account{acc}
return m.syncWallets(accs) return m.syncWallets(accs, m.dispatchMessage)
} }
// syncWallets syncs all wallets with paired devices // syncWallets syncs all wallets with paired devices
func (m *Messenger) syncWallets(accs []*accounts.Account) error { func (m *Messenger) syncWallets(accs []*accounts.Account, rawMessageHandler RawMessageHandler) error {
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
} }
@ -2454,12 +2470,14 @@ func (m *Messenger) syncWallets(accs []*accounts.Account) error {
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_WALLET_ACCOUNT, MessageType: protobuf.ApplicationMetadataMessage_SYNC_WALLET_ACCOUNT,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -2468,7 +2486,7 @@ func (m *Messenger) syncWallets(accs []*accounts.Account) error {
return m.saveChat(chat) return m.saveChat(chat)
} }
func (m *Messenger) syncContactRequestDecision(ctx context.Context, requestID string, accepted bool) error { func (m *Messenger) syncContactRequestDecision(ctx context.Context, requestID string, accepted bool, rawMessageHandler RawMessageHandler) error {
m.logger.Info("syncContactRequestDecision", zap.Any("from", requestID)) m.logger.Info("syncContactRequestDecision", zap.Any("from", requestID))
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
@ -2494,12 +2512,14 @@ func (m *Messenger) syncContactRequestDecision(ctx context.Context, requestID st
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CONTACT_REQUEST_DECISION, MessageType: protobuf.ApplicationMetadataMessage_SYNC_CONTACT_REQUEST_DECISION,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -2570,7 +2590,7 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons
} }
// syncPublicChat sync a public chat with paired devices // syncPublicChat sync a public chat with paired devices
func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error { func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat, rawMessageHandler RawMessageHandler) error {
var err error var err error
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
@ -2586,12 +2606,14 @@ func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT, MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -2600,7 +2622,7 @@ func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error
return m.saveChat(chat) return m.saveChat(chat)
} }
func (m *Messenger) syncClearHistory(ctx context.Context, publicChat *Chat) error { func (m *Messenger) syncClearHistory(ctx context.Context, publicChat *Chat, rawMessageHandler RawMessageHandler) error {
var err error var err error
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
@ -2617,12 +2639,14 @@ func (m *Messenger) syncClearHistory(ctx context.Context, publicChat *Chat) erro
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CLEAR_HISTORY, MessageType: protobuf.ApplicationMetadataMessage_SYNC_CLEAR_HISTORY,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -2631,7 +2655,7 @@ func (m *Messenger) syncClearHistory(ctx context.Context, publicChat *Chat) erro
return m.saveChat(chat) return m.saveChat(chat)
} }
func (m *Messenger) syncChatRemoving(ctx context.Context, id string) error { func (m *Messenger) syncChatRemoving(ctx context.Context, id string, rawMessageHandler RawMessageHandler) error {
var err error var err error
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
@ -2647,12 +2671,14 @@ func (m *Messenger) syncChatRemoving(ctx context.Context, id string) error {
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CHAT_REMOVED, MessageType: protobuf.ApplicationMetadataMessage_SYNC_CHAT_REMOVED,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -2662,7 +2688,7 @@ func (m *Messenger) syncChatRemoving(ctx context.Context, id string) error {
} }
// syncContact sync as contact with paired devices // syncContact sync as contact with paired devices
func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error { func (m *Messenger) syncContact(ctx context.Context, contact *Contact, rawMessageHandler RawMessageHandler) error {
var err error var err error
if contact.IsSyncing { if contact.IsSyncing {
return nil return nil
@ -2702,12 +2728,14 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT, MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -2716,7 +2744,7 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
return m.saveChat(chat) return m.saveChat(chat)
} }
func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Community) error { func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Community, rawMessageHandler RawMessageHandler) error {
logger := m.logger.Named("syncCommunity") logger := m.logger.Named("syncCommunity")
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
logger.Debug("device has no paired devices") logger.Debug("device has no paired devices")
@ -2747,12 +2775,14 @@ func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Co
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_COMMUNITY, MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_COMMUNITY,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -2762,7 +2792,7 @@ func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Co
return m.saveChat(chat) return m.saveChat(chat)
} }
func (m *Messenger) SyncBookmark(ctx context.Context, bookmark *browsers.Bookmark) error { func (m *Messenger) SyncBookmark(ctx context.Context, bookmark *browsers.Bookmark, rawMessageHandler RawMessageHandler) error {
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
} }
@ -2782,20 +2812,22 @@ func (m *Messenger) SyncBookmark(ctx context.Context, bookmark *browsers.Bookmar
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_BOOKMARK, MessageType: protobuf.ApplicationMetadataMessage_SYNC_BOOKMARK,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
chat.LastClockValue = clock chat.LastClockValue = clock
return m.saveChat(chat) return m.saveChat(chat)
} }
func (m *Messenger) SyncTrustedUser(ctx context.Context, publicKey string, ts verification.TrustStatus) error { func (m *Messenger) SyncTrustedUser(ctx context.Context, publicKey string, ts verification.TrustStatus, rawMessageHandler RawMessageHandler) error {
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
} }
@ -2812,20 +2844,23 @@ func (m *Messenger) SyncTrustedUser(ctx context.Context, publicKey string, ts ve
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_TRUSTED_USER, MessageType: protobuf.ApplicationMetadataMessage_SYNC_TRUSTED_USER,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
chat.LastClockValue = clock chat.LastClockValue = clock
return m.saveChat(chat) return m.saveChat(chat)
} }
func (m *Messenger) SyncVerificationRequest(ctx context.Context, vr *verification.Request) error { func (m *Messenger) SyncVerificationRequest(ctx context.Context, vr *verification.Request, rawMessageHandler RawMessageHandler) error {
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
} }
@ -2848,15 +2883,18 @@ func (m *Messenger) SyncVerificationRequest(ctx context.Context, vr *verificatio
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_VERIFICATION_REQUEST, MessageType: protobuf.ApplicationMetadataMessage_SYNC_VERIFICATION_REQUEST,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
chat.LastClockValue = clock chat.LastClockValue = clock
return m.saveChat(chat) return m.saveChat(chat)
} }
@ -3611,6 +3649,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
logger.Debug("Handling SyncChatRemoved", zap.Any("message", p)) logger.Debug("Handling SyncChatRemoved", zap.Any("message", p))
err := m.HandleSyncChatRemoved(messageState, p) err := m.HandleSyncChatRemoved(messageState, p)
if err != nil { if err != nil {
allMessagesProcessed = false
logger.Warn("failed to handle sync removing chat", zap.Error(err)) logger.Warn("failed to handle sync removing chat", zap.Error(err))
continue continue
} }
@ -3626,7 +3665,8 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
logger.Debug("Handling SyncChatMessagesRead", zap.Any("message", p)) logger.Debug("Handling SyncChatMessagesRead", zap.Any("message", p))
err := m.HandleSyncChatMessagesRead(messageState, p) err := m.HandleSyncChatMessagesRead(messageState, p)
if err != nil { if err != nil {
logger.Warn("failed to handle sync removing chat", zap.Error(err)) allMessagesProcessed = false
logger.Warn("failed to handle sync chat message read", zap.Error(err))
continue continue
} }
@ -4094,7 +4134,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
p := msg.ParsedMessage.Interface().(protobuf.SyncContactRequestDecision) p := msg.ParsedMessage.Interface().(protobuf.SyncContactRequestDecision)
err := m.HandleSyncContactRequestDecision(messageState, p) err := m.HandleSyncContactRequestDecision(messageState, p)
if err != nil { if err != nil {
logger.Warn("failed to handle SyncContactRequestDecisio", zap.Error(err)) logger.Warn("failed to handle SyncContactRequestDecision", zap.Error(err))
continue continue
} }
case protobuf.SyncSavedAddress: case protobuf.SyncSavedAddress:
@ -4186,6 +4226,11 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
} }
} }
return m.saveDataAndPrepareResponse(messageState)
}
func (m *Messenger) saveDataAndPrepareResponse(messageState *ReceivedMessageState) (*MessengerResponse, error) {
var err error
var contactsToSave []*Contact var contactsToSave []*Contact
messageState.ModifiedContacts.Range(func(id string, value bool) (shouldContinue bool) { messageState.ModifiedContacts.Range(func(id string, value bool) (shouldContinue bool) {
contact, ok := messageState.AllContacts.Load(id) contact, ok := messageState.AllContacts.Load(id)
@ -4519,7 +4564,7 @@ func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) (uint64, uint6
return count, countWithMentions, nil return count, countWithMentions, nil
} }
func (m *Messenger) syncChatMessagesRead(ctx context.Context, chatID string, clock uint64) error { func (m *Messenger) syncChatMessagesRead(ctx context.Context, chatID string, clock uint64, rawMessageHandler RawMessageHandler) error {
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
} }
@ -4535,12 +4580,14 @@ func (m *Messenger) syncChatMessagesRead(ctx context.Context, chatID string, clo
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CHAT_MESSAGES_READ, MessageType: protobuf.ApplicationMetadataMessage_SYNC_CHAT_MESSAGES_READ,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
return err return err
} }
@ -4557,7 +4604,7 @@ func (m *Messenger) markAllRead(chatID string, clock uint64, shouldBeSynced bool
} }
if shouldBeSynced { if shouldBeSynced {
err := m.syncChatMessagesRead(context.Background(), chatID, clock) err := m.syncChatMessagesRead(context.Background(), chatID, clock, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }
@ -4676,7 +4723,7 @@ func (m *Messenger) muteChat(chat *Chat, contact *Contact) error {
m.allChats.Store(chat.ID, chat) m.allChats.Store(chat.ID, chat)
if contact != nil { if contact != nil {
err := m.syncContact(context.Background(), contact) err := m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }
@ -4712,7 +4759,7 @@ func (m *Messenger) unmuteChat(chat *Chat, contact *Contact) error {
m.allChats.Store(chat.ID, chat) m.allChats.Store(chat.ID, chat)
if contact != nil { if contact != nil {
err := m.syncContact(context.Background(), contact) err := m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }

View File

@ -14,7 +14,7 @@ func (m *Messenger) AddBookmark(ctx context.Context, bookmark browsers.Bookmark)
if err != nil { if err != nil {
return err return err
} }
return m.SyncBookmark(ctx, &bmr) return m.SyncBookmark(ctx, &bmr, m.dispatchMessage)
} }
func (m *Messenger) RemoveBookmark(ctx context.Context, url string) error { func (m *Messenger) RemoveBookmark(ctx context.Context, url string) error {
@ -28,7 +28,7 @@ func (m *Messenger) RemoveBookmark(ctx context.Context, url string) error {
if err != nil { if err != nil {
return err return err
} }
return m.SyncBookmark(ctx, bmr) return m.SyncBookmark(ctx, bmr, m.dispatchMessage)
} }
func (m *Messenger) UpdateBookmark(ctx context.Context, oldURL string, bookmark browsers.Bookmark) error { func (m *Messenger) UpdateBookmark(ctx context.Context, oldURL string, bookmark browsers.Bookmark) error {
@ -36,7 +36,7 @@ func (m *Messenger) UpdateBookmark(ctx context.Context, oldURL string, bookmark
if err != nil { if err != nil {
return err return err
} }
return m.SyncBookmark(ctx, &bookmark) return m.SyncBookmark(ctx, &bookmark, m.dispatchMessage)
} }
func (m *Messenger) GarbageCollectRemovedBookmarks() error { func (m *Messenger) GarbageCollectRemovedBookmarks() error {

View File

@ -164,7 +164,7 @@ func (m *Messenger) createPublicChat(chatID string, response *MessengerResponse)
// Sync if it was created // Sync if it was created
if !ok || !wasActive { if !ok || !wasActive {
if err := m.syncPublicChat(context.Background(), chat); err != nil { if err := m.syncPublicChat(context.Background(), chat, m.dispatchMessage); err != nil {
return nil, err return nil, err
} }
} }
@ -407,7 +407,7 @@ func (m *Messenger) deactivateChat(chatID string, deactivationClock uint64, shou
// TODO: Remove filters // TODO: Remove filters
if shouldBeSynced { if shouldBeSynced {
err := m.syncChatRemoving(context.Background(), chat.ID) err := m.syncChatRemoving(context.Background(), chat.ID, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -443,7 +443,7 @@ func (m *Messenger) saveChat(chat *Chat) error {
// Sync chat if it's a new active public chat, but not a timeline chat // Sync chat if it's a new active public chat, but not a timeline chat
if !ok && chat.Active && chat.Public() && !chat.ProfileUpdates() && !chat.Timeline() { if !ok && chat.Active && chat.Public() && !chat.ProfileUpdates() && !chat.Timeline() {
if err := m.syncPublicChat(context.Background(), chat); err != nil { if err := m.syncPublicChat(context.Background(), chat, m.dispatchMessage); err != nil {
return err return err
} }
} }
@ -565,7 +565,7 @@ func (m *Messenger) clearHistory(id string) (*MessengerResponse, error) {
} }
} }
err = m.syncClearHistory(context.Background(), chat) err = m.syncClearHistory(context.Background(), chat, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -368,7 +368,7 @@ func (m *Messenger) JoinCommunity(ctx context.Context, communityID types.HexByte
} }
if com, ok := mr.communities[communityID.String()]; ok { if com, ok := mr.communities[communityID.String()]; ok {
err = m.syncCommunity(context.Background(), com) err = m.syncCommunity(context.Background(), com, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -493,7 +493,7 @@ func (m *Messenger) RequestToJoinCommunity(request *requests.RequestToJoinCommun
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = m.syncCommunity(context.Background(), community) err = m.syncCommunity(context.Background(), community, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -822,7 +822,7 @@ func (m *Messenger) LeaveCommunity(communityID types.HexBytes) (*MessengerRespon
m.communitiesManager.StopHistoryArchiveTasksInterval(communityID) m.communitiesManager.StopHistoryArchiveTasksInterval(communityID)
if com, ok := mr.communities[communityID.String()]; ok { if com, ok := mr.communities[communityID.String()]; ok {
err = m.syncCommunity(context.Background(), com) err = m.syncCommunity(context.Background(), com, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1052,7 +1052,7 @@ func (m *Messenger) CreateCommunity(request *requests.CreateCommunity, createDef
response.AddCommunity(community) response.AddCommunity(community)
response.AddCommunitySettings(&communitySettings) response.AddCommunitySettings(&communitySettings)
err = m.syncCommunity(context.Background(), community) err = m.syncCommunity(context.Background(), community, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -85,7 +85,7 @@ func (m *Messenger) SendContactVerificationRequest(ctx context.Context, contactI
} }
// We sync the contact with the other devices // We sync the contact with the other devices
err = m.syncContact(context.Background(), contact) err = m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -100,7 +100,7 @@ func (m *Messenger) SendContactVerificationRequest(ctx context.Context, contactI
return nil, err return nil, err
} }
err = m.SyncVerificationRequest(context.Background(), verifRequest) err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -184,7 +184,7 @@ func (m *Messenger) CancelVerificationRequest(ctx context.Context, id string) (*
} }
// We sync the contact with the other devices // We sync the contact with the other devices
err = m.syncContact(context.Background(), contact) err = m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -210,7 +210,7 @@ func (m *Messenger) CancelVerificationRequest(ctx context.Context, id string) (*
response.AddVerificationRequest(verifRequest) response.AddVerificationRequest(verifRequest)
err = m.SyncVerificationRequest(context.Background(), verifRequest) err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -302,7 +302,7 @@ func (m *Messenger) AcceptContactVerificationRequest(ctx context.Context, id str
return nil, err return nil, err
} }
err = m.SyncVerificationRequest(context.Background(), verifRequest) err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -396,7 +396,7 @@ func (m *Messenger) VerifiedTrusted(ctx context.Context, request *requests.Verif
return nil, err return nil, err
} }
err = m.SyncTrustedUser(context.Background(), contactID, verification.TrustStatusTRUSTED) err = m.SyncTrustedUser(context.Background(), contactID, verification.TrustStatusTRUSTED, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -436,13 +436,13 @@ func (m *Messenger) VerifiedTrusted(ctx context.Context, request *requests.Verif
return nil, err return nil, err
} }
err = m.SyncVerificationRequest(context.Background(), verifRequest) err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// We sync the contact with the other devices // We sync the contact with the other devices
err = m.syncContact(context.Background(), contact) err = m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -501,7 +501,7 @@ func (m *Messenger) VerifiedUntrustworthy(ctx context.Context, request *requests
return nil, err return nil, err
} }
err = m.SyncTrustedUser(context.Background(), contactID, verification.TrustStatusUNTRUSTWORTHY) err = m.SyncTrustedUser(context.Background(), contactID, verification.TrustStatusUNTRUSTWORTHY, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -541,13 +541,13 @@ func (m *Messenger) VerifiedUntrustworthy(ctx context.Context, request *requests
return nil, err return nil, err
} }
err = m.SyncVerificationRequest(context.Background(), verifRequest) err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// We sync the contact with the other devices // We sync the contact with the other devices
err = m.syncContact(context.Background(), contact) err = m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -624,7 +624,7 @@ func (m *Messenger) DeclineContactVerificationRequest(ctx context.Context, id st
response.AddVerificationRequest(verifRequest) response.AddVerificationRequest(verifRequest)
err = m.SyncVerificationRequest(context.Background(), verifRequest) err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -687,7 +687,7 @@ func (m *Messenger) MarkAsTrusted(ctx context.Context, contactID string) error {
return err return err
} }
return m.SyncTrustedUser(ctx, contactID, verification.TrustStatusTRUSTED) return m.SyncTrustedUser(ctx, contactID, verification.TrustStatusTRUSTED, m.dispatchMessage)
} }
func (m *Messenger) MarkAsUntrustworthy(ctx context.Context, contactID string) error { func (m *Messenger) MarkAsUntrustworthy(ctx context.Context, contactID string) error {
@ -696,7 +696,7 @@ func (m *Messenger) MarkAsUntrustworthy(ctx context.Context, contactID string) e
return err return err
} }
return m.SyncTrustedUser(ctx, contactID, verification.TrustStatusUNTRUSTWORTHY) return m.SyncTrustedUser(ctx, contactID, verification.TrustStatusUNTRUSTWORTHY, m.dispatchMessage)
} }
func (m *Messenger) RemoveTrustStatus(ctx context.Context, contactID string) error { func (m *Messenger) RemoveTrustStatus(ctx context.Context, contactID string) error {
@ -705,7 +705,7 @@ func (m *Messenger) RemoveTrustStatus(ctx context.Context, contactID string) err
return err return err
} }
return m.SyncTrustedUser(ctx, contactID, verification.TrustStatusUNKNOWN) return m.SyncTrustedUser(ctx, contactID, verification.TrustStatusUNKNOWN, m.dispatchMessage)
} }
func (m *Messenger) GetTrustStatus(contactID string) (verification.TrustStatus, error) { func (m *Messenger) GetTrustStatus(contactID string) (verification.TrustStatus, error) {
@ -775,7 +775,7 @@ func (m *Messenger) HandleRequestContactVerification(state *ReceivedMessageState
} }
m.logger.Info("SAVED", zap.String("id", persistedVR.ID)) m.logger.Info("SAVED", zap.String("id", persistedVR.ID))
err = m.SyncVerificationRequest(context.Background(), persistedVR) err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }
@ -865,7 +865,7 @@ func (m *Messenger) HandleAcceptContactVerification(state *ReceivedMessageState,
return err return err
} }
err = m.SyncVerificationRequest(context.Background(), persistedVR) err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }
@ -952,7 +952,7 @@ func (m *Messenger) HandleDeclineContactVerification(state *ReceivedMessageState
return err return err
} }
err = m.SyncVerificationRequest(context.Background(), persistedVR) err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }
@ -1005,7 +1005,7 @@ func (m *Messenger) HandleCancelContactVerification(state *ReceivedMessageState,
return err return err
} }
err = m.SyncVerificationRequest(context.Background(), persistedVR) err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }

View File

@ -37,7 +37,7 @@ func (m *Messenger) AcceptContactRequest(ctx context.Context, request *requests.
return nil, err return nil, err
} }
err = m.syncContactRequestDecision(ctx, request.ID.String(), true) err = m.syncContactRequestDecision(ctx, request.ID.String(), true, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -195,7 +195,7 @@ func (m *Messenger) DismissContactRequest(ctx context.Context, request *requests
return nil, err return nil, err
} }
err = m.syncContactRequestDecision(ctx, request.ID.String(), false) err = m.syncContactRequestDecision(ctx, request.ID.String(), false, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -315,7 +315,7 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
if !syncing { if !syncing {
// We sync the contact with the other devices // We sync the contact with the other devices
err := m.syncContact(context.Background(), contact) err := m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -377,7 +377,7 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
} }
// Finally we send a contact update so they are notified we added them // Finally we send a contact update so they are notified we added them
response, err := m.sendContactUpdate(context.Background(), pubKey, displayName, ensName, "") response, err := m.sendContactUpdate(context.Background(), pubKey, displayName, ensName, "", m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -452,7 +452,7 @@ func (m *Messenger) removeContact(ctx context.Context, response *MessengerRespon
return err return err
} }
err = m.syncContact(context.Background(), contact) err = m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }
@ -580,7 +580,7 @@ func (m *Messenger) SetContactLocalNickname(request *requests.SetContactLocalNic
response := &MessengerResponse{} response := &MessengerResponse{}
response.Contacts = []*Contact{contact} response.Contacts = []*Contact{contact}
err = m.syncContact(context.Background(), contact) err = m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -621,7 +621,7 @@ func (m *Messenger) blockContact(contactID string, isDesktopFunc bool) ([]*Chat,
m.allChats.Delete(buildProfileChatID(contact.ID)) m.allChats.Delete(buildProfileChatID(contact.ID))
} }
err = m.syncContact(context.Background(), contact) err = m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -696,7 +696,7 @@ func (m *Messenger) UnblockContact(contactID string) error {
m.allContacts.Store(contact.ID, contact) m.allContacts.Store(contact.ID, contact)
err = m.syncContact(context.Background(), contact) err = m.syncContact(context.Background(), contact, m.dispatchMessage)
if err != nil { if err != nil {
return err return err
} }
@ -719,14 +719,14 @@ func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImag
return err return err
} }
if _, err = m.sendContactUpdate(ctx, myID, displayName, ensName, profileImage); err != nil { if _, err = m.sendContactUpdate(ctx, myID, displayName, ensName, profileImage, m.dispatchMessage); err != nil {
return err return err
} }
// TODO: This should not be sending paired messages, as we do it above // TODO: This should not be sending paired messages, as we do it above
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) { m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
if contact.Added { if contact.Added {
if _, err = m.sendContactUpdate(ctx, contact.ID, displayName, ensName, profileImage); err != nil { if _, err = m.sendContactUpdate(ctx, contact.ID, displayName, ensName, profileImage, m.dispatchMessage); err != nil {
return false return false
} }
} }
@ -748,10 +748,10 @@ func (m *Messenger) SendContactUpdate(ctx context.Context, chatID, ensName, prof
return nil, err return nil, err
} }
return m.sendContactUpdate(ctx, chatID, displayName, ensName, profileImage) return m.sendContactUpdate(ctx, chatID, displayName, ensName, profileImage, m.dispatchMessage)
} }
func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, displayName, ensName, profileImage string) (*MessengerResponse, error) { func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, displayName, ensName, profileImage string, rawMessageHandler RawMessageHandler) (*MessengerResponse, error) {
var response MessengerResponse var response MessengerResponse
contact, ok := m.allContacts.Load(chatID) contact, ok := m.allContacts.Load(chatID)
@ -785,12 +785,14 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, displayName,
return nil, err return nil, err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chatID, LocalChatID: chatID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE, MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -725,7 +725,7 @@ func (m *Messenger) leaveGroupChat(ctx context.Context, response *MessengerRespo
} }
if remove && shouldBeSynced { if remove && shouldBeSynced {
err := m.syncChatRemoving(ctx, chat.ID) err := m.syncChatRemoving(ctx, chat.ID, m.dispatchMessage)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -210,7 +210,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
s.Require().NoError(err) s.Require().NoError(err)
// sync // sync
err = s.m.SyncDevices(context.Background(), "ens-name", "profile-image") err = s.m.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
s.Require().NoError(err) s.Require().NoError(err)
var allChats []*Chat var allChats []*Chat

View File

@ -17,7 +17,7 @@ func (m *Messenger) UpsertSavedAddress(ctx context.Context, sa wallet.SavedAddre
if err != nil { if err != nil {
return err return err
} }
return m.syncNewSavedAddress(ctx, &sa, updatedClock) return m.syncNewSavedAddress(ctx, &sa, updatedClock, m.dispatchMessage)
} }
func (m *Messenger) DeleteSavedAddress(ctx context.Context, chainID uint64, address gethcommon.Address) error { func (m *Messenger) DeleteSavedAddress(ctx context.Context, chainID uint64, address gethcommon.Address) error {
@ -25,14 +25,14 @@ func (m *Messenger) DeleteSavedAddress(ctx context.Context, chainID uint64, addr
if err != nil { if err != nil {
return err return err
} }
return m.syncDeletedSavedAddress(ctx, chainID, address, updatedClock) return m.syncDeletedSavedAddress(ctx, chainID, address, updatedClock, m.dispatchMessage)
} }
func (m *Messenger) garbageCollectRemovedSavedAddresses() error { func (m *Messenger) garbageCollectRemovedSavedAddresses() error {
return m.savedAddressesManager.DeleteSoftRemovedSavedAddresses(uint64(time.Now().AddDate(0, 0, -30).Unix())) return m.savedAddressesManager.DeleteSoftRemovedSavedAddresses(uint64(time.Now().AddDate(0, 0, -30).Unix()))
} }
func (m *Messenger) dispatchSyncSavedAddress(ctx context.Context, syncMessage protobuf.SyncSavedAddress) error { func (m *Messenger) dispatchSyncSavedAddress(ctx context.Context, syncMessage protobuf.SyncSavedAddress, rawMessageHandler RawMessageHandler) error {
if !m.hasPairedDevices() { if !m.hasPairedDevices() {
return nil return nil
} }
@ -44,12 +44,14 @@ func (m *Messenger) dispatchSyncSavedAddress(ctx context.Context, syncMessage pr
return err return err
} }
_, err = m.dispatchMessage(ctx, common.RawMessage{ rawMessage := common.RawMessage{
LocalChatID: chat.ID, LocalChatID: chat.ID,
Payload: encodedMessage, Payload: encodedMessage,
MessageType: protobuf.ApplicationMetadataMessage_SYNC_SAVED_ADDRESS, MessageType: protobuf.ApplicationMetadataMessage_SYNC_SAVED_ADDRESS,
ResendAutomatically: true, ResendAutomatically: true,
}) }
_, err = rawMessageHandler(ctx, rawMessage)
if err != nil { if err != nil {
return err return err
} }
@ -58,32 +60,32 @@ func (m *Messenger) dispatchSyncSavedAddress(ctx context.Context, syncMessage pr
return m.saveChat(chat) return m.saveChat(chat)
} }
func (m *Messenger) syncNewSavedAddress(ctx context.Context, savedAddress *wallet.SavedAddress, updateClock uint64) error { func (m *Messenger) syncNewSavedAddress(ctx context.Context, savedAddress *wallet.SavedAddress, updateClock uint64, rawMessageHandler RawMessageHandler) error {
return m.dispatchSyncSavedAddress(ctx, protobuf.SyncSavedAddress{ return m.dispatchSyncSavedAddress(ctx, protobuf.SyncSavedAddress{
Address: savedAddress.Address.Bytes(), Address: savedAddress.Address.Bytes(),
Name: savedAddress.Name, Name: savedAddress.Name,
Favourite: savedAddress.Favourite, Favourite: savedAddress.Favourite,
ChainId: savedAddress.ChainID, ChainId: savedAddress.ChainID,
UpdateClock: updateClock, UpdateClock: updateClock,
}) }, rawMessageHandler)
} }
func (m *Messenger) syncDeletedSavedAddress(ctx context.Context, chainID uint64, address gethcommon.Address, updateClock uint64) error { func (m *Messenger) syncDeletedSavedAddress(ctx context.Context, chainID uint64, address gethcommon.Address, updateClock uint64, rawMessageHandler RawMessageHandler) error {
return m.dispatchSyncSavedAddress(ctx, protobuf.SyncSavedAddress{ return m.dispatchSyncSavedAddress(ctx, protobuf.SyncSavedAddress{
Address: address.Bytes(), Address: address.Bytes(),
ChainId: chainID, ChainId: chainID,
UpdateClock: updateClock, UpdateClock: updateClock,
Removed: true, Removed: true,
}) }, rawMessageHandler)
} }
func (m *Messenger) syncSavedAddress(ctx context.Context, savedAddress wallet.SavedAddress) (err error) { func (m *Messenger) syncSavedAddress(ctx context.Context, savedAddress wallet.SavedAddress, rawMessageHandler RawMessageHandler) (err error) {
if savedAddress.Removed { if savedAddress.Removed {
if err = m.syncDeletedSavedAddress(ctx, savedAddress.ChainID, savedAddress.Address, savedAddress.UpdateClock); err != nil { if err = m.syncDeletedSavedAddress(ctx, savedAddress.ChainID, savedAddress.Address, savedAddress.UpdateClock, rawMessageHandler); err != nil {
return err return err
} }
} else { } else {
if err = m.syncNewSavedAddress(ctx, &savedAddress, savedAddress.UpdateClock); err != nil { if err = m.syncNewSavedAddress(ctx, &savedAddress, savedAddress.UpdateClock, rawMessageHandler); err != nil {
return err return err
} }
} }

View File

@ -109,7 +109,7 @@ func (s *MessengerSyncBookmarkSuite) TestSyncBookmark() {
s.Require().NoError(err) s.Require().NoError(err)
// sync // sync
err = s.m.SyncBookmark(context.Background(), &bookmark) err = s.m.SyncBookmark(context.Background(), &bookmark, s.m.dispatchMessage)
s.Require().NoError(err) s.Require().NoError(err)
// Wait for the message to reach its destination // Wait for the message to reach its destination
@ -133,7 +133,7 @@ func (s *MessengerSyncBookmarkSuite) TestSyncBookmark() {
// sync removed state // sync removed state
bookmark.Removed = true bookmark.Removed = true
err = s.m.SyncBookmark(context.Background(), &bookmark) err = s.m.SyncBookmark(context.Background(), &bookmark, s.m.dispatchMessage)
s.Require().NoError(err) s.Require().NoError(err)
// Wait for the message to reach its destination // Wait for the message to reach its destination

View File

@ -0,0 +1,210 @@
package protocol
import (
"context"
"github.com/golang/protobuf/proto"
"go.uber.org/zap"
"github.com/status-im/status-go/protocol/common"
localnotifications "github.com/status-im/status-go/services/local-notifications"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/protocol/protobuf"
)
type RawMessageHandler func(ctx context.Context, rawMessage common.RawMessage) (common.RawMessage, error)
func (m *Messenger) HandleSyncRawMessages(rawMessages []*protobuf.RawMessage) error {
state := m.buildMessageState()
for _, rawMessage := range rawMessages {
switch rawMessage.GetMessageType() {
case protobuf.ApplicationMetadataMessage_CONTACT_UPDATE:
var message protobuf.ContactUpdate
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.HandleContactUpdate(state, message)
if err != nil {
m.logger.Warn("failed to HandleContactUpdate when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT:
var message protobuf.SyncInstallationPublicChat
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
addedChat := m.HandleSyncInstallationPublicChat(state, message)
if addedChat != nil {
_, err = m.createPublicChat(addedChat.ID, state.Response)
if err != nil {
m.logger.Error("error createPublicChat when HandleSyncRawMessages", zap.Error(err))
continue
}
}
case protobuf.ApplicationMetadataMessage_SYNC_CHAT_REMOVED:
var message protobuf.SyncChatRemoved
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.HandleSyncChatRemoved(state, message)
if err != nil {
m.logger.Error("failed to HandleSyncChatRemoved when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_CHAT_MESSAGES_READ:
var message protobuf.SyncChatMessagesRead
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.HandleSyncChatMessagesRead(state, message)
if err != nil {
m.logger.Error("failed to HandleSyncChatMessagesRead when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_CLEAR_HISTORY:
var message protobuf.SyncClearHistory
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.handleSyncClearHistory(state, message)
if err != nil {
m.logger.Error("failed to handleSyncClearHistory when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT:
var message protobuf.SyncInstallationContactV2
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.HandleSyncInstallationContact(state, message)
if err != nil {
m.logger.Error("failed to HandleSyncInstallationContact when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_COMMUNITY:
var message protobuf.SyncCommunity
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.handleSyncCommunity(state, message)
if err != nil {
m.logger.Error("failed to handleSyncCommunity when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_BOOKMARK:
var message protobuf.SyncBookmark
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.handleSyncBookmark(state, message)
if err != nil {
m.logger.Error("failed to handleSyncBookmark when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_TRUSTED_USER:
var message protobuf.SyncTrustedUser
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.handleSyncTrustedUser(state, message)
if err != nil {
m.logger.Error("failed to handleSyncTrustedUser when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_VERIFICATION_REQUEST:
var message protobuf.SyncVerificationRequest
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.handleSyncVerificationRequest(state, message)
if err != nil {
m.logger.Error("failed to handleSyncVerificationRequest when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_SETTING:
var message protobuf.SyncSetting
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.handleSyncSetting(state, &message)
if err != nil {
m.logger.Error("failed to handleSyncSetting when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_PROFILE_PICTURE:
var message protobuf.SyncProfilePictures
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.HandleSyncProfilePictures(state, message)
if err != nil {
m.logger.Error("failed to HandleSyncProfilePictures when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_CONTACT_REQUEST_DECISION:
var message protobuf.SyncContactRequestDecision
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.HandleSyncContactRequestDecision(state, message)
if err != nil {
m.logger.Error("failed to HandleSyncContactRequestDecision when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_WALLET_ACCOUNT:
var message protobuf.SyncWalletAccounts
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.HandleSyncWalletAccount(state, message)
if err != nil {
m.logger.Error("failed to HandleSyncWalletAccount when HandleSyncRawMessages", zap.Error(err))
continue
}
case protobuf.ApplicationMetadataMessage_SYNC_SAVED_ADDRESS:
var message protobuf.SyncSavedAddress
err := proto.Unmarshal(rawMessage.GetPayload(), &message)
if err != nil {
return err
}
err = m.handleSyncSavedAddress(state, message)
if err != nil {
m.logger.Error("failed to handleSyncSavedAddress when HandleSyncRawMessages", zap.Error(err))
continue
}
}
}
response, err := m.saveDataAndPrepareResponse(state)
if err != nil {
return err
}
publishMessengerResponse(response)
return nil
}
// this is a copy implementation of the one in ext/service.go, we should refactor this?
func publishMessengerResponse(response *MessengerResponse) {
if !response.IsEmpty() {
notifications := response.Notifications()
// Clear notifications as not used for now
response.ClearNotifications()
signal.SendNewMessages(response)
localnotifications.PushMessages(notifications)
}
}

View File

@ -142,7 +142,7 @@ func (s *MessengerSyncSavedAddressesSuite) TestSyncExistingSavedAddresses() {
s.Require().NoError(err) s.Require().NoError(err)
// Trigger's a sync between devices // Trigger's a sync between devices
err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image") err = s.main.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
s.Require().NoError(err) s.Require().NoError(err)
// Wait and check that saved addresses are synced // Wait and check that saved addresses are synced

View File

@ -52,7 +52,7 @@ func (m *Messenger) prepareSyncSettingsMessages(currentClock uint64) (resultRaw
return return
} }
func (m *Messenger) syncSettings() error { func (m *Messenger) syncSettings(rawMessageHandler RawMessageHandler) error {
logger := m.logger.Named("syncSettings") logger := m.logger.Named("syncSettings")
clock, _ := m.getLastClockWithRelatedChat() clock, _ := m.getLastClockWithRelatedChat()
@ -64,7 +64,7 @@ func (m *Messenger) syncSettings() error {
} }
for _, rm := range rawMessages { for _, rm := range rawMessages {
_, err := m.dispatchMessage(context.Background(), *rm) _, err := rawMessageHandler(context.Background(), *rm)
if err != nil { if err != nil {
logger.Error("dispatchMessage", zap.Error(err)) logger.Error("dispatchMessage", zap.Error(err))
return err return err

View File

@ -96,7 +96,7 @@ func (s *MessengerSyncVerificationRequests) TestSyncVerificationRequests() {
s.Require().NoError(err) s.Require().NoError(err)
// sync // sync
err = s.m.SyncVerificationRequest(context.Background(), request) err = s.m.SyncVerificationRequest(context.Background(), request, s.m.dispatchMessage)
s.Require().NoError(err) s.Require().NoError(err)
// Wait for the message to reach its destination // Wait for the message to reach its destination
@ -160,7 +160,7 @@ func (s *MessengerSyncVerificationRequests) TestSyncTrust() {
s.Require().NoError(err) s.Require().NoError(err)
// sync // sync
err = s.m.SyncTrustedUser(context.Background(), "0x01", verification.TrustStatusTRUSTED) err = s.m.SyncTrustedUser(context.Background(), "0x01", verification.TrustStatusTRUSTED, s.m.dispatchMessage)
s.Require().NoError(err) s.Require().NoError(err)
// Wait for the message to reach its destination // Wait for the message to reach its destination

View File

@ -146,7 +146,7 @@ func (s *MessengerSyncWalletSuite) TestSyncWallets() {
s.Len(acc1, 2, "Must have 2 accounts") s.Len(acc1, 2, "Must have 2 accounts")
// Trigger's a sync between devices // Trigger's a sync between devices
err = s.m.SyncDevices(context.Background(), "ens-name", "profile-image") err = s.m.SyncDevices(context.Background(), "ens-name", "profile-image", nil)
s.Require().NoError(err) s.Require().NoError(err)
err = tt.RetryWithBackOff(func() error { err = tt.RetryWithBackOff(func() error {

View File

@ -2343,6 +2343,117 @@ func (m *BackedUpProfile) GetPictures() []*SyncProfilePicture {
return nil return nil
} }
type RawMessage struct {
Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
MessageType ApplicationMetadataMessage_Type `protobuf:"varint,2,opt,name=messageType,proto3,enum=protobuf.ApplicationMetadataMessage_Type" json:"messageType,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RawMessage) Reset() { *m = RawMessage{} }
func (m *RawMessage) String() string { return proto.CompactTextString(m) }
func (*RawMessage) ProtoMessage() {}
func (*RawMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{29}
}
func (m *RawMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RawMessage.Unmarshal(m, b)
}
func (m *RawMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RawMessage.Marshal(b, m, deterministic)
}
func (m *RawMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_RawMessage.Merge(m, src)
}
func (m *RawMessage) XXX_Size() int {
return xxx_messageInfo_RawMessage.Size(m)
}
func (m *RawMessage) XXX_DiscardUnknown() {
xxx_messageInfo_RawMessage.DiscardUnknown(m)
}
var xxx_messageInfo_RawMessage proto.InternalMessageInfo
func (m *RawMessage) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
func (m *RawMessage) GetMessageType() ApplicationMetadataMessage_Type {
if m != nil {
return m.MessageType
}
return ApplicationMetadataMessage_UNKNOWN
}
type SyncRawMessage struct {
RawMessages []*RawMessage `protobuf:"bytes,1,rep,name=rawMessages,proto3" json:"rawMessages,omitempty"`
// we need these to be able to login
SubAccountsJsonBytes []byte `protobuf:"bytes,2,opt,name=subAccountsJsonBytes,proto3" json:"subAccountsJsonBytes,omitempty"`
SettingsJsonBytes []byte `protobuf:"bytes,3,opt,name=settingsJsonBytes,proto3" json:"settingsJsonBytes,omitempty"`
NodeConfigJsonBytes []byte `protobuf:"bytes,4,opt,name=nodeConfigJsonBytes,proto3" json:"nodeConfigJsonBytes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SyncRawMessage) Reset() { *m = SyncRawMessage{} }
func (m *SyncRawMessage) String() string { return proto.CompactTextString(m) }
func (*SyncRawMessage) ProtoMessage() {}
func (*SyncRawMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_d61ab7221f0b5518, []int{30}
}
func (m *SyncRawMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SyncRawMessage.Unmarshal(m, b)
}
func (m *SyncRawMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SyncRawMessage.Marshal(b, m, deterministic)
}
func (m *SyncRawMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_SyncRawMessage.Merge(m, src)
}
func (m *SyncRawMessage) XXX_Size() int {
return xxx_messageInfo_SyncRawMessage.Size(m)
}
func (m *SyncRawMessage) XXX_DiscardUnknown() {
xxx_messageInfo_SyncRawMessage.DiscardUnknown(m)
}
var xxx_messageInfo_SyncRawMessage proto.InternalMessageInfo
func (m *SyncRawMessage) GetRawMessages() []*RawMessage {
if m != nil {
return m.RawMessages
}
return nil
}
func (m *SyncRawMessage) GetSubAccountsJsonBytes() []byte {
if m != nil {
return m.SubAccountsJsonBytes
}
return nil
}
func (m *SyncRawMessage) GetSettingsJsonBytes() []byte {
if m != nil {
return m.SettingsJsonBytes
}
return nil
}
func (m *SyncRawMessage) GetNodeConfigJsonBytes() []byte {
if m != nil {
return m.NodeConfigJsonBytes
}
return nil
}
func init() { func init() {
proto.RegisterEnum("protobuf.SyncTrustedUser_TrustStatus", SyncTrustedUser_TrustStatus_name, SyncTrustedUser_TrustStatus_value) proto.RegisterEnum("protobuf.SyncTrustedUser_TrustStatus", SyncTrustedUser_TrustStatus_name, SyncTrustedUser_TrustStatus_value)
proto.RegisterEnum("protobuf.SyncVerificationRequest_VerificationStatus", SyncVerificationRequest_VerificationStatus_name, SyncVerificationRequest_VerificationStatus_value) proto.RegisterEnum("protobuf.SyncVerificationRequest_VerificationStatus", SyncVerificationRequest_VerificationStatus_name, SyncVerificationRequest_VerificationStatus_value)
@ -2379,6 +2490,8 @@ func init() {
proto.RegisterType((*SyncVerificationRequest)(nil), "protobuf.SyncVerificationRequest") proto.RegisterType((*SyncVerificationRequest)(nil), "protobuf.SyncVerificationRequest")
proto.RegisterType((*SyncContactRequestDecision)(nil), "protobuf.SyncContactRequestDecision") proto.RegisterType((*SyncContactRequestDecision)(nil), "protobuf.SyncContactRequestDecision")
proto.RegisterType((*BackedUpProfile)(nil), "protobuf.BackedUpProfile") proto.RegisterType((*BackedUpProfile)(nil), "protobuf.BackedUpProfile")
proto.RegisterType((*RawMessage)(nil), "protobuf.RawMessage")
proto.RegisterType((*SyncRawMessage)(nil), "protobuf.SyncRawMessage")
} }
func init() { func init() {
@ -2386,141 +2499,150 @@ func init() {
} }
var fileDescriptor_d61ab7221f0b5518 = []byte{ var fileDescriptor_d61ab7221f0b5518 = []byte{
// 2174 bytes of a gzipped FileDescriptorProto // 2306 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x51, 0x73, 0x1b, 0x49, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0x5f, 0x73, 0x1b, 0x49,
0x11, 0xbe, 0xd5, 0x2a, 0x96, 0xd4, 0x92, 0x6d, 0xdd, 0xe4, 0x2e, 0x51, 0x9c, 0xa4, 0xe2, 0x6c, 0x11, 0xbf, 0x95, 0x14, 0x4b, 0x6a, 0xc9, 0xb2, 0x33, 0xc9, 0x25, 0x8a, 0x93, 0x54, 0x9c, 0x0d,
0x48, 0x5d, 0x1e, 0x0e, 0x1f, 0x95, 0x40, 0x1d, 0x5c, 0xee, 0x8a, 0x53, 0x64, 0x73, 0xd1, 0x25, 0xa9, 0x0b, 0x55, 0xc1, 0x77, 0x95, 0x00, 0x07, 0x97, 0xbb, 0xe2, 0x14, 0xd9, 0x5c, 0x9c, 0x3f,
0x51, 0x5c, 0x63, 0x3b, 0x01, 0x8a, 0xaa, 0xad, 0xf1, 0xee, 0xc4, 0x1a, 0xbc, 0xda, 0x5d, 0x76, 0x8e, 0x6b, 0x6c, 0x27, 0x40, 0x51, 0xb5, 0x35, 0xde, 0x1d, 0x5b, 0x83, 0x57, 0xbb, 0xcb, 0xce,
0x46, 0x0e, 0xcb, 0x0f, 0xe0, 0x07, 0xf0, 0xc2, 0xeb, 0xbd, 0xf3, 0x46, 0xd5, 0xf1, 0xc4, 0x0f, 0xc8, 0x61, 0xf9, 0x00, 0x7c, 0x00, 0x5e, 0x78, 0xbd, 0x77, 0xde, 0xa8, 0x3a, 0x9e, 0xf8, 0x00,
0xe0, 0x8d, 0x07, 0x5e, 0xa8, 0x82, 0xe2, 0x07, 0xf0, 0x2b, 0xa8, 0xe9, 0x99, 0x95, 0x76, 0x65, 0xbc, 0xf1, 0xc0, 0x0b, 0x55, 0x50, 0x7c, 0x00, 0x3e, 0x05, 0x35, 0x3d, 0xb3, 0xda, 0x5d, 0x59,
0xc9, 0x38, 0xc5, 0x13, 0x4f, 0x9a, 0xee, 0xe9, 0xee, 0xed, 0xe9, 0xee, 0xe9, 0xfe, 0x46, 0xb0, 0x32, 0x4e, 0xf1, 0x74, 0x4f, 0x9e, 0xee, 0xe9, 0xee, 0xed, 0xe9, 0xee, 0xe9, 0xfe, 0x8d, 0x0c,
0x9e, 0x32, 0x91, 0x89, 0xf8, 0x64, 0x27, 0xcd, 0x12, 0x95, 0x90, 0x26, 0xfe, 0x1c, 0x4f, 0xdf, 0xcb, 0x09, 0x13, 0xa9, 0x88, 0x8e, 0x37, 0x92, 0x34, 0x56, 0x31, 0x69, 0xe1, 0x9f, 0xc3, 0xc9,
0x6c, 0x5d, 0x95, 0x79, 0x1c, 0xf8, 0x92, 0x2b, 0x25, 0xe2, 0x13, 0x69, 0xb6, 0x3d, 0x06, 0x37, 0xd1, 0xda, 0x15, 0x99, 0x45, 0xbe, 0x27, 0xb9, 0x52, 0x22, 0x3a, 0x96, 0x66, 0x7b, 0xcd, 0x65,
0x7f, 0xc2, 0x55, 0x30, 0x16, 0xf1, 0xc9, 0x13, 0x16, 0x9c, 0xf2, 0xf0, 0x28, 0xdd, 0x65, 0x8a, 0x49, 0x12, 0x0a, 0x9f, 0x29, 0x11, 0x47, 0xde, 0x98, 0x2b, 0x16, 0x30, 0xc5, 0xbc, 0x31, 0x97,
0xed, 0x72, 0xc5, 0x44, 0x24, 0xc9, 0x1d, 0x68, 0x87, 0x4c, 0x31, 0x3f, 0x9e, 0x4e, 0x8e, 0x79, 0x92, 0x1d, 0x73, 0x23, 0xe3, 0x32, 0xb8, 0xf9, 0x53, 0xae, 0xfc, 0x91, 0x88, 0x8e, 0x9f, 0x32,
0xd6, 0x73, 0xb6, 0x9d, 0x07, 0xeb, 0x14, 0x34, 0x6b, 0x84, 0x1c, 0x72, 0x17, 0x3a, 0x2a, 0x51, 0xff, 0x84, 0x07, 0x07, 0xc9, 0x26, 0x53, 0x6c, 0x93, 0x2b, 0x26, 0x42, 0x49, 0xee, 0x40, 0x07,
0x2c, 0x2a, 0x24, 0x6a, 0x28, 0xd1, 0x46, 0x9e, 0x11, 0xf1, 0xfe, 0x5e, 0x87, 0x35, 0x6d, 0x7b, 0x95, 0xa2, 0xc9, 0xf8, 0x90, 0xa7, 0x7d, 0x67, 0xdd, 0x79, 0xb0, 0x4c, 0x41, 0xb3, 0x76, 0x90,
0x9a, 0x92, 0x0f, 0xe0, 0x4a, 0x10, 0x25, 0xc1, 0x29, 0x1a, 0xaa, 0x53, 0x43, 0x90, 0x0d, 0xa8, 0x43, 0xee, 0x42, 0x57, 0xc5, 0x8a, 0x85, 0xb9, 0x44, 0x0d, 0x25, 0x3a, 0xc8, 0x33, 0x22, 0xee,
0x89, 0x10, 0x35, 0x5b, 0xb4, 0x26, 0x42, 0xf2, 0x63, 0x68, 0x06, 0x49, 0xac, 0x58, 0xa0, 0x64, 0x3f, 0x1a, 0xb0, 0xa4, 0x6d, 0x4f, 0x12, 0x72, 0x15, 0x2e, 0xf9, 0x61, 0xec, 0x9f, 0xa0, 0xa1,
0xcf, 0xdd, 0x76, 0x1f, 0xb4, 0x1f, 0xde, 0xdb, 0x29, 0x4e, 0xb1, 0x73, 0x90, 0xc7, 0xc1, 0x30, 0x06, 0x35, 0x04, 0xe9, 0x41, 0x4d, 0x04, 0xa8, 0xd9, 0xa6, 0x35, 0x11, 0x90, 0x9f, 0x40, 0xcb,
0x96, 0x8a, 0x45, 0x11, 0x53, 0x22, 0x89, 0x07, 0x46, 0xf2, 0xd5, 0x43, 0x3a, 0x53, 0x22, 0x3f, 0x8f, 0x23, 0xc5, 0x7c, 0x25, 0xfb, 0xf5, 0xf5, 0xfa, 0x83, 0xce, 0xa3, 0x7b, 0x1b, 0xf9, 0x49,
0x82, 0x76, 0x90, 0x4c, 0x26, 0xd3, 0x58, 0x28, 0xc1, 0x65, 0xaf, 0x8e, 0x36, 0xae, 0x57, 0x6d, 0x37, 0xf6, 0xb2, 0xc8, 0xdf, 0x8e, 0xa4, 0x62, 0x61, 0x88, 0x07, 0x1b, 0x1a, 0xc9, 0x37, 0x8f,
0x0c, 0xac, 0x40, 0x4e, 0xcb, 0xb2, 0xe4, 0x25, 0x6c, 0x16, 0x66, 0x6c, 0x0c, 0x7a, 0x57, 0xb6, 0xe8, 0x54, 0x89, 0xfc, 0x18, 0x3a, 0x7e, 0x3c, 0x1e, 0x4f, 0x22, 0xa1, 0x04, 0x97, 0xfd, 0x06,
0x9d, 0x07, 0xed, 0x87, 0xf7, 0xe7, 0xea, 0x17, 0x04, 0x8c, 0x2e, 0x6a, 0x93, 0x23, 0x20, 0x25, 0xda, 0xb8, 0x5e, 0xb5, 0x31, 0xb4, 0x02, 0x19, 0x2d, 0xcb, 0x92, 0xd7, 0xb0, 0x92, 0x9b, 0xb1,
0xfb, 0x85, 0xcd, 0xb5, 0x77, 0xb1, 0xb9, 0xc4, 0x00, 0x79, 0x04, 0x8d, 0x34, 0x4b, 0xde, 0x88, 0x31, 0xe8, 0x5f, 0x5a, 0x77, 0x1e, 0x74, 0x1e, 0xdd, 0x2f, 0xd4, 0xcf, 0x09, 0x18, 0x9d, 0xd5,
0x88, 0xf7, 0x1a, 0x68, 0xeb, 0xc6, 0xdc, 0x56, 0x61, 0x63, 0xdf, 0x08, 0xd0, 0x42, 0x92, 0xbc, 0x26, 0x07, 0x40, 0x4a, 0xf6, 0x73, 0x9b, 0x4b, 0xef, 0x63, 0x73, 0x8e, 0x01, 0xf2, 0x18, 0x9a,
0x80, 0x0d, 0xbb, 0x2c, 0xfc, 0x68, 0xbe, 0x8b, 0x1f, 0x0b, 0xca, 0xe4, 0x13, 0x68, 0xd8, 0x6a, 0x49, 0x1a, 0x1f, 0x89, 0x90, 0xf7, 0x9b, 0x68, 0xeb, 0x46, 0x61, 0x2b, 0xb7, 0xb1, 0x6b, 0x04,
0xea, 0xb5, 0xd0, 0xce, 0x87, 0xd5, 0x10, 0x1f, 0x98, 0x4d, 0x5a, 0x48, 0xe9, 0xe0, 0x16, 0xe5, 0x68, 0x2e, 0x49, 0x5e, 0x41, 0xcf, 0x2e, 0x73, 0x3f, 0x5a, 0xef, 0xe3, 0xc7, 0x8c, 0x32, 0xf9,
0x57, 0x38, 0x00, 0xef, 0x14, 0xdc, 0x05, 0x6d, 0xef, 0xcf, 0x75, 0xe8, 0xbc, 0x98, 0x46, 0x4a, 0x18, 0x9a, 0xb6, 0xe2, 0xfa, 0x6d, 0xb4, 0xf3, 0x61, 0x35, 0xc4, 0x7b, 0x66, 0x93, 0xe6, 0x52,
0xf4, 0x83, 0x20, 0x99, 0xc6, 0x8a, 0x10, 0xa8, 0xc7, 0x6c, 0xc2, 0xb1, 0xbe, 0x5a, 0x14, 0xd7, 0x3a, 0xb8, 0x79, 0x89, 0xe6, 0x0e, 0xc0, 0x7b, 0x05, 0x77, 0x46, 0xdb, 0xfd, 0x4b, 0x03, 0xba,
0xe4, 0x16, 0xb4, 0x94, 0x98, 0x70, 0xa9, 0xd8, 0x24, 0xc5, 0x2a, 0x73, 0xe9, 0x9c, 0xa1, 0x77, 0xaf, 0x26, 0xa1, 0x12, 0x03, 0xdf, 0x8f, 0x27, 0x91, 0x22, 0x04, 0x1a, 0x11, 0x1b, 0x73, 0xac,
0x45, 0xc8, 0x63, 0x25, 0x82, 0x24, 0xee, 0xb9, 0xa8, 0x36, 0x67, 0x90, 0x2f, 0x01, 0x82, 0x24, 0xaf, 0x36, 0xc5, 0x35, 0xb9, 0x05, 0x6d, 0x25, 0xc6, 0x5c, 0x2a, 0x36, 0x4e, 0xb0, 0xca, 0xea,
0x4a, 0x32, 0x7f, 0xcc, 0xe4, 0xd8, 0x16, 0xd2, 0xdd, 0xb9, 0xb3, 0xe5, 0x6f, 0xef, 0x0c, 0x92, 0xb4, 0x60, 0xe8, 0x5d, 0x11, 0xf0, 0x48, 0x09, 0x3f, 0x8e, 0xfa, 0x75, 0x54, 0x2b, 0x18, 0xe4,
0x28, 0x99, 0x66, 0x4f, 0x99, 0x1c, 0xd3, 0x16, 0x2a, 0xe9, 0x25, 0xe9, 0x41, 0x03, 0x89, 0x61, 0x4b, 0x00, 0x3f, 0x0e, 0xe3, 0xd4, 0x1b, 0x31, 0x39, 0xb2, 0x85, 0x74, 0xb7, 0x70, 0xb6, 0xfc,
0x88, 0x85, 0xe4, 0xd2, 0x82, 0x24, 0x1f, 0xc1, 0xe6, 0x29, 0xcf, 0x03, 0x96, 0x85, 0xbe, 0xbd, 0xed, 0x8d, 0x61, 0x1c, 0xc6, 0x93, 0xf4, 0x19, 0x93, 0x23, 0xda, 0x46, 0x25, 0xbd, 0x24, 0x7d,
0xb2, 0x58, 0x16, 0x2d, 0xba, 0x61, 0xd9, 0xfb, 0x86, 0x4b, 0xae, 0x43, 0xe3, 0x94, 0xe7, 0xfe, 0x68, 0x22, 0xb1, 0x1d, 0x60, 0x21, 0xd5, 0x69, 0x4e, 0x92, 0x8f, 0x60, 0xe5, 0x84, 0x67, 0x3e,
0x54, 0x84, 0x98, 0xeb, 0x16, 0x5d, 0x3b, 0xe5, 0xf9, 0x91, 0x08, 0xc9, 0xe7, 0xb0, 0x26, 0x26, 0x4b, 0x03, 0xcf, 0x5e, 0x6b, 0x2c, 0x8b, 0x36, 0xed, 0x59, 0xf6, 0xae, 0xe1, 0x92, 0xeb, 0xd0,
0xec, 0x84, 0xeb, 0x3c, 0x6a, 0xcf, 0xbe, 0xb3, 0xc2, 0xb3, 0x21, 0x9e, 0x47, 0xe5, 0x43, 0x2d, 0x3c, 0xe1, 0x99, 0x37, 0x11, 0x01, 0xe6, 0xba, 0x4d, 0x97, 0x4e, 0x78, 0x76, 0x20, 0x02, 0xf2,
0x4c, 0xad, 0xce, 0x96, 0x07, 0x30, 0x77, 0x59, 0x5f, 0x4d, 0x11, 0x87, 0xfc, 0xd7, 0x3d, 0x67, 0x39, 0x2c, 0x89, 0x31, 0x3b, 0xe6, 0x3a, 0x8f, 0xda, 0xb3, 0xef, 0x2c, 0xf0, 0x6c, 0x1b, 0xcf,
0xdb, 0x7d, 0xe0, 0x52, 0x43, 0x6c, 0xfd, 0xc3, 0x81, 0xf5, 0x8a, 0x76, 0xd9, 0x19, 0xa7, 0xe2, 0xa3, 0xb2, 0x6d, 0x2d, 0x4c, 0xad, 0xce, 0x9a, 0x0b, 0x50, 0xb8, 0xac, 0xaf, 0xa6, 0x88, 0x02,
0x4c, 0x11, 0xfa, 0x5a, 0x29, 0xf4, 0x3d, 0x68, 0xa4, 0x2c, 0x8f, 0x12, 0x16, 0x62, 0x68, 0x3b, 0xfe, 0x9b, 0xbe, 0xb3, 0x5e, 0x7f, 0x50, 0xa7, 0x86, 0x58, 0xfb, 0xa7, 0x03, 0xcb, 0x15, 0xed,
0xb4, 0x20, 0xf5, 0xe7, 0xde, 0x8a, 0x50, 0xe9, 0x98, 0xea, 0xa0, 0x18, 0x82, 0x5c, 0x83, 0xb5, 0xb2, 0x33, 0x4e, 0xc5, 0x99, 0x3c, 0xf4, 0xb5, 0x52, 0xe8, 0xfb, 0xd0, 0x4c, 0x58, 0x16, 0xc6,
0x31, 0x17, 0x27, 0x63, 0x65, 0x63, 0x65, 0x29, 0xb2, 0x05, 0x4d, 0x5d, 0x78, 0x52, 0xfc, 0x86, 0x2c, 0xc0, 0xd0, 0x76, 0x69, 0x4e, 0xea, 0xcf, 0xbd, 0x13, 0x81, 0xd2, 0x31, 0xd5, 0x41, 0x31,
0x63, 0x8c, 0x5c, 0x3a, 0xa3, 0xc9, 0x3d, 0x58, 0xcf, 0x70, 0xe5, 0x2b, 0x96, 0x9d, 0x70, 0x85, 0x04, 0xb9, 0x06, 0x4b, 0x23, 0x2e, 0x8e, 0x47, 0xca, 0xc6, 0xca, 0x52, 0x64, 0x0d, 0x5a, 0xba,
0x31, 0x72, 0x69, 0xc7, 0x30, 0x0f, 0x91, 0x37, 0x6f, 0x3c, 0xcd, 0x52, 0xe3, 0xf1, 0xfe, 0xe6, 0xf0, 0xa4, 0xf8, 0x2d, 0xc7, 0x18, 0xd5, 0xe9, 0x94, 0x26, 0xf7, 0x60, 0x39, 0xc5, 0x95, 0xa7,
0xc0, 0xd5, 0xe7, 0x49, 0xc0, 0x22, 0x1b, 0xe9, 0x7d, 0xeb, 0xdc, 0x0f, 0xa0, 0x7e, 0xca, 0x73, 0x58, 0x7a, 0xcc, 0x15, 0xc6, 0xa8, 0x4e, 0xbb, 0x86, 0xb9, 0x8f, 0xbc, 0xa2, 0xf1, 0xb4, 0x4a,
0x89, 0xa1, 0xa8, 0xe4, 0x7b, 0x89, 0xf0, 0xce, 0x33, 0x9e, 0x53, 0x14, 0x27, 0x9f, 0x41, 0x67, 0x8d, 0xc7, 0xfd, 0xbb, 0x03, 0x57, 0x5e, 0xc6, 0x3e, 0x0b, 0x6d, 0xa4, 0x77, 0xad, 0x73, 0x3f,
0xa2, 0xc3, 0xce, 0x4c, 0xd8, 0x31, 0x12, 0xed, 0x87, 0xd7, 0x96, 0x27, 0x85, 0x56, 0x64, 0xf5, 0x80, 0xc6, 0x09, 0xcf, 0x24, 0x86, 0xa2, 0x92, 0xef, 0x39, 0xc2, 0x1b, 0x2f, 0x78, 0x46, 0x51,
0x09, 0x53, 0x26, 0xe5, 0xdb, 0x24, 0x0b, 0x6d, 0x15, 0xce, 0xe8, 0xad, 0xef, 0x82, 0xfb, 0x8c, 0x9c, 0x7c, 0x06, 0xdd, 0xb1, 0x0e, 0x3b, 0x33, 0x61, 0xc7, 0x48, 0x74, 0x1e, 0x5d, 0x9b, 0x9f,
0xe7, 0x4b, 0x6b, 0x9b, 0x40, 0x5d, 0x37, 0x63, 0xfc, 0x54, 0x87, 0xe2, 0xda, 0xfb, 0xad, 0x03, 0x14, 0x5a, 0x91, 0xd5, 0x27, 0x4c, 0x98, 0x94, 0xef, 0xe2, 0x34, 0xb0, 0x55, 0x38, 0xa5, 0xd7,
0x5d, 0xed, 0x63, 0xb9, 0x4b, 0xae, 0xe8, 0xbc, 0x1f, 0xc1, 0xa6, 0x28, 0x49, 0xf9, 0xb3, 0x36, 0xbe, 0x07, 0xf5, 0x17, 0x3c, 0x9b, 0x5b, 0xdb, 0x04, 0x1a, 0xba, 0x19, 0xe3, 0xa7, 0xba, 0x14,
0xbc, 0x51, 0x66, 0x0f, 0x43, 0x9c, 0x03, 0xfc, 0x4c, 0x04, 0xdc, 0x57, 0x79, 0xca, 0xad, 0x87, 0xd7, 0xee, 0xef, 0x1c, 0x58, 0xd5, 0x3e, 0x96, 0xbb, 0xe4, 0x82, 0xce, 0xfb, 0x11, 0xac, 0x88,
0x60, 0x58, 0x87, 0x79, 0xca, 0x67, 0xce, 0xd5, 0xe7, 0xce, 0x79, 0xff, 0x76, 0xe0, 0xfa, 0x8a, 0x92, 0x94, 0x37, 0x6d, 0xc3, 0xbd, 0x32, 0x7b, 0x3b, 0xc0, 0x39, 0xc0, 0x4f, 0x85, 0xcf, 0x3d,
0x76, 0x7d, 0xc9, 0x49, 0x70, 0x0f, 0xd6, 0x6d, 0xcf, 0xf1, 0xb1, 0x68, 0xed, 0x87, 0x3b, 0x96, 0x95, 0x25, 0xdc, 0x7a, 0x08, 0x86, 0xb5, 0x9f, 0x25, 0x7c, 0xea, 0x5c, 0xa3, 0x70, 0xce, 0xfd,
0x69, 0x2a, 0xf2, 0x06, 0x34, 0x79, 0x2c, 0xfd, 0xd2, 0xe7, 0x1b, 0x3c, 0x96, 0x23, 0x1d, 0x9e, 0x8f, 0x03, 0xd7, 0x17, 0xb4, 0xeb, 0x0b, 0x4e, 0x82, 0x7b, 0xb0, 0x6c, 0x7b, 0x8e, 0x87, 0x45,
0xbb, 0xd0, 0x89, 0x98, 0x54, 0xfe, 0x34, 0x0d, 0x99, 0xe2, 0xe6, 0x06, 0xd6, 0x69, 0x5b, 0xf3, 0x6b, 0x3f, 0xdc, 0xb5, 0x4c, 0x53, 0x91, 0x37, 0xa0, 0xc5, 0x23, 0xe9, 0x95, 0x3e, 0xdf, 0xe4,
0x8e, 0x0c, 0x4b, 0x9f, 0x4c, 0xe6, 0x52, 0xf1, 0x89, 0xaf, 0xd8, 0x89, 0x6e, 0xcc, 0xae, 0x3e, 0x91, 0xdc, 0xd1, 0xe1, 0xb9, 0x0b, 0xdd, 0x90, 0x49, 0xe5, 0x4d, 0x92, 0x80, 0x29, 0x6e, 0x6e,
0x99, 0x61, 0x1d, 0xb2, 0x13, 0x49, 0xee, 0xc3, 0x46, 0xa4, 0xd3, 0xee, 0xc7, 0x22, 0x38, 0xc5, 0x60, 0x83, 0x76, 0x34, 0xef, 0xc0, 0xb0, 0xf4, 0xc9, 0x64, 0x26, 0x15, 0x1f, 0x7b, 0x8a, 0x1d,
0x8f, 0x98, 0x4b, 0xb8, 0x8e, 0xdc, 0x91, 0x65, 0x7a, 0xff, 0x72, 0xe1, 0xc6, 0xca, 0xd9, 0x44, 0xeb, 0xc6, 0x5c, 0xd7, 0x27, 0x33, 0xac, 0x7d, 0x76, 0x2c, 0xc9, 0x7d, 0xe8, 0x85, 0x3a, 0xed,
0xbe, 0x07, 0x1f, 0x94, 0x1d, 0xf1, 0x51, 0x37, 0xca, 0xed, 0xe9, 0x49, 0xc9, 0xa1, 0xe7, 0x66, 0x5e, 0x24, 0xfc, 0x13, 0xfc, 0x88, 0xb9, 0x84, 0xcb, 0xc8, 0xdd, 0xb1, 0x4c, 0xf7, 0xdf, 0x75,
0xe7, 0xff, 0x38, 0x14, 0x3a, 0xb7, 0x2c, 0x0c, 0x79, 0x88, 0x53, 0xa1, 0x49, 0x0d, 0xa1, 0x7b, 0xb8, 0xb1, 0x70, 0x36, 0x91, 0x4f, 0xe0, 0x6a, 0xd9, 0x11, 0x0f, 0x75, 0xc3, 0xcc, 0x9e, 0x9e,
0xc1, 0xb1, 0x4e, 0x32, 0x0f, 0xb1, 0xe9, 0x37, 0x69, 0x41, 0x6a, 0xf9, 0xc9, 0x54, 0xfb, 0xd4, 0x94, 0x1c, 0x7a, 0x69, 0x76, 0xbe, 0xc5, 0xa1, 0xd0, 0xb9, 0x65, 0x41, 0xc0, 0x03, 0x9c, 0x0a,
0x36, 0xf2, 0x48, 0x68, 0xf9, 0x8c, 0x4f, 0x92, 0x33, 0x1e, 0xf6, 0x3a, 0x46, 0xde, 0x92, 0x64, 0x2d, 0x6a, 0x08, 0xdd, 0x0b, 0x0e, 0x75, 0x92, 0x79, 0x80, 0x4d, 0xbf, 0x45, 0x73, 0x52, 0xcb,
0x1b, 0x3a, 0x63, 0x26, 0x7d, 0x34, 0xeb, 0x4f, 0x65, 0x6f, 0x1d, 0xb7, 0x61, 0xcc, 0x64, 0x5f, 0x8f, 0x27, 0xda, 0xa7, 0x8e, 0x91, 0x47, 0x42, 0xcb, 0xa7, 0x7c, 0x1c, 0x9f, 0xf2, 0xa0, 0xdf,
0xb3, 0x8e, 0xf4, 0x64, 0xba, 0x7a, 0xc6, 0x33, 0xf1, 0x46, 0x04, 0xa6, 0xae, 0xa5, 0x62, 0x6a, 0x35, 0xf2, 0x96, 0x24, 0xeb, 0xd0, 0x1d, 0x31, 0xe9, 0xa1, 0x59, 0x6f, 0x22, 0xfb, 0xcb, 0xb8,
0x2a, 0x7b, 0x1b, 0xd8, 0x19, 0x48, 0x79, 0xeb, 0x00, 0x77, 0x10, 0xc6, 0x64, 0x53, 0xa9, 0x0a, 0x0d, 0x23, 0x26, 0x07, 0x9a, 0x75, 0xa0, 0x27, 0xd3, 0x95, 0x53, 0x9e, 0x8a, 0xa3, 0x1c, 0xfc,
0xc9, 0x4d, 0x94, 0x6c, 0x23, 0xcf, 0x88, 0x78, 0x6f, 0xcf, 0x17, 0x73, 0x31, 0x75, 0x96, 0x17, 0x48, 0xc5, 0xd4, 0x44, 0xf6, 0x7b, 0xd8, 0x19, 0x48, 0x79, 0x6b, 0x0f, 0x77, 0x10, 0xc6, 0xa4,
0xf3, 0xb9, 0x8c, 0xd5, 0x96, 0x64, 0x6c, 0x31, 0x2d, 0xee, 0xb9, 0xb4, 0x78, 0x4f, 0x60, 0x6b, 0x13, 0xa9, 0x72, 0xc9, 0x15, 0x94, 0xec, 0x20, 0xcf, 0x88, 0xb8, 0xef, 0xce, 0x16, 0x73, 0x3e,
0xf1, 0xc3, 0xfb, 0xd3, 0xe3, 0x48, 0x04, 0x83, 0x31, 0xbb, 0xe4, 0x45, 0xf2, 0xbe, 0x75, 0x61, 0x75, 0xe6, 0x17, 0xf3, 0x99, 0x8c, 0xd5, 0xe6, 0x64, 0x6c, 0x36, 0x2d, 0xf5, 0x33, 0x69, 0x71,
0xbd, 0x82, 0x7a, 0xfe, 0xab, 0x5e, 0x07, 0xab, 0xee, 0x0e, 0xb4, 0xd3, 0x4c, 0x9c, 0x31, 0xc5, 0x9f, 0xc2, 0xda, 0xec, 0x87, 0x77, 0x27, 0x87, 0xa1, 0xf0, 0x87, 0x23, 0x76, 0xc1, 0x8b, 0xe4,
0xfd, 0x53, 0x9e, 0xdb, 0x26, 0x0e, 0x96, 0xa5, 0x9b, 0xd2, 0xb6, 0x6e, 0x0c, 0x32, 0xc8, 0x44, 0x7e, 0x53, 0x87, 0xe5, 0x0a, 0xea, 0xf9, 0x9f, 0x7a, 0x5d, 0xac, 0xba, 0x3b, 0xd0, 0x49, 0x52,
0xaa, 0xfd, 0xc2, 0xa2, 0xeb, 0xd0, 0x32, 0x4b, 0xf7, 0xf4, 0x5f, 0x26, 0x22, 0xb6, 0x25, 0xd7, 0x71, 0xca, 0x14, 0xf7, 0x4e, 0x78, 0x66, 0x9b, 0x38, 0x58, 0x96, 0x6e, 0x4a, 0xeb, 0xba, 0x31,
0xa4, 0x96, 0xd2, 0x1d, 0xcf, 0x24, 0x82, 0x87, 0xd8, 0xd3, 0x9b, 0x74, 0x46, 0xcf, 0x2b, 0xa2, 0x48, 0x3f, 0x15, 0x89, 0xf6, 0x0b, 0x8b, 0xae, 0x4b, 0xcb, 0x2c, 0xdd, 0xd3, 0x7f, 0x15, 0x8b,
0x51, 0xae, 0x88, 0x97, 0xd0, 0xcd, 0xf8, 0xaf, 0xa6, 0x5c, 0x2a, 0xe9, 0xab, 0xc4, 0xd7, 0x76, 0xc8, 0x96, 0x5c, 0x8b, 0x5a, 0x4a, 0x77, 0x3c, 0x93, 0x08, 0x1e, 0x60, 0x4f, 0x6f, 0xd1, 0x29,
0xec, 0xe0, 0xbb, 0xbf, 0x0a, 0xdb, 0x59, 0xf1, 0xc3, 0xe4, 0xeb, 0x44, 0xc4, 0x74, 0x23, 0xab, 0x5d, 0x54, 0x44, 0xb3, 0x5c, 0x11, 0xaf, 0x61, 0x35, 0xe5, 0xbf, 0x9e, 0x70, 0xa9, 0xa4, 0xa7,
0xd0, 0xe4, 0x31, 0x34, 0x0b, 0x44, 0x61, 0x11, 0xcc, 0x9d, 0x15, 0x86, 0x2c, 0x94, 0x91, 0x74, 0x62, 0x4f, 0xdb, 0xb1, 0x83, 0xef, 0xfe, 0x22, 0x6c, 0x67, 0xc5, 0xf7, 0xe3, 0xe7, 0xb1, 0x88,
0xa6, 0xa0, 0x81, 0x03, 0x8f, 0x83, 0x2c, 0x4f, 0xd5, 0xac, 0xa2, 0xe7, 0x0c, 0xbd, 0x2b, 0x53, 0x68, 0x2f, 0xad, 0xd0, 0xe4, 0x09, 0xb4, 0x72, 0x44, 0x61, 0x11, 0xcc, 0x9d, 0x05, 0x86, 0x2c,
0x1e, 0x28, 0x36, 0xaf, 0xeb, 0x39, 0x43, 0xf7, 0x5d, 0x2b, 0xaa, 0xab, 0x13, 0x67, 0x4d, 0x07, 0x94, 0x91, 0x74, 0xaa, 0xa0, 0x81, 0x03, 0x8f, 0xfc, 0x34, 0x4b, 0xd4, 0xb4, 0xa2, 0x0b, 0x86,
0x23, 0xb7, 0x31, 0x67, 0x3f, 0xe3, 0xb9, 0xf4, 0xfe, 0xea, 0xc0, 0xcd, 0x0b, 0x4e, 0x64, 0xf3, 0xde, 0x95, 0x09, 0xf7, 0x15, 0x2b, 0xea, 0xba, 0x60, 0xe8, 0xbe, 0x6b, 0x45, 0x75, 0x75, 0xe2,
0xe5, 0xcc, 0xf2, 0x75, 0x1b, 0x20, 0xc5, 0xda, 0xc0, 0x74, 0x99, 0xfc, 0xb7, 0x0c, 0x47, 0x67, 0xac, 0xe9, 0x62, 0xe4, 0x7a, 0x05, 0xfb, 0x05, 0xcf, 0xa4, 0xfb, 0x37, 0x07, 0x6e, 0x9e, 0x73,
0x6b, 0x96, 0x74, 0xb7, 0x9c, 0xf4, 0x0b, 0xba, 0xc6, 0x75, 0x68, 0x04, 0x63, 0xa6, 0xf4, 0x60, 0x22, 0x9b, 0x2f, 0x67, 0x9a, 0xaf, 0xdb, 0x00, 0x09, 0xd6, 0x06, 0xa6, 0xcb, 0xe4, 0xbf, 0x6d,
0xb8, 0x62, 0xa6, 0xbd, 0x26, 0x87, 0xa1, 0xae, 0xdb, 0x02, 0x95, 0xe6, 0x7a, 0x77, 0xcd, 0x24, 0x38, 0x3a, 0x5b, 0xd3, 0xa4, 0xd7, 0xcb, 0x49, 0x3f, 0xa7, 0x6b, 0x5c, 0x87, 0xa6, 0x3f, 0x62,
0x7e, 0xc6, 0x1b, 0x62, 0x12, 0xf5, 0x6d, 0x32, 0x4d, 0xa2, 0x4e, 0x0d, 0xe1, 0xfd, 0xae, 0x06, 0x4a, 0x0f, 0x86, 0x4b, 0x66, 0xda, 0x6b, 0x72, 0x3b, 0xd0, 0x75, 0x9b, 0xa3, 0xd2, 0x4c, 0xef,
0xdd, 0xc5, 0x72, 0x26, 0x5f, 0x94, 0x10, 0xff, 0xb9, 0xa1, 0xbb, 0xa2, 0xab, 0x96, 0xf0, 0xfe, 0x2e, 0x99, 0xc4, 0x4f, 0x79, 0xdb, 0x98, 0x44, 0x7d, 0x9b, 0x4c, 0x93, 0x68, 0x50, 0x43, 0xb8,
0x57, 0xd0, 0xb1, 0xa7, 0xd6, 0xde, 0xc9, 0x5e, 0x6d, 0x11, 0x0d, 0xad, 0xbe, 0x3f, 0xb4, 0x9d, 0xbf, 0xaf, 0xc1, 0xea, 0x6c, 0x39, 0x93, 0x2f, 0x4a, 0x88, 0xff, 0xcc, 0xd0, 0x5d, 0xd0, 0x55,
0xce, 0xd6, 0x92, 0x3c, 0x86, 0x46, 0x31, 0xbc, 0x5d, 0xac, 0x87, 0x0b, 0xdc, 0x28, 0xe6, 0x78, 0x4b, 0x78, 0xff, 0x2b, 0xe8, 0xda, 0x53, 0x6b, 0xef, 0x64, 0xbf, 0x36, 0x8b, 0x86, 0x16, 0xdf,
0xa1, 0xf1, 0x3f, 0xbc, 0x3a, 0xbc, 0x4f, 0x61, 0x13, 0x77, 0xb5, 0x43, 0xb6, 0xc9, 0x5d, 0xee, 0x1f, 0xda, 0x49, 0xa6, 0x6b, 0x49, 0x9e, 0x40, 0x33, 0x1f, 0xde, 0x75, 0xac, 0x87, 0x73, 0xdc,
0x5e, 0x7f, 0x0e, 0x1f, 0x14, 0x8a, 0x2f, 0xb8, 0x94, 0x1a, 0xd7, 0x51, 0xce, 0x2e, 0xab, 0xfd, 0xc8, 0xe7, 0x78, 0xae, 0xf1, 0x7f, 0xbc, 0x3a, 0xdc, 0x4f, 0x61, 0x05, 0x77, 0xb5, 0x43, 0xb6,
0x25, 0x5c, 0xd3, 0xda, 0xfd, 0x40, 0x89, 0x33, 0xa1, 0xf2, 0x01, 0x8f, 0x15, 0xcf, 0x2e, 0xd0, 0xc9, 0x5d, 0xec, 0x5e, 0x7f, 0x0e, 0x57, 0x73, 0xc5, 0x57, 0xe6, 0x5d, 0x27, 0x29, 0x67, 0x17,
0xef, 0x82, 0x2b, 0x42, 0x13, 0xde, 0x0e, 0xd5, 0x4b, 0x6f, 0xd7, 0xf4, 0xa6, 0xaa, 0x85, 0x7e, 0xd5, 0xfe, 0x12, 0xae, 0x69, 0xed, 0x81, 0xaf, 0xc4, 0xa9, 0x50, 0xd9, 0x90, 0x47, 0x8a, 0xa7,
0x10, 0x70, 0xbc, 0x04, 0x97, 0xb5, 0xb2, 0x67, 0x8a, 0xbc, 0x6a, 0x65, 0x57, 0xc8, 0x89, 0x90, 0xe7, 0xe8, 0xaf, 0x42, 0x5d, 0x04, 0x26, 0xbc, 0x5d, 0xaa, 0x97, 0xee, 0xa6, 0xe9, 0x4d, 0x55,
0xf2, 0x1d, 0xcc, 0x7c, 0xe3, 0x40, 0x47, 0xdb, 0x79, 0x92, 0x24, 0xa7, 0x13, 0x96, 0x9d, 0xae, 0x0b, 0x03, 0xdf, 0xe7, 0x78, 0x09, 0x2e, 0x6a, 0x65, 0xcb, 0x14, 0x79, 0xd5, 0xca, 0xa6, 0x90,
0x56, 0x9c, 0x66, 0x91, 0x0d, 0x83, 0x5e, 0xce, 0xc0, 0x8b, 0x5b, 0x42, 0x56, 0x37, 0xa1, 0x85, 0x63, 0x21, 0xe5, 0x7b, 0x98, 0xf9, 0xda, 0x81, 0xae, 0xb6, 0xf3, 0x34, 0x8e, 0x4f, 0xc6, 0x2c,
0x5d, 0xdb, 0xd7, 0xb2, 0xe6, 0x56, 0x34, 0x91, 0x71, 0x94, 0x45, 0xe5, 0xd9, 0x74, 0xa5, 0x3a, 0x3d, 0x59, 0xac, 0x38, 0x49, 0x43, 0x1b, 0x06, 0xbd, 0x9c, 0x82, 0x97, 0x7a, 0x09, 0x59, 0xdd,
0x9b, 0x6e, 0x03, 0x84, 0x3c, 0xe2, 0x7a, 0xc6, 0x33, 0x85, 0xb7, 0xa2, 0x4e, 0x5b, 0x96, 0xd3, 0x84, 0x36, 0x76, 0x6d, 0x4f, 0xcb, 0x9a, 0x5b, 0xd1, 0x42, 0xc6, 0x41, 0x1a, 0x96, 0x67, 0xd3,
0x57, 0xde, 0xd7, 0xa6, 0xf8, 0x07, 0x11, 0x67, 0xd9, 0x53, 0x21, 0x55, 0x92, 0xe5, 0xe5, 0x3b, 0xa5, 0xea, 0x6c, 0xba, 0x0d, 0x10, 0xf0, 0x90, 0xeb, 0x19, 0xcf, 0x14, 0xde, 0x8a, 0x06, 0x6d,
0xe6, 0x54, 0xee, 0xd8, 0x6d, 0x80, 0x40, 0x0b, 0x1a, 0x5b, 0x35, 0x63, 0xcb, 0x72, 0xfa, 0xca, 0x5b, 0xce, 0x40, 0xb9, 0xcf, 0x4d, 0xf1, 0x0f, 0x43, 0xce, 0xd2, 0x67, 0x42, 0xaa, 0x38, 0xcd,
0xfb, 0x8b, 0x03, 0x44, 0x1b, 0xb3, 0xcf, 0xbc, 0x7d, 0x11, 0xa8, 0x69, 0xc6, 0x97, 0xc2, 0xc4, 0xca, 0x77, 0xcc, 0xa9, 0xdc, 0xb1, 0xdb, 0x00, 0xbe, 0x16, 0x34, 0xb6, 0x6a, 0xc6, 0x96, 0xe5,
0x12, 0x0e, 0xaf, 0xad, 0xc0, 0xe1, 0x2e, 0x3e, 0xdc, 0xcf, 0xe1, 0xf0, 0x3a, 0xb2, 0x0b, 0x1c, 0x0c, 0x94, 0xfb, 0x57, 0x07, 0x88, 0x36, 0x66, 0x9f, 0x79, 0xbb, 0xc2, 0x57, 0x93, 0x94, 0xcf,
0x7e, 0x13, 0x5a, 0x38, 0xcf, 0x10, 0x88, 0x5f, 0xc1, 0x2d, 0x04, 0xe2, 0x07, 0x4b, 0x81, 0xf8, 0x85, 0x89, 0x25, 0x1c, 0x5e, 0x5b, 0x80, 0xc3, 0xeb, 0xf8, 0x70, 0x3f, 0x83, 0xc3, 0x1b, 0xc8,
0x1a, 0x0a, 0xac, 0x00, 0xe2, 0x8d, 0x32, 0x10, 0x1f, 0xc3, 0xd5, 0xf3, 0x27, 0x91, 0xab, 0xdf, 0xce, 0x71, 0xf8, 0x4d, 0x68, 0xe3, 0x3c, 0x43, 0x20, 0x7e, 0x09, 0xb7, 0x10, 0x88, 0xef, 0xcd,
0x1a, 0x3f, 0x84, 0x66, 0x6a, 0x85, 0xec, 0x65, 0xbf, 0x55, 0xbd, 0x67, 0x55, 0x4b, 0x74, 0x26, 0x05, 0xe2, 0x4b, 0x28, 0xb0, 0x00, 0x88, 0x37, 0xcb, 0x40, 0x7c, 0x04, 0x57, 0xce, 0x9e, 0x44,
0xed, 0xfd, 0xa1, 0x06, 0xef, 0x6b, 0x81, 0xd7, 0x2c, 0x8a, 0xb8, 0xba, 0x78, 0x80, 0xf7, 0xa0, 0x2e, 0x7e, 0x6b, 0xfc, 0x08, 0x5a, 0x89, 0x15, 0xb2, 0x97, 0xfd, 0x56, 0xf5, 0x9e, 0x55, 0x2d,
0xc1, 0xc2, 0x30, 0xe3, 0x52, 0x16, 0x51, 0xb3, 0xa4, 0x8e, 0xcf, 0x5b, 0x34, 0x80, 0x61, 0x6b, 0xd1, 0xa9, 0xb4, 0xfb, 0xc7, 0x1a, 0x5c, 0xd6, 0x02, 0x6f, 0x59, 0x18, 0x72, 0x75, 0xfe, 0x00,
0x52, 0x4b, 0xe9, 0xd8, 0xeb, 0xdc, 0x61, 0xd4, 0x9a, 0x14, 0xd7, 0x9a, 0x87, 0x98, 0xd9, 0xf4, 0xef, 0x43, 0x93, 0x05, 0x41, 0xca, 0xa5, 0xcc, 0xa3, 0x66, 0x49, 0x1d, 0x9f, 0x77, 0x68, 0x00,
0x4f, 0x5c, 0x6b, 0xcb, 0x3a, 0xf7, 0x1a, 0x14, 0x98, 0x27, 0x5f, 0x41, 0x6a, 0xe9, 0x94, 0xa9, 0xc3, 0xd6, 0xa2, 0x96, 0xd2, 0xb1, 0xd7, 0xb9, 0xc3, 0xa8, 0xb5, 0x28, 0xae, 0x35, 0x0f, 0x31,
0xb1, 0x05, 0x56, 0xb8, 0xd6, 0xb3, 0x64, 0xd6, 0xc2, 0xf1, 0x01, 0xd3, 0x29, 0xf7, 0xf4, 0x22, 0xb3, 0xe9, 0x9f, 0xb8, 0xd6, 0x96, 0x75, 0xee, 0x35, 0x28, 0x30, 0x4f, 0xbe, 0x9c, 0xd4, 0xd2,
0xdf, 0xad, 0x52, 0xbe, 0xf5, 0x79, 0xf4, 0x2b, 0x13, 0xe7, 0x52, 0x8b, 0x1a, 0x02, 0xb3, 0x2a, 0x09, 0x53, 0x23, 0x0b, 0xac, 0x70, 0xad, 0x67, 0xc9, 0xb4, 0x85, 0xe3, 0x03, 0xa6, 0x5b, 0xee,
0xc2, 0x90, 0xc7, 0x76, 0x20, 0x59, 0x6a, 0x35, 0xd2, 0xf2, 0x5e, 0x98, 0x0a, 0xab, 0x04, 0x4b, 0xe9, 0x79, 0xbe, 0xdb, 0xa5, 0x7c, 0xeb, 0xf3, 0xe8, 0x57, 0x26, 0xce, 0xa5, 0x36, 0x35, 0x04,
0x92, 0x4f, 0xa1, 0x69, 0x7b, 0x5e, 0xd1, 0xad, 0x6f, 0x56, 0xa3, 0x5f, 0x91, 0xa7, 0x33, 0x61, 0x66, 0x55, 0x04, 0x01, 0x8f, 0xec, 0x40, 0xb2, 0xd4, 0x62, 0xa4, 0xe5, 0xbe, 0x32, 0x15, 0x56,
0xef, 0x4f, 0x8e, 0x29, 0xff, 0x03, 0x76, 0xc6, 0xc3, 0xbe, 0x8d, 0x65, 0x29, 0xca, 0x4e, 0x35, 0x09, 0x96, 0x24, 0x9f, 0x42, 0xcb, 0xf6, 0xbc, 0xbc, 0x5b, 0xdf, 0xac, 0x46, 0xbf, 0x22, 0x4f,
0xca, 0xcb, 0x5e, 0x94, 0xb7, 0xa0, 0xf5, 0x86, 0x9d, 0x25, 0xd3, 0x4c, 0x28, 0x6e, 0x83, 0x3f, 0xa7, 0xc2, 0xee, 0x9f, 0x1d, 0x53, 0xfe, 0x7b, 0xec, 0x94, 0x07, 0x03, 0x1b, 0xcb, 0x52, 0x94,
0x67, 0xe8, 0x49, 0x16, 0x8c, 0x99, 0xc0, 0x87, 0x4c, 0x1d, 0x53, 0xd9, 0x40, 0x7a, 0x18, 0x5e, 0x9d, 0x6a, 0x94, 0xe7, 0xbd, 0x28, 0x6f, 0x41, 0xfb, 0x88, 0x9d, 0xc6, 0x93, 0x54, 0x28, 0x6e,
0x70, 0x65, 0xef, 0x42, 0xc7, 0xa0, 0x2f, 0xbf, 0x5c, 0x99, 0x6d, 0xc3, 0x1b, 0x60, 0x7d, 0xfe, 0x83, 0x5f, 0x30, 0xf4, 0x24, 0xf3, 0x47, 0x4c, 0xe0, 0x43, 0xa6, 0x81, 0xa9, 0x6c, 0x22, 0xbd,
0xde, 0x81, 0x0f, 0x97, 0xe2, 0x81, 0x15, 0x95, 0xb3, 0x38, 0x1d, 0xcd, 0x09, 0x2a, 0xd3, 0x71, 0x1d, 0x9c, 0x73, 0x65, 0xef, 0x42, 0xd7, 0xa0, 0x2f, 0xaf, 0x5c, 0x99, 0x1d, 0xc3, 0x1b, 0x62,
0x0f, 0xee, 0x8c, 0x4d, 0x03, 0xf0, 0x59, 0x16, 0x8c, 0xc5, 0x19, 0xf7, 0xe5, 0x34, 0x4d, 0x93, 0x7d, 0xfe, 0xc1, 0x81, 0x0f, 0xe7, 0xe2, 0x81, 0x05, 0x95, 0x33, 0x3b, 0x1d, 0xcd, 0x09, 0x2a,
0x4c, 0xf9, 0x3c, 0x66, 0xc7, 0x91, 0xc5, 0x82, 0x4d, 0x7a, 0xcb, 0x8a, 0xf5, 0x8d, 0xd4, 0x81, 0xd3, 0x71, 0x0b, 0xee, 0x8c, 0x4c, 0x03, 0xf0, 0x58, 0xea, 0x8f, 0xc4, 0x29, 0xf7, 0xe4, 0x24,
0x11, 0xda, 0x33, 0x32, 0xde, 0x1f, 0x1d, 0x33, 0x3a, 0x0e, 0x35, 0x52, 0xd5, 0xd8, 0x97, 0x67, 0x49, 0xe2, 0x54, 0x79, 0x3c, 0x62, 0x87, 0xa1, 0xc5, 0x82, 0x2d, 0x7a, 0xcb, 0x8a, 0x0d, 0x8c,
0x97, 0x7c, 0x5b, 0x7d, 0x01, 0x6b, 0x16, 0xec, 0xea, 0xef, 0x6c, 0x2c, 0x62, 0xa8, 0x92, 0xc1, 0xd4, 0x9e, 0x11, 0xda, 0x32, 0x32, 0xee, 0x9f, 0x1c, 0x33, 0x3a, 0xf6, 0x35, 0x52, 0xd5, 0xd8,
0x9d, 0xc3, 0x39, 0x0c, 0xa6, 0x56, 0xc9, 0xfb, 0x0c, 0xda, 0x25, 0x36, 0x69, 0x43, 0xe3, 0x68, 0x97, 0xa7, 0x17, 0x7c, 0x5b, 0x7d, 0x01, 0x4b, 0x16, 0xec, 0xea, 0xef, 0xf4, 0x66, 0x31, 0x54,
0xf4, 0x6c, 0xf4, 0xf2, 0xf5, 0xa8, 0xfb, 0x9e, 0x26, 0x0e, 0xe9, 0xd1, 0xc1, 0xe1, 0xde, 0x6e, 0xc9, 0xe0, 0xc6, 0x7e, 0x01, 0x83, 0xa9, 0x55, 0x72, 0x3f, 0x83, 0x4e, 0x89, 0x4d, 0x3a, 0xd0,
0xd7, 0x21, 0xef, 0xc3, 0xfa, 0xd1, 0x08, 0xc9, 0xd7, 0x2f, 0xe9, 0xe1, 0xd3, 0x9f, 0x75, 0x6b, 0x3c, 0xd8, 0x79, 0xb1, 0xf3, 0xfa, 0xed, 0xce, 0xea, 0x07, 0x9a, 0xd8, 0xa7, 0x07, 0x7b, 0xfb,
0xde, 0x37, 0xae, 0xc1, 0xd2, 0xaf, 0x4a, 0x40, 0xdc, 0x02, 0x9b, 0x15, 0xce, 0x13, 0xa8, 0xbf, 0x5b, 0x9b, 0xab, 0x0e, 0xb9, 0x0c, 0xcb, 0x07, 0x3b, 0x48, 0xbe, 0x7d, 0x4d, 0xf7, 0x9f, 0xfd,
0xc9, 0x92, 0x49, 0x51, 0x0a, 0x7a, 0xad, 0x0f, 0xa4, 0x12, 0xdb, 0xb3, 0x6b, 0x2a, 0xd1, 0xa5, 0x7c, 0xb5, 0xe6, 0x7e, 0x5d, 0x37, 0x58, 0xfa, 0x4d, 0x09, 0x88, 0x5b, 0x60, 0xb3, 0xc0, 0x79,
0x11, 0x8c, 0x75, 0xe5, 0xc5, 0x27, 0x05, 0x8e, 0x99, 0x33, 0x74, 0x4a, 0x2c, 0xfa, 0x33, 0xed, 0x02, 0x8d, 0xa3, 0x34, 0x1e, 0xe7, 0xa5, 0xa0, 0xd7, 0xfa, 0x40, 0x2a, 0xb6, 0x3d, 0xbb, 0xa6,
0xd4, 0xbe, 0x7f, 0x66, 0xbc, 0x3e, 0xbe, 0xc1, 0x33, 0x2e, 0xd3, 0x24, 0x96, 0xc5, 0xb5, 0x9c, 0x62, 0x5d, 0x1a, 0xfe, 0x48, 0x57, 0x5e, 0x74, 0x9c, 0xe3, 0x98, 0x82, 0xa1, 0x53, 0x62, 0xd1,
0xd1, 0xba, 0x17, 0x67, 0x3c, 0x8d, 0x84, 0x51, 0x36, 0x25, 0xd2, 0xb2, 0x9c, 0xbe, 0x22, 0x7c, 0x9f, 0x69, 0xa7, 0xf6, 0xfd, 0x33, 0xe5, 0x0d, 0xf0, 0x0d, 0x9e, 0x72, 0x99, 0xc4, 0x91, 0xcc,
0xf9, 0x83, 0xa3, 0x89, 0x91, 0xfd, 0x7e, 0x35, 0xb2, 0x4b, 0x4e, 0xbd, 0xf3, 0xea, 0xdc, 0x93, 0xaf, 0xe5, 0x94, 0xd6, 0xbd, 0x38, 0xe5, 0x49, 0x28, 0x8c, 0xb2, 0x29, 0x91, 0xb6, 0xe5, 0x0c,
0x64, 0xe9, 0x33, 0xc5, 0xe4, 0xb0, 0x35, 0x1b, 0xe0, 0x3f, 0x05, 0x72, 0x5e, 0xf3, 0x5c, 0x2e, 0x14, 0xe1, 0xf3, 0x1f, 0x1c, 0x2d, 0x8c, 0xec, 0xf7, 0xab, 0x91, 0x9d, 0x73, 0xea, 0x8d, 0x37,
0xf6, 0xf7, 0x46, 0xbb, 0xc3, 0xd1, 0x57, 0x5d, 0x87, 0x74, 0xa0, 0xd9, 0x1f, 0x0c, 0xf6, 0xf6, 0x67, 0x9e, 0x24, 0x73, 0x9f, 0x29, 0x26, 0x87, 0xed, 0xe9, 0x00, 0xff, 0x19, 0x90, 0xb3, 0x9a,
0x75, 0x66, 0x6a, 0x9a, 0xda, 0xdd, 0x1b, 0x3c, 0x1f, 0x8e, 0xf6, 0x76, 0xbb, 0xae, 0xa6, 0x06, 0x67, 0x72, 0xb1, 0xbb, 0xb5, 0xb3, 0xb9, 0xbd, 0xf3, 0xd5, 0xaa, 0x43, 0xba, 0xd0, 0x1a, 0x0c,
0xfd, 0xd1, 0x60, 0xef, 0xf9, 0xde, 0x6e, 0xb7, 0xee, 0xfd, 0xd3, 0x31, 0x93, 0xbd, 0x00, 0x5b, 0x87, 0x5b, 0xbb, 0x3a, 0x33, 0x35, 0x4d, 0x6d, 0x6e, 0x0d, 0x5f, 0x6e, 0xef, 0x6c, 0x6d, 0xae,
0xc6, 0xcf, 0x5d, 0x1e, 0x08, 0xb9, 0xfa, 0xef, 0x84, 0x5b, 0xd0, 0xb2, 0xf1, 0x1c, 0x16, 0x95, 0xd6, 0x35, 0x35, 0x1c, 0xec, 0x0c, 0xb7, 0x5e, 0x6e, 0x6d, 0xae, 0x36, 0xdc, 0x7f, 0x39, 0x66,
0x36, 0x67, 0x90, 0x5f, 0xc0, 0x66, 0x68, 0xf5, 0xfd, 0x4a, 0xe5, 0x3d, 0x5a, 0xc4, 0x48, 0xcb, 0xb2, 0xe7, 0x60, 0xcb, 0xf8, 0xb9, 0xc9, 0x7d, 0x21, 0x17, 0xff, 0x9c, 0x70, 0x0b, 0xda, 0x36,
0x3e, 0xb9, 0x53, 0x2c, 0x6c, 0x78, 0x36, 0xc2, 0x0a, 0xed, 0x7d, 0x0c, 0x1b, 0x55, 0x89, 0xca, 0x9e, 0xdb, 0x79, 0xa5, 0x15, 0x0c, 0xf2, 0x4b, 0x58, 0x09, 0xac, 0xbe, 0x57, 0xa9, 0xbc, 0xc7,
0x61, 0xdf, 0xab, 0x1c, 0xd6, 0xf1, 0xbe, 0x75, 0x60, 0x73, 0xe1, 0x6f, 0xd2, 0xd5, 0xd3, 0xe6, 0xb3, 0x18, 0x69, 0xde, 0x27, 0x37, 0xf2, 0x85, 0x0d, 0x4f, 0x2f, 0xa8, 0xd0, 0xee, 0x43, 0xe8,
0x2e, 0x74, 0x42, 0x21, 0xd3, 0x88, 0xe5, 0x7e, 0xa9, 0x1f, 0xb5, 0x2d, 0x0f, 0x71, 0xf2, 0xc7, 0x55, 0x25, 0x2a, 0x87, 0xfd, 0xa0, 0x72, 0x58, 0xc7, 0xfd, 0xc6, 0x81, 0x95, 0x99, 0x9f, 0x49,
0x40, 0xca, 0x22, 0x7e, 0x19, 0x65, 0x77, 0x4b, 0x82, 0xd8, 0x4e, 0x2a, 0xe3, 0xab, 0xfe, 0x2e, 0x17, 0x4f, 0x9b, 0xbb, 0xd0, 0x0d, 0x84, 0x4c, 0x42, 0x96, 0x79, 0xa5, 0x7e, 0xd4, 0xb1, 0x3c,
0xe3, 0xeb, 0xc9, 0xfa, 0xcf, 0xdb, 0x3b, 0x9f, 0x3c, 0x2e, 0x64, 0x8f, 0xd7, 0x70, 0xf5, 0xe8, 0xc4, 0xc9, 0x0f, 0x81, 0x94, 0x45, 0xbc, 0x32, 0xca, 0x5e, 0x2d, 0x09, 0x62, 0x3b, 0xa9, 0x8c,
0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x30, 0x89, 0x4d, 0x75, 0xf4, 0x17, 0x00, 0x00, 0xaf, 0xc6, 0x7b, 0x8d, 0x2f, 0x09, 0x40, 0xd9, 0x3b, 0x0b, 0xf5, 0xca, 0x63, 0xdd, 0xa9, 0x8e,
0xf5, 0x17, 0xd0, 0xb1, 0xbf, 0xf3, 0xef, 0xeb, 0xd9, 0x53, 0xc3, 0x38, 0x7f, 0xb7, 0xf8, 0xc8,
0xa0, 0xf8, 0xcf, 0xc0, 0x2b, 0xfb, 0x8f, 0x01, 0x6b, 0x74, 0x43, 0x2b, 0xd0, 0xb2, 0xb6, 0xae,
0x85, 0x9e, 0xf6, 0xaa, 0xf4, 0xe5, 0x1f, 0x42, 0x27, 0x9d, 0x52, 0xf9, 0x14, 0xb8, 0x5a, 0xd8,
0x2f, 0x44, 0x69, 0x59, 0x90, 0x3c, 0x82, 0xab, 0x72, 0x72, 0x98, 0x4f, 0x92, 0xe7, 0x32, 0x8e,
0x9e, 0x66, 0x8a, 0xe7, 0xf3, 0x75, 0xee, 0x1e, 0x79, 0x08, 0x97, 0xf3, 0x47, 0x57, 0xa1, 0x60,
0x5e, 0xa2, 0x67, 0x37, 0xc8, 0x27, 0x70, 0x25, 0x8a, 0x03, 0x3e, 0x8c, 0xa3, 0x23, 0x71, 0x5c,
0xc8, 0x9b, 0x87, 0xe9, 0xbc, 0xad, 0xa7, 0xcb, 0xbf, 0xe8, 0x6c, 0x7c, 0xfc, 0x24, 0x77, 0xfd,
0x70, 0x09, 0x57, 0x8f, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x26, 0xe4, 0xbd, 0xdb, 0x6c, 0x19,
0x00, 0x00,
} }

View File

@ -1,6 +1,7 @@
syntax = "proto3"; syntax = "proto3";
import "sync_settings.proto"; import "sync_settings.proto";
import 'application_metadata_message.proto';
option go_package = "./;protobuf"; option go_package = "./;protobuf";
package protobuf; package protobuf;
@ -279,3 +280,17 @@ message BackedUpProfile {
uint64 display_name_clock = 3; uint64 display_name_clock = 3;
repeated SyncProfilePicture pictures = 4; repeated SyncProfilePicture pictures = 4;
} }
message RawMessage {
bytes payload = 1;
ApplicationMetadataMessage.Type messageType = 2;
}
message SyncRawMessage {
repeated RawMessage rawMessages = 1;
// we need these to be able to login
bytes subAccountsJsonBytes = 2;
bytes settingsJsonBytes = 3;
bytes nodeConfigJsonBytes = 4;
}

View File

@ -8,15 +8,10 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1"
"encoding/pem" "encoding/pem"
"fmt"
"math/big" "math/big"
"net" "net"
"net/url"
"time" "time"
"github.com/status-im/status-go/signal"
) )
var globalCertificate *tls.Certificate = nil var globalCertificate *tls.Certificate = nil
@ -90,7 +85,7 @@ func generateTLSCert() error {
return err return err
} }
cert := GenerateX509Cert(sn, notBefore, notAfter, localhost) cert := GenerateX509Cert(sn, notBefore, notAfter, Localhost)
certPem, keyPem, err := GenerateX509PEMs(cert, priv) certPem, keyPem, err := GenerateX509PEMs(cert, priv)
if err != nil { if err != nil {
return err return err
@ -115,31 +110,6 @@ func PublicTLSCert() (string, error) {
return globalPem, nil return globalPem, nil
} }
func GenerateCertFromKey(pk *ecdsa.PrivateKey, from time.Time, hostname string) (tls.Certificate, []byte, error) {
cert := GenerateX509Cert(makeSerialNumberFromKey(pk), from, from.Add(time.Hour), hostname)
certPem, keyPem, err := GenerateX509PEMs(cert, pk)
if err != nil {
return tls.Certificate{}, nil, err
}
tlsCert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
return tls.Certificate{}, nil, err
}
block, _ := pem.Decode(certPem)
if block == nil {
return tls.Certificate{}, nil, fmt.Errorf("failed to decode certPem")
}
leaf, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return tls.Certificate{}, nil, err
}
tlsCert.Leaf = leaf
return tlsCert, certPem, nil
}
// ToECDSA takes a []byte of D and uses it to create an ecdsa.PublicKey on the elliptic.P256 curve // ToECDSA takes a []byte of D and uses it to create an ecdsa.PublicKey on the elliptic.P256 curve
// this function is basically a P256 curve version of eth-node/crypto.ToECDSA without all the nice validation // this function is basically a P256 curve version of eth-node/crypto.ToECDSA without all the nice validation
func ToECDSA(d []byte) *ecdsa.PrivateKey { func ToECDSA(d []byte) *ecdsa.PrivateKey {
@ -150,82 +120,3 @@ func ToECDSA(d []byte) *ecdsa.PrivateKey {
k.PublicKey.X, k.PublicKey.Y = k.PublicKey.Curve.ScalarBaseMult(d) k.PublicKey.X, k.PublicKey.Y = k.PublicKey.Curve.ScalarBaseMult(d)
return k return k
} }
// verifyCertPublicKey checks that the ecdsa.PublicKey using in a x509.Certificate matches a known ecdsa.PublicKey
func verifyCertPublicKey(cert *x509.Certificate, publicKey *ecdsa.PublicKey) error {
certKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("unexpected public key type, expected ecdsa.PublicKey")
}
if !certKey.Equal(publicKey) {
return fmt.Errorf("server certificate MUST match the given public key")
}
return nil
}
// verifyCertSig checks that a x509.Certificate's Signature verifies against x509.Certificate's PublicKey
// If the x509.Certificate's PublicKey is not an ecdsa.PublicKey an error will be thrown
func verifyCertSig(cert *x509.Certificate) (bool, error) {
var esig struct {
R, S *big.Int
}
if _, err := asn1.Unmarshal(cert.Signature, &esig); err != nil {
return false, err
}
hash := sha256.New()
hash.Write(cert.RawTBSCertificate)
ecKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return false, fmt.Errorf("certificate public is not an ecdsa.PublicKey")
}
verified := ecdsa.Verify(ecKey, hash.Sum(nil), esig.R, esig.S)
return verified, nil
}
// verifyCert verifies an x509.Certificate against a known ecdsa.PublicKey
// combining the checks of verifyCertPublicKey and verifyCertSig.
// If an x509.Certificate fails to verify an error is also thrown
func verifyCert(cert *x509.Certificate, publicKey *ecdsa.PublicKey) error {
err := verifyCertPublicKey(cert, publicKey)
if err != nil {
return err
}
verified, err := verifyCertSig(cert)
if err != nil {
return err
}
if !verified {
return fmt.Errorf("server certificate signature MUST verify")
}
return nil
}
// getServerCert pings a given tls host, extracts and returns its x509.Certificate
// the function expects there to be only 1 certificate
func getServerCert(URL *url.URL) (*x509.Certificate, error) {
conf := &tls.Config{
InsecureSkipVerify: true, // nolint: gosec // Only skip verify to get the server's TLS cert. DO NOT skip for any other reason.
}
conn, err := tls.Dial("tcp", URL.Host, conf)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventConnectionError, Error: err.Error()})
return nil, err
}
defer conn.Close()
// No error on the dial out then the URL.Host is accessible
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess})
certs := conn.ConnectionState().PeerCertificates
if len(certs) != 1 {
return nil, fmt.Errorf("expected 1 TLS certificate, received '%d'", len(certs))
}
return certs[0], nil
}

View File

@ -44,12 +44,12 @@ func (s *CertsSuite) TestGenerateX509Cert() {
notBefore := time.Now() notBefore := time.Now()
notAfter := notBefore.Add(time.Hour) notAfter := notBefore.Add(time.Hour)
c1 := GenerateX509Cert(s.SN, notBefore, notAfter, localhost) c1 := GenerateX509Cert(s.SN, notBefore, notAfter, Localhost)
s.Require().Exactly([]string{localhost}, c1.DNSNames) s.Require().Exactly([]string{Localhost}, c1.DNSNames)
s.Require().Nil(c1.IPAddresses) s.Require().Nil(c1.IPAddresses)
c2 := GenerateX509Cert(s.SN, notBefore, notAfter, defaultIP.String()) c2 := GenerateX509Cert(s.SN, notBefore, notAfter, DefaultIP.String())
s.Require().Len(c2.IPAddresses, 1) s.Require().Len(c2.IPAddresses, 1)
s.Require().Equal(defaultIP.String(), c2.IPAddresses[0].String()) s.Require().Equal(DefaultIP.String(), c2.IPAddresses[0].String())
s.Require().Nil(c2.DNSNames) s.Require().Nil(c2.DNSNames)
} }

View File

@ -1,205 +0,0 @@
package server
import (
"bytes"
"crypto/ecdsa"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"github.com/btcsuite/btcutil/base58"
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/signal"
)
type PairingClient struct {
*http.Client
PayloadManager
baseAddress *url.URL
certPEM []byte
serverPK *ecdsa.PublicKey
serverMode Mode
serverCert *x509.Certificate
serverChallenge []byte
}
func NewPairingClient(c *ConnectionParams, config *PairingPayloadManagerConfig) (*PairingClient, error) {
u, err := c.URL()
if err != nil {
return nil, err
}
serverCert, err := getServerCert(u)
if err != nil {
return nil, err
}
err = verifyCert(serverCert, c.publicKey)
if err != nil {
return nil, err
}
certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCert.Raw})
rootCAs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
if ok := rootCAs.AppendCertsFromPEM(certPem); !ok {
return nil, fmt.Errorf("failed to append certPem to rootCAs")
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: false, // MUST BE FALSE
RootCAs: rootCAs,
},
}
cj, err := cookiejar.New(nil)
if err != nil {
return nil, err
}
pm, err := NewPairingPayloadManager(c.aesKey, config, logutils.ZapLogger().Named("PairingClient"))
if err != nil {
return nil, err
}
return &PairingClient{
Client: &http.Client{Transport: tr, Jar: cj},
baseAddress: u,
certPEM: certPem,
serverCert: serverCert,
serverPK: c.publicKey,
serverMode: c.serverMode,
PayloadManager: pm,
}, nil
}
func (c *PairingClient) PairAccount() error {
switch c.serverMode {
case Receiving:
return c.sendAccountData()
case Sending:
err := c.getChallenge()
if err != nil {
return err
}
return c.receiveAccountData()
default:
return fmt.Errorf("unrecognised server mode '%d'", c.serverMode)
}
}
func (c *PairingClient) sendAccountData() error {
err := c.Mount()
if err != nil {
return err
}
c.baseAddress.Path = pairingReceive
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.PayloadManager.ToSend()))
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error()})
return err
}
if resp.StatusCode != http.StatusOK {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error()})
return fmt.Errorf("status not ok, received '%s'", resp.Status)
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess})
c.PayloadManager.LockPayload()
return nil
}
func (c *PairingClient) receiveAccountData() error {
c.baseAddress.Path = pairingSend
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
if err != nil {
return err
}
if c.serverChallenge != nil {
ec, err := c.PayloadManager.EncryptPlain(c.serverChallenge)
if err != nil {
return err
}
req.Header.Set(sessionChallenge, base58.Encode(ec))
}
resp, err := c.Do(req)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error()})
return err
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("status not ok, received '%s'", resp.Status)
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error()})
return err
}
payload, err := ioutil.ReadAll(resp.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error()})
return err
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess})
err = c.PayloadManager.Receive(payload)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error()})
return err
}
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess})
return nil
}
func (c *PairingClient) getChallenge() error {
c.baseAddress.Path = pairingChallenge
resp, err := c.Get(c.baseAddress.String())
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("status not ok, received '%s'", resp.Status)
}
c.serverChallenge, err = ioutil.ReadAll(resp.Body)
return err
}
func StartUpPairingClient(db *multiaccounts.Database, cs, configJSON string) error {
var conf PairingPayloadSourceConfig
err := json.Unmarshal([]byte(configJSON), &conf)
if err != nil {
return err
}
ccp := new(ConnectionParams)
err = ccp.FromString(cs)
if err != nil {
return err
}
c, err := NewPairingClient(ccp, &PairingPayloadManagerConfig{db, conf})
if err != nil {
return err
}
return c.PairAccount()
}

View File

@ -3,11 +3,8 @@ package server
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand"
"crypto/tls"
"encoding/asn1" "encoding/asn1"
"math/big" "math/big"
"net"
"testing" "testing"
"time" "time"
@ -82,42 +79,6 @@ func (tcc *TestCertComponents) SetupCertComponents(t *testing.T) {
tcc.NotAfter = tcc.NotBefore.Add(time.Hour) tcc.NotAfter = tcc.NotBefore.Add(time.Hour)
} }
type TestPairingServerComponents struct {
EphemeralPK *ecdsa.PrivateKey
EphemeralAES []byte
OutboundIP net.IP
Cert tls.Certificate
PS *PairingServer
}
func (tpsc *TestPairingServerComponents) SetupPairingServerComponents(t *testing.T) {
var err error
// Get 4 key components for tls.cert generation
// 1) Ephemeral private key
tpsc.EphemeralPK, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
// 2) AES encryption key
tpsc.EphemeralAES, err = makeEncryptionKey(tpsc.EphemeralPK)
require.NoError(t, err)
// 3) Device outbound IP address
tpsc.OutboundIP, err = GetOutboundIP()
require.NoError(t, err)
// Generate tls.Certificate and Server
tpsc.Cert, _, err = GenerateCertFromKey(tpsc.EphemeralPK, time.Now(), tpsc.OutboundIP.String())
require.NoError(t, err)
tpsc.PS, err = NewPairingServer(&Config{
PK: &tpsc.EphemeralPK.PublicKey,
EK: tpsc.EphemeralAES,
Cert: &tpsc.Cert,
Hostname: tpsc.OutboundIP.String()})
require.NoError(t, err)
}
type TestLoggerComponents struct { type TestLoggerComponents struct {
Logger *zap.Logger Logger *zap.Logger
} }
@ -125,33 +86,3 @@ type TestLoggerComponents struct {
func (tlc *TestLoggerComponents) SetupLoggerComponents() { func (tlc *TestLoggerComponents) SetupLoggerComponents() {
tlc.Logger = logutils.ZapLogger() tlc.Logger = logutils.ZapLogger()
} }
type MockEncryptOnlyPayloadManager struct {
*PayloadEncryptionManager
}
func NewMockEncryptOnlyPayloadManager(aesKey []byte) (*MockEncryptOnlyPayloadManager, error) {
pem, err := NewPayloadEncryptionManager(aesKey, logutils.ZapLogger())
if err != nil {
return nil, err
}
return &MockEncryptOnlyPayloadManager{
pem,
}, nil
}
func (m *MockEncryptOnlyPayloadManager) Mount() error {
// Make a random payload
data := make([]byte, 32)
_, err := rand.Read(data)
if err != nil {
return err
}
return m.Encrypt(data)
}
func (m *MockEncryptOnlyPayloadManager) Receive(data []byte) error {
return m.Decrypt(data)
}

View File

@ -1,11 +0,0 @@
package server
import (
"crypto/ecdsa"
"github.com/status-im/status-go/protocol/common"
)
func makeEncryptionKey(key *ecdsa.PrivateKey) ([]byte, error) {
return common.MakeECDHSharedKey(key, &key.PublicKey)
}

View File

@ -2,26 +2,21 @@ package server
import ( import (
"bytes" "bytes"
"crypto/rand"
"database/sql" "database/sql"
"image" "image"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"strconv" "strconv"
"time" "time"
"github.com/btcsuite/btcutil/base58"
"go.uber.org/zap" "go.uber.org/zap"
"github.com/status-im/status-go/ipfs" "github.com/status-im/status-go/ipfs"
"github.com/status-im/status-go/multiaccounts" "github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/identity/colorhash" "github.com/status-im/status-go/protocol/identity/colorhash"
"github.com/status-im/status-go/protocol/identity/identicon" "github.com/status-im/status-go/protocol/identity/identicon"
"github.com/status-im/status-go/protocol/identity/ring" "github.com/status-im/status-go/protocol/identity/ring"
"github.com/status-im/status-go/protocol/images" "github.com/status-im/status-go/protocol/images"
"github.com/status-im/status-go/signal"
) )
const ( const (
@ -34,15 +29,6 @@ const (
discordAttachmentsPath = basePath + "/discord/attachments" discordAttachmentsPath = basePath + "/discord/attachments"
// Handler routes for pairing // Handler routes for pairing
pairingBase = "/pairing"
pairingSend = pairingBase + "/send"
pairingReceive = pairingBase + "/receive"
pairingChallenge = pairingBase + "/challenge"
// Session names
sessionChallenge = "challenge"
sessionBlocked = "blocked"
accountImagesPath = "/accountImages" accountImagesPath = "/accountImages"
contactImagesPath = "/contactImages" contactImagesPath = "/contactImages"
) )
@ -398,130 +384,3 @@ func handleIPFS(downloader *ipfs.Downloader, logger *zap.Logger) http.HandlerFun
} }
} }
} }
func handlePairingReceive(ps *PairingServer) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess})
return func(w http.ResponseWriter, r *http.Request) {
payload, err := ioutil.ReadAll(r.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error()})
ps.logger.Error("ioutil.ReadAll(r.Body)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess})
err = ps.PayloadManager.Receive(payload)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error()})
ps.logger.Error("ps.PayloadManager.Receive(payload)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess})
}
}
func handlePairingSend(ps *PairingServer) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess})
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/octet-stream")
_, err := w.Write(ps.PayloadManager.ToSend())
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error()})
ps.logger.Error("w.Write(ps.PayloadManager.ToSend())", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess})
ps.PayloadManager.LockPayload()
}
}
func challengeMiddleware(ps *PairingServer, next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s, err := ps.cookieStore.Get(r, sessionChallenge)
if err != nil {
ps.logger.Error("ps.cookieStore.Get(r, pairingStoreChallenge)", zap.Error(err))
http.Error(w, "error", http.StatusInternalServerError)
return
}
blocked, ok := s.Values[sessionBlocked].(bool)
if ok && blocked {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
// If the request header doesn't include a challenge don't punish the client, just throw a 403
pc := r.Header.Get(sessionChallenge)
if pc == "" {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
c, err := common.Decrypt(base58.Decode(pc), ps.ek)
if err != nil {
ps.logger.Error("c, err := common.Decrypt(rc, ps.ek)", zap.Error(err))
http.Error(w, "error", http.StatusInternalServerError)
return
}
// If the challenge is not in the session store don't punish the client, just throw a 403
challenge, ok := s.Values[sessionChallenge].([]byte)
if !ok {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
// Only if we have both a challenge in the session store and in the request header
// do we entertain blocking the client. Because then we know someone is trying to be sneaky.
if !bytes.Equal(c, challenge) {
s.Values[sessionBlocked] = true
err = s.Save(r, w)
if err != nil {
ps.logger.Error("err = s.Save(r, w)", zap.Error(err))
}
http.Error(w, "forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
}
}
func handlePairingChallenge(ps *PairingServer) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s, err := ps.cookieStore.Get(r, sessionChallenge)
if err != nil {
ps.logger.Error("ps.cookieStore.Get(r, pairingStoreChallenge)", zap.Error(err))
return
}
var challenge []byte
challenge, ok := s.Values[sessionChallenge].([]byte)
if !ok {
challenge = make([]byte, 64)
_, err = rand.Read(challenge)
if err != nil {
ps.logger.Error("_, err = rand.Read(auth)", zap.Error(err))
return
}
s.Values[sessionChallenge] = challenge
err = s.Save(r, w)
if err != nil {
ps.logger.Error("err = s.Save(r, w)", zap.Error(err))
return
}
}
w.Header().Set("Content-Type", "application/octet-stream")
_, err = w.Write(challenge)
if err != nil {
ps.logger.Error("_, err = w.Write(challenge)", zap.Error(err))
return
}
}
}

View File

@ -5,8 +5,8 @@ import (
) )
var ( var (
defaultIP = net.IP{127, 0, 0, 1} DefaultIP = net.IP{127, 0, 0, 1}
localhost = "localhost" Localhost = "Localhost"
) )
func GetOutboundIP() (net.IP, error) { func GetOutboundIP() (net.IP, error) {

View File

@ -1,87 +0,0 @@
package server
import (
"crypto/rand"
"encoding/hex"
"io/ioutil"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
func TestGetOutboundIPSuite(t *testing.T) {
suite.Run(t, new(GetOutboundIPSuite))
}
type GetOutboundIPSuite struct {
suite.Suite
TestPairingServerComponents
}
func (s *GetOutboundIPSuite) SetupSuite() {
s.SetupPairingServerComponents(s.T())
}
func testHandler(t *testing.T) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
say, ok := r.URL.Query()["say"]
if !ok || len(say) == 0 {
say = append(say, "nothing")
}
_, err := w.Write([]byte("Hello I like to be a tls server. You said: `" + say[0] + "` " + time.Now().String()))
if err != nil {
require.NoError(t, err)
}
}
}
func makeThingToSay() (string, error) {
b := make([]byte, 32)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
func (s *GetOutboundIPSuite) TestGetOutboundIPWithFullServerE2e() {
s.PS.mode = Sending
s.PS.SetHandlers(HandlerPatternMap{"/hello": testHandler(s.T())})
err := s.PS.Start()
s.Require().NoError(err)
// Give time for the sever to be ready, hacky I know, I'll iron this out
time.Sleep(100 * time.Millisecond)
// Server generates a QR code connection string
cp, err := s.PS.MakeConnectionParams()
s.Require().NoError(err)
qr := cp.ToString()
// Client reads QR code and parses the connection string
ccp := new(ConnectionParams)
err = ccp.FromString(qr)
s.Require().NoError(err)
c, err := NewPairingClient(ccp, nil)
s.Require().NoError(err)
thing, err := makeThingToSay()
s.Require().NoError(err)
response, err := c.Get(c.baseAddress.String() + "/hello?say=" + thing)
s.Require().NoError(err)
defer response.Body.Close()
content, err := ioutil.ReadAll(response.Body)
s.Require().NoError(err)
s.Require().Equal("Hello I like to be a tls server. You said: `"+thing+"`", string(content[:109]))
}

168
server/pairing/certs.go Normal file
View File

@ -0,0 +1,168 @@
package pairing
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"math/big"
"net"
"net/url"
"time"
"github.com/status-im/status-go/signal"
)
func makeSerialNumberFromKey(pk *ecdsa.PrivateKey) *big.Int {
h := sha256.New()
h.Write(append(pk.D.Bytes(), append(pk.Y.Bytes(), pk.X.Bytes()...)...))
return new(big.Int).SetBytes(h.Sum(nil))
}
func GenerateX509Cert(sn *big.Int, from, to time.Time, hostname string) *x509.Certificate {
c := &x509.Certificate{
SerialNumber: sn,
Subject: pkix.Name{Organization: []string{"Self-signed cert"}},
NotBefore: from,
NotAfter: to,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IsCA: true,
}
ip := net.ParseIP(hostname)
if ip != nil {
c.IPAddresses = []net.IP{ip}
} else {
c.DNSNames = []string{hostname}
}
return c
}
func GenerateX509PEMs(cert *x509.Certificate, key *ecdsa.PrivateKey) (certPem, keyPem []byte, err error) {
derBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &key.PublicKey, key)
if err != nil {
return
}
certPem = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
privBytes, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return
}
keyPem = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privBytes})
return
}
func GenerateCertFromKey(pk *ecdsa.PrivateKey, from time.Time, hostname string) (tls.Certificate, []byte, error) {
cert := GenerateX509Cert(makeSerialNumberFromKey(pk), from, from.Add(time.Hour), hostname)
certPem, keyPem, err := GenerateX509PEMs(cert, pk)
if err != nil {
return tls.Certificate{}, nil, err
}
tlsCert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
return tls.Certificate{}, nil, err
}
block, _ := pem.Decode(certPem)
if block == nil {
return tls.Certificate{}, nil, fmt.Errorf("failed to decode certPem")
}
leaf, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return tls.Certificate{}, nil, err
}
tlsCert.Leaf = leaf
return tlsCert, certPem, nil
}
// verifyCertPublicKey checks that the ecdsa.PublicKey using in a x509.Certificate matches a known ecdsa.PublicKey
func verifyCertPublicKey(cert *x509.Certificate, publicKey *ecdsa.PublicKey) error {
certKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("unexpected public key type, expected ecdsa.PublicKey")
}
if !certKey.Equal(publicKey) {
return fmt.Errorf("server certificate MUST match the given public key")
}
return nil
}
// verifyCertSig checks that a x509.Certificate's Signature verifies against x509.Certificate's PublicKey
// If the x509.Certificate's PublicKey is not an ecdsa.PublicKey an error will be thrown
func verifyCertSig(cert *x509.Certificate) (bool, error) {
var esig struct {
R, S *big.Int
}
if _, err := asn1.Unmarshal(cert.Signature, &esig); err != nil {
return false, err
}
hash := sha256.New()
hash.Write(cert.RawTBSCertificate)
ecKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return false, fmt.Errorf("certificate public is not an ecdsa.PublicKey")
}
verified := ecdsa.Verify(ecKey, hash.Sum(nil), esig.R, esig.S)
return verified, nil
}
// verifyCert verifies an x509.Certificate against a known ecdsa.PublicKey
// combining the checks of verifyCertPublicKey and verifyCertSig.
// If an x509.Certificate fails to verify an error is also thrown
func verifyCert(cert *x509.Certificate, publicKey *ecdsa.PublicKey) error {
err := verifyCertPublicKey(cert, publicKey)
if err != nil {
return err
}
verified, err := verifyCertSig(cert)
if err != nil {
return err
}
if !verified {
return fmt.Errorf("server certificate signature MUST verify")
}
return nil
}
// getServerCert pings a given tls host, extracts and returns its x509.Certificate
// the function expects there to be only 1 certificate
func getServerCert(URL *url.URL) (*x509.Certificate, error) {
conf := &tls.Config{
InsecureSkipVerify: true, // nolint: gosec // Only skip verify to get the server's TLS cert. DO NOT skip for any other reason.
}
conn, err := tls.Dial("tcp", URL.Host, conf)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventConnectionError, Error: err.Error(), Action: ActionConnect})
return nil, err
}
defer conn.Close()
// No error on the dial out then the URL.Host is accessible
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionConnect})
certs := conn.ConnectionState().PeerCertificates
if len(certs) != 1 {
return nil, fmt.Errorf("expected 1 TLS certificate, received '%d'", len(certs))
}
return certs[0], nil
}

300
server/pairing/client.go Normal file
View File

@ -0,0 +1,300 @@
package pairing
import (
"bytes"
"crypto/ecdsa"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"github.com/btcsuite/btcutil/base58"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/signal"
)
type Client struct {
*http.Client
PayloadManager
rawMessagePayloadManager *RawMessagePayloadManager
baseAddress *url.URL
certPEM []byte
serverPK *ecdsa.PublicKey
serverMode Mode
serverCert *x509.Certificate
serverChallenge []byte
}
func NewPairingClient(backend *api.GethStatusBackend, c *ConnectionParams, config *AccountPayloadManagerConfig) (*Client, error) {
u, err := c.URL()
if err != nil {
return nil, err
}
serverCert, err := getServerCert(u)
if err != nil {
return nil, err
}
err = verifyCert(serverCert, c.publicKey)
if err != nil {
return nil, err
}
certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCert.Raw})
rootCAs, err := x509.SystemCertPool()
if err != nil {
return nil, err
}
if ok := rootCAs.AppendCertsFromPEM(certPem); !ok {
return nil, fmt.Errorf("failed to append certPem to rootCAs")
}
tr := &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: false, // MUST BE FALSE
RootCAs: rootCAs,
},
}
cj, err := cookiejar.New(nil)
if err != nil {
return nil, err
}
pm, err := NewAccountPayloadManager(c.aesKey, config, logutils.ZapLogger().Named("Client"))
if err != nil {
return nil, err
}
rmpm, err := NewRawMessagePayloadManager(logutils.ZapLogger().Named("Client"), pm.accountPayload, c.aesKey, backend, config.KeystorePath)
if err != nil {
return nil, err
}
return &Client{
Client: &http.Client{Transport: tr, Jar: cj},
baseAddress: u,
certPEM: certPem,
serverCert: serverCert,
serverPK: c.publicKey,
serverMode: c.serverMode,
PayloadManager: pm,
rawMessagePayloadManager: rmpm,
}, nil
}
func (c *Client) PairAccount() error {
switch c.serverMode {
case Receiving:
return c.sendAccountData()
case Sending:
err := c.getChallenge()
if err != nil {
return err
}
return c.receiveAccountData()
default:
return fmt.Errorf("unrecognised server mode '%d'", c.serverMode)
}
}
func (c *Client) PairSyncDevice() error {
switch c.serverMode {
case Receiving:
return c.sendSyncDeviceData()
case Sending:
return c.receiveSyncDeviceData()
default:
return fmt.Errorf("unrecognised server mode '%d'", c.serverMode)
}
}
func (c *Client) sendSyncDeviceData() error {
err := c.rawMessagePayloadManager.Mount()
if err != nil {
return err
}
c.baseAddress.Path = pairingSyncDeviceReceive
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.rawMessagePayloadManager.ToSend()))
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
return err
}
if resp.StatusCode != http.StatusOK {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
return fmt.Errorf("status not ok, received '%s'", resp.Status)
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
return nil
}
func (c *Client) receiveSyncDeviceData() error {
c.baseAddress.Path = pairingSyncDeviceSend
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
if err != nil {
return err
}
if c.serverChallenge != nil {
ec, err := c.PayloadManager.EncryptPlain(c.serverChallenge)
if err != nil {
return err
}
req.Header.Set(sessionChallenge, base58.Encode(ec))
}
resp, err := c.Do(req)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
return err
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("status not ok, received '%s'", resp.Status)
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
return err
}
payload, err := io.ReadAll(resp.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
return err
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
err = c.rawMessagePayloadManager.Receive(payload)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionSyncDevice})
return err
}
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionSyncDevice})
return nil
}
func (c *Client) sendAccountData() error {
err := c.Mount()
if err != nil {
return err
}
c.baseAddress.Path = pairingReceiveAccount
resp, err := c.Post(c.baseAddress.String(), "application/octet-stream", bytes.NewBuffer(c.PayloadManager.ToSend()))
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
return err
}
if resp.StatusCode != http.StatusOK {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
return fmt.Errorf("status not ok, received '%s'", resp.Status)
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
c.PayloadManager.LockPayload()
return nil
}
func (c *Client) receiveAccountData() error {
c.baseAddress.Path = pairingSendAccount
req, err := http.NewRequest(http.MethodGet, c.baseAddress.String(), nil)
if err != nil {
return err
}
if c.serverChallenge != nil {
ec, err := c.PayloadManager.EncryptPlain(c.serverChallenge)
if err != nil {
return err
}
req.Header.Set(sessionChallenge, base58.Encode(ec))
}
resp, err := c.Do(req)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
return err
}
if resp.StatusCode != http.StatusOK {
err = fmt.Errorf("status not ok, received '%s'", resp.Status)
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
return err
}
payload, err := ioutil.ReadAll(resp.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
return err
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
err = c.PayloadManager.Receive(payload)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingAccount})
return err
}
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingAccount})
return nil
}
func (c *Client) getChallenge() error {
c.baseAddress.Path = pairingChallenge
resp, err := c.Get(c.baseAddress.String())
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("status not ok, received '%s'", resp.Status)
}
c.serverChallenge, err = ioutil.ReadAll(resp.Body)
return err
}
func StartUpPairingClient(backend *api.GethStatusBackend, cs, configJSON string) error {
c, err := setupClient(backend, cs, configJSON)
if err != nil {
return err
}
err = c.PairAccount()
if err != nil {
return err
}
return c.PairSyncDevice()
}
func setupClient(backend *api.GethStatusBackend, cs string, configJSON string) (*Client, error) {
var conf PayloadSourceConfig
err := json.Unmarshal([]byte(configJSON), &conf)
if err != nil {
return nil, err
}
ccp := new(ConnectionParams)
err = ccp.FromString(cs)
if err != nil {
return nil, err
}
c, err := NewPairingClient(backend, ccp, &AccountPayloadManagerConfig{backend.GetMultiaccountDB(), &conf})
if err != nil {
return nil, err
}
return c, nil
}

View File

@ -0,0 +1,165 @@
package pairing
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"encoding/asn1"
"math/big"
"net"
"testing"
"time"
"github.com/btcsuite/btcutil/base58"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/server"
)
const (
X = "7744735542292224619198421067303535767629647588258222392379329927711683109548"
Y = "6855516769916529066379811647277920115118980625614889267697023742462401590771"
D = "38564357061962143106230288374146033267100509055924181407058066820384455255240"
AES = "BbnZ7Gc66t54a9kEFCf7FW8SGQuYypwHVeNkRYeNoqV6"
DB58 = "6jpbvo2ucrtrnpXXF4DQYuysh697isH9ppd2aT8uSRDh"
SN = "91849736469742262272885892667727604096707836853856473239722372976236128900962"
CertTime = "eQUriVtGtkWhPJFeLZjF"
)
type TestKeyComponents struct {
X *big.Int
Y *big.Int
D *big.Int
AES []byte
DBytes []byte
PK *ecdsa.PrivateKey
}
func (tk *TestKeyComponents) SetupKeyComponents(t *testing.T) {
var ok bool
tk.X, ok = new(big.Int).SetString(X, 10)
require.True(t, ok)
tk.Y, ok = new(big.Int).SetString(Y, 10)
require.True(t, ok)
tk.D, ok = new(big.Int).SetString(D, 10)
require.True(t, ok)
tk.AES = base58.Decode(AES)
require.Len(t, tk.AES, 32)
tk.DBytes = base58.Decode(DB58)
require.Exactly(t, tk.D.Bytes(), tk.DBytes)
tk.PK = &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P256(),
X: tk.X,
Y: tk.Y,
},
D: tk.D,
}
}
type TestCertComponents struct {
NotBefore, NotAfter time.Time
SN *big.Int
}
func (tcc *TestCertComponents) SetupCertComponents(t *testing.T) {
var ok bool
tcc.SN, ok = new(big.Int).SetString(SN, 10)
require.True(t, ok)
_, err := asn1.Unmarshal(base58.Decode(CertTime), &tcc.NotBefore)
require.NoError(t, err)
tcc.NotAfter = tcc.NotBefore.Add(time.Hour)
}
type TestPairingServerComponents struct {
EphemeralPK *ecdsa.PrivateKey
EphemeralAES []byte
OutboundIP net.IP
Cert tls.Certificate
PS *Server
}
func (tpsc *TestPairingServerComponents) SetupPairingServerComponents(t *testing.T) {
var err error
// Get 4 key components for tls.cert generation
// 1) Ephemeral private key
tpsc.EphemeralPK, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
require.NoError(t, err)
// 2) AES encryption key
tpsc.EphemeralAES, err = common.MakeECDHSharedKey(tpsc.EphemeralPK, &tpsc.EphemeralPK.PublicKey)
require.NoError(t, err)
// 3) Device outbound IP address
tpsc.OutboundIP, err = server.GetOutboundIP()
require.NoError(t, err)
// Generate tls.Certificate and Server
tpsc.Cert, _, err = GenerateCertFromKey(tpsc.EphemeralPK, time.Now(), tpsc.OutboundIP.String())
require.NoError(t, err)
tpsc.PS, err = NewPairingServer(nil, &Config{
PK: &tpsc.EphemeralPK.PublicKey,
EK: tpsc.EphemeralAES,
Cert: &tpsc.Cert,
Hostname: tpsc.OutboundIP.String(),
AccountPayloadManagerConfig: &AccountPayloadManagerConfig{
PayloadSourceConfig: &PayloadSourceConfig{
KeystorePath: "",
},
}})
require.NoError(t, err)
}
type TestLoggerComponents struct {
Logger *zap.Logger
}
func (tlc *TestLoggerComponents) SetupLoggerComponents() {
tlc.Logger = logutils.ZapLogger()
}
type MockEncryptOnlyPayloadManager struct {
*PayloadEncryptionManager
}
func NewMockEncryptOnlyPayloadManager(aesKey []byte) (*MockEncryptOnlyPayloadManager, error) {
pem, err := NewPayloadEncryptionManager(aesKey, logutils.ZapLogger())
if err != nil {
return nil, err
}
return &MockEncryptOnlyPayloadManager{
pem,
}, nil
}
func (m *MockEncryptOnlyPayloadManager) Mount() error {
// Make a random payload
data := make([]byte, 32)
_, err := rand.Read(data)
if err != nil {
return err
}
return m.Encrypt(data)
}
func (m *MockEncryptOnlyPayloadManager) Receive(data []byte) error {
return m.Decrypt(data)
}

View File

@ -1,4 +1,4 @@
package server package pairing
import ( import (
"crypto/ecdsa" "crypto/ecdsa"

View File

@ -1,9 +1,11 @@
package server package pairing
import ( import (
"testing" "testing"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
internalServer "github.com/status-im/status-go/server"
) )
var ( var (
@ -20,7 +22,7 @@ type ConnectionParamsSuite struct {
TestCertComponents TestCertComponents
TestLoggerComponents TestLoggerComponents
server *PairingServer server *Server
} }
func (s *ConnectionParamsSuite) SetupSuite() { func (s *ConnectionParamsSuite) SetupSuite() {
@ -28,14 +30,14 @@ func (s *ConnectionParamsSuite) SetupSuite() {
s.SetupCertComponents(s.T()) s.SetupCertComponents(s.T())
s.SetupLoggerComponents() s.SetupLoggerComponents()
cert, _, err := GenerateCertFromKey(s.PK, s.NotBefore, defaultIP.String()) cert, _, err := GenerateCertFromKey(s.PK, s.NotBefore, internalServer.DefaultIP.String())
s.Require().NoError(err) s.Require().NoError(err)
bs := NewServer(&cert, defaultIP.String(), nil, s.Logger) bs := internalServer.NewServer(&cert, internalServer.DefaultIP.String(), nil, s.Logger)
err = bs.SetPort(1337) err = bs.SetPort(1337)
s.Require().NoError(err) s.Require().NoError(err)
s.server = &PairingServer{ s.server = &Server{
Server: bs, Server: bs,
pk: &s.PK.PublicKey, pk: &s.PK.PublicKey,
ek: s.AES, ek: s.AES,
@ -62,7 +64,7 @@ func (s *ConnectionParamsSuite) TestConnectionParams_Generate() {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Equal("https://127.0.0.1:1337", u.String()) s.Require().Equal("https://127.0.0.1:1337", u.String())
s.Require().Equal(defaultIP.String(), u.Hostname()) s.Require().Equal(internalServer.DefaultIP.String(), u.Hostname())
s.Require().Equal("1337", u.Port()) s.Require().Equal("1337", u.Port())
s.Require().True(cp.publicKey.Equal(&s.PK.PublicKey)) s.Require().True(cp.publicKey.Equal(&s.PK.PublicKey))

View File

@ -1,4 +1,4 @@
package server package pairing
// EventType type for event types. // EventType type for event types.
type EventType string type EventType string
@ -19,6 +19,15 @@ const (
// Event is a type for transfer events. // Event is a type for transfer events.
type Event struct { type Event struct {
Type EventType `json:"type"` Type EventType `json:"type"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
Action Action `json:"action"`
} }
type Action int
const (
ActionPairingAccount = iota + 1
ActionSyncDevice
ActionConnect
)

205
server/pairing/handlers.go Normal file
View File

@ -0,0 +1,205 @@
package pairing
import (
"bytes"
"crypto/rand"
"io"
"net/http"
"github.com/btcsuite/btcutil/base58"
"go.uber.org/zap"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/signal"
)
const (
// Handler routes for pairing
pairingBase = "/pairing"
pairingSendAccount = pairingBase + "/sendAccount"
pairingReceiveAccount = pairingBase + "/receiveAccount"
pairingChallenge = pairingBase + "/challenge"
pairingSyncDeviceSend = pairingBase + "/sendSyncDevice"
pairingSyncDeviceReceive = pairingBase + "/receiveSyncDevice"
// Session names
sessionChallenge = "challenge"
sessionBlocked = "blocked"
)
func handlePairingReceive(ps *Server) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
payload, err := io.ReadAll(r.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
logger.Error("ioutil.ReadAll(r.Body)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
err = ps.PayloadManager.Receive(payload)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionPairingAccount})
logger.Error("ps.PayloadManager.Receive(payload)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionPairingAccount})
}
}
func handleParingSyncDeviceReceive(ps *Server) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionSyncDevice})
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
payload, err := io.ReadAll(r.Body)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
logger.Error("io.ReadAll(r.Body)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
err = ps.rawMessagePayloadManager.Receive(payload)
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventProcessError, Error: err.Error(), Action: ActionSyncDevice})
logger.Error("ps.rawMessagePayloadManager.Receive(payload)", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventProcessSuccess, Action: ActionSyncDevice})
}
}
func handlePairingSend(ps *Server) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionPairingAccount})
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/octet-stream")
_, err := w.Write(ps.PayloadManager.ToSend())
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionPairingAccount})
logger.Error("w.Write(ps.PayloadManager.ToSend())", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionPairingAccount})
ps.PayloadManager.LockPayload()
}
}
func handlePairingSyncDeviceSend(ps *Server) http.HandlerFunc {
signal.SendLocalPairingEvent(Event{Type: EventConnectionSuccess, Action: ActionSyncDevice})
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/octet-stream")
err := ps.rawMessagePayloadManager.Mount()
if err != nil {
// maybe better to use a new event type here instead of EventTransferError?
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
logger.Error("ps.rawMessagePayloadManager.Mount()", zap.Error(err))
return
}
_, err = w.Write(ps.rawMessagePayloadManager.ToSend())
if err != nil {
signal.SendLocalPairingEvent(Event{Type: EventTransferError, Error: err.Error(), Action: ActionSyncDevice})
logger.Error("w.Write(ps.rawMessagePayloadManager.ToSend())", zap.Error(err))
return
}
signal.SendLocalPairingEvent(Event{Type: EventTransferSuccess, Action: ActionSyncDevice})
ps.rawMessagePayloadManager.LockPayload()
}
}
func challengeMiddleware(ps *Server, next http.Handler) http.HandlerFunc {
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
s, err := ps.cookieStore.Get(r, sessionChallenge)
if err != nil {
logger.Error("ps.cookieStore.Get(r, pairingStoreChallenge)", zap.Error(err))
http.Error(w, "error", http.StatusInternalServerError)
return
}
blocked, ok := s.Values[sessionBlocked].(bool)
if ok && blocked {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
// If the request header doesn't include a challenge don't punish the client, just throw a 403
pc := r.Header.Get(sessionChallenge)
if pc == "" {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
c, err := common.Decrypt(base58.Decode(pc), ps.ek)
if err != nil {
logger.Error("c, err := common.Decrypt(rc, ps.ek)", zap.Error(err))
http.Error(w, "error", http.StatusInternalServerError)
return
}
// If the challenge is not in the session store don't punish the client, just throw a 403
challenge, ok := s.Values[sessionChallenge].([]byte)
if !ok {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
// Only if we have both a challenge in the session store and in the request header
// do we entertain blocking the client. Because then we know someone is trying to be sneaky.
if !bytes.Equal(c, challenge) {
s.Values[sessionBlocked] = true
err = s.Save(r, w)
if err != nil {
logger.Error("err = s.Save(r, w)", zap.Error(err))
}
http.Error(w, "forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
}
}
func handlePairingChallenge(ps *Server) http.HandlerFunc {
logger := ps.GetLogger()
return func(w http.ResponseWriter, r *http.Request) {
s, err := ps.cookieStore.Get(r, sessionChallenge)
if err != nil {
logger.Error("ps.cookieStore.Get(r, pairingStoreChallenge)", zap.Error(err))
return
}
var challenge []byte
challenge, ok := s.Values[sessionChallenge].([]byte)
if !ok {
challenge = make([]byte, 64)
_, err = rand.Read(challenge)
if err != nil {
logger.Error("_, err = rand.Read(auth)", zap.Error(err))
return
}
s.Values[sessionChallenge] = challenge
err = s.Save(r, w)
if err != nil {
logger.Error("err = s.Save(r, w)", zap.Error(err))
return
}
}
w.Header().Set("Content-Type", "application/octet-stream")
_, err = w.Write(challenge)
if err != nil {
logger.Error("_, err = w.Write(challenge)", zap.Error(err))
return
}
}
}

View File

@ -1,4 +1,4 @@
package server package pairing
import ( import (
"crypto/rand" "crypto/rand"
@ -8,6 +8,8 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/status-im/status-go/api"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"go.uber.org/zap" "go.uber.org/zap"
@ -42,32 +44,38 @@ type PayloadManager interface {
LockPayload() LockPayload()
} }
// PairingPayloadSourceConfig represents location and access data of the pairing payload // PayloadSourceConfig represents location and access data of the pairing payload
// ONLY available from the application client // ONLY available from the application client
type PairingPayloadSourceConfig struct { type PayloadSourceConfig struct {
// required
KeystorePath string `json:"keystorePath"` KeystorePath string `json:"keystorePath"`
KeyUID string `json:"keyUID"` // following 2 fields r optional.
Password string `json:"password"` // optional cases:
// 1. server mode is Receiving and server side doesn't contain this info
// 2. server mode is Sending and client side doesn't contain this info
// they are required in other cases
KeyUID string `json:"keyUID"`
Password string `json:"password"`
} }
// PairingPayloadManagerConfig represents the initialisation parameters required for a PairingPayloadManager // AccountPayloadManagerConfig represents the initialisation parameters required for a AccountPayloadManager
type PairingPayloadManagerConfig struct { type AccountPayloadManagerConfig struct {
DB *multiaccounts.Database DB *multiaccounts.Database
PairingPayloadSourceConfig *PayloadSourceConfig
} }
// PairingPayloadManager is responsible for the whole lifecycle of a PairingPayload // AccountPayloadManager is responsible for the whole lifecycle of a AccountPayload
type PairingPayloadManager struct { type AccountPayloadManager struct {
logger *zap.Logger logger *zap.Logger
pp *PairingPayload accountPayload *AccountPayload
*PayloadEncryptionManager *PayloadEncryptionManager
ppm *PairingPayloadMarshaller accountPayloadMarshaller *AccountPayloadMarshaller
ppr PayloadRepository payloadRepository PayloadRepository
} }
// NewPairingPayloadManager generates a new and initialised PairingPayloadManager // NewAccountPayloadManager generates a new and initialised AccountPayloadManager
func NewPairingPayloadManager(aesKey []byte, config *PairingPayloadManagerConfig, logger *zap.Logger) (*PairingPayloadManager, error) { func NewAccountPayloadManager(aesKey []byte, config *AccountPayloadManagerConfig, logger *zap.Logger) (*AccountPayloadManager, error) {
l := logger.Named("PairingPayloadManager") l := logger.Named("AccountPayloadManager")
l.Debug("fired", zap.Binary("aesKey", aesKey), zap.Any("config", config)) l.Debug("fired", zap.Binary("aesKey", aesKey), zap.Any("config", config))
pem, err := NewPayloadEncryptionManager(aesKey, l) pem, err := NewPayloadEncryptionManager(aesKey, l)
@ -75,74 +83,74 @@ func NewPairingPayloadManager(aesKey []byte, config *PairingPayloadManagerConfig
return nil, err return nil, err
} }
// A new SHARED PairingPayload // A new SHARED AccountPayload
p := new(PairingPayload) p := new(AccountPayload)
return &PairingPayloadManager{ return &AccountPayloadManager{
logger: l, logger: l,
pp: p, accountPayload: p,
PayloadEncryptionManager: pem, PayloadEncryptionManager: pem,
ppm: NewPairingPayloadMarshaller(p, l), accountPayloadMarshaller: NewPairingPayloadMarshaller(p, l),
ppr: NewPairingPayloadRepository(p, config), payloadRepository: NewAccountPayloadRepository(p, config),
}, nil }, nil
} }
// Mount loads and prepares the payload to be stored in the PairingPayloadManager's state ready for later access // Mount loads and prepares the payload to be stored in the AccountPayloadManager's state ready for later access
func (ppm *PairingPayloadManager) Mount() error { func (apm *AccountPayloadManager) Mount() error {
l := ppm.logger.Named("Mount()") l := apm.logger.Named("Mount()")
l.Debug("fired") l.Debug("fired")
err := ppm.ppr.LoadFromSource() err := apm.payloadRepository.LoadFromSource()
if err != nil { if err != nil {
return err return err
} }
l.Debug("after LoadFromSource") l.Debug("after LoadFromSource")
pb, err := ppm.ppm.MarshalToProtobuf() pb, err := apm.accountPayloadMarshaller.MarshalToProtobuf()
if err != nil { if err != nil {
return err return err
} }
l.Debug( l.Debug(
"after MarshalToProtobuf", "after MarshalToProtobuf",
zap.Any("ppm.ppm.keys", ppm.ppm.keys), zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.keys", apm.accountPayloadMarshaller.keys),
zap.Any("ppm.ppm.multiaccount", ppm.ppm.multiaccount), zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.multiaccount", apm.accountPayloadMarshaller.multiaccount),
zap.String("ppm.ppm.password", ppm.ppm.password), zap.String("accountPayloadMarshaller.accountPayloadMarshaller.password", apm.accountPayloadMarshaller.password),
zap.Binary("pb", pb), zap.Binary("pb", pb),
) )
return ppm.Encrypt(pb) return apm.Encrypt(pb)
} }
// Receive takes a []byte representing raw data, parses and stores the data // Receive takes a []byte representing raw data, parses and stores the data
func (ppm *PairingPayloadManager) Receive(data []byte) error { func (apm *AccountPayloadManager) Receive(data []byte) error {
l := ppm.logger.Named("Receive()") l := apm.logger.Named("Receive()")
l.Debug("fired") l.Debug("fired")
err := ppm.Decrypt(data) err := apm.Decrypt(data)
if err != nil { if err != nil {
return err return err
} }
l.Debug("after Decrypt") l.Debug("after Decrypt")
err = ppm.ppm.UnmarshalProtobuf(ppm.Received()) err = apm.accountPayloadMarshaller.UnmarshalProtobuf(apm.Received())
if err != nil { if err != nil {
return err return err
} }
l.Debug( l.Debug(
"after UnmarshalProtobuf", "after UnmarshalProtobuf",
zap.Any("ppm.ppm.keys", ppm.ppm.keys), zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.keys", apm.accountPayloadMarshaller.keys),
zap.Any("ppm.ppm.multiaccount", ppm.ppm.multiaccount), zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.multiaccount", apm.accountPayloadMarshaller.multiaccount),
zap.String("ppm.ppm.password", ppm.ppm.password), zap.String("accountPayloadMarshaller.accountPayloadMarshaller.password", apm.accountPayloadMarshaller.password),
zap.Binary("ppm.Received()", ppm.Received()), zap.Binary("accountPayloadMarshaller.Received()", apm.Received()),
) )
return ppm.ppr.StoreToSource() return apm.payloadRepository.StoreToSource()
} }
// ResetPayload resets all payload state managed by the PairingPayloadManager // ResetPayload resets all payload state managed by the AccountPayloadManager
func (ppm *PairingPayloadManager) ResetPayload() { func (apm *AccountPayloadManager) ResetPayload() {
ppm.pp.ResetPayload() apm.accountPayload.ResetPayload()
ppm.PayloadEncryptionManager.ResetPayload() apm.PayloadEncryptionManager.ResetPayload()
} }
// EncryptionPayload represents the plain text and encrypted text of payload data // EncryptionPayload represents the plain text and encrypted text of payload data
@ -247,28 +255,28 @@ func (pem *PayloadEncryptionManager) LockPayload() {
pem.received.lock() pem.received.lock()
} }
// PairingPayload represents the payload structure a PairingServer handles // AccountPayload represents the payload structure a Server handles
type PairingPayload struct { type AccountPayload struct {
keys map[string][]byte keys map[string][]byte
multiaccount *multiaccounts.Account multiaccount *multiaccounts.Account
password string password string
} }
func (pp *PairingPayload) ResetPayload() { func (ap *AccountPayload) ResetPayload() {
*pp = PairingPayload{} *ap = AccountPayload{}
} }
// PairingPayloadMarshaller is responsible for marshalling and unmarshalling PairingServer payload data // AccountPayloadMarshaller is responsible for marshalling and unmarshalling Server payload data
type PairingPayloadMarshaller struct { type AccountPayloadMarshaller struct {
logger *zap.Logger logger *zap.Logger
*PairingPayload *AccountPayload
} }
func NewPairingPayloadMarshaller(p *PairingPayload, logger *zap.Logger) *PairingPayloadMarshaller { func NewPairingPayloadMarshaller(ap *AccountPayload, logger *zap.Logger) *AccountPayloadMarshaller {
return &PairingPayloadMarshaller{logger: logger, PairingPayload: p} return &AccountPayloadMarshaller{logger: logger, AccountPayload: ap}
} }
func (ppm *PairingPayloadMarshaller) MarshalToProtobuf() ([]byte, error) { func (ppm *AccountPayloadMarshaller) MarshalToProtobuf() ([]byte, error) {
return proto.Marshal(&protobuf.LocalPairingPayload{ return proto.Marshal(&protobuf.LocalPairingPayload{
Keys: ppm.accountKeysToProtobuf(), Keys: ppm.accountKeysToProtobuf(),
Multiaccount: ppm.multiaccount.ToProtobuf(), Multiaccount: ppm.multiaccount.ToProtobuf(),
@ -276,7 +284,7 @@ func (ppm *PairingPayloadMarshaller) MarshalToProtobuf() ([]byte, error) {
}) })
} }
func (ppm *PairingPayloadMarshaller) accountKeysToProtobuf() []*protobuf.LocalPairingPayload_Key { func (ppm *AccountPayloadMarshaller) accountKeysToProtobuf() []*protobuf.LocalPairingPayload_Key {
var keys []*protobuf.LocalPairingPayload_Key var keys []*protobuf.LocalPairingPayload_Key
for name, data := range ppm.keys { for name, data := range ppm.keys {
keys = append(keys, &protobuf.LocalPairingPayload_Key{Name: name, Data: data}) keys = append(keys, &protobuf.LocalPairingPayload_Key{Name: name, Data: data})
@ -284,7 +292,7 @@ func (ppm *PairingPayloadMarshaller) accountKeysToProtobuf() []*protobuf.LocalPa
return keys return keys
} }
func (ppm *PairingPayloadMarshaller) UnmarshalProtobuf(data []byte) error { func (ppm *AccountPayloadMarshaller) UnmarshalProtobuf(data []byte) error {
l := ppm.logger.Named("UnmarshalProtobuf()") l := ppm.logger.Named("UnmarshalProtobuf()")
l.Debug("fired") l.Debug("fired")
@ -306,7 +314,7 @@ func (ppm *PairingPayloadMarshaller) UnmarshalProtobuf(data []byte) error {
return nil return nil
} }
func (ppm *PairingPayloadMarshaller) accountKeysFromProtobuf(pbKeys []*protobuf.LocalPairingPayload_Key) { func (ppm *AccountPayloadMarshaller) accountKeysFromProtobuf(pbKeys []*protobuf.LocalPairingPayload_Key) {
l := ppm.logger.Named("accountKeysFromProtobuf()") l := ppm.logger.Named("accountKeysFromProtobuf()")
l.Debug("fired") l.Debug("fired")
@ -320,11 +328,11 @@ func (ppm *PairingPayloadMarshaller) accountKeysFromProtobuf(pbKeys []*protobuf.
l.Debug( l.Debug(
"after for _, key := range pbKeys", "after for _, key := range pbKeys",
zap.Any("pbKeys", pbKeys), zap.Any("pbKeys", pbKeys),
zap.Any("ppm.keys", ppm.keys), zap.Any("accountPayloadMarshaller.keys", ppm.keys),
) )
} }
func (ppm *PairingPayloadMarshaller) multiaccountFromProtobuf(pbMultiAccount *protobuf.MultiAccount) { func (ppm *AccountPayloadMarshaller) multiaccountFromProtobuf(pbMultiAccount *protobuf.MultiAccount) {
ppm.multiaccount = new(multiaccounts.Account) ppm.multiaccount = new(multiaccounts.Account)
ppm.multiaccount.FromProtobuf(pbMultiAccount) ppm.multiaccount.FromProtobuf(pbMultiAccount)
} }
@ -334,18 +342,18 @@ type PayloadRepository interface {
StoreToSource() error StoreToSource() error
} }
// PairingPayloadRepository is responsible for loading, parsing, validating and storing PairingServer payload data // AccountPayloadRepository is responsible for loading, parsing, validating and storing Server payload data
type PairingPayloadRepository struct { type AccountPayloadRepository struct {
*PairingPayload *AccountPayload
multiaccountsDB *multiaccounts.Database multiaccountsDB *multiaccounts.Database
keystorePath, keyUID string keystorePath, keyUID string
} }
func NewPairingPayloadRepository(p *PairingPayload, config *PairingPayloadManagerConfig) *PairingPayloadRepository { func NewAccountPayloadRepository(p *AccountPayload, config *AccountPayloadManagerConfig) *AccountPayloadRepository {
ppr := &PairingPayloadRepository{ ppr := &AccountPayloadRepository{
PairingPayload: p, AccountPayload: p,
} }
if config == nil { if config == nil {
@ -359,18 +367,18 @@ func NewPairingPayloadRepository(p *PairingPayload, config *PairingPayloadManage
return ppr return ppr
} }
func (ppr *PairingPayloadRepository) LoadFromSource() error { func (apr *AccountPayloadRepository) LoadFromSource() error {
err := ppr.loadKeys(ppr.keystorePath) err := apr.loadKeys(apr.keystorePath)
if err != nil { if err != nil {
return err return err
} }
err = ppr.validateKeys(ppr.password) err = apr.validateKeys(apr.password)
if err != nil { if err != nil {
return err return err
} }
ppr.multiaccount, err = ppr.multiaccountsDB.GetAccount(ppr.keyUID) apr.multiaccount, err = apr.multiaccountsDB.GetAccount(apr.keyUID)
if err != nil { if err != nil {
return err return err
} }
@ -378,8 +386,8 @@ func (ppr *PairingPayloadRepository) LoadFromSource() error {
return nil return nil
} }
func (ppr *PairingPayloadRepository) loadKeys(keyStorePath string) error { func (apr *AccountPayloadRepository) loadKeys(keyStorePath string) error {
ppr.keys = make(map[string][]byte) apr.keys = make(map[string][]byte)
fileWalker := func(path string, fileInfo os.FileInfo, err error) error { fileWalker := func(path string, fileInfo os.FileInfo, err error) error {
if err != nil { if err != nil {
@ -404,7 +412,7 @@ func (ppr *PairingPayloadRepository) loadKeys(keyStorePath string) error {
return fmt.Errorf("account key address has invalid length '%s'", accountKey.Address) return fmt.Errorf("account key address has invalid length '%s'", accountKey.Address)
} }
ppr.keys[fileInfo.Name()] = rawKeyFile apr.keys[fileInfo.Name()] = rawKeyFile
return nil return nil
} }
@ -417,18 +425,18 @@ func (ppr *PairingPayloadRepository) loadKeys(keyStorePath string) error {
return nil return nil
} }
func (ppr *PairingPayloadRepository) StoreToSource() error { func (apr *AccountPayloadRepository) StoreToSource() error {
err := ppr.validateKeys(ppr.password) err := apr.validateKeys(apr.password)
if err != nil { if err != nil {
return err return err
} }
err = ppr.storeKeys(ppr.keystorePath) err = apr.storeKeys(apr.keystorePath)
if err != nil { if err != nil {
return err return err
} }
err = ppr.storeMultiAccount() err = apr.storeMultiAccount()
if err != nil { if err != nil {
return err return err
} }
@ -437,8 +445,8 @@ func (ppr *PairingPayloadRepository) StoreToSource() error {
return nil return nil
} }
func (ppr *PairingPayloadRepository) validateKeys(password string) error { func (apr *AccountPayloadRepository) validateKeys(password string) error {
for _, key := range ppr.keys { for _, key := range apr.keys {
k, err := keystore.DecryptKey(key, password) k, err := keystore.DecryptKey(key, password)
if err != nil { if err != nil {
return err return err
@ -453,7 +461,7 @@ func (ppr *PairingPayloadRepository) validateKeys(password string) error {
return nil return nil
} }
func (ppr *PairingPayloadRepository) storeKeys(keyStorePath string) error { func (apr *AccountPayloadRepository) storeKeys(keyStorePath string) error {
if keyStorePath == "" { if keyStorePath == "" {
return fmt.Errorf("keyStorePath can not be empty") return fmt.Errorf("keyStorePath can not be empty")
} }
@ -463,10 +471,10 @@ func (ppr *PairingPayloadRepository) storeKeys(keyStorePath string) error {
// If lastDir == "keystore" we presume we need to create the rest of the keystore path // If lastDir == "keystore" we presume we need to create the rest of the keystore path
// else we presume the provided keystore is valid // else we presume the provided keystore is valid
if lastDir == "keystore" { if lastDir == "keystore" {
if ppr.multiaccount == nil || ppr.multiaccount.KeyUID == "" { if apr.multiaccount == nil || apr.multiaccount.KeyUID == "" {
return fmt.Errorf("no known Key UID") return fmt.Errorf("no known Key UID")
} }
keyStorePath = filepath.Join(keyStorePath, ppr.multiaccount.KeyUID) keyStorePath = filepath.Join(keyStorePath, apr.multiaccount.KeyUID)
err := os.MkdirAll(keyStorePath, 0777) err := os.MkdirAll(keyStorePath, 0777)
if err != nil { if err != nil {
@ -474,7 +482,7 @@ func (ppr *PairingPayloadRepository) storeKeys(keyStorePath string) error {
} }
} }
for name, data := range ppr.keys { for name, data := range apr.keys {
accountKey := new(keystore.EncryptedKeyJSONV3) accountKey := new(keystore.EncryptedKeyJSONV3)
if err := json.Unmarshal(data, &accountKey); err != nil { if err := json.Unmarshal(data, &accountKey); err != nil {
return fmt.Errorf("failed to read key file: %s", err) return fmt.Errorf("failed to read key file: %s", err)
@ -492,6 +500,87 @@ func (ppr *PairingPayloadRepository) storeKeys(keyStorePath string) error {
return nil return nil
} }
func (ppr *PairingPayloadRepository) storeMultiAccount() error { func (apr *AccountPayloadRepository) storeMultiAccount() error {
return ppr.multiaccountsDB.SaveAccount(*ppr.multiaccount) return apr.multiaccountsDB.SaveAccount(*apr.multiaccount)
}
type RawMessagePayloadManager struct {
logger *zap.Logger
// reference from AccountPayloadManager#accountPayload
accountPayload *AccountPayload
*PayloadEncryptionManager
payloadRepository *RawMessageRepository
}
func NewRawMessagePayloadManager(logger *zap.Logger, accountPayload *AccountPayload, aesKey []byte, backend *api.GethStatusBackend, keystorePath string) (*RawMessagePayloadManager, error) {
l := logger.Named("RawMessagePayloadManager")
pem, err := NewPayloadEncryptionManager(aesKey, l)
if err != nil {
return nil, err
}
return &RawMessagePayloadManager{
logger: l,
accountPayload: accountPayload,
PayloadEncryptionManager: pem,
payloadRepository: NewRawMessageRepository(backend, keystorePath, accountPayload),
}, nil
}
func (r *RawMessagePayloadManager) Mount() error {
err := r.payloadRepository.LoadFromSource()
if err != nil {
return err
}
return r.Encrypt(r.payloadRepository.payload)
}
func (r *RawMessagePayloadManager) Receive(data []byte) error {
err := r.Decrypt(data)
if err != nil {
return err
}
r.payloadRepository.payload = r.Received()
return r.payloadRepository.StoreToSource()
}
func (r *RawMessagePayloadManager) ResetPayload() {
r.payloadRepository.payload = make([]byte, 0)
r.PayloadEncryptionManager.ResetPayload()
}
type RawMessageRepository struct {
payload []byte
syncRawMessageHandler *SyncRawMessageHandler
keystorePath string
accountPayload *AccountPayload
}
func NewRawMessageRepository(backend *api.GethStatusBackend, keystorePath string, accountPayload *AccountPayload) *RawMessageRepository {
return &RawMessageRepository{
syncRawMessageHandler: NewSyncRawMessageHandler(backend),
keystorePath: keystorePath,
payload: make([]byte, 0),
accountPayload: accountPayload,
}
}
func (r *RawMessageRepository) LoadFromSource() error {
account := r.accountPayload.multiaccount
if account == nil || account.KeyUID == "" {
return fmt.Errorf("no known KeyUID when loading raw messages")
}
payload, err := r.syncRawMessageHandler.PrepareRawMessage(account.KeyUID)
if err != nil {
return err
}
r.payload = payload
return nil
}
func (r *RawMessageRepository) StoreToSource() error {
accountPayload := r.accountPayload
if accountPayload == nil || accountPayload.multiaccount == nil {
return fmt.Errorf("no known multiaccount when storing raw messages")
}
return r.syncRawMessageHandler.HandleRawMessage(accountPayload.multiaccount, accountPayload.password, r.keystorePath, r.payload)
} }

View File

@ -1,4 +1,4 @@
package server package pairing
import ( import (
"bytes" "bytes"
@ -44,8 +44,8 @@ type PayloadMarshallerSuite struct {
teardown func() teardown func()
config1 *PairingPayloadManagerConfig config1 *AccountPayloadManagerConfig
config2 *PairingPayloadManagerConfig config2 *AccountPayloadManagerConfig
} }
func setupTestDB(t *testing.T) (*multiaccounts.Database, func()) { func setupTestDB(t *testing.T) (*multiaccounts.Database, func()) {
@ -132,18 +132,18 @@ func (pms *PayloadMarshallerSuite) SetupTest() {
err := db1.SaveAccount(expected) err := db1.SaveAccount(expected)
pms.Require().NoError(err) pms.Require().NoError(err)
pms.config1 = &PairingPayloadManagerConfig{ pms.config1 = &AccountPayloadManagerConfig{
DB: db1, DB: db1,
PairingPayloadSourceConfig: PairingPayloadSourceConfig{ PayloadSourceConfig: &PayloadSourceConfig{
KeystorePath: keystore1, KeystorePath: keystore1,
KeyUID: keyUID, KeyUID: keyUID,
Password: password, Password: password,
}, },
} }
pms.config2 = &PairingPayloadManagerConfig{ pms.config2 = &AccountPayloadManagerConfig{
DB: db2, DB: db2,
PairingPayloadSourceConfig: PairingPayloadSourceConfig{ PayloadSourceConfig: &PayloadSourceConfig{
KeystorePath: keystore2, KeystorePath: keystore2,
KeyUID: keyUID, KeyUID: keyUID,
Password: password, Password: password,
@ -156,11 +156,11 @@ func (pms *PayloadMarshallerSuite) TearDownTest() {
} }
func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LoadPayloads() { func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LoadPayloads() {
// Make a PairingPayload // Make a Payload
pp := new(PairingPayload) pp := new(AccountPayload)
// Make and LoadFromSource PairingPayloadRepository 1 // Make and LoadFromSource PairingPayloadRepository 1
ppr := NewPairingPayloadRepository(pp, pms.config1) ppr := NewAccountPayloadRepository(pp, pms.config1)
err := ppr.LoadFromSource() err := ppr.LoadFromSource()
pms.Require().NoError(err) pms.Require().NoError(err)
@ -189,11 +189,11 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LoadPayloads() {
} }
func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_MarshalToProtobuf() { func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_MarshalToProtobuf() {
// Make a PairingPayload // Make a Payload
pp := new(PairingPayload) pp := new(AccountPayload)
// Make and LoadFromSource PairingPayloadRepository 1 // Make and LoadFromSource PairingPayloadRepository 1
ppr := NewPairingPayloadRepository(pp, pms.config1) ppr := NewAccountPayloadRepository(pp, pms.config1)
err := ppr.LoadFromSource() err := ppr.LoadFromSource()
pms.Require().NoError(err) pms.Require().NoError(err)
@ -218,11 +218,11 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_MarshalToProtobuf() {
} }
func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() { func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() {
// Make a PairingPayload // Make a Payload
pp := new(PairingPayload) pp := new(AccountPayload)
// Make and LoadFromSource PairingPayloadRepository 1 // Make and LoadFromSource PairingPayloadRepository 1
ppr := NewPairingPayloadRepository(pp, pms.config1) ppr := NewAccountPayloadRepository(pp, pms.config1)
err := ppr.LoadFromSource() err := ppr.LoadFromSource()
pms.Require().NoError(err) pms.Require().NoError(err)
@ -232,8 +232,8 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() {
pb, err := ppm.MarshalToProtobuf() pb, err := ppm.MarshalToProtobuf()
pms.Require().NoError(err) pms.Require().NoError(err)
// Make a PairingPayload // Make a Payload
pp2 := new(PairingPayload) pp2 := new(AccountPayload)
// Make PairingPayloadMarshaller 2 // Make PairingPayloadMarshaller 2
ppm2 := NewPairingPayloadMarshaller(pp2, pms.Logger) ppm2 := NewPairingPayloadMarshaller(pp2, pms.Logger)
@ -271,11 +271,11 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() {
} }
func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() { func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
// Make a PairingPayload // Make a Payload
pp := new(PairingPayload) pp := new(AccountPayload)
// Make and LoadFromSource PairingPayloadRepository 1 // Make and LoadFromSource PairingPayloadRepository 1
ppr := NewPairingPayloadRepository(pp, pms.config1) ppr := NewAccountPayloadRepository(pp, pms.config1)
err := ppr.LoadFromSource() err := ppr.LoadFromSource()
pms.Require().NoError(err) pms.Require().NoError(err)
@ -285,8 +285,8 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
pb, err := ppm.MarshalToProtobuf() pb, err := ppm.MarshalToProtobuf()
pms.Require().NoError(err) pms.Require().NoError(err)
// Make a PairingPayload // Make a Payload
pp2 := new(PairingPayload) pp2 := new(AccountPayload)
// Make PairingPayloadMarshaller 2 // Make PairingPayloadMarshaller 2
ppm2 := NewPairingPayloadMarshaller(pp2, pms.Logger) ppm2 := NewPairingPayloadMarshaller(pp2, pms.Logger)
@ -295,7 +295,7 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
pms.Require().NoError(err) pms.Require().NoError(err)
// Make and Load PairingPayloadRepository 2 // Make and Load PairingPayloadRepository 2
ppr2 := NewPairingPayloadRepository(pp2, pms.config2) ppr2 := NewAccountPayloadRepository(pp2, pms.config2)
err = ppr2.StoreToSource() err = ppr2.StoreToSource()
pms.Require().NoError(err) pms.Require().NoError(err)

View File

@ -0,0 +1,20 @@
package pairing
import (
"context"
"github.com/status-im/status-go/protocol/common"
)
type RawMessageCollector struct {
rawMessages []*common.RawMessage
}
func (r *RawMessageCollector) dispatchMessage(_ context.Context, rawMessage common.RawMessage) (common.RawMessage, error) {
r.rawMessages = append(r.rawMessages, &rawMessage)
return rawMessage, nil
}
func (r *RawMessageCollector) getRawMessages() []*common.RawMessage {
return r.rawMessages
}

View File

@ -0,0 +1,142 @@
package pairing
import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"github.com/google/uuid"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/golang/protobuf/proto"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/protocol/protobuf"
)
type SyncRawMessageHandler struct {
backend *api.GethStatusBackend
}
func NewSyncRawMessageHandler(backend *api.GethStatusBackend) *SyncRawMessageHandler {
return &SyncRawMessageHandler{backend: backend}
}
func (s *SyncRawMessageHandler) PrepareRawMessage(keyUID string) ([]byte, error) {
messenger := s.backend.Messenger()
if messenger == nil {
return nil, fmt.Errorf("messenger is nil when handlePairingSyncDeviceSend")
}
currentAccount, err := s.backend.GetActiveAccount()
if err != nil {
return nil, err
}
if keyUID != currentAccount.KeyUID {
return nil, fmt.Errorf("keyUID not equal")
}
messenger.SetLocalPairing(true)
defer func() {
messenger.SetLocalPairing(false)
}()
rawMessageCollector := new(RawMessageCollector)
err = messenger.SyncDevices(context.TODO(), currentAccount.Name, currentAccount.Identicon, rawMessageCollector.dispatchMessage)
if err != nil {
return nil, err
}
syncRawMessage := new(protobuf.SyncRawMessage)
for _, m := range rawMessageCollector.getRawMessages() {
rawMessage := new(protobuf.RawMessage)
rawMessage.Payload = m.Payload
rawMessage.MessageType = m.MessageType
syncRawMessage.RawMessages = append(syncRawMessage.RawMessages, rawMessage)
}
accountService := s.backend.StatusNode().AccountService()
var (
subAccounts []*accounts.Account
setting settings.Settings
)
subAccounts, err = accountService.GetAccountsByKeyUID(keyUID)
if err != nil {
return nil, err
}
syncRawMessage.SubAccountsJsonBytes, err = json.Marshal(subAccounts)
if err != nil {
return nil, err
}
setting, err = accountService.GetSettings()
if err != nil {
return nil, err
}
syncRawMessage.SettingsJsonBytes, err = json.Marshal(setting)
if err != nil {
return nil, err
}
nodeConfig := s.backend.StatusNode().Config()
if syncRawMessage.NodeConfigJsonBytes, err = json.Marshal(nodeConfig); err != nil {
return nil, err
}
return proto.Marshal(syncRawMessage)
}
func (s *SyncRawMessageHandler) HandleRawMessage(account *multiaccounts.Account, password, keystorePath string, payload []byte) error {
rawMessages, subAccounts, setting, nodeConfig, err := s.unmarshalSyncRawMessage(payload)
if err != nil {
return err
}
newKeystoreDir := filepath.Join(keystorePath, account.KeyUID)
accountManager := s.backend.AccountManager()
err = accountManager.InitKeystore(newKeystoreDir)
if err != nil {
return err
}
nodeConfig.RootDataDir = filepath.Dir(keystorePath)
nodeConfig.DataDir = filepath.Join(nodeConfig.RootDataDir, filepath.Base(nodeConfig.DataDir))
nodeConfig.KeyStoreDir = newKeystoreDir
installationID := uuid.New().String()
nodeConfig.ShhextConfig.InstallationID = installationID
setting.InstallationID = installationID
err = s.backend.StartNodeWithAccountAndInitialConfig(*account, password, *setting, nodeConfig, subAccounts)
if err != nil {
return err
}
messenger := s.backend.Messenger()
return messenger.HandleSyncRawMessages(rawMessages)
}
func (s *SyncRawMessageHandler) unmarshalSyncRawMessage(payload []byte) ([]*protobuf.RawMessage, []*accounts.Account, *settings.Settings, *params.NodeConfig, error) {
var (
syncRawMessage protobuf.SyncRawMessage
subAccounts []*accounts.Account
setting *settings.Settings
nodeConfig *params.NodeConfig
)
err := proto.Unmarshal(payload, &syncRawMessage)
if err != nil {
return nil, nil, nil, nil, err
}
err = json.Unmarshal(syncRawMessage.SubAccountsJsonBytes, &subAccounts)
if err != nil {
return nil, nil, nil, nil, err
}
err = json.Unmarshal(syncRawMessage.SettingsJsonBytes, &setting)
if err != nil {
return nil, nil, nil, nil, err
}
err = json.Unmarshal(syncRawMessage.NodeConfigJsonBytes, &nodeConfig)
if err != nil {
return nil, nil, nil, nil, err
}
return syncRawMessage.RawMessages, subAccounts, setting, nodeConfig, nil
}

View File

@ -1,4 +1,4 @@
package server package pairing
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
@ -10,15 +10,18 @@ import (
"net" "net"
"time" "time"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/server"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"github.com/status-im/status-go/logutils" "github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/multiaccounts"
) )
type PairingServer struct { type Server struct {
Server server.Server
PayloadManager PayloadManager
rawMessagePayloadManager *RawMessagePayloadManager
pk *ecdsa.PublicKey pk *ecdsa.PublicKey
ek []byte ek []byte
@ -35,8 +38,8 @@ type Config struct {
Hostname string Hostname string
Mode Mode Mode Mode
// Payload management fields // AccountPayload management fields
*PairingPayloadManagerConfig *AccountPayloadManagerConfig
} }
func makeCookieStore() (*sessions.CookieStore, error) { func makeCookieStore() (*sessions.CookieStore, error) {
@ -55,10 +58,10 @@ func makeCookieStore() (*sessions.CookieStore, error) {
return sessions.NewCookieStore(auth, enc), nil return sessions.NewCookieStore(auth, enc), nil
} }
// NewPairingServer returns a *PairingServer init from the given *Config // NewPairingServer returns a *Server init from the given *Config
func NewPairingServer(config *Config) (*PairingServer, error) { func NewPairingServer(backend *api.GethStatusBackend, config *Config) (*Server, error) {
logger := logutils.ZapLogger().Named("PairingServer") logger := logutils.ZapLogger().Named("Server")
pm, err := NewPairingPayloadManager(config.EK, config.PairingPayloadManagerConfig, logger) pm, err := NewAccountPayloadManager(config.EK, config.AccountPayloadManagerConfig, logger)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -68,25 +71,32 @@ func NewPairingServer(config *Config) (*PairingServer, error) {
return nil, err return nil, err
} }
return &PairingServer{Server: NewServer( rmpm, err := NewRawMessagePayloadManager(logger, pm.accountPayload, config.EK, backend, config.KeystorePath)
if err != nil {
return nil, err
}
return &Server{Server: server.NewServer(
config.Cert, config.Cert,
config.Hostname, config.Hostname,
nil, nil,
logger, logger,
), ),
pk: config.PK, pk: config.PK,
ek: config.EK, ek: config.EK,
mode: config.Mode, mode: config.Mode,
PayloadManager: pm, PayloadManager: pm,
cookieStore: cs, cookieStore: cs,
rawMessagePayloadManager: rmpm,
}, nil }, nil
} }
// MakeConnectionParams generates a *ConnectionParams based on the Server's current state // MakeConnectionParams generates a *ConnectionParams based on the Server's current state
func (s *PairingServer) MakeConnectionParams() (*ConnectionParams, error) { func (s *Server) MakeConnectionParams() (*ConnectionParams, error) {
netIP := net.ParseIP(s.hostname) hostname := s.GetHostname()
netIP := net.ParseIP(hostname)
if netIP == nil { if netIP == nil {
return nil, fmt.Errorf("invalid ip address given '%s'", s.hostname) return nil, fmt.Errorf("invalid ip address given '%s'", hostname)
} }
netIP4 := netIP.To4() netIP4 := netIP.To4()
@ -97,40 +107,42 @@ func (s *PairingServer) MakeConnectionParams() (*ConnectionParams, error) {
return NewConnectionParams(netIP, s.MustGetPort(), s.pk, s.ek, s.mode), nil return NewConnectionParams(netIP, s.MustGetPort(), s.pk, s.ek, s.mode), nil
} }
func (s *PairingServer) StartPairing() error { func (s *Server) StartPairing() error {
switch s.mode { switch s.mode {
case Receiving: case Receiving:
return s.startReceivingAccountData() return s.startReceivingData()
case Sending: case Sending:
return s.startSendingAccountData() return s.startSendingData()
default: default:
return fmt.Errorf("invalid server mode '%d'", s.mode) return fmt.Errorf("invalid server mode '%d'", s.mode)
} }
} }
func (s *PairingServer) startReceivingAccountData() error { func (s *Server) startReceivingData() error {
s.SetHandlers(HandlerPatternMap{ s.SetHandlers(server.HandlerPatternMap{
pairingReceive: handlePairingReceive(s), pairingReceiveAccount: handlePairingReceive(s),
pairingChallenge: handlePairingChallenge(s), pairingChallenge: handlePairingChallenge(s),
pairingSyncDeviceReceive: handleParingSyncDeviceReceive(s),
}) })
return s.Start() return s.Start()
} }
func (s *PairingServer) startSendingAccountData() error { func (s *Server) startSendingData() error {
err := s.Mount() err := s.Mount()
if err != nil { if err != nil {
return err return err
} }
s.SetHandlers(HandlerPatternMap{ s.SetHandlers(server.HandlerPatternMap{
pairingSend: challengeMiddleware(s, handlePairingSend(s)), pairingSendAccount: challengeMiddleware(s, handlePairingSend(s)),
pairingChallenge: handlePairingChallenge(s), pairingChallenge: handlePairingChallenge(s),
pairingSyncDeviceSend: challengeMiddleware(s, handlePairingSyncDeviceSend(s)),
}) })
return s.Start() return s.Start()
} }
// MakeFullPairingServer generates a fully configured and randomly seeded PairingServer // MakeFullPairingServer generates a fully configured and randomly seeded Server
func MakeFullPairingServer(db *multiaccounts.Database, mode Mode, storeConfig PairingPayloadSourceConfig) (*PairingServer, error) { func MakeFullPairingServer(backend *api.GethStatusBackend, mode Mode, storeConfig *PayloadSourceConfig) (*Server, error) {
tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil { if err != nil {
return nil, err return nil, err
@ -142,7 +154,7 @@ func MakeFullPairingServer(db *multiaccounts.Database, mode Mode, storeConfig Pa
return nil, err return nil, err
} }
outboundIP, err := GetOutboundIP() outboundIP, err := server.GetOutboundIP()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -151,8 +163,7 @@ func MakeFullPairingServer(db *multiaccounts.Database, mode Mode, storeConfig Pa
if err != nil { if err != nil {
return nil, err return nil, err
} }
return NewPairingServer(backend, &Config{
return NewPairingServer(&Config{
// Things that can be generated, and CANNOT come from the app client (well they could be this is better) // Things that can be generated, and CANNOT come from the app client (well they could be this is better)
PK: &tlsKey.PublicKey, PK: &tlsKey.PublicKey,
EK: AESKey, EK: AESKey,
@ -162,26 +173,26 @@ func MakeFullPairingServer(db *multiaccounts.Database, mode Mode, storeConfig Pa
// Things that can't be generated, but DO come from the app client // Things that can't be generated, but DO come from the app client
Mode: mode, Mode: mode,
PairingPayloadManagerConfig: &PairingPayloadManagerConfig{ AccountPayloadManagerConfig: &AccountPayloadManagerConfig{
// Things that can't be generated, but DO NOT come from app client // Things that can't be generated, but DO NOT come from app client
DB: db, DB: backend.GetMultiaccountDB(),
// Things that can't be generated, but DO come from the app client // Things that can't be generated, but DO come from the app client
PairingPayloadSourceConfig: storeConfig, PayloadSourceConfig: storeConfig,
}, },
}) })
} }
// StartUpPairingServer generates a PairingServer, starts the pairing server in the correct mode // StartUpPairingServer generates a Server, starts the pairing server in the correct mode
// and returns the ConnectionParams string to allow a PairingClient to make a successful connection. // and returns the ConnectionParams string to allow a Client to make a successful connection.
func StartUpPairingServer(db *multiaccounts.Database, mode Mode, configJSON string) (string, error) { func StartUpPairingServer(backend *api.GethStatusBackend, mode Mode, configJSON string) (string, error) {
var conf PairingPayloadSourceConfig var conf PayloadSourceConfig
err := json.Unmarshal([]byte(configJSON), &conf) err := json.Unmarshal([]byte(configJSON), &conf)
if err != nil { if err != nil {
return "", err return "", err
} }
ps, err := MakeFullPairingServer(db, mode, conf) ps, err := MakeFullPairingServer(backend, mode, &conf)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -1,12 +1,19 @@
package server package pairing
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"encoding/hex"
"io/ioutil"
"net/http"
"regexp" "regexp"
"testing" "testing"
"time"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/status-im/status-go/server"
) )
func TestPairingServerSuite(t *testing.T) { func TestPairingServerSuite(t *testing.T) {
@ -61,12 +68,14 @@ func (s *PairingServerSuite) TestPairingServer_StartPairing() {
err = ccp.FromString(qr) err = ccp.FromString(qr)
s.Require().NoError(err) s.Require().NoError(err)
c, err := NewPairingClient(ccp, nil) c, err := NewPairingClient(nil, ccp, &AccountPayloadManagerConfig{
PayloadSourceConfig: &PayloadSourceConfig{KeystorePath: ""},
})
s.Require().NoError(err) s.Require().NoError(err)
// Compare cert values // Compare cert values
cert := c.serverCert cert := c.serverCert
cl := s.PS.cert.Leaf cl := s.PS.GetCert().Leaf
s.Require().Equal(cl.Signature, cert.Signature) s.Require().Equal(cl.Signature, cert.Signature)
s.Require().Zero(cl.PublicKey.(*ecdsa.PublicKey).X.Cmp(cert.PublicKey.(*ecdsa.PublicKey).X)) s.Require().Zero(cl.PublicKey.(*ecdsa.PublicKey).X.Cmp(cert.PublicKey.(*ecdsa.PublicKey).X))
s.Require().Zero(cl.PublicKey.(*ecdsa.PublicKey).Y.Cmp(cert.PublicKey.(*ecdsa.PublicKey).Y)) s.Require().Zero(cl.PublicKey.(*ecdsa.PublicKey).Y.Cmp(cert.PublicKey.(*ecdsa.PublicKey).Y))
@ -102,7 +111,7 @@ func (s *PairingServerSuite) TestPairingServer_StartPairing() {
} }
} }
func (s *PairingServerSuite) sendingSetup() *PairingClient { func (s *PairingServerSuite) sendingSetup() *Client {
// Replace PairingServer.PayloadManager with a MockEncryptOnlyPayloadManager // Replace PairingServer.PayloadManager with a MockEncryptOnlyPayloadManager
pm, err := NewMockEncryptOnlyPayloadManager(s.EphemeralAES) pm, err := NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
s.Require().NoError(err) s.Require().NoError(err)
@ -122,7 +131,9 @@ func (s *PairingServerSuite) sendingSetup() *PairingClient {
err = ccp.FromString(qr) err = ccp.FromString(qr)
s.Require().NoError(err) s.Require().NoError(err)
c, err := NewPairingClient(ccp, nil) c, err := NewPairingClient(nil, ccp, &AccountPayloadManagerConfig{
PayloadSourceConfig: &PayloadSourceConfig{KeystorePath: ""},
})
s.Require().NoError(err) s.Require().NoError(err)
// Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager // Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager
@ -187,3 +198,66 @@ func (s *PairingServerSuite) TestPairingServer_handlePairingChallengeMiddleware_
s.Require().Error(err) s.Require().Error(err)
s.Require().Equal("status not ok, received '403 Forbidden'", err.Error()) s.Require().Equal("status not ok, received '403 Forbidden'", err.Error())
} }
func testHandler(t *testing.T) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
say, ok := r.URL.Query()["say"]
if !ok || len(say) == 0 {
say = append(say, "nothing")
}
_, err := w.Write([]byte("Hello I like to be a tls server. You said: `" + say[0] + "` " + time.Now().String()))
if err != nil {
require.NoError(t, err)
}
}
}
func makeThingToSay() (string, error) {
b := make([]byte, 32)
_, err := rand.Read(b)
if err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
func (s *PairingServerSuite) TestGetOutboundIPWithFullServerE2e() {
s.PS.mode = Sending
s.PS.SetHandlers(server.HandlerPatternMap{"/hello": testHandler(s.T())})
err := s.PS.Start()
s.Require().NoError(err)
// Give time for the sever to be ready, hacky I know, I'll iron this out
time.Sleep(100 * time.Millisecond)
// Server generates a QR code connection string
cp, err := s.PS.MakeConnectionParams()
s.Require().NoError(err)
qr := cp.ToString()
// Client reads QR code and parses the connection string
ccp := new(ConnectionParams)
err = ccp.FromString(qr)
s.Require().NoError(err)
c, err := NewPairingClient(nil, ccp, &AccountPayloadManagerConfig{
PayloadSourceConfig: &PayloadSourceConfig{KeystorePath: ""},
})
s.Require().NoError(err)
thing, err := makeThingToSay()
s.Require().NoError(err)
response, err := c.Get(c.baseAddress.String() + "/hello?say=" + thing)
s.Require().NoError(err)
defer response.Body.Close()
content, err := ioutil.ReadAll(response.Body)
s.Require().NoError(err)
s.Require().Equal("Hello I like to be a tls server. You said: `"+thing+"`", string(content[:109]))
}

View File

@ -0,0 +1,316 @@
package pairing
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/account/generator"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/protocol/identity/alias"
"github.com/status-im/status-go/services/browsers"
"github.com/status-im/status-go/sqlite"
)
const pathWalletRoot = "m/44'/60'/0'/0"
const pathEIP1581 = "m/43'/60'/1581'"
const pathDefaultChat = pathEIP1581 + "/0'/0"
const pathDefaultWallet = pathWalletRoot + "/0"
var paths = []string{pathWalletRoot, pathEIP1581, pathDefaultChat, pathDefaultWallet}
const keystoreDir = "keystore"
func TestSyncDeviceSuite(t *testing.T) {
suite.Run(t, new(SyncDeviceSuite))
}
type SyncDeviceSuite struct {
suite.Suite
password string
clientAsSenderTmpdir string
clientAsReceiverTmpdir string
}
func (s *SyncDeviceSuite) SetupTest() {
s.password = "password"
clientAsSenderTmpdir, err := os.MkdirTemp("", "TestPairingSyncDeviceClientAsSender")
require.NoError(s.T(), err)
s.clientAsSenderTmpdir = clientAsSenderTmpdir
clientAsReceiverTmpdir, err := os.MkdirTemp("", "TestPairingSyncDeviceClientAsReceiver")
require.NoError(s.T(), err)
s.clientAsReceiverTmpdir = clientAsReceiverTmpdir
}
func (s *SyncDeviceSuite) TearDownTest() {
os.RemoveAll(s.clientAsSenderTmpdir)
os.RemoveAll(s.clientAsReceiverTmpdir)
}
func (s *SyncDeviceSuite) prepareBackendWithAccount(tmpdir string) *api.GethStatusBackend {
backend := s.prepareBackendWithoutAccount(tmpdir)
accountManager := backend.AccountManager()
generator := accountManager.AccountsGenerator()
generatedAccountInfos, err := generator.GenerateAndDeriveAddresses(12, 1, "", paths)
require.NoError(s.T(), err)
generatedAccountInfo := generatedAccountInfos[0]
account := multiaccounts.Account{
KeyUID: generatedAccountInfo.KeyUID,
KDFIterations: sqlite.ReducedKDFIterationsNumber,
}
err = accountManager.InitKeystore(filepath.Join(tmpdir, keystoreDir, account.KeyUID))
require.NoError(s.T(), err)
err = backend.OpenAccounts()
require.NoError(s.T(), err)
derivedAddresses := generatedAccountInfo.Derived
_, err = generator.StoreDerivedAccounts(generatedAccountInfo.ID, s.password, paths)
require.NoError(s.T(), err)
settings, err := defaultSettings(generatedAccountInfo.GeneratedAccountInfo, derivedAddresses, nil)
require.NoError(s.T(), err)
nodeConfig, err := defaultNodeConfig(tmpdir, settings.InstallationID, account.KeyUID)
require.NoError(s.T(), err)
walletDerivedAccount := derivedAddresses[pathDefaultWallet]
walletAccount := &accounts.Account{
PublicKey: types.Hex2Bytes(walletDerivedAccount.PublicKey),
KeyUID: generatedAccountInfo.KeyUID,
Address: types.HexToAddress(walletDerivedAccount.Address),
Color: "",
Wallet: true,
Path: pathDefaultWallet,
Name: "Ethereum account",
}
chatDerivedAccount := derivedAddresses[pathDefaultChat]
chatAccount := &accounts.Account{
PublicKey: types.Hex2Bytes(chatDerivedAccount.PublicKey),
KeyUID: generatedAccountInfo.KeyUID,
Address: types.HexToAddress(chatDerivedAccount.Address),
Name: settings.Name,
Chat: true,
Path: pathDefaultChat,
}
accounts := []*accounts.Account{walletAccount, chatAccount}
err = backend.StartNodeWithAccountAndInitialConfig(account, s.password, *settings, nodeConfig, accounts)
require.NoError(s.T(), err)
return backend
}
func (s *SyncDeviceSuite) prepareBackendWithoutAccount(tmpdir string) *api.GethStatusBackend {
backend := api.NewGethStatusBackend()
backend.UpdateRootDataDir(tmpdir)
return backend
}
func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
clientTmpDir := filepath.Join(s.clientAsSenderTmpdir, "client")
clientBackend := s.prepareBackendWithAccount(clientTmpDir)
serverTmpDir := filepath.Join(s.clientAsSenderTmpdir, "server")
serverBackend := s.prepareBackendWithoutAccount(serverTmpDir)
defer func() {
require.NoError(s.T(), serverBackend.Logout())
}()
err := serverBackend.AccountManager().InitKeystore(filepath.Join(serverTmpDir, keystoreDir))
require.NoError(s.T(), err)
err = serverBackend.OpenAccounts()
require.NoError(s.T(), err)
serverKeystorePath := filepath.Join(serverTmpDir, keystoreDir)
configJSON := fmt.Sprintf(`{"KeystorePath":"%s"}`, serverKeystorePath)
cs, err := StartUpPairingServer(serverBackend, Receiving, configJSON)
require.NoError(s.T(), err)
// generate some data for the client
clientBrowserAPI := clientBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
_, err = clientBrowserAPI.StoreBookmark(context.TODO(), browsers.Bookmark{
Name: "status.im",
URL: "https://status.im",
})
require.NoError(s.T(), err)
activeAccount, err := clientBackend.GetActiveAccount()
require.NoError(s.T(), err)
clientKeystorePath := filepath.Join(clientTmpDir, keystoreDir, activeAccount.KeyUID)
var config = PayloadSourceConfig{
KeystorePath: clientKeystorePath,
KeyUID: activeAccount.KeyUID,
Password: s.password,
}
configBytes, err := json.Marshal(config)
require.NoError(s.T(), err)
err = StartUpPairingClient(clientBackend, cs, string(configBytes))
require.NoError(s.T(), err)
require.NoError(s.T(), clientBackend.Logout())
serverBrowserAPI := serverBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
bookmarks, err := serverBrowserAPI.GetBookmarks(context.TODO())
require.NoError(s.T(), err)
require.Equal(s.T(), 1, len(bookmarks))
require.Equal(s.T(), "status.im", bookmarks[0].Name)
}
func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
clientTmpDir := filepath.Join(s.clientAsReceiverTmpdir, "client")
clientBackend := s.prepareBackendWithoutAccount(clientTmpDir)
serverTmpDir := filepath.Join(s.clientAsReceiverTmpdir, "server")
serverBackend := s.prepareBackendWithAccount(serverTmpDir)
defer func() {
require.NoError(s.T(), clientBackend.Logout())
}()
activeAccount, err := serverBackend.GetActiveAccount()
require.NoError(s.T(), err)
serverKeystorePath := filepath.Join(serverTmpDir, keystoreDir, activeAccount.KeyUID)
var config = PayloadSourceConfig{
KeystorePath: serverKeystorePath,
KeyUID: activeAccount.KeyUID,
Password: s.password,
}
configBytes, err := json.Marshal(config)
require.NoError(s.T(), err)
cs, err := StartUpPairingServer(serverBackend, Sending, string(configBytes))
require.NoError(s.T(), err)
// generate some data for the server
serverBrowserAPI := serverBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
_, err = serverBrowserAPI.StoreBookmark(context.TODO(), browsers.Bookmark{
Name: "status.im",
URL: "https://status.im",
})
require.NoError(s.T(), err)
err = clientBackend.AccountManager().InitKeystore(filepath.Join(clientTmpDir, keystoreDir))
require.NoError(s.T(), err)
err = clientBackend.OpenAccounts()
require.NoError(s.T(), err)
clientKeystorePath := filepath.Join(clientTmpDir, keystoreDir)
configJSON := fmt.Sprintf(`{"KeystorePath":"%s"}`, clientKeystorePath)
err = StartUpPairingClient(clientBackend, cs, configJSON)
require.NoError(s.T(), err)
require.NoError(s.T(), serverBackend.Logout())
clientBrowserAPI := clientBackend.StatusNode().BrowserService().APIs()[0].Service.(*browsers.API)
bookmarks, err := clientBrowserAPI.GetBookmarks(context.TODO())
require.NoError(s.T(), err)
require.Equal(s.T(), 1, len(bookmarks))
require.Equal(s.T(), "status.im", bookmarks[0].Name)
}
func defaultSettings(generatedAccountInfo generator.GeneratedAccountInfo, derivedAddresses map[string]generator.AccountInfo, mnemonic *string) (*settings.Settings, error) {
chatKeyString := derivedAddresses[pathDefaultChat].PublicKey
settings := &settings.Settings{}
settings.KeyUID = generatedAccountInfo.KeyUID
settings.Address = types.HexToAddress(generatedAccountInfo.Address)
settings.WalletRootAddress = types.HexToAddress(derivedAddresses[pathWalletRoot].Address)
// Set chat key & name
name, err := alias.GenerateFromPublicKeyString(chatKeyString)
if err != nil {
return nil, err
}
settings.Name = name
settings.PublicKey = chatKeyString
settings.DappsAddress = types.HexToAddress(derivedAddresses[pathDefaultWallet].Address)
settings.EIP1581Address = types.HexToAddress(derivedAddresses[pathEIP1581].Address)
settings.Mnemonic = mnemonic
settings.SigningPhrase = "balabala"
settings.SendPushNotifications = true
settings.InstallationID = uuid.New().String()
settings.UseMailservers = true
settings.PreviewPrivacy = true
settings.Currency = "usd"
settings.ProfilePicturesVisibility = 1
settings.LinkPreviewRequestEnabled = true
visibleTokens := make(map[string][]string)
visibleTokens["mainnet"] = []string{"SNT"}
visibleTokensJSON, err := json.Marshal(visibleTokens)
if err != nil {
return nil, err
}
visibleTokenJSONRaw := json.RawMessage(visibleTokensJSON)
settings.WalletVisibleTokens = &visibleTokenJSONRaw
networks := make([]map[string]string, 0)
networksJSON, err := json.Marshal(networks)
if err != nil {
return nil, err
}
networkRawMessage := json.RawMessage(networksJSON)
settings.Networks = &networkRawMessage
settings.CurrentNetwork = "mainnet_rpc"
return settings, nil
}
func defaultNodeConfig(tmpdir, installationID, keyUID string) (*params.NodeConfig, error) {
// Set mainnet
nodeConfig := &params.NodeConfig{}
nodeConfig.NetworkID = 1
nodeConfig.LogLevel = "ERROR"
nodeConfig.DataDir = filepath.Join(tmpdir, "ethereum/mainnet_rpc")
nodeConfig.KeyStoreDir = filepath.Join(tmpdir, keystoreDir, keyUID)
nodeConfig.UpstreamConfig = params.UpstreamRPCConfig{
Enabled: true,
URL: "https://mainnet.infura.io/v3/800c641949d64d768a5070a1b0511938",
}
nodeConfig.Name = "StatusIM"
clusterConfig, err := params.LoadClusterConfigFromFleet("eth.prod")
if err != nil {
return nil, err
}
nodeConfig.ClusterConfig = *clusterConfig
nodeConfig.WalletConfig = params.WalletConfig{Enabled: false}
nodeConfig.LocalNotificationsConfig = params.LocalNotificationsConfig{Enabled: true}
nodeConfig.BrowsersConfig = params.BrowsersConfig{Enabled: false}
nodeConfig.PermissionsConfig = params.PermissionsConfig{Enabled: true}
nodeConfig.MailserversConfig = params.MailserversConfig{Enabled: true}
nodeConfig.EnableNTPSync = true
nodeConfig.WakuConfig = params.WakuConfig{
Enabled: true,
LightClient: true,
MinimumPoW: 0.000001,
}
nodeConfig.ShhextConfig = params.ShhextConfig{
BackupDisabledDataDir: "",
InstallationID: installationID,
MaxMessageDeliveryAttempts: 6,
MailServerConfirmations: true,
VerifyTransactionURL: "",
VerifyENSURL: "",
VerifyENSContractAddress: "",
VerifyTransactionChainID: 1,
DataSyncEnabled: true,
PFSEnabled: true,
}
return nodeConfig, nil
}

View File

@ -34,6 +34,18 @@ func (s *Server) getHost() string {
return fmt.Sprintf("%s:%d", s.hostname, s.GetPort()) return fmt.Sprintf("%s:%d", s.hostname, s.GetPort())
} }
func (s *Server) GetHostname() string {
return s.hostname
}
func (s *Server) GetCert() *tls.Certificate {
return s.cert
}
func (s *Server) GetLogger() *zap.Logger {
return s.logger
}
func (s *Server) mustGetHost() string { func (s *Server) mustGetHost() string {
return fmt.Sprintf("%s:%d", s.hostname, s.MustGetPort()) return fmt.Sprintf("%s:%d", s.hostname, s.MustGetPort())
} }

View File

@ -28,7 +28,7 @@ func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader, multiaccountsDB *mu
s := &MediaServer{ s := &MediaServer{
Server: NewServer( Server: NewServer(
globalCertificate, globalCertificate,
localhost, Localhost,
signal.SendMediaServerStarted, signal.SendMediaServerStarted,
logutils.ZapLogger().Named("MediaServer"), logutils.ZapLogger().Named("MediaServer"),
), ),

View File

@ -30,14 +30,14 @@ func (s *ServerURLSuite) SetupTest() {
s.SetupLoggerComponents() s.SetupLoggerComponents()
s.server = &MediaServer{Server: Server{ s.server = &MediaServer{Server: Server{
hostname: defaultIP.String(), hostname: DefaultIP.String(),
portManger: newPortManager(s.Logger, nil), portManger: newPortManager(s.Logger, nil),
}} }}
err := s.server.SetPort(1337) err := s.server.SetPort(1337)
s.Require().NoError(err) s.Require().NoError(err)
s.serverNoPort = &MediaServer{Server: Server{ s.serverNoPort = &MediaServer{Server: Server{
hostname: defaultIP.String(), hostname: DefaultIP.String(),
portManger: newPortManager(s.Logger, nil), portManger: newPortManager(s.Logger, nil),
}} }}
go func() { go func() {

View File

@ -4,6 +4,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/server" "github.com/status-im/status-go/server"
"github.com/status-im/status-go/account" "github.com/status-im/status-go/account"
@ -68,3 +69,12 @@ func (s *Service) APIs() []rpc.API {
func (s *Service) Protocols() []p2p.Protocol { func (s *Service) Protocols() []p2p.Protocol {
return nil return nil
} }
func (s *Service) GetAccountsByKeyUID(keyUID string) ([]*accounts.Account, error) {
return s.db.GetAccountsByKeyUID(keyUID)
}
func (s *Service) GetSettings() (settings.Settings, error) {
return s.db.GetSettings()
}

View File

@ -872,7 +872,7 @@ func (api *PublicAPI) SendPairInstallation(ctx context.Context) (*protocol.Messe
} }
func (api *PublicAPI) SyncDevices(ctx context.Context, name, picture string) error { func (api *PublicAPI) SyncDevices(ctx context.Context, name, picture string) error {
return api.service.messenger.SyncDevices(ctx, name, picture) return api.service.messenger.SyncDevices(ctx, name, picture, nil)
} }
func (api *PublicAPI) AddBookmark(ctx context.Context, bookmark browsers.Bookmark) error { func (api *PublicAPI) AddBookmark(ctx context.Context, bookmark browsers.Bookmark) error {