Sync all devices after initial pairing (#3047)
This commit is contained in:
parent
bea710c8be
commit
ec7c0e9c7d
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/status-im/status-go/node"
|
||||
"github.com/status-im/status-go/nodecfg"
|
||||
"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/services/ext"
|
||||
"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)
|
||||
}
|
||||
|
||||
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
|
||||
func (b *GethStatusBackend) SignHash(hexEncodedHash string) (string, error) {
|
||||
hash, err := hexutil.Decode(hexEncodedHash)
|
||||
|
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/status-im/status-go/protocol/identity/colorhash"
|
||||
"github.com/status-im/status-go/protocol/identity/emojihash"
|
||||
"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/typeddata"
|
||||
"github.com/status-im/status-go/signal"
|
||||
|
@ -938,42 +939,40 @@ func GenerateImages(filepath string, aX, aY, bX, bY int) string {
|
|||
return string(data)
|
||||
}
|
||||
|
||||
// GetConnectionStringForBeingBootstrapped starts a server.Receiving server.PairingServer
|
||||
// then generates a server.ConnectionParams. Used when the device is Logged out or has no Account keys
|
||||
// GetConnectionStringForBeingBootstrapped starts a pairing.Receiving pairing.PairingServer
|
||||
// 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
|
||||
//
|
||||
// Example: A desktop device (device without camera) receiving account data from mobile (device with camera)
|
||||
func GetConnectionStringForBeingBootstrapped(configJSON string) string {
|
||||
if configJSON == "" {
|
||||
return makeJSONResponse(fmt.Errorf("no config given, PairingPayloadSourceConfig is expected"))
|
||||
return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
|
||||
}
|
||||
|
||||
cs, err := server.StartUpPairingServer(statusBackend.GetMultiaccountDB(), server.Receiving, configJSON)
|
||||
cs, err := pairing.StartUpPairingServer(statusBackend, pairing.Receiving, configJSON)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
// GetConnectionStringForBootstrappingAnotherDevice starts a server.Sending server.PairingServer
|
||||
// then generates a server.ConnectionParams. Used when the device is Logged in and therefore has Account keys
|
||||
// GetConnectionStringForBootstrappingAnotherDevice starts a pairing.Sending pairing.Server
|
||||
// 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
|
||||
//
|
||||
// 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)
|
||||
func GetConnectionStringForBootstrappingAnotherDevice(configJSON string) string {
|
||||
if configJSON == "" {
|
||||
return makeJSONResponse(fmt.Errorf("no config given, PairingPayloadSourceConfig is expected"))
|
||||
return makeJSONResponse(fmt.Errorf("no config given, PayloadSourceConfig is expected"))
|
||||
}
|
||||
|
||||
cs, err := server.StartUpPairingServer(statusBackend.GetMultiaccountDB(), server.Sending, configJSON)
|
||||
cs, err := pairing.StartUpPairingServer(statusBackend, pairing.Sending, configJSON)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
// InputConnectionStringForBootstrapping starts a server.PairingClient
|
||||
// InputConnectionStringForBootstrapping starts a pairing.Client
|
||||
// The given server.ConnectionParams string will determine the server.Mode
|
||||
//
|
||||
// server.Mode = server.Sending
|
||||
|
@ -988,10 +987,10 @@ func GetConnectionStringForBootstrappingAnotherDevice(configJSON string) string
|
|||
// a device with a screen (mobile or desktop devices)
|
||||
func InputConnectionStringForBootstrapping(cs, configJSON string) string {
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -120,15 +120,29 @@ func NewDB(db *sql.DB) (*Database, error) {
|
|||
}
|
||||
|
||||
// DB Gets db sql.DB
|
||||
func (db Database) DB() *sql.DB {
|
||||
func (db *Database) DB() *sql.DB {
|
||||
return db.db
|
||||
}
|
||||
|
||||
// Close closes database.
|
||||
func (db Database) Close() error {
|
||||
func (db *Database) Close() error {
|
||||
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) {
|
||||
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`)
|
||||
|
|
|
@ -211,6 +211,10 @@ func (b *StatusNode) AccountService() *accountssvc.Service {
|
|||
return b.accountsSrvc
|
||||
}
|
||||
|
||||
func (b *StatusNode) BrowserService() *browsers.Service {
|
||||
return b.browsersSrvc
|
||||
}
|
||||
|
||||
func (b *StatusNode) WakuService() *waku.Waku {
|
||||
return b.wakuSrvc
|
||||
}
|
||||
|
|
|
@ -157,6 +157,9 @@ type Messenger struct {
|
|||
mailPeersMutex sync.Mutex
|
||||
handleMessagesMutex sync.Mutex
|
||||
handleImportMessagesMutex sync.Mutex
|
||||
|
||||
// flag to disable checking #hasPairedDevices
|
||||
localPairing bool
|
||||
}
|
||||
|
||||
type connStatus int
|
||||
|
@ -1335,7 +1338,7 @@ func (m *Messenger) watchIdentityImageChanges() {
|
|||
for {
|
||||
select {
|
||||
case <-channel:
|
||||
err := m.syncProfilePictures()
|
||||
err := m.syncProfilePictures(m.dispatchMessage)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
func (m *Messenger) SetLocalPairing(localPairing bool) {
|
||||
m.localPairing = localPairing
|
||||
}
|
||||
func (m *Messenger) hasPairedDevices() bool {
|
||||
logger := m.logger.Named("hasPairedDevices")
|
||||
|
||||
if m.localPairing {
|
||||
return true
|
||||
}
|
||||
|
||||
var count int
|
||||
m.allInstallations.Range(func(installationID string, installation *multidevice.Installation) (shouldContinue bool) {
|
||||
if installation.Enabled {
|
||||
|
@ -2166,7 +2176,7 @@ func (m *Messenger) ShareImageMessage(request *requests.ShareImageMessage) (*Mes
|
|||
return response, nil
|
||||
}
|
||||
|
||||
func (m *Messenger) syncProfilePictures() error {
|
||||
func (m *Messenger) syncProfilePictures(rawMessageHandler RawMessageHandler) error {
|
||||
if !m.hasPairedDevices() {
|
||||
return nil
|
||||
}
|
||||
|
@ -2207,12 +2217,14 @@ func (m *Messenger) syncProfilePictures() error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_PROFILE_PICTURE,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2223,7 +2235,11 @@ func (m *Messenger) syncProfilePictures() error {
|
|||
|
||||
// SyncDevices sends all public chats and contacts to paired devices
|
||||
// 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)
|
||||
|
||||
displayName, err := m.settings.DisplayName()
|
||||
|
@ -2231,14 +2247,14 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
|
|||
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
|
||||
}
|
||||
|
||||
m.allChats.Range(func(chatID string, chat *Chat) (shouldContinue bool) {
|
||||
isPublicChat := !chat.Timeline() && !chat.ProfileUpdates() && chat.Public()
|
||||
if isPublicChat && chat.Active {
|
||||
err = m.syncPublicChat(ctx, chat)
|
||||
err = m.syncPublicChat(ctx, chat, rawMessageHandler)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -2251,7 +2267,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
|
|||
}
|
||||
|
||||
if !pending {
|
||||
err = m.syncChatRemoving(ctx, chatID)
|
||||
err = m.syncChatRemoving(ctx, chatID, rawMessageHandler)
|
||||
if err != nil {
|
||||
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 {
|
||||
err := m.syncChatMessagesRead(ctx, chatID, chat.ReadMessagesAtClockValue)
|
||||
err := m.syncChatMessagesRead(ctx, chatID, chat.ReadMessagesAtClockValue, rawMessageHandler)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if isPublicChat && chat.Active && chat.DeletedAtClockValue > 0 {
|
||||
err = m.syncClearHistory(ctx, chat)
|
||||
err = m.syncClearHistory(ctx, chat, rawMessageHandler)
|
||||
if err != nil {
|
||||
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) {
|
||||
if contact.ID != myID &&
|
||||
(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
|
||||
}
|
||||
}
|
||||
|
@ -2293,7 +2309,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
|
|||
return err
|
||||
}
|
||||
for _, c := range cs {
|
||||
if err = m.syncCommunity(ctx, c); err != nil {
|
||||
if err = m.syncCommunity(ctx, c, rawMessageHandler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -2303,7 +2319,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
|
|||
return err
|
||||
}
|
||||
for _, b := range bookmarks {
|
||||
if err = m.SyncBookmark(ctx, b); err != nil {
|
||||
if err = m.SyncBookmark(ctx, b, rawMessageHandler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -2313,7 +2329,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
|
|||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -2323,17 +2339,17 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
|
|||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
err = m.syncSettings()
|
||||
err = m.syncSettings(rawMessageHandler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.syncProfilePictures()
|
||||
err = m.syncProfilePictures(rawMessageHandler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2352,14 +2368,14 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
|
|||
for id, state := range ids {
|
||||
if state == common.ContactRequestStateAccepted || state == common.ContactRequestStateDismissed {
|
||||
accepted := state == common.ContactRequestStateAccepted
|
||||
err := m.syncContactRequestDecision(ctx, id, accepted)
|
||||
err := m.syncContactRequestDecision(ctx, id, accepted, rawMessageHandler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = m.syncWallets(accounts)
|
||||
err = m.syncWallets(accounts, rawMessageHandler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2372,7 +2388,7 @@ func (m *Messenger) SyncDevices(ctx context.Context, ensName, photoPath string)
|
|||
for i := range savedAddresses {
|
||||
sa := savedAddresses[i]
|
||||
|
||||
err = m.syncSavedAddress(ctx, sa)
|
||||
err = m.syncSavedAddress(ctx, sa, rawMessageHandler)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2385,7 +2401,7 @@ func (m *Messenger) SaveAccounts(accs []*accounts.Account) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.syncWallets(accs)
|
||||
return m.syncWallets(accs, m.dispatchMessage)
|
||||
}
|
||||
|
||||
func (m *Messenger) DeleteAccount(address types.Address) error {
|
||||
|
@ -2402,11 +2418,11 @@ func (m *Messenger) DeleteAccount(address types.Address) error {
|
|||
acc.Removed = true
|
||||
|
||||
accs := []*accounts.Account{acc}
|
||||
return m.syncWallets(accs)
|
||||
return m.syncWallets(accs, m.dispatchMessage)
|
||||
}
|
||||
|
||||
// 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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -2454,12 +2470,14 @@ func (m *Messenger) syncWallets(accs []*accounts.Account) error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_WALLET_ACCOUNT,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2468,7 +2486,7 @@ func (m *Messenger) syncWallets(accs []*accounts.Account) error {
|
|||
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))
|
||||
if !m.hasPairedDevices() {
|
||||
return nil
|
||||
|
@ -2494,12 +2512,14 @@ func (m *Messenger) syncContactRequestDecision(ctx context.Context, requestID st
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CONTACT_REQUEST_DECISION,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2570,7 +2590,7 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons
|
|||
}
|
||||
|
||||
// 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
|
||||
if !m.hasPairedDevices() {
|
||||
return nil
|
||||
|
@ -2586,12 +2606,14 @@ func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2600,7 +2622,7 @@ func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error
|
|||
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
|
||||
if !m.hasPairedDevices() {
|
||||
return nil
|
||||
|
@ -2617,12 +2639,14 @@ func (m *Messenger) syncClearHistory(ctx context.Context, publicChat *Chat) erro
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CLEAR_HISTORY,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2631,7 +2655,7 @@ func (m *Messenger) syncClearHistory(ctx context.Context, publicChat *Chat) erro
|
|||
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
|
||||
if !m.hasPairedDevices() {
|
||||
return nil
|
||||
|
@ -2647,12 +2671,14 @@ func (m *Messenger) syncChatRemoving(ctx context.Context, id string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CHAT_REMOVED,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2662,7 +2688,7 @@ func (m *Messenger) syncChatRemoving(ctx context.Context, id string) error {
|
|||
}
|
||||
|
||||
// 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
|
||||
if contact.IsSyncing {
|
||||
return nil
|
||||
|
@ -2702,12 +2728,14 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2716,7 +2744,7 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
|
|||
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")
|
||||
if !m.hasPairedDevices() {
|
||||
logger.Debug("device has no paired devices")
|
||||
|
@ -2747,12 +2775,14 @@ func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Co
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_COMMUNITY,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2762,7 +2792,7 @@ func (m *Messenger) syncCommunity(ctx context.Context, community *communities.Co
|
|||
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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -2782,20 +2812,22 @@ func (m *Messenger) SyncBookmark(ctx context.Context, bookmark *browsers.Bookmar
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_BOOKMARK,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chat.LastClockValue = clock
|
||||
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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -2812,20 +2844,23 @@ func (m *Messenger) SyncTrustedUser(ctx context.Context, publicKey string, ts ve
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_TRUSTED_USER,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chat.LastClockValue = clock
|
||||
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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -2848,15 +2883,18 @@ func (m *Messenger) SyncVerificationRequest(ctx context.Context, vr *verificatio
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_VERIFICATION_REQUEST,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chat.LastClockValue = clock
|
||||
return m.saveChat(chat)
|
||||
}
|
||||
|
@ -3611,6 +3649,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
logger.Debug("Handling SyncChatRemoved", zap.Any("message", p))
|
||||
err := m.HandleSyncChatRemoved(messageState, p)
|
||||
if err != nil {
|
||||
allMessagesProcessed = false
|
||||
logger.Warn("failed to handle sync removing chat", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
@ -3626,7 +3665,8 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
logger.Debug("Handling SyncChatMessagesRead", zap.Any("message", p))
|
||||
err := m.HandleSyncChatMessagesRead(messageState, p)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -4094,7 +4134,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
p := msg.ParsedMessage.Interface().(protobuf.SyncContactRequestDecision)
|
||||
err := m.HandleSyncContactRequestDecision(messageState, p)
|
||||
if err != nil {
|
||||
logger.Warn("failed to handle SyncContactRequestDecisio", zap.Error(err))
|
||||
logger.Warn("failed to handle SyncContactRequestDecision", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
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
|
||||
messageState.ModifiedContacts.Range(func(id string, value bool) (shouldContinue bool) {
|
||||
contact, ok := messageState.AllContacts.Load(id)
|
||||
|
@ -4519,7 +4564,7 @@ func (m *Messenger) MarkMessagesSeen(chatID string, ids []string) (uint64, uint6
|
|||
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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -4535,12 +4580,14 @@ func (m *Messenger) syncChatMessagesRead(ctx context.Context, chatID string, clo
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_CHAT_MESSAGES_READ,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -4557,7 +4604,7 @@ func (m *Messenger) markAllRead(chatID string, clock uint64, shouldBeSynced bool
|
|||
}
|
||||
|
||||
if shouldBeSynced {
|
||||
err := m.syncChatMessagesRead(context.Background(), chatID, clock)
|
||||
err := m.syncChatMessagesRead(context.Background(), chatID, clock, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4676,7 +4723,7 @@ func (m *Messenger) muteChat(chat *Chat, contact *Contact) error {
|
|||
m.allChats.Store(chat.ID, chat)
|
||||
|
||||
if contact != nil {
|
||||
err := m.syncContact(context.Background(), contact)
|
||||
err := m.syncContact(context.Background(), contact, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -4712,7 +4759,7 @@ func (m *Messenger) unmuteChat(chat *Chat, contact *Contact) error {
|
|||
m.allChats.Store(chat.ID, chat)
|
||||
|
||||
if contact != nil {
|
||||
err := m.syncContact(context.Background(), contact)
|
||||
err := m.syncContact(context.Background(), contact, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ func (m *Messenger) AddBookmark(ctx context.Context, bookmark browsers.Bookmark)
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.SyncBookmark(ctx, &bmr)
|
||||
return m.SyncBookmark(ctx, &bmr, m.dispatchMessage)
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
@ -36,7 +36,7 @@ func (m *Messenger) UpdateBookmark(ctx context.Context, oldURL string, bookmark
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.SyncBookmark(ctx, &bookmark)
|
||||
return m.SyncBookmark(ctx, &bookmark, m.dispatchMessage)
|
||||
}
|
||||
|
||||
func (m *Messenger) GarbageCollectRemovedBookmarks() error {
|
||||
|
|
|
@ -164,7 +164,7 @@ func (m *Messenger) createPublicChat(chatID string, response *MessengerResponse)
|
|||
|
||||
// Sync if it was created
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -407,7 +407,7 @@ func (m *Messenger) deactivateChat(chatID string, deactivationClock uint64, shou
|
|||
// TODO: Remove filters
|
||||
|
||||
if shouldBeSynced {
|
||||
err := m.syncChatRemoving(context.Background(), chat.ID)
|
||||
err := m.syncChatRemoving(context.Background(), chat.ID, m.dispatchMessage)
|
||||
if err != nil {
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -368,7 +368,7 @@ func (m *Messenger) JoinCommunity(ctx context.Context, communityID types.HexByte
|
|||
}
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -493,7 +493,7 @@ func (m *Messenger) RequestToJoinCommunity(request *requests.RequestToJoinCommun
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = m.syncCommunity(context.Background(), community)
|
||||
err = m.syncCommunity(context.Background(), community, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -822,7 +822,7 @@ func (m *Messenger) LeaveCommunity(communityID types.HexBytes) (*MessengerRespon
|
|||
m.communitiesManager.StopHistoryArchiveTasksInterval(communityID)
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1052,7 +1052,7 @@ func (m *Messenger) CreateCommunity(request *requests.CreateCommunity, createDef
|
|||
|
||||
response.AddCommunity(community)
|
||||
response.AddCommunitySettings(&communitySettings)
|
||||
err = m.syncCommunity(context.Background(), community)
|
||||
err = m.syncCommunity(context.Background(), community, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ func (m *Messenger) SendContactVerificationRequest(ctx context.Context, contactI
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ func (m *Messenger) SendContactVerificationRequest(ctx context.Context, contactI
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest)
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ func (m *Messenger) CancelVerificationRequest(ctx context.Context, id string) (*
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ func (m *Messenger) CancelVerificationRequest(ctx context.Context, id string) (*
|
|||
|
||||
response.AddVerificationRequest(verifRequest)
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest)
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ func (m *Messenger) AcceptContactVerificationRequest(ctx context.Context, id str
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest)
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -396,7 +396,7 @@ func (m *Messenger) VerifiedTrusted(ctx context.Context, request *requests.Verif
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.SyncTrustedUser(context.Background(), contactID, verification.TrustStatusTRUSTED)
|
||||
err = m.SyncTrustedUser(context.Background(), contactID, verification.TrustStatusTRUSTED, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -436,13 +436,13 @@ func (m *Messenger) VerifiedTrusted(ctx context.Context, request *requests.Verif
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest)
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -501,7 +501,7 @@ func (m *Messenger) VerifiedUntrustworthy(ctx context.Context, request *requests
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.SyncTrustedUser(context.Background(), contactID, verification.TrustStatusUNTRUSTWORTHY)
|
||||
err = m.SyncTrustedUser(context.Background(), contactID, verification.TrustStatusUNTRUSTWORTHY, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -541,13 +541,13 @@ func (m *Messenger) VerifiedUntrustworthy(ctx context.Context, request *requests
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest)
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -624,7 +624,7 @@ func (m *Messenger) DeclineContactVerificationRequest(ctx context.Context, id st
|
|||
|
||||
response.AddVerificationRequest(verifRequest)
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest)
|
||||
err = m.SyncVerificationRequest(context.Background(), verifRequest, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -687,7 +687,7 @@ func (m *Messenger) MarkAsTrusted(ctx context.Context, contactID string) error {
|
|||
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 {
|
||||
|
@ -696,7 +696,7 @@ func (m *Messenger) MarkAsUntrustworthy(ctx context.Context, contactID string) e
|
|||
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 {
|
||||
|
@ -705,7 +705,7 @@ func (m *Messenger) RemoveTrustStatus(ctx context.Context, contactID string) 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) {
|
||||
|
@ -775,7 +775,7 @@ func (m *Messenger) HandleRequestContactVerification(state *ReceivedMessageState
|
|||
}
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -865,7 +865,7 @@ func (m *Messenger) HandleAcceptContactVerification(state *ReceivedMessageState,
|
|||
return err
|
||||
}
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), persistedVR)
|
||||
err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -952,7 +952,7 @@ func (m *Messenger) HandleDeclineContactVerification(state *ReceivedMessageState
|
|||
return err
|
||||
}
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), persistedVR)
|
||||
err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1005,7 +1005,7 @@ func (m *Messenger) HandleCancelContactVerification(state *ReceivedMessageState,
|
|||
return err
|
||||
}
|
||||
|
||||
err = m.SyncVerificationRequest(context.Background(), persistedVR)
|
||||
err = m.SyncVerificationRequest(context.Background(), persistedVR, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func (m *Messenger) AcceptContactRequest(ctx context.Context, request *requests.
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.syncContactRequestDecision(ctx, request.ID.String(), true)
|
||||
err = m.syncContactRequestDecision(ctx, request.ID.String(), true, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ func (m *Messenger) DismissContactRequest(ctx context.Context, request *requests
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = m.syncContactRequestDecision(ctx, request.ID.String(), false)
|
||||
err = m.syncContactRequestDecision(ctx, request.ID.String(), false, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -315,7 +315,7 @@ func (m *Messenger) addContact(pubKey, ensName, nickname, displayName, contactRe
|
|||
|
||||
if !syncing {
|
||||
// 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 {
|
||||
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
|
||||
response, err := m.sendContactUpdate(context.Background(), pubKey, displayName, ensName, "")
|
||||
response, err := m.sendContactUpdate(context.Background(), pubKey, displayName, ensName, "", m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ func (m *Messenger) removeContact(ctx context.Context, response *MessengerRespon
|
|||
return err
|
||||
}
|
||||
|
||||
err = m.syncContact(context.Background(), contact)
|
||||
err = m.syncContact(context.Background(), contact, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -580,7 +580,7 @@ func (m *Messenger) SetContactLocalNickname(request *requests.SetContactLocalNic
|
|||
response := &MessengerResponse{}
|
||||
response.Contacts = []*Contact{contact}
|
||||
|
||||
err = m.syncContact(context.Background(), contact)
|
||||
err = m.syncContact(context.Background(), contact, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -621,7 +621,7 @@ func (m *Messenger) blockContact(contactID string, isDesktopFunc bool) ([]*Chat,
|
|||
m.allChats.Delete(buildProfileChatID(contact.ID))
|
||||
}
|
||||
|
||||
err = m.syncContact(context.Background(), contact)
|
||||
err = m.syncContact(context.Background(), contact, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -696,7 +696,7 @@ func (m *Messenger) UnblockContact(contactID string) error {
|
|||
|
||||
m.allContacts.Store(contact.ID, contact)
|
||||
|
||||
err = m.syncContact(context.Background(), contact)
|
||||
err = m.syncContact(context.Background(), contact, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -719,14 +719,14 @@ func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImag
|
|||
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
|
||||
}
|
||||
|
||||
// TODO: This should not be sending paired messages, as we do it above
|
||||
m.allContacts.Range(func(contactID string, contact *Contact) (shouldContinue bool) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -748,10 +748,10 @@ func (m *Messenger) SendContactUpdate(ctx context.Context, chatID, ensName, prof
|
|||
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
|
||||
|
||||
contact, ok := m.allContacts.Load(chatID)
|
||||
|
@ -785,12 +785,14 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, displayName,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chatID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -725,7 +725,7 @@ func (m *Messenger) leaveGroupChat(ctx context.Context, response *MessengerRespo
|
|||
}
|
||||
|
||||
if remove && shouldBeSynced {
|
||||
err := m.syncChatRemoving(ctx, chat.ID)
|
||||
err := m.syncChatRemoving(ctx, chat.ID, m.dispatchMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -210,7 +210,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// 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)
|
||||
|
||||
var allChats []*Chat
|
||||
|
|
|
@ -17,7 +17,7 @@ func (m *Messenger) UpsertSavedAddress(ctx context.Context, sa wallet.SavedAddre
|
|||
if err != nil {
|
||||
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 {
|
||||
|
@ -25,14 +25,14 @@ func (m *Messenger) DeleteSavedAddress(ctx context.Context, chainID uint64, addr
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.syncDeletedSavedAddress(ctx, chainID, address, updatedClock)
|
||||
return m.syncDeletedSavedAddress(ctx, chainID, address, updatedClock, m.dispatchMessage)
|
||||
}
|
||||
|
||||
func (m *Messenger) garbageCollectRemovedSavedAddresses() error {
|
||||
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() {
|
||||
return nil
|
||||
}
|
||||
|
@ -44,12 +44,14 @@ func (m *Messenger) dispatchSyncSavedAddress(ctx context.Context, syncMessage pr
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, common.RawMessage{
|
||||
rawMessage := common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_SAVED_ADDRESS,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = rawMessageHandler(ctx, rawMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -58,32 +60,32 @@ func (m *Messenger) dispatchSyncSavedAddress(ctx context.Context, syncMessage pr
|
|||
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{
|
||||
Address: savedAddress.Address.Bytes(),
|
||||
Name: savedAddress.Name,
|
||||
Favourite: savedAddress.Favourite,
|
||||
ChainId: savedAddress.ChainID,
|
||||
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{
|
||||
Address: address.Bytes(),
|
||||
ChainId: chainID,
|
||||
UpdateClock: updateClock,
|
||||
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 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
|
||||
}
|
||||
} else {
|
||||
if err = m.syncNewSavedAddress(ctx, &savedAddress, savedAddress.UpdateClock); err != nil {
|
||||
if err = m.syncNewSavedAddress(ctx, &savedAddress, savedAddress.UpdateClock, rawMessageHandler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ func (s *MessengerSyncBookmarkSuite) TestSyncBookmark() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// sync
|
||||
err = s.m.SyncBookmark(context.Background(), &bookmark)
|
||||
err = s.m.SyncBookmark(context.Background(), &bookmark, s.m.dispatchMessage)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Wait for the message to reach its destination
|
||||
|
@ -133,7 +133,7 @@ func (s *MessengerSyncBookmarkSuite) TestSyncBookmark() {
|
|||
|
||||
// sync removed state
|
||||
bookmark.Removed = true
|
||||
err = s.m.SyncBookmark(context.Background(), &bookmark)
|
||||
err = s.m.SyncBookmark(context.Background(), &bookmark, s.m.dispatchMessage)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Wait for the message to reach its destination
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -142,7 +142,7 @@ func (s *MessengerSyncSavedAddressesSuite) TestSyncExistingSavedAddresses() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// 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)
|
||||
|
||||
// Wait and check that saved addresses are synced
|
||||
|
|
|
@ -52,7 +52,7 @@ func (m *Messenger) prepareSyncSettingsMessages(currentClock uint64) (resultRaw
|
|||
return
|
||||
}
|
||||
|
||||
func (m *Messenger) syncSettings() error {
|
||||
func (m *Messenger) syncSettings(rawMessageHandler RawMessageHandler) error {
|
||||
logger := m.logger.Named("syncSettings")
|
||||
|
||||
clock, _ := m.getLastClockWithRelatedChat()
|
||||
|
@ -64,7 +64,7 @@ func (m *Messenger) syncSettings() error {
|
|||
}
|
||||
|
||||
for _, rm := range rawMessages {
|
||||
_, err := m.dispatchMessage(context.Background(), *rm)
|
||||
_, err := rawMessageHandler(context.Background(), *rm)
|
||||
if err != nil {
|
||||
logger.Error("dispatchMessage", zap.Error(err))
|
||||
return err
|
||||
|
|
|
@ -96,7 +96,7 @@ func (s *MessengerSyncVerificationRequests) TestSyncVerificationRequests() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// sync
|
||||
err = s.m.SyncVerificationRequest(context.Background(), request)
|
||||
err = s.m.SyncVerificationRequest(context.Background(), request, s.m.dispatchMessage)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Wait for the message to reach its destination
|
||||
|
@ -160,7 +160,7 @@ func (s *MessengerSyncVerificationRequests) TestSyncTrust() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// 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)
|
||||
|
||||
// Wait for the message to reach its destination
|
||||
|
|
|
@ -146,7 +146,7 @@ func (s *MessengerSyncWalletSuite) TestSyncWallets() {
|
|||
s.Len(acc1, 2, "Must have 2 accounts")
|
||||
|
||||
// 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)
|
||||
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
|
|
|
@ -2343,6 +2343,117 @@ func (m *BackedUpProfile) GetPictures() []*SyncProfilePicture {
|
|||
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() {
|
||||
proto.RegisterEnum("protobuf.SyncTrustedUser_TrustStatus", SyncTrustedUser_TrustStatus_name, SyncTrustedUser_TrustStatus_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((*SyncContactRequestDecision)(nil), "protobuf.SyncContactRequestDecision")
|
||||
proto.RegisterType((*BackedUpProfile)(nil), "protobuf.BackedUpProfile")
|
||||
proto.RegisterType((*RawMessage)(nil), "protobuf.RawMessage")
|
||||
proto.RegisterType((*SyncRawMessage)(nil), "protobuf.SyncRawMessage")
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -2386,141 +2499,150 @@ func init() {
|
|||
}
|
||||
|
||||
var fileDescriptor_d61ab7221f0b5518 = []byte{
|
||||
// 2174 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0x51, 0x73, 0x1b, 0x49,
|
||||
0x11, 0xbe, 0xd5, 0x2a, 0x96, 0xd4, 0x92, 0x6d, 0xdd, 0xe4, 0x2e, 0x51, 0x9c, 0xa4, 0xe2, 0x6c,
|
||||
0x48, 0x5d, 0x1e, 0x0e, 0x1f, 0x95, 0x40, 0x1d, 0x5c, 0xee, 0x8a, 0x53, 0x64, 0x73, 0xd1, 0x25,
|
||||
0x51, 0x5c, 0x63, 0x3b, 0x01, 0x8a, 0xaa, 0xad, 0xf1, 0xee, 0xc4, 0x1a, 0xbc, 0xda, 0x5d, 0x76,
|
||||
0x46, 0x0e, 0xcb, 0x0f, 0xe0, 0x07, 0xf0, 0xc2, 0xeb, 0xbd, 0xf3, 0x46, 0xd5, 0xf1, 0xc4, 0x0f,
|
||||
0xe0, 0x8d, 0x07, 0x5e, 0xa8, 0x82, 0xe2, 0x07, 0xf0, 0x2b, 0xa8, 0xe9, 0x99, 0x95, 0x76, 0x65,
|
||||
0xc9, 0x38, 0xc5, 0x13, 0x4f, 0x9a, 0xee, 0xe9, 0xee, 0xed, 0xe9, 0xee, 0xe9, 0xfe, 0x46, 0xb0,
|
||||
0x9e, 0x32, 0x91, 0x89, 0xf8, 0x64, 0x27, 0xcd, 0x12, 0x95, 0x90, 0x26, 0xfe, 0x1c, 0x4f, 0xdf,
|
||||
0x6c, 0x5d, 0x95, 0x79, 0x1c, 0xf8, 0x92, 0x2b, 0x25, 0xe2, 0x13, 0x69, 0xb6, 0x3d, 0x06, 0x37,
|
||||
0x7f, 0xc2, 0x55, 0x30, 0x16, 0xf1, 0xc9, 0x13, 0x16, 0x9c, 0xf2, 0xf0, 0x28, 0xdd, 0x65, 0x8a,
|
||||
0xed, 0x72, 0xc5, 0x44, 0x24, 0xc9, 0x1d, 0x68, 0x87, 0x4c, 0x31, 0x3f, 0x9e, 0x4e, 0x8e, 0x79,
|
||||
0xd6, 0x73, 0xb6, 0x9d, 0x07, 0xeb, 0x14, 0x34, 0x6b, 0x84, 0x1c, 0x72, 0x17, 0x3a, 0x2a, 0x51,
|
||||
0x2c, 0x2a, 0x24, 0x6a, 0x28, 0xd1, 0x46, 0x9e, 0x11, 0xf1, 0xfe, 0x5e, 0x87, 0x35, 0x6d, 0x7b,
|
||||
0x9a, 0x92, 0x0f, 0xe0, 0x4a, 0x10, 0x25, 0xc1, 0x29, 0x1a, 0xaa, 0x53, 0x43, 0x90, 0x0d, 0xa8,
|
||||
0x89, 0x10, 0x35, 0x5b, 0xb4, 0x26, 0x42, 0xf2, 0x63, 0x68, 0x06, 0x49, 0xac, 0x58, 0xa0, 0x64,
|
||||
0xcf, 0xdd, 0x76, 0x1f, 0xb4, 0x1f, 0xde, 0xdb, 0x29, 0x4e, 0xb1, 0x73, 0x90, 0xc7, 0xc1, 0x30,
|
||||
0x96, 0x8a, 0x45, 0x11, 0x53, 0x22, 0x89, 0x07, 0x46, 0xf2, 0xd5, 0x43, 0x3a, 0x53, 0x22, 0x3f,
|
||||
0x82, 0x76, 0x90, 0x4c, 0x26, 0xd3, 0x58, 0x28, 0xc1, 0x65, 0xaf, 0x8e, 0x36, 0xae, 0x57, 0x6d,
|
||||
0x0c, 0xac, 0x40, 0x4e, 0xcb, 0xb2, 0xe4, 0x25, 0x6c, 0x16, 0x66, 0x6c, 0x0c, 0x7a, 0x57, 0xb6,
|
||||
0x9d, 0x07, 0xed, 0x87, 0xf7, 0xe7, 0xea, 0x17, 0x04, 0x8c, 0x2e, 0x6a, 0x93, 0x23, 0x20, 0x25,
|
||||
0xfb, 0x85, 0xcd, 0xb5, 0x77, 0xb1, 0xb9, 0xc4, 0x00, 0x79, 0x04, 0x8d, 0x34, 0x4b, 0xde, 0x88,
|
||||
0x88, 0xf7, 0x1a, 0x68, 0xeb, 0xc6, 0xdc, 0x56, 0x61, 0x63, 0xdf, 0x08, 0xd0, 0x42, 0x92, 0xbc,
|
||||
0x80, 0x0d, 0xbb, 0x2c, 0xfc, 0x68, 0xbe, 0x8b, 0x1f, 0x0b, 0xca, 0xe4, 0x13, 0x68, 0xd8, 0x6a,
|
||||
0xea, 0xb5, 0xd0, 0xce, 0x87, 0xd5, 0x10, 0x1f, 0x98, 0x4d, 0x5a, 0x48, 0xe9, 0xe0, 0x16, 0xe5,
|
||||
0x57, 0x38, 0x00, 0xef, 0x14, 0xdc, 0x05, 0x6d, 0xef, 0xcf, 0x75, 0xe8, 0xbc, 0x98, 0x46, 0x4a,
|
||||
0xf4, 0x83, 0x20, 0x99, 0xc6, 0x8a, 0x10, 0xa8, 0xc7, 0x6c, 0xc2, 0xb1, 0xbe, 0x5a, 0x14, 0xd7,
|
||||
0xe4, 0x16, 0xb4, 0x94, 0x98, 0x70, 0xa9, 0xd8, 0x24, 0xc5, 0x2a, 0x73, 0xe9, 0x9c, 0xa1, 0x77,
|
||||
0x45, 0xc8, 0x63, 0x25, 0x82, 0x24, 0xee, 0xb9, 0xa8, 0x36, 0x67, 0x90, 0x2f, 0x01, 0x82, 0x24,
|
||||
0x4a, 0x32, 0x7f, 0xcc, 0xe4, 0xd8, 0x16, 0xd2, 0xdd, 0xb9, 0xb3, 0xe5, 0x6f, 0xef, 0x0c, 0x92,
|
||||
0x28, 0x99, 0x66, 0x4f, 0x99, 0x1c, 0xd3, 0x16, 0x2a, 0xe9, 0x25, 0xe9, 0x41, 0x03, 0x89, 0x61,
|
||||
0x88, 0x85, 0xe4, 0xd2, 0x82, 0x24, 0x1f, 0xc1, 0xe6, 0x29, 0xcf, 0x03, 0x96, 0x85, 0xbe, 0xbd,
|
||||
0xb2, 0x58, 0x16, 0x2d, 0xba, 0x61, 0xd9, 0xfb, 0x86, 0x4b, 0xae, 0x43, 0xe3, 0x94, 0xe7, 0xfe,
|
||||
0x54, 0x84, 0x98, 0xeb, 0x16, 0x5d, 0x3b, 0xe5, 0xf9, 0x91, 0x08, 0xc9, 0xe7, 0xb0, 0x26, 0x26,
|
||||
0xec, 0x84, 0xeb, 0x3c, 0x6a, 0xcf, 0xbe, 0xb3, 0xc2, 0xb3, 0x21, 0x9e, 0x47, 0xe5, 0x43, 0x2d,
|
||||
0x4c, 0xad, 0xce, 0x96, 0x07, 0x30, 0x77, 0x59, 0x5f, 0x4d, 0x11, 0x87, 0xfc, 0xd7, 0x3d, 0x67,
|
||||
0xdb, 0x7d, 0xe0, 0x52, 0x43, 0x6c, 0xfd, 0xc3, 0x81, 0xf5, 0x8a, 0x76, 0xd9, 0x19, 0xa7, 0xe2,
|
||||
0x4c, 0x11, 0xfa, 0x5a, 0x29, 0xf4, 0x3d, 0x68, 0xa4, 0x2c, 0x8f, 0x12, 0x16, 0x62, 0x68, 0x3b,
|
||||
0xb4, 0x20, 0xf5, 0xe7, 0xde, 0x8a, 0x50, 0xe9, 0x98, 0xea, 0xa0, 0x18, 0x82, 0x5c, 0x83, 0xb5,
|
||||
0x31, 0x17, 0x27, 0x63, 0x65, 0x63, 0x65, 0x29, 0xb2, 0x05, 0x4d, 0x5d, 0x78, 0x52, 0xfc, 0x86,
|
||||
0x63, 0x8c, 0x5c, 0x3a, 0xa3, 0xc9, 0x3d, 0x58, 0xcf, 0x70, 0xe5, 0x2b, 0x96, 0x9d, 0x70, 0x85,
|
||||
0x31, 0x72, 0x69, 0xc7, 0x30, 0x0f, 0x91, 0x37, 0x6f, 0x3c, 0xcd, 0x52, 0xe3, 0xf1, 0xfe, 0xe6,
|
||||
0xc0, 0xd5, 0xe7, 0x49, 0xc0, 0x22, 0x1b, 0xe9, 0x7d, 0xeb, 0xdc, 0x0f, 0xa0, 0x7e, 0xca, 0x73,
|
||||
0x89, 0xa1, 0xa8, 0xe4, 0x7b, 0x89, 0xf0, 0xce, 0x33, 0x9e, 0x53, 0x14, 0x27, 0x9f, 0x41, 0x67,
|
||||
0xa2, 0xc3, 0xce, 0x4c, 0xd8, 0x31, 0x12, 0xed, 0x87, 0xd7, 0x96, 0x27, 0x85, 0x56, 0x64, 0xf5,
|
||||
0x09, 0x53, 0x26, 0xe5, 0xdb, 0x24, 0x0b, 0x6d, 0x15, 0xce, 0xe8, 0xad, 0xef, 0x82, 0xfb, 0x8c,
|
||||
0xe7, 0x4b, 0x6b, 0x9b, 0x40, 0x5d, 0x37, 0x63, 0xfc, 0x54, 0x87, 0xe2, 0xda, 0xfb, 0xad, 0x03,
|
||||
0x5d, 0xed, 0x63, 0xb9, 0x4b, 0xae, 0xe8, 0xbc, 0x1f, 0xc1, 0xa6, 0x28, 0x49, 0xf9, 0xb3, 0x36,
|
||||
0xbc, 0x51, 0x66, 0x0f, 0x43, 0x9c, 0x03, 0xfc, 0x4c, 0x04, 0xdc, 0x57, 0x79, 0xca, 0xad, 0x87,
|
||||
0x60, 0x58, 0x87, 0x79, 0xca, 0x67, 0xce, 0xd5, 0xe7, 0xce, 0x79, 0xff, 0x76, 0xe0, 0xfa, 0x8a,
|
||||
0x76, 0x7d, 0xc9, 0x49, 0x70, 0x0f, 0xd6, 0x6d, 0xcf, 0xf1, 0xb1, 0x68, 0xed, 0x87, 0x3b, 0x96,
|
||||
0x69, 0x2a, 0xf2, 0x06, 0x34, 0x79, 0x2c, 0xfd, 0xd2, 0xe7, 0x1b, 0x3c, 0x96, 0x23, 0x1d, 0x9e,
|
||||
0xbb, 0xd0, 0x89, 0x98, 0x54, 0xfe, 0x34, 0x0d, 0x99, 0xe2, 0xe6, 0x06, 0xd6, 0x69, 0x5b, 0xf3,
|
||||
0x8e, 0x0c, 0x4b, 0x9f, 0x4c, 0xe6, 0x52, 0xf1, 0x89, 0xaf, 0xd8, 0x89, 0x6e, 0xcc, 0xae, 0x3e,
|
||||
0x99, 0x61, 0x1d, 0xb2, 0x13, 0x49, 0xee, 0xc3, 0x46, 0xa4, 0xd3, 0xee, 0xc7, 0x22, 0x38, 0xc5,
|
||||
0x8f, 0x98, 0x4b, 0xb8, 0x8e, 0xdc, 0x91, 0x65, 0x7a, 0xff, 0x72, 0xe1, 0xc6, 0xca, 0xd9, 0x44,
|
||||
0xbe, 0x07, 0x1f, 0x94, 0x1d, 0xf1, 0x51, 0x37, 0xca, 0xed, 0xe9, 0x49, 0xc9, 0xa1, 0xe7, 0x66,
|
||||
0xe7, 0xff, 0x38, 0x14, 0x3a, 0xb7, 0x2c, 0x0c, 0x79, 0x88, 0x53, 0xa1, 0x49, 0x0d, 0xa1, 0x7b,
|
||||
0xc1, 0xb1, 0x4e, 0x32, 0x0f, 0xb1, 0xe9, 0x37, 0x69, 0x41, 0x6a, 0xf9, 0xc9, 0x54, 0xfb, 0xd4,
|
||||
0x36, 0xf2, 0x48, 0x68, 0xf9, 0x8c, 0x4f, 0x92, 0x33, 0x1e, 0xf6, 0x3a, 0x46, 0xde, 0x92, 0x64,
|
||||
0x1b, 0x3a, 0x63, 0x26, 0x7d, 0x34, 0xeb, 0x4f, 0x65, 0x6f, 0x1d, 0xb7, 0x61, 0xcc, 0x64, 0x5f,
|
||||
0xb3, 0x8e, 0xf4, 0x64, 0xba, 0x7a, 0xc6, 0x33, 0xf1, 0x46, 0x04, 0xa6, 0xae, 0xa5, 0x62, 0x6a,
|
||||
0x2a, 0x7b, 0x1b, 0xd8, 0x19, 0x48, 0x79, 0xeb, 0x00, 0x77, 0x10, 0xc6, 0x64, 0x53, 0xa9, 0x0a,
|
||||
0xc9, 0x4d, 0x94, 0x6c, 0x23, 0xcf, 0x88, 0x78, 0x6f, 0xcf, 0x17, 0x73, 0x31, 0x75, 0x96, 0x17,
|
||||
0xf3, 0xb9, 0x8c, 0xd5, 0x96, 0x64, 0x6c, 0x31, 0x2d, 0xee, 0xb9, 0xb4, 0x78, 0x4f, 0x60, 0x6b,
|
||||
0xf1, 0xc3, 0xfb, 0xd3, 0xe3, 0x48, 0x04, 0x83, 0x31, 0xbb, 0xe4, 0x45, 0xf2, 0xbe, 0x75, 0x61,
|
||||
0xbd, 0x82, 0x7a, 0xfe, 0xab, 0x5e, 0x07, 0xab, 0xee, 0x0e, 0xb4, 0xd3, 0x4c, 0x9c, 0x31, 0xc5,
|
||||
0xfd, 0x53, 0x9e, 0xdb, 0x26, 0x0e, 0x96, 0xa5, 0x9b, 0xd2, 0xb6, 0x6e, 0x0c, 0x32, 0xc8, 0x44,
|
||||
0xaa, 0xfd, 0xc2, 0xa2, 0xeb, 0xd0, 0x32, 0x4b, 0xf7, 0xf4, 0x5f, 0x26, 0x22, 0xb6, 0x25, 0xd7,
|
||||
0xa4, 0x96, 0xd2, 0x1d, 0xcf, 0x24, 0x82, 0x87, 0xd8, 0xd3, 0x9b, 0x74, 0x46, 0xcf, 0x2b, 0xa2,
|
||||
0x51, 0xae, 0x88, 0x97, 0xd0, 0xcd, 0xf8, 0xaf, 0xa6, 0x5c, 0x2a, 0xe9, 0xab, 0xc4, 0xd7, 0x76,
|
||||
0xec, 0xe0, 0xbb, 0xbf, 0x0a, 0xdb, 0x59, 0xf1, 0xc3, 0xe4, 0xeb, 0x44, 0xc4, 0x74, 0x23, 0xab,
|
||||
0xd0, 0xe4, 0x31, 0x34, 0x0b, 0x44, 0x61, 0x11, 0xcc, 0x9d, 0x15, 0x86, 0x2c, 0x94, 0x91, 0x74,
|
||||
0xa6, 0xa0, 0x81, 0x03, 0x8f, 0x83, 0x2c, 0x4f, 0xd5, 0xac, 0xa2, 0xe7, 0x0c, 0xbd, 0x2b, 0x53,
|
||||
0x1e, 0x28, 0x36, 0xaf, 0xeb, 0x39, 0x43, 0xf7, 0x5d, 0x2b, 0xaa, 0xab, 0x13, 0x67, 0x4d, 0x07,
|
||||
0x23, 0xb7, 0x31, 0x67, 0x3f, 0xe3, 0xb9, 0xf4, 0xfe, 0xea, 0xc0, 0xcd, 0x0b, 0x4e, 0x64, 0xf3,
|
||||
0xe5, 0xcc, 0xf2, 0x75, 0x1b, 0x20, 0xc5, 0xda, 0xc0, 0x74, 0x99, 0xfc, 0xb7, 0x0c, 0x47, 0x67,
|
||||
0x6b, 0x96, 0x74, 0xb7, 0x9c, 0xf4, 0x0b, 0xba, 0xc6, 0x75, 0x68, 0x04, 0x63, 0xa6, 0xf4, 0x60,
|
||||
0xb8, 0x62, 0xa6, 0xbd, 0x26, 0x87, 0xa1, 0xae, 0xdb, 0x02, 0x95, 0xe6, 0x7a, 0x77, 0xcd, 0x24,
|
||||
0x7e, 0xc6, 0x1b, 0x62, 0x12, 0xf5, 0x6d, 0x32, 0x4d, 0xa2, 0x4e, 0x0d, 0xe1, 0xfd, 0xae, 0x06,
|
||||
0xdd, 0xc5, 0x72, 0x26, 0x5f, 0x94, 0x10, 0xff, 0xb9, 0xa1, 0xbb, 0xa2, 0xab, 0x96, 0xf0, 0xfe,
|
||||
0x57, 0xd0, 0xb1, 0xa7, 0xd6, 0xde, 0xc9, 0x5e, 0x6d, 0x11, 0x0d, 0xad, 0xbe, 0x3f, 0xb4, 0x9d,
|
||||
0xce, 0xd6, 0x92, 0x3c, 0x86, 0x46, 0x31, 0xbc, 0x5d, 0xac, 0x87, 0x0b, 0xdc, 0x28, 0xe6, 0x78,
|
||||
0xa1, 0xf1, 0x3f, 0xbc, 0x3a, 0xbc, 0x4f, 0x61, 0x13, 0x77, 0xb5, 0x43, 0xb6, 0xc9, 0x5d, 0xee,
|
||||
0x5e, 0x7f, 0x0e, 0x1f, 0x14, 0x8a, 0x2f, 0xb8, 0x94, 0x1a, 0xd7, 0x51, 0xce, 0x2e, 0xab, 0xfd,
|
||||
0x25, 0x5c, 0xd3, 0xda, 0xfd, 0x40, 0x89, 0x33, 0xa1, 0xf2, 0x01, 0x8f, 0x15, 0xcf, 0x2e, 0xd0,
|
||||
0xef, 0x82, 0x2b, 0x42, 0x13, 0xde, 0x0e, 0xd5, 0x4b, 0x6f, 0xd7, 0xf4, 0xa6, 0xaa, 0x85, 0x7e,
|
||||
0x10, 0x70, 0xbc, 0x04, 0x97, 0xb5, 0xb2, 0x67, 0x8a, 0xbc, 0x6a, 0x65, 0x57, 0xc8, 0x89, 0x90,
|
||||
0xf2, 0x1d, 0xcc, 0x7c, 0xe3, 0x40, 0x47, 0xdb, 0x79, 0x92, 0x24, 0xa7, 0x13, 0x96, 0x9d, 0xae,
|
||||
0x56, 0x9c, 0x66, 0x91, 0x0d, 0x83, 0x5e, 0xce, 0xc0, 0x8b, 0x5b, 0x42, 0x56, 0x37, 0xa1, 0x85,
|
||||
0x5d, 0xdb, 0xd7, 0xb2, 0xe6, 0x56, 0x34, 0x91, 0x71, 0x94, 0x45, 0xe5, 0xd9, 0x74, 0xa5, 0x3a,
|
||||
0x9b, 0x6e, 0x03, 0x84, 0x3c, 0xe2, 0x7a, 0xc6, 0x33, 0x85, 0xb7, 0xa2, 0x4e, 0x5b, 0x96, 0xd3,
|
||||
0x57, 0xde, 0xd7, 0xa6, 0xf8, 0x07, 0x11, 0x67, 0xd9, 0x53, 0x21, 0x55, 0x92, 0xe5, 0xe5, 0x3b,
|
||||
0xe6, 0x54, 0xee, 0xd8, 0x6d, 0x80, 0x40, 0x0b, 0x1a, 0x5b, 0x35, 0x63, 0xcb, 0x72, 0xfa, 0xca,
|
||||
0xfb, 0x8b, 0x03, 0x44, 0x1b, 0xb3, 0xcf, 0xbc, 0x7d, 0x11, 0xa8, 0x69, 0xc6, 0x97, 0xc2, 0xc4,
|
||||
0x12, 0x0e, 0xaf, 0xad, 0xc0, 0xe1, 0x2e, 0x3e, 0xdc, 0xcf, 0xe1, 0xf0, 0x3a, 0xb2, 0x0b, 0x1c,
|
||||
0x7e, 0x13, 0x5a, 0x38, 0xcf, 0x10, 0x88, 0x5f, 0xc1, 0x2d, 0x04, 0xe2, 0x07, 0x4b, 0x81, 0xf8,
|
||||
0x1a, 0x0a, 0xac, 0x00, 0xe2, 0x8d, 0x32, 0x10, 0x1f, 0xc3, 0xd5, 0xf3, 0x27, 0x91, 0xab, 0xdf,
|
||||
0x1a, 0x3f, 0x84, 0x66, 0x6a, 0x85, 0xec, 0x65, 0xbf, 0x55, 0xbd, 0x67, 0x55, 0x4b, 0x74, 0x26,
|
||||
0xed, 0xfd, 0xa1, 0x06, 0xef, 0x6b, 0x81, 0xd7, 0x2c, 0x8a, 0xb8, 0xba, 0x78, 0x80, 0xf7, 0xa0,
|
||||
0xc1, 0xc2, 0x30, 0xe3, 0x52, 0x16, 0x51, 0xb3, 0xa4, 0x8e, 0xcf, 0x5b, 0x34, 0x80, 0x61, 0x6b,
|
||||
0x52, 0x4b, 0xe9, 0xd8, 0xeb, 0xdc, 0x61, 0xd4, 0x9a, 0x14, 0xd7, 0x9a, 0x87, 0x98, 0xd9, 0xf4,
|
||||
0x4f, 0x5c, 0x6b, 0xcb, 0x3a, 0xf7, 0x1a, 0x14, 0x98, 0x27, 0x5f, 0x41, 0x6a, 0xe9, 0x94, 0xa9,
|
||||
0xb1, 0x05, 0x56, 0xb8, 0xd6, 0xb3, 0x64, 0xd6, 0xc2, 0xf1, 0x01, 0xd3, 0x29, 0xf7, 0xf4, 0x22,
|
||||
0xdf, 0xad, 0x52, 0xbe, 0xf5, 0x79, 0xf4, 0x2b, 0x13, 0xe7, 0x52, 0x8b, 0x1a, 0x02, 0xb3, 0x2a,
|
||||
0xc2, 0x90, 0xc7, 0x76, 0x20, 0x59, 0x6a, 0x35, 0xd2, 0xf2, 0x5e, 0x98, 0x0a, 0xab, 0x04, 0x4b,
|
||||
0x92, 0x4f, 0xa1, 0x69, 0x7b, 0x5e, 0xd1, 0xad, 0x6f, 0x56, 0xa3, 0x5f, 0x91, 0xa7, 0x33, 0x61,
|
||||
0xef, 0x4f, 0x8e, 0x29, 0xff, 0x03, 0x76, 0xc6, 0xc3, 0xbe, 0x8d, 0x65, 0x29, 0xca, 0x4e, 0x35,
|
||||
0xca, 0xcb, 0x5e, 0x94, 0xb7, 0xa0, 0xf5, 0x86, 0x9d, 0x25, 0xd3, 0x4c, 0x28, 0x6e, 0x83, 0x3f,
|
||||
0x67, 0xe8, 0x49, 0x16, 0x8c, 0x99, 0xc0, 0x87, 0x4c, 0x1d, 0x53, 0xd9, 0x40, 0x7a, 0x18, 0x5e,
|
||||
0x70, 0x65, 0xef, 0x42, 0xc7, 0xa0, 0x2f, 0xbf, 0x5c, 0x99, 0x6d, 0xc3, 0x1b, 0x60, 0x7d, 0xfe,
|
||||
0xde, 0x81, 0x0f, 0x97, 0xe2, 0x81, 0x15, 0x95, 0xb3, 0x38, 0x1d, 0xcd, 0x09, 0x2a, 0xd3, 0x71,
|
||||
0x0f, 0xee, 0x8c, 0x4d, 0x03, 0xf0, 0x59, 0x16, 0x8c, 0xc5, 0x19, 0xf7, 0xe5, 0x34, 0x4d, 0x93,
|
||||
0x4c, 0xf9, 0x3c, 0x66, 0xc7, 0x91, 0xc5, 0x82, 0x4d, 0x7a, 0xcb, 0x8a, 0xf5, 0x8d, 0xd4, 0x81,
|
||||
0x11, 0xda, 0x33, 0x32, 0xde, 0x1f, 0x1d, 0x33, 0x3a, 0x0e, 0x35, 0x52, 0xd5, 0xd8, 0x97, 0x67,
|
||||
0x97, 0x7c, 0x5b, 0x7d, 0x01, 0x6b, 0x16, 0xec, 0xea, 0xef, 0x6c, 0x2c, 0x62, 0xa8, 0x92, 0xc1,
|
||||
0x9d, 0xc3, 0x39, 0x0c, 0xa6, 0x56, 0xc9, 0xfb, 0x0c, 0xda, 0x25, 0x36, 0x69, 0x43, 0xe3, 0x68,
|
||||
0xf4, 0x6c, 0xf4, 0xf2, 0xf5, 0xa8, 0xfb, 0x9e, 0x26, 0x0e, 0xe9, 0xd1, 0xc1, 0xe1, 0xde, 0x6e,
|
||||
0xd7, 0x21, 0xef, 0xc3, 0xfa, 0xd1, 0x08, 0xc9, 0xd7, 0x2f, 0xe9, 0xe1, 0xd3, 0x9f, 0x75, 0x6b,
|
||||
0xde, 0x37, 0xae, 0xc1, 0xd2, 0xaf, 0x4a, 0x40, 0xdc, 0x02, 0x9b, 0x15, 0xce, 0x13, 0xa8, 0xbf,
|
||||
0xc9, 0x92, 0x49, 0x51, 0x0a, 0x7a, 0xad, 0x0f, 0xa4, 0x12, 0xdb, 0xb3, 0x6b, 0x2a, 0xd1, 0xa5,
|
||||
0x11, 0x8c, 0x75, 0xe5, 0xc5, 0x27, 0x05, 0x8e, 0x99, 0x33, 0x74, 0x4a, 0x2c, 0xfa, 0x33, 0xed,
|
||||
0xd4, 0xbe, 0x7f, 0x66, 0xbc, 0x3e, 0xbe, 0xc1, 0x33, 0x2e, 0xd3, 0x24, 0x96, 0xc5, 0xb5, 0x9c,
|
||||
0xd1, 0xba, 0x17, 0x67, 0x3c, 0x8d, 0x84, 0x51, 0x36, 0x25, 0xd2, 0xb2, 0x9c, 0xbe, 0x22, 0x7c,
|
||||
0xf9, 0x83, 0xa3, 0x89, 0x91, 0xfd, 0x7e, 0x35, 0xb2, 0x4b, 0x4e, 0xbd, 0xf3, 0xea, 0xdc, 0x93,
|
||||
0x64, 0xe9, 0x33, 0xc5, 0xe4, 0xb0, 0x35, 0x1b, 0xe0, 0x3f, 0x05, 0x72, 0x5e, 0xf3, 0x5c, 0x2e,
|
||||
0xf6, 0xf7, 0x46, 0xbb, 0xc3, 0xd1, 0x57, 0x5d, 0x87, 0x74, 0xa0, 0xd9, 0x1f, 0x0c, 0xf6, 0xf6,
|
||||
0x75, 0x66, 0x6a, 0x9a, 0xda, 0xdd, 0x1b, 0x3c, 0x1f, 0x8e, 0xf6, 0x76, 0xbb, 0xae, 0xa6, 0x06,
|
||||
0xfd, 0xd1, 0x60, 0xef, 0xf9, 0xde, 0x6e, 0xb7, 0xee, 0xfd, 0xd3, 0x31, 0x93, 0xbd, 0x00, 0x5b,
|
||||
0xc6, 0xcf, 0x5d, 0x1e, 0x08, 0xb9, 0xfa, 0xef, 0x84, 0x5b, 0xd0, 0xb2, 0xf1, 0x1c, 0x16, 0x95,
|
||||
0x36, 0x67, 0x90, 0x5f, 0xc0, 0x66, 0x68, 0xf5, 0xfd, 0x4a, 0xe5, 0x3d, 0x5a, 0xc4, 0x48, 0xcb,
|
||||
0x3e, 0xb9, 0x53, 0x2c, 0x6c, 0x78, 0x36, 0xc2, 0x0a, 0xed, 0x7d, 0x0c, 0x1b, 0x55, 0x89, 0xca,
|
||||
0x61, 0xdf, 0xab, 0x1c, 0xd6, 0xf1, 0xbe, 0x75, 0x60, 0x73, 0xe1, 0x6f, 0xd2, 0xd5, 0xd3, 0xe6,
|
||||
0x2e, 0x74, 0x42, 0x21, 0xd3, 0x88, 0xe5, 0x7e, 0xa9, 0x1f, 0xb5, 0x2d, 0x0f, 0x71, 0xf2, 0xc7,
|
||||
0x40, 0xca, 0x22, 0x7e, 0x19, 0x65, 0x77, 0x4b, 0x82, 0xd8, 0x4e, 0x2a, 0xe3, 0xab, 0xfe, 0x2e,
|
||||
0xe3, 0xeb, 0xc9, 0xfa, 0xcf, 0xdb, 0x3b, 0x9f, 0x3c, 0x2e, 0x64, 0x8f, 0xd7, 0x70, 0xf5, 0xe8,
|
||||
0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x30, 0x89, 0x4d, 0x75, 0xf4, 0x17, 0x00, 0x00,
|
||||
// 2306 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x59, 0x5f, 0x73, 0x1b, 0x49,
|
||||
0x11, 0xbf, 0x95, 0x14, 0x4b, 0x6a, 0xc9, 0xb2, 0x33, 0xc9, 0x25, 0x8a, 0x93, 0x54, 0x9c, 0x0d,
|
||||
0xa9, 0x0b, 0x55, 0xc1, 0x77, 0x95, 0x00, 0x07, 0x97, 0xbb, 0xe2, 0x14, 0xd9, 0x5c, 0x9c, 0x3f,
|
||||
0x8e, 0x6b, 0x6c, 0x27, 0x40, 0x51, 0xb5, 0x35, 0xde, 0x1d, 0x5b, 0x83, 0x57, 0xbb, 0xcb, 0xce,
|
||||
0xc8, 0x61, 0xf9, 0x00, 0x7c, 0x00, 0x5e, 0x78, 0xbd, 0x77, 0xde, 0xa8, 0x3a, 0x9e, 0xf8, 0x00,
|
||||
0xbc, 0xf1, 0xc0, 0x0b, 0x55, 0x50, 0x7c, 0x00, 0x3e, 0x05, 0x35, 0x3d, 0xb3, 0xda, 0x5d, 0x59,
|
||||
0x32, 0x4e, 0xf1, 0x74, 0x4f, 0x9e, 0xee, 0xe9, 0xee, 0xed, 0xe9, 0xee, 0xe9, 0xfe, 0x8d, 0x0c,
|
||||
0xcb, 0x09, 0x13, 0xa9, 0x88, 0x8e, 0x37, 0x92, 0x34, 0x56, 0x31, 0x69, 0xe1, 0x9f, 0xc3, 0xc9,
|
||||
0xd1, 0xda, 0x15, 0x99, 0x45, 0xbe, 0x27, 0xb9, 0x52, 0x22, 0x3a, 0x96, 0x66, 0x7b, 0xcd, 0x65,
|
||||
0x49, 0x12, 0x0a, 0x9f, 0x29, 0x11, 0x47, 0xde, 0x98, 0x2b, 0x16, 0x30, 0xc5, 0xbc, 0x31, 0x97,
|
||||
0x92, 0x1d, 0x73, 0x23, 0xe3, 0x32, 0xb8, 0xf9, 0x53, 0xae, 0xfc, 0x91, 0x88, 0x8e, 0x9f, 0x32,
|
||||
0xff, 0x84, 0x07, 0x07, 0xc9, 0x26, 0x53, 0x6c, 0x93, 0x2b, 0x26, 0x42, 0x49, 0xee, 0x40, 0x07,
|
||||
0x95, 0xa2, 0xc9, 0xf8, 0x90, 0xa7, 0x7d, 0x67, 0xdd, 0x79, 0xb0, 0x4c, 0x41, 0xb3, 0x76, 0x90,
|
||||
0x43, 0xee, 0x42, 0x57, 0xc5, 0x8a, 0x85, 0xb9, 0x44, 0x0d, 0x25, 0x3a, 0xc8, 0x33, 0x22, 0xee,
|
||||
0x3f, 0x1a, 0xb0, 0xa4, 0x6d, 0x4f, 0x12, 0x72, 0x15, 0x2e, 0xf9, 0x61, 0xec, 0x9f, 0xa0, 0xa1,
|
||||
0x06, 0x35, 0x04, 0xe9, 0x41, 0x4d, 0x04, 0xa8, 0xd9, 0xa6, 0x35, 0x11, 0x90, 0x9f, 0x40, 0xcb,
|
||||
0x8f, 0x23, 0xc5, 0x7c, 0x25, 0xfb, 0xf5, 0xf5, 0xfa, 0x83, 0xce, 0xa3, 0x7b, 0x1b, 0xf9, 0x49,
|
||||
0x37, 0xf6, 0xb2, 0xc8, 0xdf, 0x8e, 0xa4, 0x62, 0x61, 0x88, 0x07, 0x1b, 0x1a, 0xc9, 0x37, 0x8f,
|
||||
0xe8, 0x54, 0x89, 0xfc, 0x18, 0x3a, 0x7e, 0x3c, 0x1e, 0x4f, 0x22, 0xa1, 0x04, 0x97, 0xfd, 0x06,
|
||||
0xda, 0xb8, 0x5e, 0xb5, 0x31, 0xb4, 0x02, 0x19, 0x2d, 0xcb, 0x92, 0xd7, 0xb0, 0x92, 0x9b, 0xb1,
|
||||
0x31, 0xe8, 0x5f, 0x5a, 0x77, 0x1e, 0x74, 0x1e, 0xdd, 0x2f, 0xd4, 0xcf, 0x09, 0x18, 0x9d, 0xd5,
|
||||
0x26, 0x07, 0x40, 0x4a, 0xf6, 0x73, 0x9b, 0x4b, 0xef, 0x63, 0x73, 0x8e, 0x01, 0xf2, 0x18, 0x9a,
|
||||
0x49, 0x1a, 0x1f, 0x89, 0x90, 0xf7, 0x9b, 0x68, 0xeb, 0x46, 0x61, 0x2b, 0xb7, 0xb1, 0x6b, 0x04,
|
||||
0x68, 0x2e, 0x49, 0x5e, 0x41, 0xcf, 0x2e, 0x73, 0x3f, 0x5a, 0xef, 0xe3, 0xc7, 0x8c, 0x32, 0xf9,
|
||||
0x18, 0x9a, 0xb6, 0xe2, 0xfa, 0x6d, 0xb4, 0xf3, 0x61, 0x35, 0xc4, 0x7b, 0x66, 0x93, 0xe6, 0x52,
|
||||
0x3a, 0xb8, 0x79, 0x89, 0xe6, 0x0e, 0xc0, 0x7b, 0x05, 0x77, 0x46, 0xdb, 0xfd, 0x4b, 0x03, 0xba,
|
||||
0xaf, 0x26, 0xa1, 0x12, 0x03, 0xdf, 0x8f, 0x27, 0x91, 0x22, 0x04, 0x1a, 0x11, 0x1b, 0x73, 0xac,
|
||||
0xaf, 0x36, 0xc5, 0x35, 0xb9, 0x05, 0x6d, 0x25, 0xc6, 0x5c, 0x2a, 0x36, 0x4e, 0xb0, 0xca, 0xea,
|
||||
0xb4, 0x60, 0xe8, 0x5d, 0x11, 0xf0, 0x48, 0x09, 0x3f, 0x8e, 0xfa, 0x75, 0x54, 0x2b, 0x18, 0xe4,
|
||||
0x4b, 0x00, 0x3f, 0x0e, 0xe3, 0xd4, 0x1b, 0x31, 0x39, 0xb2, 0x85, 0x74, 0xb7, 0x70, 0xb6, 0xfc,
|
||||
0xed, 0x8d, 0x61, 0x1c, 0xc6, 0x93, 0xf4, 0x19, 0x93, 0x23, 0xda, 0x46, 0x25, 0xbd, 0x24, 0x7d,
|
||||
0x68, 0x22, 0xb1, 0x1d, 0x60, 0x21, 0xd5, 0x69, 0x4e, 0x92, 0x8f, 0x60, 0xe5, 0x84, 0x67, 0x3e,
|
||||
0x4b, 0x03, 0xcf, 0x5e, 0x6b, 0x2c, 0x8b, 0x36, 0xed, 0x59, 0xf6, 0xae, 0xe1, 0x92, 0xeb, 0xd0,
|
||||
0x3c, 0xe1, 0x99, 0x37, 0x11, 0x01, 0xe6, 0xba, 0x4d, 0x97, 0x4e, 0x78, 0x76, 0x20, 0x02, 0xf2,
|
||||
0x39, 0x2c, 0x89, 0x31, 0x3b, 0xe6, 0x3a, 0x8f, 0xda, 0xb3, 0xef, 0x2c, 0xf0, 0x6c, 0x1b, 0xcf,
|
||||
0xa3, 0xb2, 0x6d, 0x2d, 0x4c, 0xad, 0xce, 0x9a, 0x0b, 0x50, 0xb8, 0xac, 0xaf, 0xa6, 0x88, 0x02,
|
||||
0xfe, 0x9b, 0xbe, 0xb3, 0x5e, 0x7f, 0x50, 0xa7, 0x86, 0x58, 0xfb, 0xa7, 0x03, 0xcb, 0x15, 0xed,
|
||||
0xb2, 0x33, 0x4e, 0xc5, 0x99, 0x3c, 0xf4, 0xb5, 0x52, 0xe8, 0xfb, 0xd0, 0x4c, 0x58, 0x16, 0xc6,
|
||||
0x2c, 0xc0, 0xd0, 0x76, 0x69, 0x4e, 0xea, 0xcf, 0xbd, 0x13, 0x81, 0xd2, 0x31, 0xd5, 0x41, 0x31,
|
||||
0x04, 0xb9, 0x06, 0x4b, 0x23, 0x2e, 0x8e, 0x47, 0xca, 0xc6, 0xca, 0x52, 0x64, 0x0d, 0x5a, 0xba,
|
||||
0xf0, 0xa4, 0xf8, 0x2d, 0xc7, 0x18, 0xd5, 0xe9, 0x94, 0x26, 0xf7, 0x60, 0x39, 0xc5, 0x95, 0xa7,
|
||||
0x58, 0x7a, 0xcc, 0x15, 0xc6, 0xa8, 0x4e, 0xbb, 0x86, 0xb9, 0x8f, 0xbc, 0xa2, 0xf1, 0xb4, 0x4a,
|
||||
0x8d, 0xc7, 0xfd, 0xbb, 0x03, 0x57, 0x5e, 0xc6, 0x3e, 0x0b, 0x6d, 0xa4, 0x77, 0xad, 0x73, 0x3f,
|
||||
0x80, 0xc6, 0x09, 0xcf, 0x24, 0x86, 0xa2, 0x92, 0xef, 0x39, 0xc2, 0x1b, 0x2f, 0x78, 0x46, 0x51,
|
||||
0x9c, 0x7c, 0x06, 0xdd, 0xb1, 0x0e, 0x3b, 0x33, 0x61, 0xc7, 0x48, 0x74, 0x1e, 0x5d, 0x9b, 0x9f,
|
||||
0x14, 0x5a, 0x91, 0xd5, 0x27, 0x4c, 0x98, 0x94, 0xef, 0xe2, 0x34, 0xb0, 0x55, 0x38, 0xa5, 0xd7,
|
||||
0xbe, 0x07, 0xf5, 0x17, 0x3c, 0x9b, 0x5b, 0xdb, 0x04, 0x1a, 0xba, 0x19, 0xe3, 0xa7, 0xba, 0x14,
|
||||
0xd7, 0xee, 0xef, 0x1c, 0x58, 0xd5, 0x3e, 0x96, 0xbb, 0xe4, 0x82, 0xce, 0xfb, 0x11, 0xac, 0x88,
|
||||
0x92, 0x94, 0x37, 0x6d, 0xc3, 0xbd, 0x32, 0x7b, 0x3b, 0xc0, 0x39, 0xc0, 0x4f, 0x85, 0xcf, 0x3d,
|
||||
0x95, 0x25, 0xdc, 0x7a, 0x08, 0x86, 0xb5, 0x9f, 0x25, 0x7c, 0xea, 0x5c, 0xa3, 0x70, 0xce, 0xfd,
|
||||
0x8f, 0x03, 0xd7, 0x17, 0xb4, 0xeb, 0x0b, 0x4e, 0x82, 0x7b, 0xb0, 0x6c, 0x7b, 0x8e, 0x87, 0x45,
|
||||
0x6b, 0x3f, 0xdc, 0xb5, 0x4c, 0x53, 0x91, 0x37, 0xa0, 0xc5, 0x23, 0xe9, 0x95, 0x3e, 0xdf, 0xe4,
|
||||
0x91, 0xdc, 0xd1, 0xe1, 0xb9, 0x0b, 0xdd, 0x90, 0x49, 0xe5, 0x4d, 0x92, 0x80, 0x29, 0x6e, 0x6e,
|
||||
0x60, 0x83, 0x76, 0x34, 0xef, 0xc0, 0xb0, 0xf4, 0xc9, 0x64, 0x26, 0x15, 0x1f, 0x7b, 0x8a, 0x1d,
|
||||
0xeb, 0xc6, 0x5c, 0xd7, 0x27, 0x33, 0xac, 0x7d, 0x76, 0x2c, 0xc9, 0x7d, 0xe8, 0x85, 0x3a, 0xed,
|
||||
0x5e, 0x24, 0xfc, 0x13, 0xfc, 0x88, 0xb9, 0x84, 0xcb, 0xc8, 0xdd, 0xb1, 0x4c, 0xf7, 0xdf, 0x75,
|
||||
0xb8, 0xb1, 0x70, 0x36, 0x91, 0x4f, 0xe0, 0x6a, 0xd9, 0x11, 0x0f, 0x75, 0xc3, 0xcc, 0x9e, 0x9e,
|
||||
0x94, 0x1c, 0x7a, 0x69, 0x76, 0xbe, 0xc5, 0xa1, 0xd0, 0xb9, 0x65, 0x41, 0xc0, 0x03, 0x9c, 0x0a,
|
||||
0x2d, 0x6a, 0x08, 0xdd, 0x0b, 0x0e, 0x75, 0x92, 0x79, 0x80, 0x4d, 0xbf, 0x45, 0x73, 0x52, 0xcb,
|
||||
0x8f, 0x27, 0xda, 0xa7, 0x8e, 0x91, 0x47, 0x42, 0xcb, 0xa7, 0x7c, 0x1c, 0x9f, 0xf2, 0xa0, 0xdf,
|
||||
0x35, 0xf2, 0x96, 0x24, 0xeb, 0xd0, 0x1d, 0x31, 0xe9, 0xa1, 0x59, 0x6f, 0x22, 0xfb, 0xcb, 0xb8,
|
||||
0x0d, 0x23, 0x26, 0x07, 0x9a, 0x75, 0xa0, 0x27, 0xd3, 0x95, 0x53, 0x9e, 0x8a, 0xa3, 0x1c, 0xfc,
|
||||
0x48, 0xc5, 0xd4, 0x44, 0xf6, 0x7b, 0xd8, 0x19, 0x48, 0x79, 0x6b, 0x0f, 0x77, 0x10, 0xc6, 0xa4,
|
||||
0x13, 0xa9, 0x72, 0xc9, 0x15, 0x94, 0xec, 0x20, 0xcf, 0x88, 0xb8, 0xef, 0xce, 0x16, 0x73, 0x3e,
|
||||
0x75, 0xe6, 0x17, 0xf3, 0x99, 0x8c, 0xd5, 0xe6, 0x64, 0x6c, 0x36, 0x2d, 0xf5, 0x33, 0x69, 0x71,
|
||||
0x9f, 0xc2, 0xda, 0xec, 0x87, 0x77, 0x27, 0x87, 0xa1, 0xf0, 0x87, 0x23, 0x76, 0xc1, 0x8b, 0xe4,
|
||||
0x7e, 0x53, 0x87, 0xe5, 0x0a, 0xea, 0xf9, 0x9f, 0x7a, 0x5d, 0xac, 0xba, 0x3b, 0xd0, 0x49, 0x52,
|
||||
0x71, 0xca, 0x14, 0xf7, 0x4e, 0x78, 0x66, 0x9b, 0x38, 0x58, 0x96, 0x6e, 0x4a, 0xeb, 0xba, 0x31,
|
||||
0x48, 0x3f, 0x15, 0x89, 0xf6, 0x0b, 0x8b, 0xae, 0x4b, 0xcb, 0x2c, 0xdd, 0xd3, 0x7f, 0x15, 0x8b,
|
||||
0xc8, 0x96, 0x5c, 0x8b, 0x5a, 0x4a, 0x77, 0x3c, 0x93, 0x08, 0x1e, 0x60, 0x4f, 0x6f, 0xd1, 0x29,
|
||||
0x5d, 0x54, 0x44, 0xb3, 0x5c, 0x11, 0xaf, 0x61, 0x35, 0xe5, 0xbf, 0x9e, 0x70, 0xa9, 0xa4, 0xa7,
|
||||
0x62, 0x4f, 0xdb, 0xb1, 0x83, 0xef, 0xfe, 0x22, 0x6c, 0x67, 0xc5, 0xf7, 0xe3, 0xe7, 0xb1, 0x88,
|
||||
0x68, 0x2f, 0xad, 0xd0, 0xe4, 0x09, 0xb4, 0x72, 0x44, 0x61, 0x11, 0xcc, 0x9d, 0x05, 0x86, 0x2c,
|
||||
0x94, 0x91, 0x74, 0xaa, 0xa0, 0x81, 0x03, 0x8f, 0xfc, 0x34, 0x4b, 0xd4, 0xb4, 0xa2, 0x0b, 0x86,
|
||||
0xde, 0x95, 0x09, 0xf7, 0x15, 0x2b, 0xea, 0xba, 0x60, 0xe8, 0xbe, 0x6b, 0x45, 0x75, 0x75, 0xe2,
|
||||
0xac, 0xe9, 0x62, 0xe4, 0x7a, 0x05, 0xfb, 0x05, 0xcf, 0xa4, 0xfb, 0x37, 0x07, 0x6e, 0x9e, 0x73,
|
||||
0x22, 0x9b, 0x2f, 0x67, 0x9a, 0xaf, 0xdb, 0x00, 0x09, 0xd6, 0x06, 0xa6, 0xcb, 0xe4, 0xbf, 0x6d,
|
||||
0x38, 0x3a, 0x5b, 0xd3, 0xa4, 0xd7, 0xcb, 0x49, 0x3f, 0xa7, 0x6b, 0x5c, 0x87, 0xa6, 0x3f, 0x62,
|
||||
0x4a, 0x0f, 0x86, 0x4b, 0x66, 0xda, 0x6b, 0x72, 0x3b, 0xd0, 0x75, 0x9b, 0xa3, 0xd2, 0x4c, 0xef,
|
||||
0x2e, 0x99, 0xc4, 0x4f, 0x79, 0xdb, 0x98, 0x44, 0x7d, 0x9b, 0x4c, 0x93, 0x68, 0x50, 0x43, 0xb8,
|
||||
0xbf, 0xaf, 0xc1, 0xea, 0x6c, 0x39, 0x93, 0x2f, 0x4a, 0x88, 0xff, 0xcc, 0xd0, 0x5d, 0xd0, 0x55,
|
||||
0x4b, 0x78, 0xff, 0x2b, 0xe8, 0xda, 0x53, 0x6b, 0xef, 0x64, 0xbf, 0x36, 0x8b, 0x86, 0x16, 0xdf,
|
||||
0x1f, 0xda, 0x49, 0xa6, 0x6b, 0x49, 0x9e, 0x40, 0x33, 0x1f, 0xde, 0x75, 0xac, 0x87, 0x73, 0xdc,
|
||||
0xc8, 0xe7, 0x78, 0xae, 0xf1, 0x7f, 0xbc, 0x3a, 0xdc, 0x4f, 0x61, 0x05, 0x77, 0xb5, 0x43, 0xb6,
|
||||
0xc9, 0x5d, 0xec, 0x5e, 0x7f, 0x0e, 0x57, 0x73, 0xc5, 0x57, 0xe6, 0x5d, 0x27, 0x29, 0x67, 0x17,
|
||||
0xd5, 0xfe, 0x12, 0xae, 0x69, 0xed, 0x81, 0xaf, 0xc4, 0xa9, 0x50, 0xd9, 0x90, 0x47, 0x8a, 0xa7,
|
||||
0xe7, 0xe8, 0xaf, 0x42, 0x5d, 0x04, 0x26, 0xbc, 0x5d, 0xaa, 0x97, 0xee, 0xa6, 0xe9, 0x4d, 0x55,
|
||||
0x0b, 0x03, 0xdf, 0xe7, 0x78, 0x09, 0x2e, 0x6a, 0x65, 0xcb, 0x14, 0x79, 0xd5, 0xca, 0xa6, 0x90,
|
||||
0x63, 0x21, 0xe5, 0x7b, 0x98, 0xf9, 0xda, 0x81, 0xae, 0xb6, 0xf3, 0x34, 0x8e, 0x4f, 0xc6, 0x2c,
|
||||
0x3d, 0x59, 0xac, 0x38, 0x49, 0x43, 0x1b, 0x06, 0xbd, 0x9c, 0x82, 0x97, 0x7a, 0x09, 0x59, 0xdd,
|
||||
0x84, 0x36, 0x76, 0x6d, 0x4f, 0xcb, 0x9a, 0x5b, 0xd1, 0x42, 0xc6, 0x41, 0x1a, 0x96, 0x67, 0xd3,
|
||||
0xa5, 0xea, 0x6c, 0xba, 0x0d, 0x10, 0xf0, 0x90, 0xeb, 0x19, 0xcf, 0x14, 0xde, 0x8a, 0x06, 0x6d,
|
||||
0x5b, 0xce, 0x40, 0xb9, 0xcf, 0x4d, 0xf1, 0x0f, 0x43, 0xce, 0xd2, 0x67, 0x42, 0xaa, 0x38, 0xcd,
|
||||
0xca, 0x77, 0xcc, 0xa9, 0xdc, 0xb1, 0xdb, 0x00, 0xbe, 0x16, 0x34, 0xb6, 0x6a, 0xc6, 0x96, 0xe5,
|
||||
0x0c, 0x94, 0xfb, 0x57, 0x07, 0x88, 0x36, 0x66, 0x9f, 0x79, 0xbb, 0xc2, 0x57, 0x93, 0x94, 0xcf,
|
||||
0x85, 0x89, 0x25, 0x1c, 0x5e, 0x5b, 0x80, 0xc3, 0xeb, 0xf8, 0x70, 0x3f, 0x83, 0xc3, 0x1b, 0xc8,
|
||||
0xce, 0x71, 0xf8, 0x4d, 0x68, 0xe3, 0x3c, 0x43, 0x20, 0x7e, 0x09, 0xb7, 0x10, 0x88, 0xef, 0xcd,
|
||||
0x05, 0xe2, 0x4b, 0x28, 0xb0, 0x00, 0x88, 0x37, 0xcb, 0x40, 0x7c, 0x04, 0x57, 0xce, 0x9e, 0x44,
|
||||
0x2e, 0x7e, 0x6b, 0xfc, 0x08, 0x5a, 0x89, 0x15, 0xb2, 0x97, 0xfd, 0x56, 0xf5, 0x9e, 0x55, 0x2d,
|
||||
0xd1, 0xa9, 0xb4, 0xfb, 0xc7, 0x1a, 0x5c, 0xd6, 0x02, 0x6f, 0x59, 0x18, 0x72, 0x75, 0xfe, 0x00,
|
||||
0xef, 0x43, 0x93, 0x05, 0x41, 0xca, 0xa5, 0xcc, 0xa3, 0x66, 0x49, 0x1d, 0x9f, 0x77, 0x68, 0x00,
|
||||
0xc3, 0xd6, 0xa2, 0x96, 0xd2, 0xb1, 0xd7, 0xb9, 0xc3, 0xa8, 0xb5, 0x28, 0xae, 0x35, 0x0f, 0x31,
|
||||
0xb3, 0xe9, 0x9f, 0xb8, 0xd6, 0x96, 0x75, 0xee, 0x35, 0x28, 0x30, 0x4f, 0xbe, 0x9c, 0xd4, 0xd2,
|
||||
0x09, 0x53, 0x23, 0x0b, 0xac, 0x70, 0xad, 0x67, 0xc9, 0xb4, 0x85, 0xe3, 0x03, 0xa6, 0x5b, 0xee,
|
||||
0xe9, 0x79, 0xbe, 0xdb, 0xa5, 0x7c, 0xeb, 0xf3, 0xe8, 0x57, 0x26, 0xce, 0xa5, 0x36, 0x35, 0x04,
|
||||
0x66, 0x55, 0x04, 0x01, 0x8f, 0xec, 0x40, 0xb2, 0xd4, 0x62, 0xa4, 0xe5, 0xbe, 0x32, 0x15, 0x56,
|
||||
0x09, 0x96, 0x24, 0x9f, 0x42, 0xcb, 0xf6, 0xbc, 0xbc, 0x5b, 0xdf, 0xac, 0x46, 0xbf, 0x22, 0x4f,
|
||||
0xa7, 0xc2, 0xee, 0x9f, 0x1d, 0x53, 0xfe, 0x7b, 0xec, 0x94, 0x07, 0x03, 0x1b, 0xcb, 0x52, 0x94,
|
||||
0x9d, 0x6a, 0x94, 0xe7, 0xbd, 0x28, 0x6f, 0x41, 0xfb, 0x88, 0x9d, 0xc6, 0x93, 0x54, 0x28, 0x6e,
|
||||
0x83, 0x5f, 0x30, 0xf4, 0x24, 0xf3, 0x47, 0x4c, 0xe0, 0x43, 0xa6, 0x81, 0xa9, 0x6c, 0x22, 0xbd,
|
||||
0x1d, 0x9c, 0x73, 0x65, 0xef, 0x42, 0xd7, 0xa0, 0x2f, 0xaf, 0x5c, 0x99, 0x1d, 0xc3, 0x1b, 0x62,
|
||||
0x7d, 0xfe, 0xc1, 0x81, 0x0f, 0xe7, 0xe2, 0x81, 0x05, 0x95, 0x33, 0x3b, 0x1d, 0xcd, 0x09, 0x2a,
|
||||
0xd3, 0x71, 0x0b, 0xee, 0x8c, 0x4c, 0x03, 0xf0, 0x58, 0xea, 0x8f, 0xc4, 0x29, 0xf7, 0xe4, 0x24,
|
||||
0x49, 0xe2, 0x54, 0x79, 0x3c, 0x62, 0x87, 0xa1, 0xc5, 0x82, 0x2d, 0x7a, 0xcb, 0x8a, 0x0d, 0x8c,
|
||||
0xd4, 0x9e, 0x11, 0xda, 0x32, 0x32, 0xee, 0x9f, 0x1c, 0x33, 0x3a, 0xf6, 0x35, 0x52, 0xd5, 0xd8,
|
||||
0x97, 0xa7, 0x17, 0x7c, 0x5b, 0x7d, 0x01, 0x4b, 0x16, 0xec, 0xea, 0xef, 0xf4, 0x66, 0x31, 0x54,
|
||||
0xc9, 0xe0, 0xc6, 0x7e, 0x01, 0x83, 0xa9, 0x55, 0x72, 0x3f, 0x83, 0x4e, 0x89, 0x4d, 0x3a, 0xd0,
|
||||
0x3c, 0xd8, 0x79, 0xb1, 0xf3, 0xfa, 0xed, 0xce, 0xea, 0x07, 0x9a, 0xd8, 0xa7, 0x07, 0x7b, 0xfb,
|
||||
0x5b, 0x9b, 0xab, 0x0e, 0xb9, 0x0c, 0xcb, 0x07, 0x3b, 0x48, 0xbe, 0x7d, 0x4d, 0xf7, 0x9f, 0xfd,
|
||||
0x7c, 0xb5, 0xe6, 0x7e, 0x5d, 0x37, 0x58, 0xfa, 0x4d, 0x09, 0x88, 0x5b, 0x60, 0xb3, 0xc0, 0x79,
|
||||
0x02, 0x8d, 0xa3, 0x34, 0x1e, 0xe7, 0xa5, 0xa0, 0xd7, 0xfa, 0x40, 0x2a, 0xb6, 0x3d, 0xbb, 0xa6,
|
||||
0x62, 0x5d, 0x1a, 0xfe, 0x48, 0x57, 0x5e, 0x74, 0x9c, 0xe3, 0x98, 0x82, 0xa1, 0x53, 0x62, 0xd1,
|
||||
0x9f, 0x69, 0xa7, 0xf6, 0xfd, 0x33, 0xe5, 0x0d, 0xf0, 0x0d, 0x9e, 0x72, 0x99, 0xc4, 0x91, 0xcc,
|
||||
0xaf, 0xe5, 0x94, 0xd6, 0xbd, 0x38, 0xe5, 0x49, 0x28, 0x8c, 0xb2, 0x29, 0x91, 0xb6, 0xe5, 0x0c,
|
||||
0x14, 0xe1, 0xf3, 0x1f, 0x1c, 0x2d, 0x8c, 0xec, 0xf7, 0xab, 0x91, 0x9d, 0x73, 0xea, 0x8d, 0x37,
|
||||
0x67, 0x9e, 0x24, 0x73, 0x9f, 0x29, 0x26, 0x87, 0xed, 0xe9, 0x00, 0xff, 0x19, 0x90, 0xb3, 0x9a,
|
||||
0x67, 0x72, 0xb1, 0xbb, 0xb5, 0xb3, 0xb9, 0xbd, 0xf3, 0xd5, 0xaa, 0x43, 0xba, 0xd0, 0x1a, 0x0c,
|
||||
0x87, 0x5b, 0xbb, 0x3a, 0x33, 0x35, 0x4d, 0x6d, 0x6e, 0x0d, 0x5f, 0x6e, 0xef, 0x6c, 0x6d, 0xae,
|
||||
0xd6, 0x35, 0x35, 0x1c, 0xec, 0x0c, 0xb7, 0x5e, 0x6e, 0x6d, 0xae, 0x36, 0xdc, 0x7f, 0x39, 0x66,
|
||||
0xb2, 0xe7, 0x60, 0xcb, 0xf8, 0xb9, 0xc9, 0x7d, 0x21, 0x17, 0xff, 0x9c, 0x70, 0x0b, 0xda, 0x36,
|
||||
0x9e, 0xdb, 0x79, 0xa5, 0x15, 0x0c, 0xf2, 0x4b, 0x58, 0x09, 0xac, 0xbe, 0x57, 0xa9, 0xbc, 0xc7,
|
||||
0xb3, 0x18, 0x69, 0xde, 0x27, 0x37, 0xf2, 0x85, 0x0d, 0x4f, 0x2f, 0xa8, 0xd0, 0xee, 0x43, 0xe8,
|
||||
0x55, 0x25, 0x2a, 0x87, 0xfd, 0xa0, 0x72, 0x58, 0xc7, 0xfd, 0xc6, 0x81, 0x95, 0x99, 0x9f, 0x49,
|
||||
0x17, 0x4f, 0x9b, 0xbb, 0xd0, 0x0d, 0x84, 0x4c, 0x42, 0x96, 0x79, 0xa5, 0x7e, 0xd4, 0xb1, 0x3c,
|
||||
0xc4, 0xc9, 0x0f, 0x81, 0x94, 0x45, 0xbc, 0x32, 0xca, 0x5e, 0x2d, 0x09, 0x62, 0x3b, 0xa9, 0x8c,
|
||||
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,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "sync_settings.proto";
|
||||
import 'application_metadata_message.proto';
|
||||
|
||||
option go_package = "./;protobuf";
|
||||
package protobuf;
|
||||
|
@ -279,3 +280,17 @@ message BackedUpProfile {
|
|||
uint64 display_name_clock = 3;
|
||||
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;
|
||||
}
|
||||
|
|
111
server/certs.go
111
server/certs.go
|
@ -8,15 +8,10 @@ import (
|
|||
"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"
|
||||
)
|
||||
|
||||
var globalCertificate *tls.Certificate = nil
|
||||
|
@ -90,7 +85,7 @@ func generateTLSCert() error {
|
|||
return err
|
||||
}
|
||||
|
||||
cert := GenerateX509Cert(sn, notBefore, notAfter, localhost)
|
||||
cert := GenerateX509Cert(sn, notBefore, notAfter, Localhost)
|
||||
certPem, keyPem, err := GenerateX509PEMs(cert, priv)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -115,31 +110,6 @@ func PublicTLSCert() (string, error) {
|
|||
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
|
||||
// this function is basically a P256 curve version of eth-node/crypto.ToECDSA without all the nice validation
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -44,12 +44,12 @@ func (s *CertsSuite) TestGenerateX509Cert() {
|
|||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(time.Hour)
|
||||
|
||||
c1 := GenerateX509Cert(s.SN, notBefore, notAfter, localhost)
|
||||
s.Require().Exactly([]string{localhost}, c1.DNSNames)
|
||||
c1 := GenerateX509Cert(s.SN, notBefore, notAfter, Localhost)
|
||||
s.Require().Exactly([]string{Localhost}, c1.DNSNames)
|
||||
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().Equal(defaultIP.String(), c2.IPAddresses[0].String())
|
||||
s.Require().Equal(DefaultIP.String(), c2.IPAddresses[0].String())
|
||||
s.Require().Nil(c2.DNSNames)
|
||||
}
|
||||
|
|
205
server/client.go
205
server/client.go
|
@ -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()
|
||||
}
|
|
@ -3,11 +3,8 @@ package server
|
|||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/asn1"
|
||||
"math/big"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -82,42 +79,6 @@ func (tcc *TestCertComponents) SetupCertComponents(t *testing.T) {
|
|||
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 {
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
@ -125,33 +86,3 @@ type TestLoggerComponents struct {
|
|||
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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -2,26 +2,21 @@ package server
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"image"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcutil/base58"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/ipfs"
|
||||
"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/identicon"
|
||||
"github.com/status-im/status-go/protocol/identity/ring"
|
||||
"github.com/status-im/status-go/protocol/images"
|
||||
"github.com/status-im/status-go/signal"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -34,15 +29,6 @@ const (
|
|||
discordAttachmentsPath = basePath + "/discord/attachments"
|
||||
|
||||
// Handler routes for pairing
|
||||
pairingBase = "/pairing"
|
||||
pairingSend = pairingBase + "/send"
|
||||
pairingReceive = pairingBase + "/receive"
|
||||
pairingChallenge = pairingBase + "/challenge"
|
||||
|
||||
// Session names
|
||||
sessionChallenge = "challenge"
|
||||
sessionBlocked = "blocked"
|
||||
|
||||
accountImagesPath = "/accountImages"
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
defaultIP = net.IP{127, 0, 0, 1}
|
||||
localhost = "localhost"
|
||||
DefaultIP = net.IP{127, 0, 0, 1}
|
||||
Localhost = "Localhost"
|
||||
)
|
||||
|
||||
func GetOutboundIP() (net.IP, error) {
|
||||
|
|
|
@ -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]))
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package pairing
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
|
@ -1,9 +1,11 @@
|
|||
package server
|
||||
package pairing
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
internalServer "github.com/status-im/status-go/server"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -20,7 +22,7 @@ type ConnectionParamsSuite struct {
|
|||
TestCertComponents
|
||||
TestLoggerComponents
|
||||
|
||||
server *PairingServer
|
||||
server *Server
|
||||
}
|
||||
|
||||
func (s *ConnectionParamsSuite) SetupSuite() {
|
||||
|
@ -28,14 +30,14 @@ func (s *ConnectionParamsSuite) SetupSuite() {
|
|||
s.SetupCertComponents(s.T())
|
||||
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)
|
||||
|
||||
bs := NewServer(&cert, defaultIP.String(), nil, s.Logger)
|
||||
bs := internalServer.NewServer(&cert, internalServer.DefaultIP.String(), nil, s.Logger)
|
||||
err = bs.SetPort(1337)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.server = &PairingServer{
|
||||
s.server = &Server{
|
||||
Server: bs,
|
||||
pk: &s.PK.PublicKey,
|
||||
ek: s.AES,
|
||||
|
@ -62,7 +64,7 @@ func (s *ConnectionParamsSuite) TestConnectionParams_Generate() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
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().True(cp.publicKey.Equal(&s.PK.PublicKey))
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package pairing
|
||||
|
||||
// EventType type for event types.
|
||||
type EventType string
|
||||
|
@ -19,6 +19,15 @@ const (
|
|||
|
||||
// Event is a type for transfer events.
|
||||
type Event struct {
|
||||
Type EventType `json:"type"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Type EventType `json:"type"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Action Action `json:"action"`
|
||||
}
|
||||
|
||||
type Action int
|
||||
|
||||
const (
|
||||
ActionPairingAccount = iota + 1
|
||||
ActionSyncDevice
|
||||
ActionConnect
|
||||
)
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package pairing
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
|
@ -8,6 +8,8 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/status-im/status-go/api"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"go.uber.org/zap"
|
||||
|
||||
|
@ -42,32 +44,38 @@ type PayloadManager interface {
|
|||
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
|
||||
type PairingPayloadSourceConfig struct {
|
||||
type PayloadSourceConfig struct {
|
||||
// required
|
||||
KeystorePath string `json:"keystorePath"`
|
||||
KeyUID string `json:"keyUID"`
|
||||
Password string `json:"password"`
|
||||
// following 2 fields r optional.
|
||||
// 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
|
||||
type PairingPayloadManagerConfig struct {
|
||||
// AccountPayloadManagerConfig represents the initialisation parameters required for a AccountPayloadManager
|
||||
type AccountPayloadManagerConfig struct {
|
||||
DB *multiaccounts.Database
|
||||
PairingPayloadSourceConfig
|
||||
*PayloadSourceConfig
|
||||
}
|
||||
|
||||
// PairingPayloadManager is responsible for the whole lifecycle of a PairingPayload
|
||||
type PairingPayloadManager struct {
|
||||
logger *zap.Logger
|
||||
pp *PairingPayload
|
||||
// AccountPayloadManager is responsible for the whole lifecycle of a AccountPayload
|
||||
type AccountPayloadManager struct {
|
||||
logger *zap.Logger
|
||||
accountPayload *AccountPayload
|
||||
*PayloadEncryptionManager
|
||||
ppm *PairingPayloadMarshaller
|
||||
ppr PayloadRepository
|
||||
accountPayloadMarshaller *AccountPayloadMarshaller
|
||||
payloadRepository PayloadRepository
|
||||
}
|
||||
|
||||
// NewPairingPayloadManager generates a new and initialised PairingPayloadManager
|
||||
func NewPairingPayloadManager(aesKey []byte, config *PairingPayloadManagerConfig, logger *zap.Logger) (*PairingPayloadManager, error) {
|
||||
l := logger.Named("PairingPayloadManager")
|
||||
// NewAccountPayloadManager generates a new and initialised AccountPayloadManager
|
||||
func NewAccountPayloadManager(aesKey []byte, config *AccountPayloadManagerConfig, logger *zap.Logger) (*AccountPayloadManager, error) {
|
||||
l := logger.Named("AccountPayloadManager")
|
||||
l.Debug("fired", zap.Binary("aesKey", aesKey), zap.Any("config", config))
|
||||
|
||||
pem, err := NewPayloadEncryptionManager(aesKey, l)
|
||||
|
@ -75,74 +83,74 @@ func NewPairingPayloadManager(aesKey []byte, config *PairingPayloadManagerConfig
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// A new SHARED PairingPayload
|
||||
p := new(PairingPayload)
|
||||
// A new SHARED AccountPayload
|
||||
p := new(AccountPayload)
|
||||
|
||||
return &PairingPayloadManager{
|
||||
return &AccountPayloadManager{
|
||||
logger: l,
|
||||
pp: p,
|
||||
accountPayload: p,
|
||||
PayloadEncryptionManager: pem,
|
||||
ppm: NewPairingPayloadMarshaller(p, l),
|
||||
ppr: NewPairingPayloadRepository(p, config),
|
||||
accountPayloadMarshaller: NewPairingPayloadMarshaller(p, l),
|
||||
payloadRepository: NewAccountPayloadRepository(p, config),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Mount loads and prepares the payload to be stored in the PairingPayloadManager's state ready for later access
|
||||
func (ppm *PairingPayloadManager) Mount() error {
|
||||
l := ppm.logger.Named("Mount()")
|
||||
// Mount loads and prepares the payload to be stored in the AccountPayloadManager's state ready for later access
|
||||
func (apm *AccountPayloadManager) Mount() error {
|
||||
l := apm.logger.Named("Mount()")
|
||||
l.Debug("fired")
|
||||
|
||||
err := ppm.ppr.LoadFromSource()
|
||||
err := apm.payloadRepository.LoadFromSource()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug("after LoadFromSource")
|
||||
|
||||
pb, err := ppm.ppm.MarshalToProtobuf()
|
||||
pb, err := apm.accountPayloadMarshaller.MarshalToProtobuf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug(
|
||||
"after MarshalToProtobuf",
|
||||
zap.Any("ppm.ppm.keys", ppm.ppm.keys),
|
||||
zap.Any("ppm.ppm.multiaccount", ppm.ppm.multiaccount),
|
||||
zap.String("ppm.ppm.password", ppm.ppm.password),
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.keys", apm.accountPayloadMarshaller.keys),
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.multiaccount", apm.accountPayloadMarshaller.multiaccount),
|
||||
zap.String("accountPayloadMarshaller.accountPayloadMarshaller.password", apm.accountPayloadMarshaller.password),
|
||||
zap.Binary("pb", pb),
|
||||
)
|
||||
|
||||
return ppm.Encrypt(pb)
|
||||
return apm.Encrypt(pb)
|
||||
}
|
||||
|
||||
// Receive takes a []byte representing raw data, parses and stores the data
|
||||
func (ppm *PairingPayloadManager) Receive(data []byte) error {
|
||||
l := ppm.logger.Named("Receive()")
|
||||
func (apm *AccountPayloadManager) Receive(data []byte) error {
|
||||
l := apm.logger.Named("Receive()")
|
||||
l.Debug("fired")
|
||||
|
||||
err := ppm.Decrypt(data)
|
||||
err := apm.Decrypt(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug("after Decrypt")
|
||||
|
||||
err = ppm.ppm.UnmarshalProtobuf(ppm.Received())
|
||||
err = apm.accountPayloadMarshaller.UnmarshalProtobuf(apm.Received())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.Debug(
|
||||
"after UnmarshalProtobuf",
|
||||
zap.Any("ppm.ppm.keys", ppm.ppm.keys),
|
||||
zap.Any("ppm.ppm.multiaccount", ppm.ppm.multiaccount),
|
||||
zap.String("ppm.ppm.password", ppm.ppm.password),
|
||||
zap.Binary("ppm.Received()", ppm.Received()),
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.keys", apm.accountPayloadMarshaller.keys),
|
||||
zap.Any("accountPayloadMarshaller.accountPayloadMarshaller.multiaccount", apm.accountPayloadMarshaller.multiaccount),
|
||||
zap.String("accountPayloadMarshaller.accountPayloadMarshaller.password", apm.accountPayloadMarshaller.password),
|
||||
zap.Binary("accountPayloadMarshaller.Received()", apm.Received()),
|
||||
)
|
||||
|
||||
return ppm.ppr.StoreToSource()
|
||||
return apm.payloadRepository.StoreToSource()
|
||||
}
|
||||
|
||||
// ResetPayload resets all payload state managed by the PairingPayloadManager
|
||||
func (ppm *PairingPayloadManager) ResetPayload() {
|
||||
ppm.pp.ResetPayload()
|
||||
ppm.PayloadEncryptionManager.ResetPayload()
|
||||
// ResetPayload resets all payload state managed by the AccountPayloadManager
|
||||
func (apm *AccountPayloadManager) ResetPayload() {
|
||||
apm.accountPayload.ResetPayload()
|
||||
apm.PayloadEncryptionManager.ResetPayload()
|
||||
}
|
||||
|
||||
// EncryptionPayload represents the plain text and encrypted text of payload data
|
||||
|
@ -247,28 +255,28 @@ func (pem *PayloadEncryptionManager) LockPayload() {
|
|||
pem.received.lock()
|
||||
}
|
||||
|
||||
// PairingPayload represents the payload structure a PairingServer handles
|
||||
type PairingPayload struct {
|
||||
// AccountPayload represents the payload structure a Server handles
|
||||
type AccountPayload struct {
|
||||
keys map[string][]byte
|
||||
multiaccount *multiaccounts.Account
|
||||
password string
|
||||
}
|
||||
|
||||
func (pp *PairingPayload) ResetPayload() {
|
||||
*pp = PairingPayload{}
|
||||
func (ap *AccountPayload) ResetPayload() {
|
||||
*ap = AccountPayload{}
|
||||
}
|
||||
|
||||
// PairingPayloadMarshaller is responsible for marshalling and unmarshalling PairingServer payload data
|
||||
type PairingPayloadMarshaller struct {
|
||||
// AccountPayloadMarshaller is responsible for marshalling and unmarshalling Server payload data
|
||||
type AccountPayloadMarshaller struct {
|
||||
logger *zap.Logger
|
||||
*PairingPayload
|
||||
*AccountPayload
|
||||
}
|
||||
|
||||
func NewPairingPayloadMarshaller(p *PairingPayload, logger *zap.Logger) *PairingPayloadMarshaller {
|
||||
return &PairingPayloadMarshaller{logger: logger, PairingPayload: p}
|
||||
func NewPairingPayloadMarshaller(ap *AccountPayload, logger *zap.Logger) *AccountPayloadMarshaller {
|
||||
return &AccountPayloadMarshaller{logger: logger, AccountPayload: ap}
|
||||
}
|
||||
|
||||
func (ppm *PairingPayloadMarshaller) MarshalToProtobuf() ([]byte, error) {
|
||||
func (ppm *AccountPayloadMarshaller) MarshalToProtobuf() ([]byte, error) {
|
||||
return proto.Marshal(&protobuf.LocalPairingPayload{
|
||||
Keys: ppm.accountKeysToProtobuf(),
|
||||
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
|
||||
for name, data := range ppm.keys {
|
||||
keys = append(keys, &protobuf.LocalPairingPayload_Key{Name: name, Data: data})
|
||||
|
@ -284,7 +292,7 @@ func (ppm *PairingPayloadMarshaller) accountKeysToProtobuf() []*protobuf.LocalPa
|
|||
return keys
|
||||
}
|
||||
|
||||
func (ppm *PairingPayloadMarshaller) UnmarshalProtobuf(data []byte) error {
|
||||
func (ppm *AccountPayloadMarshaller) UnmarshalProtobuf(data []byte) error {
|
||||
l := ppm.logger.Named("UnmarshalProtobuf()")
|
||||
l.Debug("fired")
|
||||
|
||||
|
@ -306,7 +314,7 @@ func (ppm *PairingPayloadMarshaller) UnmarshalProtobuf(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ppm *PairingPayloadMarshaller) accountKeysFromProtobuf(pbKeys []*protobuf.LocalPairingPayload_Key) {
|
||||
func (ppm *AccountPayloadMarshaller) accountKeysFromProtobuf(pbKeys []*protobuf.LocalPairingPayload_Key) {
|
||||
l := ppm.logger.Named("accountKeysFromProtobuf()")
|
||||
l.Debug("fired")
|
||||
|
||||
|
@ -320,11 +328,11 @@ func (ppm *PairingPayloadMarshaller) accountKeysFromProtobuf(pbKeys []*protobuf.
|
|||
l.Debug(
|
||||
"after for _, key := range 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.FromProtobuf(pbMultiAccount)
|
||||
}
|
||||
|
@ -334,18 +342,18 @@ type PayloadRepository interface {
|
|||
StoreToSource() error
|
||||
}
|
||||
|
||||
// PairingPayloadRepository is responsible for loading, parsing, validating and storing PairingServer payload data
|
||||
type PairingPayloadRepository struct {
|
||||
*PairingPayload
|
||||
// AccountPayloadRepository is responsible for loading, parsing, validating and storing Server payload data
|
||||
type AccountPayloadRepository struct {
|
||||
*AccountPayload
|
||||
|
||||
multiaccountsDB *multiaccounts.Database
|
||||
|
||||
keystorePath, keyUID string
|
||||
}
|
||||
|
||||
func NewPairingPayloadRepository(p *PairingPayload, config *PairingPayloadManagerConfig) *PairingPayloadRepository {
|
||||
ppr := &PairingPayloadRepository{
|
||||
PairingPayload: p,
|
||||
func NewAccountPayloadRepository(p *AccountPayload, config *AccountPayloadManagerConfig) *AccountPayloadRepository {
|
||||
ppr := &AccountPayloadRepository{
|
||||
AccountPayload: p,
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
|
@ -359,18 +367,18 @@ func NewPairingPayloadRepository(p *PairingPayload, config *PairingPayloadManage
|
|||
return ppr
|
||||
}
|
||||
|
||||
func (ppr *PairingPayloadRepository) LoadFromSource() error {
|
||||
err := ppr.loadKeys(ppr.keystorePath)
|
||||
func (apr *AccountPayloadRepository) LoadFromSource() error {
|
||||
err := apr.loadKeys(apr.keystorePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ppr.validateKeys(ppr.password)
|
||||
err = apr.validateKeys(apr.password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ppr.multiaccount, err = ppr.multiaccountsDB.GetAccount(ppr.keyUID)
|
||||
apr.multiaccount, err = apr.multiaccountsDB.GetAccount(apr.keyUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -378,8 +386,8 @@ func (ppr *PairingPayloadRepository) LoadFromSource() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ppr *PairingPayloadRepository) loadKeys(keyStorePath string) error {
|
||||
ppr.keys = make(map[string][]byte)
|
||||
func (apr *AccountPayloadRepository) loadKeys(keyStorePath string) error {
|
||||
apr.keys = make(map[string][]byte)
|
||||
|
||||
fileWalker := func(path string, fileInfo os.FileInfo, err error) error {
|
||||
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)
|
||||
}
|
||||
|
||||
ppr.keys[fileInfo.Name()] = rawKeyFile
|
||||
apr.keys[fileInfo.Name()] = rawKeyFile
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -417,18 +425,18 @@ func (ppr *PairingPayloadRepository) loadKeys(keyStorePath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ppr *PairingPayloadRepository) StoreToSource() error {
|
||||
err := ppr.validateKeys(ppr.password)
|
||||
func (apr *AccountPayloadRepository) StoreToSource() error {
|
||||
err := apr.validateKeys(apr.password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ppr.storeKeys(ppr.keystorePath)
|
||||
err = apr.storeKeys(apr.keystorePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ppr.storeMultiAccount()
|
||||
err = apr.storeMultiAccount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -437,8 +445,8 @@ func (ppr *PairingPayloadRepository) StoreToSource() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ppr *PairingPayloadRepository) validateKeys(password string) error {
|
||||
for _, key := range ppr.keys {
|
||||
func (apr *AccountPayloadRepository) validateKeys(password string) error {
|
||||
for _, key := range apr.keys {
|
||||
k, err := keystore.DecryptKey(key, password)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -453,7 +461,7 @@ func (ppr *PairingPayloadRepository) validateKeys(password string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ppr *PairingPayloadRepository) storeKeys(keyStorePath string) error {
|
||||
func (apr *AccountPayloadRepository) storeKeys(keyStorePath string) error {
|
||||
if keyStorePath == "" {
|
||||
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
|
||||
// else we presume the provided keystore is valid
|
||||
if lastDir == "keystore" {
|
||||
if ppr.multiaccount == nil || ppr.multiaccount.KeyUID == "" {
|
||||
if apr.multiaccount == nil || apr.multiaccount.KeyUID == "" {
|
||||
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)
|
||||
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)
|
||||
if err := json.Unmarshal(data, &accountKey); err != nil {
|
||||
return fmt.Errorf("failed to read key file: %s", err)
|
||||
|
@ -492,6 +500,87 @@ func (ppr *PairingPayloadRepository) storeKeys(keyStorePath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ppr *PairingPayloadRepository) storeMultiAccount() error {
|
||||
return ppr.multiaccountsDB.SaveAccount(*ppr.multiaccount)
|
||||
func (apr *AccountPayloadRepository) storeMultiAccount() error {
|
||||
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)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package pairing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -44,8 +44,8 @@ type PayloadMarshallerSuite struct {
|
|||
|
||||
teardown func()
|
||||
|
||||
config1 *PairingPayloadManagerConfig
|
||||
config2 *PairingPayloadManagerConfig
|
||||
config1 *AccountPayloadManagerConfig
|
||||
config2 *AccountPayloadManagerConfig
|
||||
}
|
||||
|
||||
func setupTestDB(t *testing.T) (*multiaccounts.Database, func()) {
|
||||
|
@ -132,18 +132,18 @@ func (pms *PayloadMarshallerSuite) SetupTest() {
|
|||
err := db1.SaveAccount(expected)
|
||||
pms.Require().NoError(err)
|
||||
|
||||
pms.config1 = &PairingPayloadManagerConfig{
|
||||
pms.config1 = &AccountPayloadManagerConfig{
|
||||
DB: db1,
|
||||
PairingPayloadSourceConfig: PairingPayloadSourceConfig{
|
||||
PayloadSourceConfig: &PayloadSourceConfig{
|
||||
KeystorePath: keystore1,
|
||||
KeyUID: keyUID,
|
||||
Password: password,
|
||||
},
|
||||
}
|
||||
|
||||
pms.config2 = &PairingPayloadManagerConfig{
|
||||
pms.config2 = &AccountPayloadManagerConfig{
|
||||
DB: db2,
|
||||
PairingPayloadSourceConfig: PairingPayloadSourceConfig{
|
||||
PayloadSourceConfig: &PayloadSourceConfig{
|
||||
KeystorePath: keystore2,
|
||||
KeyUID: keyUID,
|
||||
Password: password,
|
||||
|
@ -156,11 +156,11 @@ func (pms *PayloadMarshallerSuite) TearDownTest() {
|
|||
}
|
||||
|
||||
func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LoadPayloads() {
|
||||
// Make a PairingPayload
|
||||
pp := new(PairingPayload)
|
||||
// Make a Payload
|
||||
pp := new(AccountPayload)
|
||||
|
||||
// Make and LoadFromSource PairingPayloadRepository 1
|
||||
ppr := NewPairingPayloadRepository(pp, pms.config1)
|
||||
ppr := NewAccountPayloadRepository(pp, pms.config1)
|
||||
err := ppr.LoadFromSource()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
|
@ -189,11 +189,11 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_LoadPayloads() {
|
|||
}
|
||||
|
||||
func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_MarshalToProtobuf() {
|
||||
// Make a PairingPayload
|
||||
pp := new(PairingPayload)
|
||||
// Make a Payload
|
||||
pp := new(AccountPayload)
|
||||
|
||||
// Make and LoadFromSource PairingPayloadRepository 1
|
||||
ppr := NewPairingPayloadRepository(pp, pms.config1)
|
||||
ppr := NewAccountPayloadRepository(pp, pms.config1)
|
||||
err := ppr.LoadFromSource()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
|
@ -218,11 +218,11 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_MarshalToProtobuf() {
|
|||
}
|
||||
|
||||
func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() {
|
||||
// Make a PairingPayload
|
||||
pp := new(PairingPayload)
|
||||
// Make a Payload
|
||||
pp := new(AccountPayload)
|
||||
|
||||
// Make and LoadFromSource PairingPayloadRepository 1
|
||||
ppr := NewPairingPayloadRepository(pp, pms.config1)
|
||||
ppr := NewAccountPayloadRepository(pp, pms.config1)
|
||||
err := ppr.LoadFromSource()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
|
@ -232,8 +232,8 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() {
|
|||
pb, err := ppm.MarshalToProtobuf()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// Make a PairingPayload
|
||||
pp2 := new(PairingPayload)
|
||||
// Make a Payload
|
||||
pp2 := new(AccountPayload)
|
||||
|
||||
// Make PairingPayloadMarshaller 2
|
||||
ppm2 := NewPairingPayloadMarshaller(pp2, pms.Logger)
|
||||
|
@ -271,11 +271,11 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_UnmarshalProtobuf() {
|
|||
}
|
||||
|
||||
func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
|
||||
// Make a PairingPayload
|
||||
pp := new(PairingPayload)
|
||||
// Make a Payload
|
||||
pp := new(AccountPayload)
|
||||
|
||||
// Make and LoadFromSource PairingPayloadRepository 1
|
||||
ppr := NewPairingPayloadRepository(pp, pms.config1)
|
||||
ppr := NewAccountPayloadRepository(pp, pms.config1)
|
||||
err := ppr.LoadFromSource()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
|
@ -285,8 +285,8 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
|
|||
pb, err := ppm.MarshalToProtobuf()
|
||||
pms.Require().NoError(err)
|
||||
|
||||
// Make a PairingPayload
|
||||
pp2 := new(PairingPayload)
|
||||
// Make a Payload
|
||||
pp2 := new(AccountPayload)
|
||||
|
||||
// Make PairingPayloadMarshaller 2
|
||||
ppm2 := NewPairingPayloadMarshaller(pp2, pms.Logger)
|
||||
|
@ -295,7 +295,7 @@ func (pms *PayloadMarshallerSuite) TestPayloadMarshaller_StorePayloads() {
|
|||
pms.Require().NoError(err)
|
||||
|
||||
// Make and Load PairingPayloadRepository 2
|
||||
ppr2 := NewPairingPayloadRepository(pp2, pms.config2)
|
||||
ppr2 := NewAccountPayloadRepository(pp2, pms.config2)
|
||||
|
||||
err = ppr2.StoreToSource()
|
||||
pms.Require().NoError(err)
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package server
|
||||
package pairing
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
@ -10,15 +10,18 @@ import (
|
|||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/api"
|
||||
"github.com/status-im/status-go/server"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
|
||||
"github.com/status-im/status-go/logutils"
|
||||
"github.com/status-im/status-go/multiaccounts"
|
||||
)
|
||||
|
||||
type PairingServer struct {
|
||||
Server
|
||||
type Server struct {
|
||||
server.Server
|
||||
PayloadManager
|
||||
rawMessagePayloadManager *RawMessagePayloadManager
|
||||
|
||||
pk *ecdsa.PublicKey
|
||||
ek []byte
|
||||
|
@ -35,8 +38,8 @@ type Config struct {
|
|||
Hostname string
|
||||
Mode Mode
|
||||
|
||||
// Payload management fields
|
||||
*PairingPayloadManagerConfig
|
||||
// AccountPayload management fields
|
||||
*AccountPayloadManagerConfig
|
||||
}
|
||||
|
||||
func makeCookieStore() (*sessions.CookieStore, error) {
|
||||
|
@ -55,10 +58,10 @@ func makeCookieStore() (*sessions.CookieStore, error) {
|
|||
return sessions.NewCookieStore(auth, enc), nil
|
||||
}
|
||||
|
||||
// NewPairingServer returns a *PairingServer init from the given *Config
|
||||
func NewPairingServer(config *Config) (*PairingServer, error) {
|
||||
logger := logutils.ZapLogger().Named("PairingServer")
|
||||
pm, err := NewPairingPayloadManager(config.EK, config.PairingPayloadManagerConfig, logger)
|
||||
// NewPairingServer returns a *Server init from the given *Config
|
||||
func NewPairingServer(backend *api.GethStatusBackend, config *Config) (*Server, error) {
|
||||
logger := logutils.ZapLogger().Named("Server")
|
||||
pm, err := NewAccountPayloadManager(config.EK, config.AccountPayloadManagerConfig, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -68,25 +71,32 @@ func NewPairingServer(config *Config) (*PairingServer, error) {
|
|||
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.Hostname,
|
||||
nil,
|
||||
logger,
|
||||
),
|
||||
pk: config.PK,
|
||||
ek: config.EK,
|
||||
mode: config.Mode,
|
||||
PayloadManager: pm,
|
||||
cookieStore: cs,
|
||||
pk: config.PK,
|
||||
ek: config.EK,
|
||||
mode: config.Mode,
|
||||
PayloadManager: pm,
|
||||
cookieStore: cs,
|
||||
rawMessagePayloadManager: rmpm,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MakeConnectionParams generates a *ConnectionParams based on the Server's current state
|
||||
func (s *PairingServer) MakeConnectionParams() (*ConnectionParams, error) {
|
||||
netIP := net.ParseIP(s.hostname)
|
||||
func (s *Server) MakeConnectionParams() (*ConnectionParams, error) {
|
||||
hostname := s.GetHostname()
|
||||
netIP := net.ParseIP(hostname)
|
||||
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()
|
||||
|
@ -97,40 +107,42 @@ func (s *PairingServer) MakeConnectionParams() (*ConnectionParams, error) {
|
|||
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 {
|
||||
case Receiving:
|
||||
return s.startReceivingAccountData()
|
||||
return s.startReceivingData()
|
||||
case Sending:
|
||||
return s.startSendingAccountData()
|
||||
return s.startSendingData()
|
||||
default:
|
||||
return fmt.Errorf("invalid server mode '%d'", s.mode)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PairingServer) startReceivingAccountData() error {
|
||||
s.SetHandlers(HandlerPatternMap{
|
||||
pairingReceive: handlePairingReceive(s),
|
||||
pairingChallenge: handlePairingChallenge(s),
|
||||
func (s *Server) startReceivingData() error {
|
||||
s.SetHandlers(server.HandlerPatternMap{
|
||||
pairingReceiveAccount: handlePairingReceive(s),
|
||||
pairingChallenge: handlePairingChallenge(s),
|
||||
pairingSyncDeviceReceive: handleParingSyncDeviceReceive(s),
|
||||
})
|
||||
return s.Start()
|
||||
}
|
||||
|
||||
func (s *PairingServer) startSendingAccountData() error {
|
||||
func (s *Server) startSendingData() error {
|
||||
err := s.Mount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.SetHandlers(HandlerPatternMap{
|
||||
pairingSend: challengeMiddleware(s, handlePairingSend(s)),
|
||||
pairingChallenge: handlePairingChallenge(s),
|
||||
s.SetHandlers(server.HandlerPatternMap{
|
||||
pairingSendAccount: challengeMiddleware(s, handlePairingSend(s)),
|
||||
pairingChallenge: handlePairingChallenge(s),
|
||||
pairingSyncDeviceSend: challengeMiddleware(s, handlePairingSyncDeviceSend(s)),
|
||||
})
|
||||
return s.Start()
|
||||
}
|
||||
|
||||
// MakeFullPairingServer generates a fully configured and randomly seeded PairingServer
|
||||
func MakeFullPairingServer(db *multiaccounts.Database, mode Mode, storeConfig PairingPayloadSourceConfig) (*PairingServer, error) {
|
||||
// MakeFullPairingServer generates a fully configured and randomly seeded Server
|
||||
func MakeFullPairingServer(backend *api.GethStatusBackend, mode Mode, storeConfig *PayloadSourceConfig) (*Server, error) {
|
||||
tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -142,7 +154,7 @@ func MakeFullPairingServer(db *multiaccounts.Database, mode Mode, storeConfig Pa
|
|||
return nil, err
|
||||
}
|
||||
|
||||
outboundIP, err := GetOutboundIP()
|
||||
outboundIP, err := server.GetOutboundIP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -151,8 +163,7 @@ func MakeFullPairingServer(db *multiaccounts.Database, mode Mode, storeConfig Pa
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPairingServer(&Config{
|
||||
return NewPairingServer(backend, &Config{
|
||||
// Things that can be generated, and CANNOT come from the app client (well they could be this is better)
|
||||
PK: &tlsKey.PublicKey,
|
||||
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
|
||||
Mode: mode,
|
||||
|
||||
PairingPayloadManagerConfig: &PairingPayloadManagerConfig{
|
||||
AccountPayloadManagerConfig: &AccountPayloadManagerConfig{
|
||||
// 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
|
||||
PairingPayloadSourceConfig: storeConfig,
|
||||
PayloadSourceConfig: storeConfig,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// StartUpPairingServer generates a PairingServer, starts the pairing server in the correct mode
|
||||
// and returns the ConnectionParams string to allow a PairingClient to make a successful connection.
|
||||
func StartUpPairingServer(db *multiaccounts.Database, mode Mode, configJSON string) (string, error) {
|
||||
var conf PairingPayloadSourceConfig
|
||||
// StartUpPairingServer generates a Server, starts the pairing server in the correct mode
|
||||
// and returns the ConnectionParams string to allow a Client to make a successful connection.
|
||||
func StartUpPairingServer(backend *api.GethStatusBackend, mode Mode, configJSON string) (string, error) {
|
||||
var conf PayloadSourceConfig
|
||||
err := json.Unmarshal([]byte(configJSON), &conf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ps, err := MakeFullPairingServer(db, mode, conf)
|
||||
ps, err := MakeFullPairingServer(backend, mode, &conf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
|
@ -1,12 +1,19 @@
|
|||
package server
|
||||
package pairing
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/status-im/status-go/server"
|
||||
)
|
||||
|
||||
func TestPairingServerSuite(t *testing.T) {
|
||||
|
@ -61,12 +68,14 @@ func (s *PairingServerSuite) TestPairingServer_StartPairing() {
|
|||
err = ccp.FromString(qr)
|
||||
s.Require().NoError(err)
|
||||
|
||||
c, err := NewPairingClient(ccp, nil)
|
||||
c, err := NewPairingClient(nil, ccp, &AccountPayloadManagerConfig{
|
||||
PayloadSourceConfig: &PayloadSourceConfig{KeystorePath: ""},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Compare cert values
|
||||
cert := c.serverCert
|
||||
cl := s.PS.cert.Leaf
|
||||
cl := s.PS.GetCert().Leaf
|
||||
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).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
|
||||
pm, err := NewMockEncryptOnlyPayloadManager(s.EphemeralAES)
|
||||
s.Require().NoError(err)
|
||||
|
@ -122,7 +131,9 @@ func (s *PairingServerSuite) sendingSetup() *PairingClient {
|
|||
err = ccp.FromString(qr)
|
||||
s.Require().NoError(err)
|
||||
|
||||
c, err := NewPairingClient(ccp, nil)
|
||||
c, err := NewPairingClient(nil, ccp, &AccountPayloadManagerConfig{
|
||||
PayloadSourceConfig: &PayloadSourceConfig{KeystorePath: ""},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Replace PairingClient.PayloadManager with a MockEncryptOnlyPayloadManager
|
||||
|
@ -187,3 +198,66 @@ func (s *PairingServerSuite) TestPairingServer_handlePairingChallengeMiddleware_
|
|||
s.Require().Error(err)
|
||||
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]))
|
||||
}
|
|
@ -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 := ¶ms.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
|
||||
}
|
|
@ -34,6 +34,18 @@ func (s *Server) getHost() string {
|
|||
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 {
|
||||
return fmt.Sprintf("%s:%d", s.hostname, s.MustGetPort())
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader, multiaccountsDB *mu
|
|||
s := &MediaServer{
|
||||
Server: NewServer(
|
||||
globalCertificate,
|
||||
localhost,
|
||||
Localhost,
|
||||
signal.SendMediaServerStarted,
|
||||
logutils.ZapLogger().Named("MediaServer"),
|
||||
),
|
||||
|
|
|
@ -30,14 +30,14 @@ func (s *ServerURLSuite) SetupTest() {
|
|||
s.SetupLoggerComponents()
|
||||
|
||||
s.server = &MediaServer{Server: Server{
|
||||
hostname: defaultIP.String(),
|
||||
hostname: DefaultIP.String(),
|
||||
portManger: newPortManager(s.Logger, nil),
|
||||
}}
|
||||
err := s.server.SetPort(1337)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.serverNoPort = &MediaServer{Server: Server{
|
||||
hostname: defaultIP.String(),
|
||||
hostname: DefaultIP.String(),
|
||||
portManger: newPortManager(s.Logger, nil),
|
||||
}}
|
||||
go func() {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"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/account"
|
||||
|
@ -68,3 +69,12 @@ func (s *Service) APIs() []rpc.API {
|
|||
func (s *Service) Protocols() []p2p.Protocol {
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -872,7 +872,7 @@ func (api *PublicAPI) SendPairInstallation(ctx context.Context) (*protocol.Messe
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue